Compare commits
16 Commits
e490bc99f5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d0abb8f6a | |||
| 9081354ce8 | |||
| 7a3463e8cc | |||
| 1311cb830b | |||
| 74c28c9344 | |||
| 9de4d77a63 | |||
| 8f4bc5b802 | |||
| de1e4b5b4f | |||
| df96d619b9 | |||
| 3a305e1bce | |||
| 7a3c36000c | |||
| 8e163be240 | |||
| 3d001c690f | |||
| 5433430628 | |||
| cdc0225ca8 | |||
| 9cce1d7fdb |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
rustflags = ["-C", "target-cpu=native", "-Cforce-frame-pointers=yes"]
|
||||
619
Cargo.lock
generated
619
Cargo.lock
generated
@@ -21,14 +21,31 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
name = "advent-of-code-2025"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"color-eyre",
|
||||
"criterion",
|
||||
"itertools 0.14.0",
|
||||
"pprof",
|
||||
"rayon",
|
||||
"test-log",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
@@ -38,18 +55,77 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
|
||||
dependencies = [
|
||||
"equator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -71,18 +147,46 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -123,6 +227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -131,8 +236,22 @@ version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.49"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -168,6 +287,21 @@ dependencies = [
|
||||
"tracing-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "cpp_demangle"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
@@ -180,7 +314,7 @@ dependencies = [
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
@@ -201,7 +335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -235,12 +369,57 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equator"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
||||
dependencies = [
|
||||
"equator-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equator-macro"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
@@ -251,6 +430,42 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "findshlibs"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.32.3"
|
||||
@@ -268,6 +483,18 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.5.2"
|
||||
@@ -280,6 +507,34 @@ version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inferno"
|
||||
version = "0.11.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"indexmap",
|
||||
"is-terminal",
|
||||
"itoa",
|
||||
"log",
|
||||
"num-format",
|
||||
"once_cell",
|
||||
"quick-xml",
|
||||
"rgb",
|
||||
"str_stack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.17"
|
||||
@@ -288,9 +543,15 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@@ -300,6 +561,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@@ -328,6 +598,21 @@ version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
@@ -349,6 +634,15 @@ version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
@@ -358,13 +652,34 @@ dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-format"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -391,6 +706,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell_polyfill"
|
||||
version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.5"
|
||||
@@ -437,6 +758,29 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pprof"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38a01da47675efa7673b032bf8efd8214f1917d89685e07e395ab125ea42b187"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
"criterion",
|
||||
"findshlibs",
|
||||
"inferno",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
"spin",
|
||||
"symbolic-demangle",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
@@ -446,6 +790,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
@@ -455,6 +808,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
@@ -504,12 +863,34 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
@@ -531,6 +912,12 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -583,12 +970,68 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "str_stack"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-common"
|
||||
version = "12.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d8046c5674ab857104bc4559d505f4809b8060d57806e45d49737c97afeb60"
|
||||
dependencies = [
|
||||
"debugid",
|
||||
"memmap2",
|
||||
"stable_deref_trait",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic-demangle"
|
||||
version = "12.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1accb6e5c4b0f682de907623912e616b44be1c9e725775155546669dbff720ec"
|
||||
dependencies = [
|
||||
"cpp_demangle",
|
||||
"rustc-demangle",
|
||||
"symbolic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
@@ -600,6 +1043,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-log"
|
||||
version = "0.2.19"
|
||||
@@ -621,6 +1077,26 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
@@ -717,12 +1193,34 @@ version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
@@ -733,6 +1231,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.106"
|
||||
@@ -788,21 +1295,52 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@@ -812,6 +1350,77 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.31"
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -4,19 +4,30 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
color-eyre = "0.6"
|
||||
tracing = "0.1"
|
||||
itertools = "0.14"
|
||||
rayon = "1.11"
|
||||
tracing = { version = "0.1", features = ["release_max_level_info"] }
|
||||
tracing-error = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5.1"
|
||||
criterion = "0.5"
|
||||
pprof = { version = "0.15" , features = ["flamegraph", "criterion"] }
|
||||
test-log = { version = "0.2", default-features = false, features = ["trace"] }
|
||||
|
||||
# Improve perf on debug builds: https://docs.rs/color-eyre/latest/color_eyre/#improving-perf-on-debug-builds
|
||||
[profile.dev.package.backtrace]
|
||||
opt-level = 3
|
||||
|
||||
# Gotta go fast
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = "fat"
|
||||
panic = "abort"
|
||||
debug = false # set true for profiling
|
||||
|
||||
[lib]
|
||||
name = "aoc"
|
||||
path = "src/lib.rs"
|
||||
|
||||
30
README.md
30
README.md
@@ -6,26 +6,46 @@ Rusty and over-engineered edition.
|
||||
|
||||
By request of AoC creator, I haven't included the input files (e.g. src/input/day01.txt). Log into the Advent of Code site and save the inputs there to the src/input/ folder.
|
||||
|
||||
Then to run: `cargo run`.
|
||||
To run all days: `cargo run`.
|
||||
|
||||
To run a specific day and/or part: `cargo run -- --day 1 --part 1`.
|
||||
|
||||
To run in super-fast prod mode: `cargo run --release`.
|
||||
|
||||
To run with debug logs enabled: `RUST_LOG=debug cargo run`.
|
||||
|
||||
To run the tests against included test input files: `RUST_LOG=debug cargo test -- --no-capture`.
|
||||
To run all the tests against included test input files: `RUST_LOG=debug cargo test -- --no-capture`.
|
||||
|
||||
To run the tests for a specific day and/or part: `RUST_LOG=debug cargo test day01::test::test_part1 -- --no-capture`.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Because this is over-engineered, I've included benchmarks for each day's solution. Because, why not?
|
||||
|
||||
To run benchmarks: `cargo bench`.
|
||||
To run benchmarks: `cargo bench`. Or a specific day and/or part: `cargo bench -- "day02 part1"`.
|
||||
|
||||
### Results
|
||||
|
||||
These were all run on my personal machine, an AMD Ryzen 9 3900X 12-Core Processor with 32 GB RAM, on Linux (WSL).
|
||||
These were all run on my personal machine, an AMD Ryzen 9 3900X 12-Core Processor with 32 GB RAM, on Linux (WSL), with nightly rust.
|
||||
|
||||
Timings are given as: [lower-bound **best-estimate** upper-bound]
|
||||
|
||||
| Day | Part 1 | Part 2 |
|
||||
|-----|--------|--------|
|
||||
| 01 | [101.34 µs **101.95 µs** 102.61 µs] | [105.90 µs **106.40 µs** 106.95 µs] |
|
||||
| 01 | [79.998 µs **80.349 µs** 80.721 µs] | [76.289 µs **76.616 µs** 76.950 µs] |
|
||||
| 02 | [2.0386 ms **2.0483 ms** 2.0584 ms] | [2.0823 ms **2.0918 ms** 2.1015 ms] |
|
||||
| 03 | [45.711 µs **45.937 µs** 46.177 µs] | [267.18 µs **267.95 µs** 268.75 µs] |
|
||||
| 04 | [143.40 µs **144.00 µs** 144.73 µs] | [1.6165 ms **1.6258 ms** 1.6355 ms] |
|
||||
| 05 | [187.25 µs **188.93 µs** 190.74 µs] | [63.809 µs **64.204 µs** 64.606 µs] |
|
||||
| 06 | [128.44 µs **129.44 µs** 130.52 µs] | [165.05 µs **165.70 µs** 166.36 µs] |
|
||||
| 07 | [83.803 µs **84.601 µs** 85.435 µs] | [81.456 µs **82.360 µs** 83.386 µs] |
|
||||
|
||||
## Profiling
|
||||
|
||||
To aid in increasing performance, the `pprof` crate can be used to generate flamegraphs off of the benchmarks.
|
||||
|
||||
To run profiling across all benchmarks: `cargo bench --bench aoc -- --profile-time 10`.
|
||||
|
||||
To run profile the benchmark for a specific day and/or part: `cargo bench --bench aoc -- --profile-time 30 "day01 part1"`.
|
||||
|
||||
The flamegraphs will be generated in `target/criterion/<benchmark_name>/profile/flamegraph.svg`.
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
use aoc::day01;
|
||||
use criterion::{Criterion, criterion_group, criterion_main};
|
||||
use pprof::criterion::{Output, PProfProfiler};
|
||||
|
||||
fn day01_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("day01 part1", |b| b.iter(|| day01::part1(day01::INPUT)));
|
||||
c.bench_function("day01 part2", |b| b.iter(|| day01::part2(day01::INPUT)));
|
||||
const PPROF_SAMPLING_FREQ_HZ: i32 = 997;
|
||||
|
||||
macro_rules! bench_days {
|
||||
($($day_num:literal => $day_mod:ident),* $(,)?) => {
|
||||
$(
|
||||
mod $day_mod {
|
||||
use super::*;
|
||||
use aoc::$day_mod;
|
||||
|
||||
pub fn part1(c: &mut Criterion) {
|
||||
c.bench_function(concat!(stringify!($day_mod), " part1"), |b| {
|
||||
b.iter(|| $day_mod::part1($day_mod::INPUT))
|
||||
});
|
||||
}
|
||||
|
||||
pub fn part2(c: &mut Criterion) {
|
||||
c.bench_function(concat!(stringify!($day_mod), " part2"), |b| {
|
||||
b.iter(|| $day_mod::part2($day_mod::INPUT))
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
criterion_group! {
|
||||
name = benches;
|
||||
config = Criterion::default().with_profiler(PProfProfiler::new(PPROF_SAMPLING_FREQ_HZ, Output::Flamegraph(None)));
|
||||
targets = $($day_mod::part1, $day_mod::part2),*
|
||||
}
|
||||
criterion_main!(benches);
|
||||
};
|
||||
}
|
||||
|
||||
criterion_group!(benches, day01_benchmark);
|
||||
criterion_main!(benches);
|
||||
aoc::all_days!(bench_days);
|
||||
|
||||
@@ -4,7 +4,7 @@ use color_eyre::{
|
||||
Result,
|
||||
eyre::{Error, eyre},
|
||||
};
|
||||
use tracing::{debug, info, instrument};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
@@ -98,18 +98,6 @@ pub fn part2(input: &str) -> Result<i32> {
|
||||
Ok(visited_zero_count)
|
||||
}
|
||||
|
||||
pub fn solve() -> Result<()> {
|
||||
info!("Day 1");
|
||||
{
|
||||
let _span = tracing::info_span!("day01").entered();
|
||||
let p1 = part1(INPUT)?;
|
||||
info!("Part 1: {}", p1);
|
||||
let p2 = part2(INPUT)?;
|
||||
info!("Part 2: {}", p2);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
1
src/day02/input/test1.txt
Normal file
1
src/day02/input/test1.txt
Normal file
@@ -0,0 +1 @@
|
||||
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124
|
||||
185
src/day02/mod.rs
Normal file
185
src/day02/mod.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use color_eyre::{
|
||||
Result,
|
||||
eyre::{Error, eyre},
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use tracing::{debug, debug_span, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct ProductRange(std::ops::RangeInclusive<i64>);
|
||||
|
||||
impl FromStr for ProductRange {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.split('-');
|
||||
let start = parts
|
||||
.next()
|
||||
.ok_or(eyre!("Invalid product range: no start"))?
|
||||
.parse::<i64>()?;
|
||||
let end = parts
|
||||
.next()
|
||||
.ok_or(eyre!("Invalid product range: no end"))?
|
||||
.parse::<i64>()?;
|
||||
if parts.next().is_some() {
|
||||
return Err(eyre!("Invalid product range: too many parts"));
|
||||
}
|
||||
Ok(ProductRange(start..=end))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ProductRange {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}-{}", self.0.start(), self.0.end())
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ProductRange {
|
||||
type Item = i64;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.0.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl ProductRange {
|
||||
fn invalid_ids(self) -> Result<Vec<i64>> {
|
||||
let start = *self.0.start();
|
||||
let end = *self.0.end();
|
||||
|
||||
let mut invalid_ids = Vec::new();
|
||||
|
||||
// Determine digit ranges we need to check
|
||||
let start_digits = if start == 0 { 1 } else { start.ilog10() + 1 };
|
||||
let end_digits = if end == 0 { 1 } else { end.ilog10() + 1 };
|
||||
|
||||
for num_digits in start_digits..=end_digits {
|
||||
// Skip odd digit counts - they're all valid
|
||||
if num_digits % 2 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let half_digits = num_digits / 2;
|
||||
let half_min = 10_i64.pow(half_digits - 1);
|
||||
let half_max = 10_i64.pow(half_digits) - 1;
|
||||
let multiplier = 10_i64.pow(half_digits) + 1; // Pre-calculate: half * multiplier = AABB pattern
|
||||
|
||||
// Generate all patterns where first half == second half
|
||||
for half in half_min..=half_max {
|
||||
let id = half * multiplier;
|
||||
if id >= start && id <= end {
|
||||
invalid_ids.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Invalid IDs: {:?}", &invalid_ids);
|
||||
Ok(invalid_ids)
|
||||
}
|
||||
|
||||
fn invalid_ids2(self) -> Result<Vec<i64>> {
|
||||
let start = *self.0.start();
|
||||
let end = *self.0.end();
|
||||
|
||||
let mut invalid_ids = std::collections::HashSet::new();
|
||||
|
||||
// Determine digit ranges we need to check
|
||||
let start_digits = if start == 0 { 1 } else { start.ilog10() + 1 };
|
||||
let end_digits = if end == 0 { 1 } else { end.ilog10() + 1 };
|
||||
|
||||
for num_digits in start_digits..=end_digits {
|
||||
// Try all possible chunk sizes that divide evenly
|
||||
for chunk_size in 1..=num_digits / 2 {
|
||||
if num_digits % chunk_size != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let num_chunks = num_digits / chunk_size;
|
||||
if num_chunks < 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate all possible chunk patterns
|
||||
let chunk_min = 10_i64.pow(chunk_size - 1);
|
||||
let chunk_max = 10_i64.pow(chunk_size) - 1;
|
||||
let chunk_power = 10_i64.pow(chunk_size);
|
||||
|
||||
// Calculate multiplier for repeating pattern
|
||||
// For ABCABC: chunk * (10^6 + 10^3 + 1) = chunk * 1001001
|
||||
let mut multiplier = 0_i64;
|
||||
for i in 0..num_chunks {
|
||||
multiplier += chunk_power.pow(i);
|
||||
}
|
||||
|
||||
for chunk in chunk_min..=chunk_max {
|
||||
let id = chunk * multiplier;
|
||||
|
||||
if id >= start && id <= end {
|
||||
invalid_ids.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let invalid_ids: Vec<i64> = invalid_ids.into_iter().collect();
|
||||
|
||||
debug!("Invalid IDs: {:?}", &invalid_ids);
|
||||
Ok(invalid_ids)
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<i64> {
|
||||
input
|
||||
.trim()
|
||||
.split(',')
|
||||
.collect::<Vec<_>>()
|
||||
.into_par_iter()
|
||||
.map(|range| {
|
||||
let _span = debug_span!("range", range = %range).entered();
|
||||
let range: ProductRange = range.parse()?;
|
||||
Ok(range.invalid_ids()?.iter().sum::<i64>())
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<i64> {
|
||||
input
|
||||
.trim()
|
||||
.split(',')
|
||||
.collect::<Vec<_>>()
|
||||
.into_par_iter()
|
||||
.map(|range| {
|
||||
let _span = debug_span!("range", range = %range).entered();
|
||||
let range: ProductRange = range.parse()?;
|
||||
Ok(range.invalid_ids2()?.iter().sum::<i64>())
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(part1(TEST_INPUT1).unwrap(), 1227775554);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(part2(TEST_INPUT1).unwrap(), 4174379265);
|
||||
}
|
||||
}
|
||||
4
src/day03/input/test1.txt
Normal file
4
src/day03/input/test1.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
987654321111111
|
||||
811111111111119
|
||||
234234234234278
|
||||
818181911112111
|
||||
72
src/day03/mod.rs
Normal file
72
src/day03/mod.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use color_eyre::Result;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Battery {
|
||||
column: usize,
|
||||
joltage: u8,
|
||||
}
|
||||
|
||||
impl Default for Battery {
|
||||
fn default() -> Self {
|
||||
Battery {
|
||||
column: 0,
|
||||
joltage: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn largest_output_joltage<const N: usize>(input: &str) -> Result<u64> {
|
||||
let mut output_joltage: u64 = 0;
|
||||
for line in input.trim().split('\n') {
|
||||
let mut batteries: [Battery; N] = [Battery::default(); N];
|
||||
let line_len = line.len();
|
||||
for (column, joltage) in line.bytes().map(|c| c - b'0').enumerate() {
|
||||
let min = N.saturating_sub(line_len - column);
|
||||
for i in min..N {
|
||||
if joltage > batteries[i].joltage {
|
||||
batteries[i].column = column;
|
||||
batteries[i].joltage = joltage;
|
||||
batteries[i + 1..].fill(Battery::default());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let line_joltage = batteries
|
||||
.iter()
|
||||
.fold(0u64, |acc, &b| acc * 10 + b.joltage as u64);
|
||||
debug!(line, line_joltage);
|
||||
output_joltage += line_joltage;
|
||||
}
|
||||
Ok(output_joltage)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<u64> {
|
||||
largest_output_joltage::<2>(input)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<u64> {
|
||||
largest_output_joltage::<12>(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(part1(TEST_INPUT1).unwrap(), 357);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(part2(TEST_INPUT1).unwrap(), 3121910778619);
|
||||
}
|
||||
}
|
||||
10
src/day04/input/test1.txt
Normal file
10
src/day04/input/test1.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
..@@.@@@@.
|
||||
@@@.@.@.@@
|
||||
@@@@@.@.@@
|
||||
@.@@@@..@.
|
||||
@@.@@@@.@@
|
||||
.@@@@@@@.@
|
||||
.@.@.@.@@@
|
||||
@.@@@.@@@@
|
||||
.@@@@@@@@.
|
||||
@.@.@@@.@.
|
||||
172
src/day04/mod.rs
Normal file
172
src/day04/mod.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use color_eyre::{Result, eyre::eyre};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
const ADJACENT_DELTAS: [(isize, isize); 8] = [
|
||||
(-1, -1),
|
||||
(-1, 0),
|
||||
(-1, 1),
|
||||
(0, -1),
|
||||
(0, 1),
|
||||
(1, -1),
|
||||
(1, 0),
|
||||
(1, 1),
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Paper,
|
||||
AccessiblePaper,
|
||||
}
|
||||
|
||||
impl FromStr for Cell {
|
||||
type Err = color_eyre::Report;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"." => Ok(Cell::Empty),
|
||||
"@" => Ok(Cell::Paper),
|
||||
"x" => Ok(Cell::AccessiblePaper),
|
||||
_ => Err(eyre!("Invalid cell character: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
fn from_byte(b: u8) -> Result<Self, color_eyre::Report> {
|
||||
match b {
|
||||
b'.' => Ok(Cell::Empty),
|
||||
b'@' => Ok(Cell::Paper),
|
||||
b'x' => Ok(Cell::AccessiblePaper),
|
||||
_ => Err(eyre!("Invalid cell byte: {}", b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Grid<const R: usize, const C: usize> {
|
||||
cells: [[Cell; C]; R],
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> FromStr for Grid<R, C> {
|
||||
type Err = color_eyre::Report;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut cells = [[Cell::Empty; C]; R];
|
||||
|
||||
for (row, line) in s.lines().enumerate() {
|
||||
for (col, byte) in line.bytes().enumerate() {
|
||||
cells[row][col] = Cell::from_byte(byte)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Grid { cells })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> Display for Grid<R, C> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for row in 0..R {
|
||||
for col in 0..C {
|
||||
let symbol = match self.cells[row][col] {
|
||||
Cell::Empty => '.',
|
||||
Cell::Paper => '@',
|
||||
Cell::AccessiblePaper => 'x',
|
||||
};
|
||||
write!(f, "{}", symbol)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> Grid<R, C> {
|
||||
fn count_accessible_papers(&mut self, replace_with: Cell) -> usize {
|
||||
let mut count = 0;
|
||||
for row in 0..R {
|
||||
for col in 0..C {
|
||||
if self.cells[row][col] != Cell::Paper {
|
||||
continue;
|
||||
}
|
||||
let mut adjacent_papers = 0;
|
||||
for &(dr, dc) in &ADJACENT_DELTAS {
|
||||
let adj_row = row as isize + dr;
|
||||
let adj_col = col as isize + dc;
|
||||
if adj_row >= 0
|
||||
&& adj_col >= 0
|
||||
&& (adj_row as usize) < R
|
||||
&& (adj_col as usize) < C
|
||||
{
|
||||
let adjacent = self.cells[adj_row as usize][adj_col as usize];
|
||||
if matches!(adjacent, Cell::Paper | Cell::AccessiblePaper) {
|
||||
adjacent_papers += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if adjacent_papers < 4 {
|
||||
self.cells[row][col] = replace_with;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_part1<const R: usize, const C: usize>(input: &str) -> Result<usize> {
|
||||
let mut grid = Grid::<R, C>::from_str(input)?;
|
||||
debug!("Parsed grid:\n{}", grid);
|
||||
let count = grid.count_accessible_papers(Cell::AccessiblePaper);
|
||||
debug!("Processed grid:\n{}", grid);
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<usize> {
|
||||
solve_part1::<135, 135>(input)
|
||||
}
|
||||
|
||||
fn solve_part2<const R: usize, const C: usize>(input: &str) -> Result<usize> {
|
||||
let mut grid = Grid::<R, C>::from_str(input)?;
|
||||
debug!("Parsed grid:\n{}", grid);
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let removed = grid.count_accessible_papers(Cell::Empty);
|
||||
if removed == 0 {
|
||||
break;
|
||||
}
|
||||
debug!("Removed {} in grid:\n{}", removed, grid);
|
||||
count += removed;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<usize> {
|
||||
solve_part2::<135, 135>(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(solve_part1::<10, 10>(TEST_INPUT1).unwrap(), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(solve_part2::<10, 10>(TEST_INPUT1).unwrap(), 43);
|
||||
}
|
||||
}
|
||||
11
src/day05/input/test1.txt
Normal file
11
src/day05/input/test1.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
3-5
|
||||
10-14
|
||||
16-20
|
||||
12-18
|
||||
|
||||
1
|
||||
5
|
||||
8
|
||||
11
|
||||
17
|
||||
32
|
||||
125
src/day05/mod.rs
Normal file
125
src/day05/mod.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
use color_eyre::{
|
||||
Result,
|
||||
eyre::{Error, eyre},
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct FreshRange(pub std::ops::RangeInclusive<i64>);
|
||||
|
||||
impl FromStr for FreshRange {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.split('-');
|
||||
let start = parts
|
||||
.next()
|
||||
.ok_or(eyre!("Invalid fresh range: no start"))?
|
||||
.parse::<i64>()?;
|
||||
let end = parts
|
||||
.next()
|
||||
.ok_or(eyre!("Invalid fresh range: no end"))?
|
||||
.parse::<i64>()?;
|
||||
if parts.next().is_some() {
|
||||
return Err(eyre!("Invalid fresh range: too many parts"));
|
||||
}
|
||||
Ok(FreshRange(start..=end))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FreshRange {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}-{}", self.0.start(), self.0.end())
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<usize> {
|
||||
let mut processing_ranges = true;
|
||||
let mut fresh_ranges = Vec::new();
|
||||
let mut fresh_ingredients = 0;
|
||||
for line in input.trim().lines() {
|
||||
if line.is_empty() {
|
||||
processing_ranges = false;
|
||||
continue;
|
||||
}
|
||||
if processing_ranges {
|
||||
let range = line.parse::<FreshRange>()?;
|
||||
debug!(range = %range);
|
||||
fresh_ranges.push(range);
|
||||
} else {
|
||||
let ingredient = line.parse::<i64>()?;
|
||||
debug!(ingredient);
|
||||
for range in &mut fresh_ranges {
|
||||
if range.0.contains(&ingredient) {
|
||||
fresh_ingredients += 1;
|
||||
debug!(fresh_ingredients, "fresh!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(fresh_ingredients)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<usize> {
|
||||
let mut fresh_ranges: Vec<Option<FreshRange>> = Vec::new();
|
||||
for line in input.trim().lines() {
|
||||
if line.is_empty() {
|
||||
break;
|
||||
}
|
||||
let range = line.parse::<FreshRange>()?;
|
||||
let mut overlap_range = range.clone();
|
||||
debug!(range = %range);
|
||||
for range_slot in fresh_ranges.iter_mut() {
|
||||
if let Some(existing_range) = range_slot {
|
||||
if overlap_range.0.end() < existing_range.0.start()
|
||||
|| overlap_range.0.start() > existing_range.0.end()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let start = *overlap_range.0.start().min(existing_range.0.start());
|
||||
let end = *overlap_range.0.end().max(existing_range.0.end());
|
||||
if start <= end {
|
||||
overlap_range = FreshRange(start..=end);
|
||||
debug!(overlap_range = %overlap_range, existing_range = %existing_range, "merging existing range");
|
||||
*range_slot = None; // this existing range is now completely merged with the current range
|
||||
}
|
||||
}
|
||||
}
|
||||
fresh_ranges.push(Some(overlap_range));
|
||||
}
|
||||
Ok(fresh_ranges
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|r| (r.0.end() - r.0.start() + 1) as usize)
|
||||
.sum())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(part1(TEST_INPUT1).unwrap(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(part2(TEST_INPUT1).unwrap(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2_triple_overlap() {
|
||||
assert_eq!(part2("3-4\n2-5\n1-6").unwrap(), 6);
|
||||
}
|
||||
}
|
||||
4
src/day06/input/test1.txt
Normal file
4
src/day06/input/test1.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
123 328 51 64
|
||||
45 64 387 23
|
||||
6 98 215 314
|
||||
* + * +
|
||||
212
src/day06/mod.rs
Normal file
212
src/day06/mod.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use color_eyre::{
|
||||
Result,
|
||||
eyre::{Context, Error, OptionExt, eyre},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Operation {
|
||||
Add,
|
||||
Multiply,
|
||||
}
|
||||
|
||||
impl FromStr for Operation {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"+" => Ok(Operation::Add),
|
||||
"*" => Ok(Operation::Multiply),
|
||||
_ => Err(eyre!("invalid operation: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
fn from_byte(byte: u8) -> Result<Self> {
|
||||
match byte {
|
||||
b'+' => Ok(Operation::Add),
|
||||
b'*' => Ok(Operation::Multiply),
|
||||
_ => Err(eyre!("invalid operation byte: {}", byte)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Problem {
|
||||
numbers: Vec<u64>,
|
||||
operation: Operation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CephalopodProblem {
|
||||
columns: Vec<Vec<u8>>,
|
||||
operation: Operation,
|
||||
width: u8,
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<u64> {
|
||||
let mut lines = input.trim().lines();
|
||||
let first_numbers: Vec<u64> = lines
|
||||
.next()
|
||||
.ok_or_eyre("no first line in input")?
|
||||
.split_whitespace()
|
||||
.map(|s| s.parse::<u64>().context("parsing number"))
|
||||
.collect::<Result<Vec<u64>>>()?;
|
||||
let mut problems = first_numbers
|
||||
.into_iter()
|
||||
.map(|n| Problem {
|
||||
numbers: vec![n],
|
||||
operation: Operation::Add,
|
||||
})
|
||||
.collect::<Vec<Problem>>();
|
||||
for line in lines {
|
||||
let first_byte = line.bytes().nth(0).ok_or_eyre("empty line in input")?;
|
||||
if matches!(first_byte, b'*' | b'+') {
|
||||
for (index, op_result) in line.split_whitespace().map(|s| s.parse()).enumerate() {
|
||||
let op = op_result?;
|
||||
problems[index].operation = op;
|
||||
}
|
||||
} else {
|
||||
for (index, num_result) in line.split_whitespace().map(|s| s.parse()).enumerate() {
|
||||
let num = num_result?;
|
||||
problems[index].numbers.push(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(problems
|
||||
.into_iter()
|
||||
.map(|problem| match problem.operation {
|
||||
Operation::Add => {
|
||||
let sum = problem.numbers.iter().sum::<u64>();
|
||||
debug!("{} = {}", problem.numbers.iter().join(" + "), sum);
|
||||
return sum;
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let product = problem.numbers.iter().product::<u64>();
|
||||
debug!("{} = {}", problem.numbers.iter().join(" * "), product);
|
||||
return product;
|
||||
}
|
||||
})
|
||||
.sum())
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<usize> {
|
||||
let mut lines = input.lines();
|
||||
let last_line = lines.next_back().ok_or_eyre("no last line in input")?;
|
||||
let mut problems = Vec::new();
|
||||
let mut spaces: u8 = 1;
|
||||
for byte in last_line.bytes().rev() {
|
||||
if byte.is_ascii_whitespace() {
|
||||
spaces += 1;
|
||||
} else {
|
||||
problems.push(CephalopodProblem {
|
||||
columns: (0..spaces).map(|_| Vec::new()).collect(),
|
||||
operation: Operation::from_byte(byte)?,
|
||||
width: spaces,
|
||||
});
|
||||
spaces = 0;
|
||||
}
|
||||
}
|
||||
debug!(last_problem = ?problems[0]);
|
||||
debug!(second_last_problem = ?problems[1]);
|
||||
problems.reverse();
|
||||
for line in lines {
|
||||
let bytes = line.as_bytes();
|
||||
let mut offset = 0;
|
||||
for i in 0..problems.len() {
|
||||
if offset + (problems[i].width as usize) > bytes.len() {
|
||||
return Err(eyre!("line too short for problem columns"));
|
||||
}
|
||||
for (cell_col, &byte) in bytes[offset..offset + problems[i].width as usize]
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
if !byte.is_ascii_whitespace() {
|
||||
problems[i].columns[cell_col].push(byte - b'0');
|
||||
}
|
||||
}
|
||||
offset += problems[i].width as usize + 1;
|
||||
}
|
||||
}
|
||||
debug!(first_problem = ?problems[0]);
|
||||
debug!(second_problem = ?problems[1]);
|
||||
Ok(problems
|
||||
.iter()
|
||||
.map(|problem| match problem.operation {
|
||||
Operation::Add => {
|
||||
let sum: usize = problem
|
||||
.columns
|
||||
.iter()
|
||||
.map(|col| {
|
||||
col.iter()
|
||||
.fold(0usize, |acc, &digit| acc * 10 + (digit as usize))
|
||||
})
|
||||
.sum();
|
||||
debug!(
|
||||
"{} = {}",
|
||||
problem
|
||||
.columns
|
||||
.iter()
|
||||
.map(|col| {
|
||||
col.iter()
|
||||
.fold(0usize, |acc, &digit| acc * 10 + (digit as usize))
|
||||
.to_string()
|
||||
})
|
||||
.join(" + "),
|
||||
sum
|
||||
);
|
||||
sum
|
||||
}
|
||||
Operation::Multiply => {
|
||||
let product: usize = problem
|
||||
.columns
|
||||
.iter()
|
||||
.map(|col| {
|
||||
col.iter()
|
||||
.fold(0usize, |acc, &digit| acc * 10 + (digit as usize))
|
||||
})
|
||||
.product();
|
||||
debug!(
|
||||
"{} = {}",
|
||||
problem
|
||||
.columns
|
||||
.iter()
|
||||
.map(|col| {
|
||||
col.iter()
|
||||
.fold(0usize, |acc, &digit| acc * 10 + (digit as usize))
|
||||
.to_string()
|
||||
})
|
||||
.join(" * "),
|
||||
product
|
||||
);
|
||||
product
|
||||
}
|
||||
})
|
||||
.sum())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(part1(TEST_INPUT1).unwrap(), 4277556);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(part2(TEST_INPUT1).unwrap(), 3263827);
|
||||
}
|
||||
}
|
||||
16
src/day07/input/test1.txt
Normal file
16
src/day07/input/test1.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
.......S.......
|
||||
...............
|
||||
.......^.......
|
||||
...............
|
||||
......^.^......
|
||||
...............
|
||||
.....^.^.^.....
|
||||
...............
|
||||
....^.^...^....
|
||||
...............
|
||||
...^.^...^.^...
|
||||
...............
|
||||
..^...^.....^..
|
||||
...............
|
||||
.^.^.^.^.^...^.
|
||||
...............
|
||||
180
src/day07/mod.rs
Normal file
180
src/day07/mod.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use std::{
|
||||
fmt::{Display, Formatter},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use color_eyre::{Result, eyre::eyre};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
pub const INPUT: &str = include_str!("input/input.txt");
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Source,
|
||||
Splitter,
|
||||
Beam(usize),
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl FromStr for Cell {
|
||||
type Err = color_eyre::Report;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"S" => Ok(Cell::Source),
|
||||
"^" => Ok(Cell::Splitter),
|
||||
"|" => Ok(Cell::Beam(1)),
|
||||
"." => Ok(Cell::Empty),
|
||||
_ => Err(eyre!("Invalid cell character: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
fn from_byte(b: u8) -> Result<Self, color_eyre::Report> {
|
||||
match b {
|
||||
b'S' => Ok(Cell::Source),
|
||||
b'^' => Ok(Cell::Splitter),
|
||||
b'|' => Ok(Cell::Beam(1)),
|
||||
b'.' => Ok(Cell::Empty),
|
||||
_ => Err(eyre!("Invalid cell byte: {}", b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Grid<const R: usize, const C: usize> {
|
||||
cells: [[Cell; C]; R],
|
||||
splits: usize,
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> FromStr for Grid<R, C> {
|
||||
type Err = color_eyre::Report;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut cells = [[Cell::Empty; C]; R];
|
||||
|
||||
for (row, line) in s.lines().enumerate() {
|
||||
for (col, byte) in line.bytes().enumerate() {
|
||||
cells[row][col] = Cell::from_byte(byte)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Grid { cells, splits: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> Display for Grid<R, C> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
for row in 0..R {
|
||||
for col in 0..C {
|
||||
let symbol = match self.cells[row][col] {
|
||||
Cell::Source => 'S',
|
||||
Cell::Splitter => '^',
|
||||
Cell::Beam(t) => (b'0' + t as u8) as char,
|
||||
Cell::Empty => '.',
|
||||
};
|
||||
write!(f, "{}", symbol)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<const R: usize, const C: usize> Grid<R, C> {
|
||||
fn emit_beam(&mut self) {
|
||||
for row in 1..R {
|
||||
for col in 0..C {
|
||||
if matches!(self.cells[row][col], Cell::Beam(_)) {
|
||||
// already filled by a splitter to the left
|
||||
continue;
|
||||
}
|
||||
if matches!(self.cells[row - 1][col], Cell::Source | Cell::Beam(_)) {
|
||||
let timelines = match self.cells[row - 1][col] {
|
||||
Cell::Source => 1,
|
||||
Cell::Beam(t) => t,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if self.cells[row][col] == Cell::Splitter {
|
||||
// assumption: splitter always has empty cells on left and right
|
||||
let left = self.cells[row][col - 1];
|
||||
let left_timelines = match self.cells[row - 1][col - 1] {
|
||||
Cell::Source => 1,
|
||||
Cell::Beam(t) => t,
|
||||
_ => 0,
|
||||
};
|
||||
if let Cell::Beam(left_t) = left {
|
||||
self.cells[row][col - 1] = Cell::Beam(timelines + left_t);
|
||||
} else {
|
||||
self.cells[row][col - 1] = Cell::Beam(timelines + left_timelines);
|
||||
}
|
||||
let right = self.cells[row][col + 1];
|
||||
let right_timelines = match self.cells[row - 1][col + 1] {
|
||||
Cell::Source => 1,
|
||||
Cell::Beam(t) => t,
|
||||
_ => 0,
|
||||
};
|
||||
if let Cell::Beam(right_t) = right {
|
||||
self.cells[row][col + 1] = Cell::Beam(timelines + right_t);
|
||||
} else {
|
||||
self.cells[row][col + 1] = Cell::Beam(timelines + right_timelines);
|
||||
}
|
||||
self.splits += 1;
|
||||
} else {
|
||||
self.cells[row][col] = Cell::Beam(timelines);
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("After row {}:\n{}", row, self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_part1<const R: usize, const C: usize>(input: &str) -> Result<usize> {
|
||||
let mut grid = Grid::<R, C>::from_str(input)?;
|
||||
grid.emit_beam();
|
||||
Ok(grid.splits)
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part1(input: &str) -> Result<usize> {
|
||||
solve_part1::<142, 141>(input)
|
||||
}
|
||||
|
||||
fn solve_part2<const R: usize, const C: usize>(input: &str) -> Result<usize> {
|
||||
let mut grid = Grid::<R, C>::from_str(input)?;
|
||||
grid.emit_beam();
|
||||
Ok(grid.cells[R - 1]
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
if let Cell::Beam(timelines) = c {
|
||||
timelines
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum::<usize>())
|
||||
}
|
||||
|
||||
#[instrument(skip(input))]
|
||||
pub fn part2(input: &str) -> Result<usize> {
|
||||
solve_part2::<142, 141>(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_log::test;
|
||||
|
||||
const TEST_INPUT1: &str = include_str!("input/test1.txt");
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
assert_eq!(solve_part1::<16, 15>(TEST_INPUT1).unwrap(), 21);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
assert_eq!(solve_part2::<16, 15>(TEST_INPUT1).unwrap(), 40);
|
||||
}
|
||||
}
|
||||
17
src/days.rs
Normal file
17
src/days.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Single source of truth for all implemented days
|
||||
// Add new days here and they'll automatically be available in both the runner and benchmarks
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! all_days {
|
||||
($macro_name:path) => {
|
||||
$macro_name! {
|
||||
1 => day01,
|
||||
2 => day02,
|
||||
3 => day03,
|
||||
4 => day04,
|
||||
5 => day05,
|
||||
6 => day06,
|
||||
7 => day07,
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1 +1,8 @@
|
||||
pub mod day01;
|
||||
pub mod day02;
|
||||
pub mod day03;
|
||||
pub mod day04;
|
||||
pub mod day05;
|
||||
pub mod day06;
|
||||
pub mod day07;
|
||||
pub mod days;
|
||||
|
||||
28
src/main.rs
28
src/main.rs
@@ -1,5 +1,8 @@
|
||||
pub mod day01;
|
||||
mod days;
|
||||
mod runner;
|
||||
|
||||
use aoc::*;
|
||||
use clap::Parser;
|
||||
use color_eyre::Result;
|
||||
use tracing::info;
|
||||
use tracing_error::ErrorLayer;
|
||||
@@ -7,6 +10,21 @@ use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "Advent of Code 2025")]
|
||||
#[command(about = "Solutions for Advent of Code 2025", long_about = None)]
|
||||
struct Args {
|
||||
/// Day to run (1-25). If not specified, runs all days.
|
||||
#[arg(short, long)]
|
||||
day: Option<u8>,
|
||||
|
||||
/// Part to run (1 or 2). If not specified, runs all parts.
|
||||
#[arg(short, long)]
|
||||
part: Option<u8>,
|
||||
}
|
||||
|
||||
all_days!(runner::days);
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
@@ -20,10 +38,12 @@ fn main() -> Result<()> {
|
||||
)
|
||||
.init();
|
||||
|
||||
let args = Args::parse();
|
||||
|
||||
info!("Advent of Code 2025");
|
||||
{
|
||||
let _span = tracing::info_span!("aoc").entered();
|
||||
day01::solve()?;
|
||||
}
|
||||
|
||||
run_days(args.day, args.part)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
57
src/runner.rs
Normal file
57
src/runner.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use color_eyre::Result;
|
||||
use tracing::info;
|
||||
|
||||
macro_rules! days {
|
||||
($($day_num:literal => $day_mod:ident),* $(,)?) => {
|
||||
pub fn run_days(day: Option<u8>, part: Option<u8>) -> Result<()> {
|
||||
match day {
|
||||
$(
|
||||
Some($day_num) => $crate::runner::run_day($day_num, part, $day_mod::part1, $day_mod::part2, $day_mod::INPUT)?,
|
||||
)*
|
||||
Some(d) => color_eyre::eyre::bail!("Day {} is not yet implemented", d),
|
||||
None => {
|
||||
$(
|
||||
$crate::runner::run_day($day_num, None, $day_mod::part1, $day_mod::part2, $day_mod::INPUT)?;
|
||||
)*
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use days;
|
||||
|
||||
pub fn run_day<T1, T2>(
|
||||
day: u8,
|
||||
part: Option<u8>,
|
||||
part1_fn: fn(&str) -> Result<T1>,
|
||||
part2_fn: fn(&str) -> Result<T2>,
|
||||
input: &str,
|
||||
) -> Result<()>
|
||||
where
|
||||
T1: std::fmt::Display,
|
||||
T2: std::fmt::Display,
|
||||
{
|
||||
info!("Day {}", day);
|
||||
let day_name = format!("{:02}", day);
|
||||
let _span = tracing::info_span!("day", day = %day_name).entered();
|
||||
|
||||
if part.is_none() || part == Some(1) {
|
||||
let result = part1_fn(input)?;
|
||||
info!("Part 1: {}", result);
|
||||
}
|
||||
|
||||
if part.is_none() || part == Some(2) {
|
||||
let result = part2_fn(input)?;
|
||||
info!("Part 2: {}", result);
|
||||
}
|
||||
|
||||
if let Some(p) = part {
|
||||
if p != 1 && p != 2 {
|
||||
color_eyre::eyre::bail!("Part {} is invalid. Must be 1 or 2.", p);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user