From 1d23bef4bd3d04372c4485a7200fc90911eda253 Mon Sep 17 00:00:00 2001 From: orion kindel Date: Tue, 2 Dec 2025 15:33:01 -0600 Subject: [PATCH] day 2 --- spago.lock | 152 +++++++++++++++++++++++++++++++++++++++++++++++ spago.yaml | 7 +++ src/Day2.purs | 34 +++++++++++ src/Day2/Id.purs | 104 ++++++++++++++++++++++++++++++++ src/Main.purs | 2 + 5 files changed, 299 insertions(+) create mode 100644 src/Day2.purs create mode 100644 src/Day2/Id.purs diff --git a/spago.lock b/spago.lock index 104f644..66b1233 100644 --- a/spago.lock +++ b/spago.lock @@ -5,22 +5,31 @@ "path": "./", "core": { "dependencies": [ + "arrays", "bifunctors", + "bigints", "console", "control", "effect", "either", "exceptions", + "filterable", "foldable-traversable", "lists", + "maybe", "parsing", + "partial", "prelude", + "st", + "strings", "transformers", "tuples" ], "build_plan": [ "arrays", + "assert", "bifunctors", + "bigints", "console", "const", "contravariant", @@ -31,6 +40,7 @@ "enums", "exceptions", "exists", + "filterable", "foldable-traversable", "functions", "functors", @@ -39,17 +49,23 @@ "integers", "invariant", "lazy", + "lcg", "lists", "maybe", "newtype", "nonempty", "nullable", "numbers", + "ordered-collections", "orders", "parsing", "partial", "prelude", "profunctor", + "quickcheck", + "quickcheck-laws", + "random", + "record", "refs", "safe-coerce", "st", @@ -6596,6 +6612,16 @@ "unsafe-coerce" ] }, + "assert": { + "type": "git", + "url": "https://github.com/purescript/purescript-assert.git", + "rev": "27c0edb57d2ee497eb5fab664f5601c35b613eda", + "dependencies": [ + "console", + "effect", + "prelude" + ] + }, "bifunctors": { "type": "git", "url": "https://github.com/purescript/purescript-bifunctors.git", @@ -6608,6 +6634,26 @@ "tuples" ] }, + "bigints": { + "type": "git", + "url": "https://github.com/purescript-contrib/purescript-bigints.git", + "rev": "e73f55b866e437c7bf04c7d262de7c205c47bbca", + "dependencies": [ + "arrays", + "assert", + "console", + "effect", + "foldable-traversable", + "integers", + "maybe", + "numbers", + "partial", + "prelude", + "quickcheck", + "quickcheck-laws", + "strings" + ] + }, "console": { "type": "git", "url": "https://github.com/purescript/purescript-console.git", @@ -6715,6 +6761,19 @@ "unsafe-coerce" ] }, + "filterable": { + "type": "git", + "url": "https://github.com/purescript/purescript-filterable.git", + "rev": "7c5b8c72779997f2b17d12ce478ff81e7ddda285", + "dependencies": [ + "arrays", + "either", + "foldable-traversable", + "identity", + "lists", + "ordered-collections" + ] + }, "foldable-traversable": { "type": "git", "url": "https://github.com/purescript/purescript-foldable-traversable.git", @@ -6819,6 +6878,19 @@ "prelude" ] }, + "lcg": { + "type": "git", + "url": "https://github.com/purescript/purescript-lcg.git", + "rev": "67c6c6483a563a59ae036d9dca0f1be2835326a5", + "dependencies": [ + "effect", + "integers", + "maybe", + "partial", + "prelude", + "random" + ] + }, "lists": { "type": "git", "url": "https://github.com/purescript/purescript-lists.git", @@ -6890,6 +6962,24 @@ "maybe" ] }, + "ordered-collections": { + "type": "git", + "url": "https://github.com/purescript/purescript-ordered-collections.git", + "rev": "313ec0c3556854cd4e4752a77a86b102fc41ad05", + "dependencies": [ + "arrays", + "foldable-traversable", + "gen", + "lists", + "maybe", + "partial", + "prelude", + "st", + "tailrec", + "tuples", + "unfoldable" + ] + }, "orders": { "type": "git", "url": "https://github.com/purescript/purescript-orders.git", @@ -6958,6 +7048,68 @@ "tuples" ] }, + "quickcheck": { + "type": "git", + "url": "https://github.com/purescript/purescript-quickcheck.git", + "rev": "bf5029f97e6c0d7552d3a08d2ab793a19e2c5e3d", + "dependencies": [ + "arrays", + "console", + "control", + "effect", + "either", + "enums", + "exceptions", + "foldable-traversable", + "gen", + "identity", + "integers", + "lazy", + "lcg", + "lists", + "maybe", + "newtype", + "nonempty", + "numbers", + "partial", + "prelude", + "record", + "st", + "strings", + "tailrec", + "transformers", + "tuples", + "unfoldable" + ] + }, + "quickcheck-laws": { + "type": "git", + "url": "https://github.com/purescript-contrib/purescript-quickcheck-laws.git", + "rev": "04f00fb78d88f38a2f2bb73b75f97ce5bf5624fc", + "dependencies": [ + "enums", + "quickcheck" + ] + }, + "random": { + "type": "git", + "url": "https://github.com/purescript/purescript-random.git", + "rev": "9540bc965a9596da02fefd9949418bb19c92533a", + "dependencies": [ + "effect", + "integers" + ] + }, + "record": { + "type": "git", + "url": "https://github.com/purescript/purescript-record.git", + "rev": "c89cd1ada6b636692571fc374196b1c39c4c9f70", + "dependencies": [ + "functions", + "prelude", + "unsafe-coerce" + ] + }, "refs": { "type": "git", "url": "https://github.com/purescript/purescript-refs.git", diff --git a/spago.yaml b/spago.yaml index 7c3a51e..66a2644 100644 --- a/spago.yaml +++ b/spago.yaml @@ -3,16 +3,23 @@ package: strict: true pedanticPackages: true dependencies: + - arrays - bifunctors + - bigints - console - control - effect - either - exceptions + - filterable - foldable-traversable - lists + - maybe - parsing + - partial - prelude + - st + - strings - transformers - tuples name: project diff --git a/src/Day2.purs b/src/Day2.purs new file mode 100644 index 0000000..3579f0b --- /dev/null +++ b/src/Day2.purs @@ -0,0 +1,34 @@ +module Day2 where + +import Prelude +import Data.String as String + +import Control.Monad.Error.Class (liftEither) +import Data.Bifunctor (lmap) +import Data.Filterable (filter) +import Data.Foldable (sum) +import Day2.Id as Id +import Effect (Effect) +import Effect.Console as Console +import Effect.Exception (error) + +main :: Effect Unit +main = do + Console.log "=== Day 2 ===" + run exampleInput <#> ("[exampl] " <> _) >>= Console.log + run fullInput <#> ("[actual] " <> _) >>= Console.log + Console.log "" + +run :: String -> Effect String +run input = do + idRanges <- liftEither $ lmap (error <<< show) $ Id.parseIds input + let + ids = Id.flat idRanges + invalidIds = filter (not Id.isValid) ids + pure $ show $ sum invalidIds + +exampleInput :: String +exampleInput = """11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124""" + +fullInput :: String +fullInput = String.trim """385350926-385403705,48047-60838,6328350434-6328506208,638913-698668,850292-870981,656-1074,742552-796850,4457-6851,138-206,4644076-4851885,3298025-3353031,8594410816-8594543341,396-498,1558-2274,888446-916096,12101205-12154422,2323146444-2323289192,37-57,101-137,46550018-46679958,79-96,317592-341913,495310-629360,33246-46690,14711-22848,1-17,2850-4167,3723700171-3723785996,190169-242137,272559-298768,275-365,7697-11193,61-78,75373-110112,425397-451337,9796507-9899607,991845-1013464,77531934-77616074""" diff --git a/src/Day2/Id.purs b/src/Day2/Id.purs new file mode 100644 index 0000000..3b07a15 --- /dev/null +++ b/src/Day2/Id.purs @@ -0,0 +1,104 @@ +module Day2.Id where + +import Prelude + +import Control.Alt ((<|>)) +import Control.Monad.Rec.Class (Step(..), tailRec) +import Control.Monad.ST as ST +import Control.Monad.ST.Ref as ST.Ref +import Data.Array (any) +import Data.Array as Array +import Data.Array.ST as ST.Array +import Data.BigInt (BigInt) +import Data.BigInt as BigInt +import Data.Either (Either, either) +import Data.Foldable (all) +import Data.List as List +import Data.Maybe (Maybe(..), fromJust) +import Data.String as String +import Data.Tuple.Nested (type (/\), (/\)) +import Parsing (ParseError(..), fail, region, runParser) as Parse +import Parsing (Parser) +import Parsing.Combinators (sepBy) as Parse +import Parsing.String (regex, string) as Parse +import Partial.Unsafe (unsafeCrashWith, unsafePartial) + +type Id = BigInt +type IdRange = Id /\ Id + +flat :: Array IdRange -> Array Id +flat ranges = do + start /\ end <- ranges + bigintRange start end + +parseIds :: String -> Either Parse.ParseError (Array IdRange) +parseIds = flip Parse.runParser parser + +parser :: Parser String (Array IdRange) +parser = + let + ctx :: forall a. String -> Parser String a -> Parser String a + ctx c = Parse.region \(Parse.ParseError m pos) -> Parse.ParseError (c <> " > " <> m) pos + + idRange = (/\) <$> (ctx "first id" $ bigint <* Parse.string "-") <*> (ctx "second id" bigint) + in + List.toUnfoldable <$> Parse.sepBy (ctx "id range" idRange) (Parse.string ",") + +isValid :: Id -> Boolean +isValid id = + let + idS = BigInt.toString id + len = String.length idS + candidates = Array.range 1 (len / 2) + isRepeated cand + | len `mod` cand /= 0 = false + | otherwise = + let + strs = strChunks cand idS + first = unsafePartial fromJust $ Array.head strs + rest = unsafePartial fromJust $ Array.tail strs + in + all (_ == first) rest + valid _ = not any isRepeated candidates + + -- part 1 + _naiive = + let + init = String.take (String.length idS / 2) idS + tail = String.drop (String.length idS / 2) idS + in + init /= tail + in + len <= 1 || valid unit + +bigint :: Parser String BigInt +bigint = do + section <- bigintRegex <|> Parse.fail "Expected Int" + case BigInt.fromString section of + Nothing -> Parse.fail "Expected Int" + Just x -> pure x + +bigintRegex :: Parser String String +bigintRegex = + either unsafeCrashWith identity $ Parse.regex pattern mempty + where + pattern = "[+-]?[0-9]+" + +-- might use a small amount of memory lol +bigintRange :: BigInt -> BigInt -> Array BigInt +bigintRange a b = ST.run do + n <- ST.Ref.new a + array <- ST.Array.new + void $ ST.Array.push a array + ST.while (ST.Ref.read n <#> (_ < b)) do + n' <- ST.Ref.modify (_ + BigInt.fromInt 1) n + ST.Array.push n' array + ST.Array.freeze array + +strChunks :: Int -> String -> Array String +strChunks n s = + let + go ("" /\ as) = Done as + go (s' /\ as) = Loop $ String.drop n s' /\ (as <> [String.take n s']) + in + tailRec go (s /\ []) diff --git a/src/Main.purs b/src/Main.purs index 8df515f..b526d4a 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -3,8 +3,10 @@ module Main where import Prelude import Day1 as Day1 +import Day2 as Day2 import Effect (Effect) main :: Effect Unit main = do Day1.main + Day2.main