diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs index 065ce64..94338da 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs @@ -1,7 +1,6 @@ namespace AdventOfCode2023 open System -open System.Collections.Generic [] type Range = @@ -14,12 +13,6 @@ type Range = [] module Day5 = - let remap (range : Range) (i : uint32) = - if range.SourceStart <= i && i - range.SourceStart < range.Len then - i + (range.DestStart - range.SourceStart) |> ValueSome - else - ValueNone - let parse (s : string) = use mutable lines = StringSplitEnumerator.make '\n' s lines.MoveNext () |> ignore @@ -75,11 +68,12 @@ module Day5 = for interval in map do if not hasRemappedThisLayer then - match remap interval remapped with - | ValueNone -> () - | ValueSome n -> + if + interval.SourceStart <= remapped + && remapped - interval.SourceStart < interval.Len + then hasRemappedThisLayer <- true - remapped <- n + remapped <- remapped + (interval.DestStart - interval.SourceStart) if remapped < best then best <- remapped @@ -89,22 +83,22 @@ module Day5 = // The input ranges are inclusive at both ends. // Returns any range we didn't map. let private split - (result : ResizeArray) - (start, finish) + (resultStarts : ResizeArray) + (resultEnds : ResizeArray) + start + finish (rangeFromLayer : Range) : (uint32 * uint32 * (uint32 * uint32) voption) voption = - let low, high = - rangeFromLayer.SourceStart, rangeFromLayer.SourceStart + rangeFromLayer.Len - 1ul + let low = rangeFromLayer.SourceStart + let high = rangeFromLayer.SourceStart + rangeFromLayer.Len - 1ul if low <= start then if finish <= high then // low ... start .. finish .. high // so the entire input range gets mapped down - result.Add ( - start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, - finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart - ) + resultStarts.Add (start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) + resultEnds.Add (finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) ValueNone elif start <= high then @@ -112,10 +106,8 @@ module Day5 = // so start .. high gets mapped down // and high + 1 .. finish stays where it is. // high < finish is already guaranteed by previous if block. - result.Add ( - start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, - high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart - ) + resultStarts.Add (start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) + resultEnds.Add (high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) ValueSome (high + 1ul, finish, ValueNone) else @@ -125,20 +117,16 @@ module Day5 = // so start .. low - 1 stays where it is // low .. high gets mapped down // and high + 1 .. finish stays where it is - result.Add ( - low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, - high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart - ) + resultStarts.Add (low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) + resultEnds.Add (high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) ValueSome (start, low - 1ul, ValueSome (high + 1ul, finish)) elif low < finish then // start .. low .. finish .. high // so start .. low - 1 stays where it is // and low .. finish gets mapped down - result.Add ( - low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, - finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart - ) + resultStarts.Add (low + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) + resultEnds.Add (finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart) ValueSome (start, low - 1ul, ValueNone) else @@ -147,18 +135,20 @@ module Day5 = let part2 (s : string) : uint32 = let seeds, mappings = parse s - let mutable intervals = ResizeArray () + let mutable intervalStarts = ResizeArray () + let mutable intervalEnds = ResizeArray () for i = 0 to (seeds.Length - 1) / 2 do - let t = seeds.[2 * i], seeds.[2 * i + 1] + seeds.[2 * i] - 1ul - intervals.Add t + intervalStarts.Add seeds.[2 * i] + intervalEnds.Add (seeds.[2 * i + 1] + seeds.[2 * i] - 1ul) - let mutable nextIntervals = ResizeArray () + let mutable nextIntervalStarts = ResizeArray () + let mutable nextIntervalEnds = ResizeArray () for mapLayer in mappings do let mutable i = 0 - while i < intervals.Count do + while i < intervalStarts.Count do // split interval according to every map let mutable allMoved = false let mutable currentRange = 0 @@ -166,30 +156,36 @@ module Day5 = while not allMoved && currentRange < mapLayer.Count do let range = mapLayer.[currentRange] // range is e.g. 50 98 2, i.e. "98-99 goes to 50-51" - match split nextIntervals intervals.[i] range with + match split nextIntervalStarts nextIntervalEnds intervalStarts.[i] intervalEnds.[i] range with | ValueNone -> allMoved <- true | ValueSome (start, finish, v) -> - intervals.[i] <- (start, finish) + intervalStarts.[i] <- start + intervalEnds.[i] <- finish match v with | ValueNone -> () - | ValueSome (start, finish) -> intervals.Add (start, finish) + | ValueSome (start, finish) -> + intervalStarts.Add start + intervalEnds.Add finish currentRange <- currentRange + 1 if not allMoved then - nextIntervals.Add intervals.[i] + nextIntervalStarts.Add intervalStarts.[i] + nextIntervalEnds.Add intervalEnds.[i] i <- i + 1 - let oldIntervals = intervals + let oldIntervals = intervalStarts oldIntervals.Clear () - intervals <- nextIntervals - nextIntervals <- oldIntervals + intervalStarts <- nextIntervalStarts + nextIntervalStarts <- oldIntervals - let mutable best = UInt32.MaxValue + let oldIntervals = intervalEnds + oldIntervals.Clear () + intervalEnds <- nextIntervalEnds + nextIntervalEnds <- oldIntervals - for i, _ in intervals do - best <- min best i - best + // SIMD go brrr + System.Linq.Enumerable.Min intervalStarts