From 713b139bc27f492aad5afaaed3afe8022c80286c Mon Sep 17 00:00:00 2001 From: Smaug123 Date: Tue, 5 Dec 2023 20:11:20 +0000 Subject: [PATCH] Better --- .../AdventOfCode2023.FSharp.Lib/Day4.fs | 8 +- .../AdventOfCode2023.FSharp.Lib/Day5.fs | 180 +++++++++--------- .../EfficientString.fs | 6 + .../AdventOfCode2023.FSharp/Program.fs | 5 + AdventOfCode2023.FSharp/Test/TestDay5.fs | 10 +- 5 files changed, 109 insertions(+), 100 deletions(-) diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day4.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day4.fs index 4a3f71b..69ea3d3 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day4.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day4.fs @@ -8,10 +8,10 @@ module Day4 = let inline parseByte (chars : ReadOnlySpan) : byte = Byte.Parse (chars, NumberStyles.None, NumberFormatInfo.InvariantInfo) - //let mutable answer = 0uy - //for c in chars do - // answer <- answer * 10uy + (byte c - 48uy) - //answer + //let mutable answer = 0uy + //for c in chars do + // answer <- answer * 10uy + (byte c - 48uy) + //answer let part1 (s : string) = use lines = StringSplitEnumerator.make '\n' s diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs index cc19451..065ce64 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/Day5.fs @@ -6,31 +6,34 @@ open System.Collections.Generic [] type Range = { - SourceStart : uint64 - DestStart : uint64 - Len : uint64 + SourceStart : uint32 + DestStart : uint32 + Len : uint32 } [] module Day5 = - let remap (range : Range) (i : uint64) = - if range.SourceStart <= i && i < range.SourceStart + range.Len then - i - range.SourceStart + range.DestStart - |> ValueSome + 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 + let seeds = use mutable line1 = StringSplitEnumerator.make' ' ' lines.Current StringSplitEnumerator.chomp "seeds:" &line1 let result = ResizeArray () + while line1.MoveNext () do - result.Add (UInt64.Parse line1.Current) + result.Add (UInt32.Parse line1.Current) + result.ToArray () + lines.MoveNext () |> ignore let mappings = ResizeArray () @@ -42,157 +45,150 @@ module Day5 = if not (isNull currentMapping) then mappings.Add currentMapping currentMapping <- null + else if isNull currentMapping then + currentMapping <- ResizeArray () else - if isNull currentMapping then - currentMapping <- ResizeArray () - else - use mutable line = StringSplitEnumerator.make' ' ' line - let destStart = StringSplitEnumerator.consumeU64 &line - let sourceStart = StringSplitEnumerator.consumeU64 &line - let rangeLen = StringSplitEnumerator.consumeU64 &line - { - SourceStart = sourceStart - DestStart = destStart - Len = rangeLen - } - |> currentMapping.Add + use mutable line = StringSplitEnumerator.make' ' ' line + let destStart = StringSplitEnumerator.consumeU32 &line + let sourceStart = StringSplitEnumerator.consumeU32 &line + let rangeLen = StringSplitEnumerator.consumeU32 &line + + { + SourceStart = sourceStart + DestStart = destStart + Len = rangeLen + } + |> currentMapping.Add seeds, mappings let part1 (s : string) = let seeds, mappings = parse s - let mutable best = UInt64.MaxValue + let mutable best = UInt32.MaxValue + for seed in seeds do let mutable remapped = seed + for map in mappings do let mutable hasRemappedThisLayer = false + for interval in map do if not hasRemappedThisLayer then match remap interval remapped with - | ValueNone -> - () + | ValueNone -> () | ValueSome n -> hasRemappedThisLayer <- true remapped <- n + if remapped < best then best <- remapped best - // The input ranges are inclusive at both ends. - let private split'' (ranges : (uint64 * uint64) ResizeArray) sourceStart sourceLen : unit = - let startsStack = ResizeArray () - let endsStack = ResizeArray () - startsStack.Add sourceStart - endsStack.Add (sourceStart + sourceLen - 1uL) - while startsStack.Count > 0 do - let mutable i = 0 - while i < ranges.Count do - let low, high = ranges.[i] - if startsStack.Count > 0 && low <= startsStack.[startsStack.Count - 1] && endsStack.[endsStack.Count - 1] <= high then - // splitting because: - // low ... start .. finish .. high - ranges.[i] <- (low, startsStack.[startsStack.Count - 1] - 1uL) - ranges.Add (startsStack.[startsStack.Count - 1], endsStack.[endsStack.Count - 1]) - ranges.Add (endsStack.[endsStack.Count - 1] + 1uL, high) - startsStack.RemoveAt (startsStack.Count - 1) - endsStack.RemoveAt (endsStack.Count - 1) - elif startsStack.Count > 0 && low <= startsStack.[startsStack.Count - 1] && startsStack.[startsStack.Count - 1] <= high then - // splitting because: - // low .. start .. high .. finish - ranges.[i] <- (low, startsStack.[startsStack.Count - 1] - 1uL) - ranges.Add (startsStack.[startsStack.Count - 1], high) - startsStack.[startsStack.Count - 1] <- high + 1uL - elif startsStack.Count > 0 && low > startsStack.[startsStack.Count - 1] && low < endsStack.[endsStack.Count - 1] && endsStack.[endsStack.Count - 1] <= high then - // splitting because: - // start .. low .. finish .. high - ranges.[i] <- (low, endsStack.[endsStack.Count - 1]) - ranges.Add (endsStack.[endsStack.Count - 1], high) - endsStack.[endsStack.Count - 1] <- low - 1uL - elif startsStack.Count > 0 && low > startsStack.[startsStack.Count - 1] && low < endsStack.[endsStack.Count - 1] && high < endsStack.[endsStack.Count - 1] then - // splitting because: - // start .. low .. high .. finish - startsStack.Add (high + 1uL) - endsStack.Add endsStack.[endsStack.Count - 1] - endsStack.[endsStack.Count - 2] <- low - 1uL - i <- i + 1 - // The input ranges are inclusive at both ends. // Returns any range we didn't map. - let private split (result : ResizeArray) (start, finish) (rangeFromLayer : Range) : (uint64 * uint64 * (uint64 * uint64) voption) voption = - let low, high = rangeFromLayer.SourceStart, rangeFromLayer.SourceStart + rangeFromLayer.Len - 1uL + let private split + (result : ResizeArray) + (start, finish) + (rangeFromLayer : Range) + : (uint32 * uint32 * (uint32 * uint32) voption) voption + = + let low, high = + rangeFromLayer.SourceStart, 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.SourceStart + rangeFromLayer.DestStart, finish - rangeFromLayer.SourceStart + rangeFromLayer.DestStart) + result.Add ( + start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, + finish + rangeFromLayer.DestStart - rangeFromLayer.SourceStart + ) + ValueNone elif start <= high then // low .. start .. high .. finish // 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.SourceStart + rangeFromLayer.DestStart, high - rangeFromLayer.SourceStart + rangeFromLayer.DestStart) - ValueSome (high + 1uL, finish, ValueNone) - else - ValueSome (start, finish, ValueNone) - else - if high <= finish then - // start .. low .. high .. finish - // 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.SourceStart + rangeFromLayer.DestStart, high - rangeFromLayer.SourceStart + rangeFromLayer.DestStart) - 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.SourceStart + rangeFromLayer.DestStart, finish - rangeFromLayer.SourceStart + rangeFromLayer.DestStart) - ValueSome (start, low - 1uL, ValueNone) - else - ValueSome (start, finish, ValueNone) + result.Add ( + start + rangeFromLayer.DestStart - rangeFromLayer.SourceStart, + high + rangeFromLayer.DestStart - rangeFromLayer.SourceStart + ) - let part2 (s : string) : uint64 = + ValueSome (high + 1ul, finish, ValueNone) + else + ValueSome (start, finish, ValueNone) + else if high <= finish then + // start .. low .. high .. finish + // 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 + ) + + 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 + ) + + ValueSome (start, low - 1ul, ValueNone) + else + ValueSome (start, finish, ValueNone) + + let part2 (s : string) : uint32 = let seeds, mappings = parse s let mutable intervals = ResizeArray () for i = 0 to (seeds.Length - 1) / 2 do - let t = seeds.[2 * i], seeds.[2 * i + 1] + seeds.[2 * i] - 1uL + let t = seeds.[2 * i], seeds.[2 * i + 1] + seeds.[2 * i] - 1ul intervals.Add t let mutable nextIntervals = ResizeArray () for mapLayer in mappings do let mutable i = 0 + while i < intervals.Count do // split interval according to every map let mutable allMoved = false let mutable currentRange = 0 + 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 - | ValueNone -> - allMoved <- true + | ValueNone -> allMoved <- true | ValueSome (start, finish, v) -> intervals.[i] <- (start, finish) + match v with | ValueNone -> () - | ValueSome (start, finish) -> - intervals.Add (start, finish) + | ValueSome (start, finish) -> intervals.Add (start, finish) + currentRange <- currentRange + 1 + if not allMoved then nextIntervals.Add intervals.[i] + i <- i + 1 + let oldIntervals = intervals oldIntervals.Clear () intervals <- nextIntervals nextIntervals <- oldIntervals - let mutable best = UInt64.MaxValue + let mutable best = UInt32.MaxValue + for i, _ in intervals do best <- min best i diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/EfficientString.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/EfficientString.fs index d9cf07b..fdfc2f4 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/EfficientString.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp.Lib/EfficientString.fs @@ -96,6 +96,12 @@ module StringSplitEnumerator = Int32.Parse e.Current + let consumeU32 (e : byref) : uint32 = + if not (e.MoveNext ()) then + failwith "expected an int, got nothing" + + UInt32.Parse e.Current + let consumeU64 (e : byref) : uint64 = if not (e.MoveNext ()) then failwith "expected an int, got nothing" diff --git a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs index 8a14ef2..94fa667 100644 --- a/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs +++ b/AdventOfCode2023.FSharp/AdventOfCode2023.FSharp/Program.fs @@ -24,6 +24,7 @@ module Program = let sw = Stopwatch.StartNew () printfn "=====Day 1=====" + do sw.Restart () let input = Path.Combine (dir.FullName, "day1.txt") |> File.ReadAllText @@ -38,6 +39,7 @@ module Program = Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms") printfn "=====Day 2=====" + do let input = Path.Combine (dir.FullName, "day2.txt") |> File.ReadAllText sw.Restart () @@ -52,6 +54,7 @@ module Program = Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms") printfn "=====Day 3=====" + do let input = Path.Combine (dir.FullName, "day3.txt") |> File.ReadAllBytes @@ -89,6 +92,7 @@ module Program = Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms") printfn "=====Day 4=====" + do let input = Path.Combine (dir.FullName, "day4.txt") |> File.ReadAllText sw.Restart () @@ -103,6 +107,7 @@ module Program = Console.Error.WriteLine ((1_000.0 * float sw.ElapsedTicks / float Stopwatch.Frequency).ToString () + "ms") printfn "=====Day 5=====" + do let input = Path.Combine (dir.FullName, "day5.txt") |> File.ReadAllText sw.Restart () diff --git a/AdventOfCode2023.FSharp/Test/TestDay5.fs b/AdventOfCode2023.FSharp/Test/TestDay5.fs index 818ce20..2739873 100644 --- a/AdventOfCode2023.FSharp/Test/TestDay5.fs +++ b/AdventOfCode2023.FSharp/Test/TestDay5.fs @@ -12,10 +12,12 @@ module TestDay5 = let sample = Assembly.getEmbeddedResource typeof.Assembly "day5.txt" [] - let part1Sample () = sample |> Day5.part1 |> shouldEqual 35uL + let part1Sample () = + sample |> Day5.part1 |> shouldEqual 35ul [] - let part2Sample () = sample |> Day5.part2 |> shouldEqual 46uL + let part2Sample () = + sample |> Day5.part2 |> shouldEqual 46ul [] let part1Actual () = @@ -28,7 +30,7 @@ module TestDay5 = Assert.Inconclusive () failwith "unreachable" - Day5.part1 s |> shouldEqual 806029445uL + Day5.part1 s |> shouldEqual 806029445ul [] let part2Actual () = @@ -41,4 +43,4 @@ module TestDay5 = Assert.Inconclusive () failwith "unreachable" - Day5.part2 s |> shouldEqual 59370572uL + Day5.part2 s |> shouldEqual 59370572ul