This commit is contained in:
Patrick Stevens
2022-12-21 10:06:41 +00:00
committed by GitHub
parent 258d770fe0
commit 6a08987e2a
14 changed files with 2908 additions and 8 deletions

View File

@@ -30,6 +30,7 @@
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day18.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day19.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day20.txt" />
<EmbeddedResource Include="..\AdventOfCode2022.Test\Inputs\Day21.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -2,5 +2,5 @@ namespace AdventOfCode2022.App
[<RequireQualifiedAccess>]
module Inputs =
let days = Array.init 20 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let days = Array.init 21 (fun day -> Assembly.readResource $"Day%i{day + 1}.txt")
let inline day (i : int) = days.[i - 1]

View File

@@ -74,6 +74,24 @@ type Benchmark16To20 () =
[<GlobalCleanup>]
member _.Cleanup () = Run.shouldWrite <- true
type Benchmark21To25 () =
[<GlobalSetup>]
member _.Setup () = Run.shouldWrite <- false
[<Params(21)>]
member val Day = 0 with get, set
[<Params(false, true)>]
member val IsPartOne = false with get, set
[<Benchmark>]
member this.Benchmark () : unit =
Run.allRuns.[this.Day - 1] (not this.IsPartOne) (Inputs.day this.Day)
[<GlobalCleanup>]
member _.Cleanup () = Run.shouldWrite <- true
module Program =
[<EntryPoint>]
@@ -89,6 +107,7 @@ module Program =
let _summary = BenchmarkRunner.Run<Benchmark6To10> config
let _summary = BenchmarkRunner.Run<Benchmark11To15> config
let _summary = BenchmarkRunner.Run<Benchmark16To20> config
let _summary = BenchmarkRunner.Run<Benchmark21To25> config
0
| _ ->

View File

@@ -288,6 +288,21 @@ module Run =
if shouldWrite then
printfn "%i" output
let day21 (partTwo : bool) (input : string) =
let day21 = StringSplitEnumerator.make '\n' input
if not partTwo then
let output = Day21.part1 day21
if shouldWrite then
printfn "%i" output
else
let output = Day21.part2 day21
if shouldWrite then
printfn "%i" output
let allRuns =
[|
day1
@@ -310,4 +325,5 @@ module Run =
day18
day19
day20
day21
|]

View File

@@ -28,6 +28,7 @@
<Compile Include="Day18.fs" />
<Compile Include="Day19.fs" />
<Compile Include="Day20.fs" />
<Compile Include="Day21.fs" />
<EmbeddedResource Include="Inputs\Day1.txt" />
<EmbeddedResource Include="Inputs\Day2.txt" />
<EmbeddedResource Include="Inputs\Day3.txt" />
@@ -48,6 +49,7 @@
<EmbeddedResource Include="Inputs\Day18.txt" />
<EmbeddedResource Include="Inputs\Day19.txt" />
<EmbeddedResource Include="Inputs\Day20.txt" />
<EmbeddedResource Include="Inputs\Day21.txt" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,49 @@
namespace AdventOfCode2022.Test
open NUnit.Framework
open FsUnitTyped
open AdventOfCode2022
[<TestFixture>]
module TestDay21 =
let input =
"""root: pppw + sjmn
dbpl: 5
cczh: sllz + lgvd
zczc: 2
ptdq: humn - dvpt
dvpt: 3
lfqf: 4
humn: 5
ljgn: 2
sjmn: drzm * dbpl
sllz: 4
pppw: cczh / lfqf
lgvd: ljgn * ptdq
drzm: hmdt - zczc
hmdt: 32
"""
[<Test>]
let ``Part 1, given`` () =
Day21.part1 (StringSplitEnumerator.make '\n' input) |> shouldEqual 152L
[<Test>]
let ``Part 1`` () =
let input = Assembly.readResource "Day21.txt"
Day21.part1 (StringSplitEnumerator.make '\n' input)
|> shouldEqual 54703080378102L
[<Test>]
let ``Part 2, given`` () =
Day21.part2 (StringSplitEnumerator.make '\n' input) |> shouldEqual 301L
[<Test>]
let ``Part 2`` () =
let input = Assembly.readResource "Day21.txt"
Day21.part2 (StringSplitEnumerator.make '\n' input)
|> shouldEqual 3952673930912L

File diff suppressed because it is too large Load Diff

View File

@@ -32,6 +32,7 @@
<Compile Include="Day18.fs" />
<Compile Include="Day19.fs" />
<Compile Include="Day20.fs" />
<Compile Include="Day21.fs" />
</ItemGroup>
<ItemGroup>

View File

@@ -21,13 +21,6 @@ module Day20 =
output.ToArray ()
[<Struct>]
type Day20Entry =
{
OriginalPos : int
Value : int
}
let inline clone< ^T when ^T : struct> (arr : ^T[]) : ^T[] =
let newArr = Array.zeroCreate arr.Length
Buffer.BlockCopy (arr, 0, newArr, 0, arr.Length * sizeof< ^T>)

200
AdventOfCode2022/Day21.fs Normal file
View File

@@ -0,0 +1,200 @@
namespace AdventOfCode2022
open System
open System.Collections.Generic
#if DEBUG
open Checked
#else
#nowarn "9"
#endif
[<RequireQualifiedAccess>]
module Day21 =
type Day21Operation =
| Add = 0
| Times = 1
| Divide = 2
| Minus = 3
let inline parseOp (c : char) =
match c with
| '+' -> Day21Operation.Add
| '*' -> Day21Operation.Times
| '/' -> Day21Operation.Divide
| '-' -> Day21Operation.Minus
| _ -> failwithf "bad op char: %c" c
type Day21Name = string
type Day21Input =
| Literal of int
| Operation of Day21Name * Day21Name * Day21Operation
| Calculated of float
/// Returns the name of the root node and human node, too.
let parse (line : StringSplitEnumerator) : Dictionary<Day21Name, Day21Input> * Day21Name * Day21Name =
use mutable enum = line.GetEnumerator ()
let output = Dictionary ()
while enum.MoveNext () do
if not (enum.Current.IsWhiteSpace ()) then
let colon = enum.Current.IndexOf ':'
let name = enum.Current.Slice(0, colon).ToString ()
let rhs = enum.Current.Slice(colon + 2).TrimEnd ()
let expr =
match Int32.TryParse rhs with
| true, v -> Day21Input.Literal v
| false, _ ->
let space1 = rhs.IndexOf ' '
let space2 = rhs.LastIndexOf ' '
let s1 = rhs.Slice(0, space1).ToString ()
let op =
let op = rhs.Slice (space1 + 1, space2 - space1 - 1)
if op.Length <> 1 then
failwithf "Expected exactly one char for op, got %i" op.Length
parseOp op.[0]
let s2 = rhs.Slice(space2 + 1).ToString ()
Day21Input.Operation (s1, s2, op)
output.Add (name, expr)
output, "root", "humn"
let inline compute (v1 : float) (v2 : float) (op : Day21Operation) : float =
match op with
| Day21Operation.Add -> v1 + v2
| Day21Operation.Times -> v1 * v2
| Day21Operation.Minus -> v1 - v2
| Day21Operation.Divide -> v1 / v2
| _ -> failwith "bad enum"
let rec evaluate (d : Dictionary<Day21Name, Day21Input>) (s : Day21Name) : float =
match d.[s] with
| Day21Input.Literal v ->
let result = float v
d.[s] <- Day21Input.Calculated result
result
| Day21Input.Calculated f -> f
| Day21Input.Operation (s1, s2, op) ->
let v1 = evaluate d s1
let v2 = evaluate d s2
let result = compute v1 v2 op
d.[s] <- Day21Input.Calculated result
result
let inline round (v : float) : int64 =
let rounded = int64 v
if abs (float rounded - v) > 0.00000001 then
failwith "not an int"
rounded
let part1 (lines : StringSplitEnumerator) : int64 =
let original, root, _ = parse lines
let result = evaluate original root
round result
type Day21Expr =
| Literal of float
| Variable
| Calc of Day21Expr * Day21Expr * Day21Operation
let rec convert
(human : Day21Name)
(key : Day21Name)
(d : Dictionary<Day21Name, Day21Input>)
(result : Dictionary<Day21Name, Day21Expr>)
: Day21Expr
=
match result.TryGetValue key with
| true, v -> v
| false, _ ->
if key = human then
let answer = Day21Expr.Variable
result.[human] <- answer
answer
else
match d.[key] with
| Day21Input.Literal v ->
let answer = Day21Expr.Literal (float v)
result.[key] <- answer
answer
| Day21Input.Calculated _ -> failwith "no never"
| Day21Input.Operation (s1, s2, op) ->
let v1 = convert human s1 d result
let v2 = convert human s2 d result
// One wave of simplification
let answer =
match v1, v2 with
| Day21Expr.Literal l1, Day21Expr.Literal l2 -> Day21Expr.Literal (compute l1 l2 op)
| _, _ -> Day21Expr.Calc (v1, v2, op)
result.[key] <- answer
answer
let part2 (lines : StringSplitEnumerator) : int64 =
let original, root, human = parse lines
let lhs, rhs =
match original.[root] with
| Day21Input.Literal _
| Day21Input.Calculated _ -> failwith "expected operation"
| Day21Input.Operation (s1, s2, _) -> s1, s2
let converted = Dictionary original.Count
let mutable lhs = convert human lhs original converted
let mutable rhs = convert human rhs original converted
let mutable answer = nan
while Double.IsNaN answer do
match lhs, rhs with
| Day21Expr.Literal l, Day21Expr.Variable
| Day21Expr.Variable, Day21Expr.Literal l -> answer <- l
| Day21Expr.Literal l, Day21Expr.Calc (v1, v2, op)
| Day21Expr.Calc (v1, v2, op), Day21Expr.Literal l ->
match v1, v2 with
| v1, Day21Expr.Literal v2 ->
lhs <- v1
rhs <-
match op with
| Day21Operation.Add -> Day21Expr.Literal (l - v2)
| Day21Operation.Times -> Day21Expr.Literal (l / v2)
| Day21Operation.Divide -> Day21Expr.Literal (l * v2)
| Day21Operation.Minus -> Day21Expr.Literal (l + v2)
| _ -> failwith "bad op"
| Day21Expr.Literal v1, v2 ->
lhs <- v2
rhs <-
match op with
| Day21Operation.Add -> Day21Expr.Literal (l - v1)
| Day21Operation.Times -> Day21Expr.Literal (l / v1)
| Day21Operation.Divide -> Day21Expr.Literal (v1 / l)
| Day21Operation.Minus -> Day21Expr.Literal (v1 - l)
| _ -> failwith "bad op"
| _, _ -> failwith "problem is too hard: had variables on both sides"
| Day21Expr.Variable, Day21Expr.Variable
| Day21Expr.Variable, Day21Expr.Calc _
| Day21Expr.Calc _, Day21Expr.Calc _
| Day21Expr.Calc _, Day21Expr.Variable -> failwith "one side is always a literal"
| Day21Expr.Literal _, Day21Expr.Literal _ -> failwith "can't both be literals"
round answer

View File

@@ -0,0 +1,14 @@
``` ini
BenchmarkDotNet=v0.13.2, OS=macOS 13.0.1 (22A400) [Darwin 22.1.0]
Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD DEBUG
DefaultJob : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD
```
| Method | Day | IsPartOne | Mean | Error | StdDev |
|---------- |---- |---------- |---------:|---------:|---------:|
| **Benchmark** | **21** | **False** | **678.3 μs** | **13.00 μs** | **15.48 μs** |
| **Benchmark** | **21** | **True** | **612.3 μs** | **9.40 μs** | **8.79 μs** |

View File

@@ -0,0 +1,3 @@
Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Day,IsPartOne,Mean,Error,StdDev
Benchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,0000000000,Empty,RyuJit,Arm64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 7.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,21,False,678.3 μs,13.00 μs,15.48 μs
Benchmark,DefaultJob,False,Default,Default,Default,Default,Default,Default,0000000000,Empty,RyuJit,Arm64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 7.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,21,True,612.3 μs,9.40 μs,8.79 μs
1 Method Job AnalyzeLaunchVariance EvaluateOverhead MaxAbsoluteError MaxRelativeError MinInvokeCount MinIterationTime OutlierMode Affinity EnvironmentVariables Jit Platform PowerPlanMode Runtime AllowVeryLargeObjects Concurrent CpuGroups Force HeapAffinitizeMask HeapCount NoAffinitize RetainVm Server Arguments BuildConfiguration Clock EngineFactory NuGetReferences Toolchain IsMutator InvocationCount IterationCount IterationTime LaunchCount MaxIterationCount MaxWarmupIterationCount MemoryRandomization MinIterationCount MinWarmupIterationCount RunStrategy UnrollFactor WarmupCount Day IsPartOne Mean Error StdDev
2 Benchmark DefaultJob False Default Default Default Default Default Default 0000000000 Empty RyuJit Arm64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 7.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 21 False 678.3 μs 13.00 μs 15.48 μs
3 Benchmark DefaultJob False Default Default Default Default Default Default 0000000000 Empty RyuJit Arm64 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c .NET 7.0 False True False True Default Default False False False Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default Default 16 Default 21 True 612.3 μs 9.40 μs 8.79 μs

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>AdventOfCode2022.App.Benchmark21To25-20221221-100214</title>
<style type="text/css">
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
td, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }
tr { background-color: #fff; border-top: 1px solid #ccc; }
tr:nth-child(even) { background: #f8f8f8; }
</style>
</head>
<body>
<pre><code>
BenchmarkDotNet=v0.13.2, OS=macOS 13.0.1 (22A400) [Darwin 22.1.0]
Apple M1 Max, 1 CPU, 10 logical and 10 physical cores
.NET SDK=7.0.100
[Host] : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD DEBUG
DefaultJob : .NET 7.0.0 (7.0.22.51805), Arm64 RyuJIT AdvSIMD
</code></pre>
<pre><code></code></pre>
<table>
<thead><tr><th>Method</th><th>Day</th><th>IsPartOne</th><th>Mean</th><th>Error</th><th>StdDev</th>
</tr>
</thead><tbody><tr><td>Benchmark</td><td>21</td><td>False</td><td>678.3 &mu;s</td><td>13.00 &mu;s</td><td>15.48 &mu;s</td>
</tr><tr><td>Benchmark</td><td>21</td><td>True</td><td>612.3 &mu;s</td><td>9.40 &mu;s</td><td>8.79 &mu;s</td>
</tr></tbody></table>
</body>
</html>

View File

@@ -9,3 +9,5 @@ BenchmarkDotNet reports:
* [Day 1 through Day 5](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark1To5-report-github.md).
* [Day 6 through Day 10](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark6To10-report-github.md).
* [Day 11 through Day 15](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark11To15-report-github.md).
* [Day 16 through Day 20](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark16To20-report-github.md).
* [Day 21 through Day 25](./BenchmarkDotNet.Artifacts/results/AdventOfCode2022.App.Benchmark21To25-report-github.md).