diff --git a/Cargo.lock b/Cargo.lock index e0509a6..d87b511 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -39,16 +40,17 @@ dependencies = [ "env_logger", "gcollections", "grid", - "indexmap", + "indexmap 2.1.0", "indicatif", "intervallum", - "itertools", + "itertools 0.12.0", "log", "num", "pest", "pest_derive", "petgraph", "regex", + "rustworkx-core", "strum", "test-log", ] @@ -98,7 +100,7 @@ dependencies = [ "ahash", "cached_proc_macro", "cached_proc_macro_types", - "hashbrown", + "hashbrown 0.14.3", "instant", "once_cell", "thiserror", @@ -156,6 +158,37 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -310,12 +343,29 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "grid" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.14.3" @@ -324,6 +374,7 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", + "rayon", ] [[package]] @@ -350,6 +401,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.1.0" @@ -357,7 +418,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.3", + "rayon", ] [[package]] @@ -406,6 +468,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.0" @@ -585,7 +656,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.1.0", ] [[package]] @@ -594,6 +665,22 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "priority-queue" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff39edfcaec0d64e8d0da38564fad195d2d51b680940295fcc307366e101e61" +dependencies = [ + "autocfg", + "indexmap 1.9.3", +] + [[package]] name = "proc-macro2" version = "1.0.70" @@ -612,6 +699,76 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-cond" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac2a28c5317e6d26ac87a8629c0eb362690ed1d739f4040e21cfaafdf04e6f8" +dependencies = [ + "either", + "itertools 0.10.5", + "rayon", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.10.2" @@ -669,6 +826,25 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "rustworkx-core" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72abf7976bc09a30391248b3c6509338b235c02b0e9b0bf8af686c289cad3f45" +dependencies = [ + "ahash", + "fixedbitset", + "hashbrown 0.14.3", + "indexmap 2.1.0", + "num-traits", + "petgraph", + "priority-queue", + "rand", + "rand_pcg", + "rayon", + "rayon-cond", +] + [[package]] name = "semver" version = "1.0.20" @@ -822,6 +998,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index ba44b91..4ccda34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ pest = "*" pest_derive = "*" petgraph = "*" regex = "*" +rustworkx-core = "*" strum = { version = "*", features = ["derive"] } [dev-dependencies] diff --git a/src/bin/25.rs b/src/bin/25.rs new file mode 100644 index 0000000..31ac973 --- /dev/null +++ b/src/bin/25.rs @@ -0,0 +1,91 @@ +/*! +# 2023 Day 25 - Snowverload +## Cutting connections + + + +This is another one that was trivial in Python (due to the `networkx` library), +and so applying the same solution in Rust using `rustworkx-core`. + +```python +from pathlib import Path +import networkx as nx + +def read(fn): + txt = Path(fn).read_text() + lines = [t.replace(":", "").split() for t in txt.splitlines()] + return {a[0]: frozenset(a[1:]) for a in lines} + +data = read("25data.txt") + +graph = nx.from_dict_of_lists(data) +cuts = nx.minimum_edge_cut(graph) +graph.remove_edges(cuts) + +a,b = (graph.subgraph(c) for c in nx.connected_components(graph)) +print(len(a), "*", len(b), "=", len(a) * len(b)) +``` +*/ + +use petgraph::graph::UnGraph; +use rustworkx_core::connectivity::stoer_wagner_min_cut; + +fn read(text: &str) -> UnGraph<&str, ()> { + let mut graph = UnGraph::new_undirected(); + let mut nodes = std::collections::HashMap::new(); + for line in text.lines() { + let (node, edges) = line.split_once(": ").unwrap(); + let node = *nodes.entry(node).or_insert_with(|| graph.add_node(node)); + for edge in edges.split(' ') { + let edge = *nodes.entry(edge).or_insert_with(|| graph.add_node(edge)); + graph.add_edge(node, edge, ()); + } + } + graph +} + +fn compute(text: &str) -> usize { + let graph = read(text); + let len = find_edges(&graph); + len * (graph.node_count() - len) +} + +fn find_edges(graph: &UnGraph<&str, ()>) -> usize { + let (cut, items) = stoer_wagner_min_cut(graph, |_| Ok::<_, ()>(1)) + .unwrap() + .unwrap(); + assert_eq!(cut, 3); + items.len() +} + +fn main() { + let text = std::fs::read_to_string("input/25.txt").unwrap(); + let result = compute(&text); + println!("Answer = {result}"); +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT: &str = "\ +jqt: rhn xhk nvd +rsh: frs pzl lsr +xhk: hfx +cmg: qnr nvd lhk bvb +rhn: xhk bvb hfx +bvb: xhk hfx +pzl: lsr hfx nvd +qnr: nvd +ntq: jqt hfx bvb xhk +nvd: lhk +lsr: lhk +rzs: qnr cmg lsr rsh +frs: qnr lhk lsr"; + + #[test] + fn test_first() { + let result = compute(INPUT); + assert_eq!(result, 54); + } +}