namespace AdventOfCode2023 open System open System.Globalization [] module Day15 = let hash (s : ReadOnlySpan) : int = let mutable v = 0 for c in s do v <- v + int (byte c) v <- (17 * v) % 256 v let part1 (s : string) = let s = s.AsSpan().TrimEnd () use chunks = StringSplitEnumerator.make' ',' s let mutable answer = 0 for chunk in chunks do answer <- answer + hash chunk answer let removeFirst<'a> (toRemove : 'a -> bool) (arr : ResizeArray<'a>) : unit = let mutable i = 0 while i < arr.Count do if toRemove arr.[i] then for j = i to arr.Count - 2 do arr.[j] <- arr.[j + 1] arr.RemoveAt (arr.Count - 1) i <- arr.Count i <- i + 1 let replace (withKey : 'a -> 'key) (key : 'key) (value : 'a) (arr : ResizeArray<'a>) : unit = let mutable i = 0 while i < arr.Count do if withKey arr.[i] = key then arr.[i] <- value i <- arr.Count i <- i + 1 if i < arr.Count + 1 then // no replacement was made arr.Add value let inline focusingPower (boxNumber : int) (arr : ResizeArray<_ * _>) : int = let mutable answer = 0 for i = 0 to arr.Count - 1 do answer <- answer + (boxNumber + 1) * (i + 1) * snd arr.[i] answer let part2 (s : string) = let s = s.AsSpan().TrimEnd () use chunks = StringSplitEnumerator.make' ',' s let lenses = Array.init 256 (fun _ -> ResizeArray ()) for chunk in chunks do if chunk.[chunk.Length - 1] = '-' then let label = chunk.Slice(0, chunk.Length - 1).ToString () removeFirst (fun (label2, _focalLength) -> label2 = label) lenses.[hash (label.AsSpan ())] else let equalsPos = chunk.IndexOf '=' let focalLength = Int32.Parse (chunk.Slice (equalsPos + 1), NumberStyles.None, CultureInfo.InvariantCulture) let label = chunk.Slice(0, equalsPos).ToString () replace fst label (label, focalLength) lenses.[hash (label.AsSpan ())] let mutable answer = 0 for i = 0 to 255 do answer <- answer + focusingPower i lenses.[i] answer