package main import ( "bufio" "errors" "fmt" "os" "strconv" "strings" ) type freshRange struct { start, end int } type freshDB struct { ranges []freshRange } func (r *freshRange) contains(c int) bool { return c >= r.start && c <= r.end } func (r *freshRange) nInRange() int { total := (r.end - r.start) + 1 return total } /* Check for an error, panic if there is */ func check(err error) { if err != nil { panic(err) } } func dbFromSlice(ranges []int) (db freshDB, err error) { if len(ranges)%2 != 0 { return freshDB{}, errors.New("Input slice not of even length") } db.ranges = make([]freshRange, 0, len(ranges)/2) for i := 0; i < len(ranges); i += 2 { db.ranges = append(db.ranges, freshRange{ranges[i], ranges[i+1]}) } return db, nil } /* Parse ranges out of a bufio.Scanner */ func parse(scanner *bufio.Scanner) (db freshDB, err error) { db.ranges = make([]freshRange, 0, 5) for scanner.Scan() { text := scanner.Text() if text == "" { return db, nil } startEnd := strings.Split(text, "-") if len(startEnd) != 2 { return db, errors.New("Fresh range does not contain exactly two numbers") } start, err := strconv.Atoi(startEnd[0]) if err != nil { return db, err } end, err := strconv.Atoi(startEnd[1]) if err != nil { return db, err } db.ranges = append(db.ranges, freshRange{start, end}) } return db, errors.New("No empty line in file to delimit fresh ranges") } func (db *freshDB) insert(toinsert freshRange) { for i := range db.ranges { r := &db.ranges[i] if r.contains(toinsert.start) { if toinsert.end > r.end { r.end = toinsert.end } return } if r.contains(toinsert.end) { if toinsert.start < r.start { r.start = toinsert.start } return } if toinsert.contains(r.start) && toinsert.contains(r.end) { r.start = toinsert.start r.end = toinsert.end return } } db.ranges = append(db.ranges, toinsert) } /* Collapse a list of fresh ingredient ranges to avoid overlaps */ func (db *freshDB) collapse() (before int, after int) { before = len(db.ranges) collapsed := freshDB{make([]freshRange, 0, len(db.ranges))} for _, r := range db.ranges { collapsed.insert(r) } db.ranges = collapsed.ranges after = len(db.ranges) return before, after } func main() { file, err := os.Open("five.txt") check(err) defer file.Close() scanner := bufio.NewScanner(file) db, err := parse(scanner) check(err) nRaw := len(db.ranges) for { before, after := db.collapse() fmt.Printf("Collapsed %d to %d ranges\n", before, after) if before == after { break } } potentialNFresh := 0 for _, r := range db.ranges { potentialNFresh += r.nInRange() } fmt.Printf( "N raw %d and consolidated %d. %d potential fresh ingredients.\n", nRaw, len(db.ranges), potentialNFresh, ) }