namespace AdventOfCode2023 open System open System.Collections.Generic open System.Globalization [] module Day12 = let rec solve (line : ReadOnlySpan) (groups : IReadOnlyList) (currentGroupIndex : int) = if line.Length = 0 then if currentGroupIndex = groups.Count then LanguagePrimitives.GenericOne else LanguagePrimitives.GenericZero else match line.[0] with | '#' -> if currentGroupIndex >= groups.Count then LanguagePrimitives.GenericZero else let mutable isOk = true for i = 1 to groups.[currentGroupIndex] - 1 do if isOk && (i >= line.Length || (line.[i] <> '#' && line.[i] <> '?')) then isOk <- false if not isOk then LanguagePrimitives.GenericZero else if groups.[currentGroupIndex] < line.Length then if line.[groups.[currentGroupIndex]] = '#' then LanguagePrimitives.GenericZero else solve (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) else solve ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) | '.' -> solve (line.Slice 1) groups currentGroupIndex | '?' -> let ifDot = solve (line.Slice 1) groups currentGroupIndex let ifHash = if currentGroupIndex >= groups.Count then LanguagePrimitives.GenericZero else let mutable isOk = true for i = 1 to groups.[currentGroupIndex] - 1 do if isOk && (i >= line.Length || (line.[i] <> '#' && line.[i] <> '?')) then isOk <- false if not isOk then LanguagePrimitives.GenericZero else if groups.[currentGroupIndex] < line.Length then if groups.[currentGroupIndex] < line.Length && line.[groups.[currentGroupIndex]] = '#' then LanguagePrimitives.GenericZero else solve (line.Slice (groups.[currentGroupIndex] + 1)) groups (currentGroupIndex + 1) else solve ReadOnlySpan<_>.Empty groups (currentGroupIndex + 1) ifDot + ifHash | _ -> if currentGroupIndex = groups.Count then LanguagePrimitives.GenericOne else LanguagePrimitives.GenericZero let part1 (s : string) = use mutable lines = StringSplitEnumerator.make '\n' s let mutable answer = 0uL let arr = ResizeArray () for line in lines do if not line.IsEmpty then arr.Clear () use ints = StringSplitEnumerator.make' ',' (line.Slice (line.IndexOf ' ' + 1)) for int in ints do arr.Add (Int32.Parse (int, NumberStyles.None, CultureInfo.InvariantCulture)) let solved = solve line arr 0 answer <- answer + solved answer let part2 (s : string) = use mutable lines = StringSplitEnumerator.make '\n' s let mutable answer = 0uL let arr = ResizeArray () for line in lines do if not line.IsEmpty then arr.Clear () let spaceIndex =line.IndexOf ' ' for _ = 0 to 4 do use ints = StringSplitEnumerator.make' ',' (line.Slice (spaceIndex + 1)) for int in ints do arr.Add (Int32.Parse (int, NumberStyles.None, CultureInfo.InvariantCulture)) let sliced = line.Slice(0, spaceIndex).ToString () let line = String.Concat (sliced, '?', sliced, '?', sliced, '?', sliced, '?', sliced) let solved = solve (line.AsSpan()) arr 0 printfn $"%s{line} : %i{solved}" answer <- answer + solved answer