From cc452c76a4e018ca5108a9187cf3dde563c4d663 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Mon, 1 Jan 2024 11:32:43 +0000 Subject: [PATCH] fix day-5 p2 --- day-5/data.go | 25 +++++----- day-5/one.go | 33 ++++++------- day-5/queue.go | 51 ++++++++++++++++++++ day-5/solution.go | 7 +-- day-5/two.go | 115 ++++++++++++++++++++++++---------------------- day-5/util.go | 35 ++++++-------- 6 files changed, 156 insertions(+), 110 deletions(-) create mode 100644 day-5/queue.go diff --git a/day-5/data.go b/day-5/data.go index d789ba4..f7cb169 100644 --- a/day-5/data.go +++ b/day-5/data.go @@ -1,19 +1,22 @@ package main -type Data struct { +type data struct { dest int source int offset int } -var seeds = []int{} - -var dataMap = map[string][]Data{ - "seed-to-soil": make([]Data, 0), - "soil-to-fertilizer": make([]Data, 0), - "fertilizer-to-water": make([]Data, 0), - "water-to-light": make([]Data, 0), - "light-to-temperature": make([]Data, 0), - "temperature-to-humidity": make([]Data, 0), - "humidity-to-location": make([]Data, 0), +func newData(nums ...int) data { + return data{dest: nums[0], source: nums[1], offset: nums[2]} } + +func (d data) transform(start, end int) (int, int) { + f := func(x int) int { + return x - d.source + d.dest + } + return f(start), f(end - 1) +} + +var dataMap = map[string][]data{} + +var identifiers = []string{} diff --git a/day-5/one.go b/day-5/one.go index 796ef11..87c4289 100644 --- a/day-5/one.go +++ b/day-5/one.go @@ -2,43 +2,36 @@ package main import ( "math" - - log "github.com/sirupsen/logrus" ) -var identifiers = []string{"seed-to-soil", "soil-to-fertilizer", "fertilizer-to-water", "water-to-light", "light-to-temperature", "temperature-to-humidity", "humidity-to-location"} +var seeds = []int{} +// next recursively calculates each destination for each set of data in dataMap func next(i int, datapoint int) int { if i == len(identifiers) { return datapoint } - dest := func() int { - datas := dataMap[identifiers[i]] - dest := 0 - for _, data := range datas { - if datapoint >= data.source && datapoint <= data.source+data.offset { - dest = data.dest + (datapoint - data.source) - break - } + dest := 0 + for _, data := range dataMap[identifiers[i]] { + if datapoint >= data.source && datapoint <= data.source+data.offset { + dest = data.dest + (datapoint - data.source) + break } - if dest == 0 { - dest = datapoint - } - return dest - }() - //log.Debug(identifiers[i], ": ", dest) + } + if dest == 0 { + dest = datapoint + } return next(i+1, dest) } -// one returns the lowest location +// one returns the lowest location for any seed in seeds func one(lines []string) (int, error) { - lowest := math.MaxInt parseLines(lines) + lowest := math.MaxInt for _, seed := range seeds { location := next(0, seed) - log.Info(location) if location < lowest { lowest = location } diff --git a/day-5/queue.go b/day-5/queue.go new file mode 100644 index 0000000..b7d4733 --- /dev/null +++ b/day-5/queue.go @@ -0,0 +1,51 @@ +package main + +import ( + log "github.com/sirupsen/logrus" +) + +// queue represents a FIFO queue of bounds +type queue struct { + size int + elements []bound +} + +// newQueue returns a queue type +// it initializes the queue size and elements +func newQueue(size int, elems []bound) queue { + return queue{size: size, elements: elems} +} + +// enqueue adds an item to the queue +func (q *queue) enqueue(elem bound) { + if q.size >= 0 && q.getLength() == q.size { + log.Info("Queue Overflow") + return + } + q.elements = append(q.elements, elem) +} + +// dequeue pops an element from the start of the queue +func (q *queue) dequeue() bound { + if q.isEmpty() { + log.Info("Queue UnderFlow") + return bound{} + } + element := q.elements[0] + if q.getLength() == 1 { + q.elements = nil + return element + } + q.elements = q.elements[1:] + return element +} + +// getLength returns the number of items in the queue +func (q *queue) getLength() int { + return len(q.elements) +} + +// isEmpty returns true if no items are in the queue +func (q *queue) isEmpty() bool { + return len(q.elements) == 0 +} diff --git a/day-5/solution.go b/day-5/solution.go index fc95902..01f9446 100644 --- a/day-5/solution.go +++ b/day-5/solution.go @@ -7,7 +7,7 @@ import ( ) func init() { - log.SetLevel(log.DebugLevel) + log.SetLevel(log.InfoLevel) } func main() { @@ -19,9 +19,6 @@ func main() { } fmt.Printf("solution one: %d\n", ans) - ans, err = two(lines) - if err != nil { - log.Fatal(err) - } + ans = two(lines) fmt.Printf("solution two: %d\n", ans) } diff --git a/day-5/two.go b/day-5/two.go index 8a47f1a..1fc2ed6 100644 --- a/day-5/two.go +++ b/day-5/two.go @@ -1,78 +1,85 @@ package main import ( - "fmt" "math" "sync" - "sync/atomic" - "time" - - log "github.com/sirupsen/logrus" ) -type WaitGroupCount struct { - sync.WaitGroup - count int64 -} +var wg sync.WaitGroup -func (wg *WaitGroupCount) Add(delta int) { - atomic.AddInt64(&wg.count, int64(delta)) - wg.WaitGroup.Add(delta) -} - -func (wg *WaitGroupCount) Done() { - atomic.AddInt64(&wg.count, -1) - wg.WaitGroup.Done() -} - -func (wg *WaitGroupCount) GetCount() int { - return int(atomic.LoadInt64(&wg.count)) -} - -var wg = WaitGroupCount{} - -//var checked = make([]bound, 0) +const UNLIMITED = -1 +// bound represents the lower and upper limits of a single range type bound struct { - start int - end int + lower, upper int } -var bounds = []bound{} +// newBound returns a bound type +func newBound(lower, upper int) bound { + return bound{lower: lower, upper: upper} +} -// two returns the lowest location -func two(lines []string) (int, error) { - lowest := math.MaxInt - - for i := 0; i < len(seeds); i += 2 { - bounds = append(bounds, bound{start: seeds[i], end: seeds[i] + seeds[i+1]}) +// nextTransform recursively calculates each new set of seed ranges for each set of data in dataMap +func nextTransform(i int, in []bound) []bound { + if i == len(identifiers) { + return in } - startTime := time.Now() + q := newQueue(UNLIMITED, in) + in = make([]bound, 0) + for !q.isEmpty() { + r := q.dequeue() - go func() { - for { - elapsed := time.Since(startTime) - fmt.Printf("[%s] wg count: %d\n", elapsed.Round(time.Second), wg.GetCount()) - time.Sleep(time.Second) - } - }() + hasOverlap := func() bool { + for _, data := range dataMap[identifiers[i]] { + start := max(r.lower, data.source) + end := min(r.upper, data.source+data.offset) - for _, bound := range bounds { - wg.Add(1) - go func(start int, end int) { - defer wg.Done() - for i := start; i < end; i++ { - location := next(0, i) - if location < lowest { - lowest = location + if isOverlapping(start, end) { + // add new seed range + in = append(in, newBound(data.transform(start, end))) + + // append unmatched portions of seed range back into queue + if start > r.lower { + q.enqueue(newBound(r.lower, start-1)) + } + if r.upper > end { + q.enqueue(newBound(end, r.upper-1)) + } + return true } } - }(bound.start, bound.end) - log.Info(bound, " completed") + return false + }() + + // there was no overlap, add the seed range as is + if !hasOverlap { + in = append(in, r) + } + } + + return nextTransform(i+1, in) +} + +// two returns the lowest location for any seed in seedRanges +func two(lines []string) int { + var seedRanges = []bound{} + for i := 0; i < len(seeds); i += 2 { + wg.Add(1) + go func(i int) { + defer wg.Done() + seedRanges = append(seedRanges, nextTransform(0, []bound{newBound(seeds[i], seeds[i]+seeds[i+1]-1)})...) + }(i) } wg.Wait() - return lowest - 1, nil // returning a value one too high? not sure why. + lowest := math.MaxInt + for _, r := range seedRanges { + if r.lower < lowest { + lowest = r.lower + } + } + + return lowest } diff --git a/day-5/util.go b/day-5/util.go index c6503e3..c5395d5 100644 --- a/day-5/util.go +++ b/day-5/util.go @@ -2,12 +2,13 @@ package main import ( "bufio" - "log" "os" "regexp" "strconv" "strings" "unicode" + + log "github.com/sirupsen/logrus" ) // readlines reads lines from stdin. @@ -27,28 +28,27 @@ func readlines() []string { return lines } +var r = regexp.MustCompile(`([\w-]+) map[:]`) + // parseLines parses input to a data map func parseLines(lines []string) { - var _regex_identifier = regexp.MustCompile(`(?P[\w-]+) map[:]`) - f := func(c rune) bool { return !unicode.IsDigit(c) } - for i := 0; i < len(lines); i++ { - if i == 0 { - seeds = convertToInts(strings.FieldsFunc(lines[i], f)) - continue - } + seeds = convertToInts(strings.FieldsFunc(lines[0], f)) - m := _regex_identifier.FindStringSubmatch(lines[i]) + for i := 2; i < len(lines); i++ { + m := r.FindStringSubmatch(lines[i]) if len(m) == 2 { + identifiers = append(identifiers, m[1]) for i = i + 1; i < len(lines) && len(lines[i]) != 0; i++ { - nums := convertToInts(strings.FieldsFunc(lines[i], f)) - dataMap[m[1]] = append(dataMap[m[1]], Data{dest: nums[0], source: nums[1], offset: nums[2]}) + d := newData(convertToInts(strings.FieldsFunc(lines[i], f))...) + dataMap[m[1]] = append(dataMap[m[1]], d) } } } + log.Debug(identifiers) } // convertToInts converts a string representing ints to an array of ints @@ -61,13 +61,8 @@ func convertToInts(data []string) []int { return nums } -/* -func isChecked(i int) bool { - for _, bound := range checked { - if i > bound.start && i < bound.end { - return true - } - } - return false +// isOverlapping returns true if two ranges overlap. see: +// https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/325964#325964 +func isOverlapping(maxStart, minEnd int) bool { + return maxStart < minEnd } -*/