diff --git a/21/Cargo.lock b/21/Cargo.lock index 1dfee22..2e61ea1 100644 --- a/21/Cargo.lock +++ b/21/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" + [[package]] name = "cfg-if" version = "1.0.0" @@ -64,8 +79,10 @@ name = "day21" version = "0.1.0" dependencies = [ "indicatif", - "petgraph", + "polyfit-rs", + "primes", "rayon", + "test-case", ] [[package]] @@ -80,34 +97,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "indicatif" version = "0.17.7" @@ -142,6 +131,16 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + [[package]] name = "memoffset" version = "0.9.0" @@ -151,6 +150,72 @@ dependencies = [ "autocfg", ] +[[package]] +name = "nalgebra" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -158,13 +223,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] -name = "petgraph" -version = "0.6.4" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "polyfit-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab98d9704c7300e37472a6790a447eaf501d664a1889737faa53c26790d2b697" dependencies = [ - "fixedbitset", - "indexmap", + "nalgebra", ] [[package]] @@ -173,6 +243,36 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +[[package]] +name = "primes" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a61082d8bceecd71a3870e9162002bb75f7ba9c7aa8b76227e887782fef9c8" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + [[package]] name = "rayon" version = "1.8.0" @@ -193,12 +293,111 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "simba" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.42", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.42", + "test-case-core", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "wide" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/21/Cargo.toml b/21/Cargo.toml index 441e2ba..2bc0ee8 100644 --- a/21/Cargo.toml +++ b/21/Cargo.toml @@ -7,5 +7,7 @@ edition = "2021" [dependencies] indicatif = "0.17.7" -petgraph = "0.6.4" +polyfit-rs = "0.2.1" +primes = "0.3.0" rayon = "1.8.0" +test-case = "3.3.1" diff --git a/21/src/main.rs b/21/src/main.rs index 53f1a6c..a02160f 100644 --- a/21/src/main.rs +++ b/21/src/main.rs @@ -1,9 +1,7 @@ use core::panic; -use std::collections::HashSet; use indicatif::{ProgressBar, ProgressStyle}; -use petgraph::algo::k_shortest_path; -use petgraph::prelude::*; use rayon::prelude::*; +use std::collections::HashSet; use std::fs::File; use std::io::{BufRead, BufReader, Lines}; use std::time::Instant; @@ -31,46 +29,33 @@ fn main() { // PARSE -type Position = (usize, usize); +type Position = (isize, isize); +type WrappedPosition = (usize, usize); type Offset = (isize, isize); #[derive(Debug, Clone)] struct MapTile { c: char, - idx: NodeIndex, } impl MapTile { - fn new(c: char, idx: NodeIndex) -> Self { - Self { c, idx } + fn new(c: char) -> Self { + Self { c } } } -type GraphType = DiGraph; struct GardenMap { map: Vec>, - graph: GraphType, start: Position, } impl From> for GardenMap { fn from(lines: Lines) -> Self { - let mut graph = DiGraph::new(); let map = lines - .map(|line| { - line.unwrap() - .chars() - .map(|c| MapTile::new(c, graph.add_node(c))) - .collect() - }) + .map(|line| line.unwrap().chars().map(|c| MapTile::new(c)).collect()) .collect(); - let mut new = Self { - map, - start: (0, 0), - graph, - }; + let mut new = Self { map, start: (0, 0) }; new.find_start(); - new.build_graph(); new } } @@ -78,6 +63,21 @@ impl From> for GardenMap { const ADJACENCIES: [Offset; 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)]; impl GardenMap { + fn wrap_pos(&self, pos: &Position) -> WrappedPosition { + let (width, height) = (self.width() as isize, self.height() as isize); + ( + if pos.0 < 0 { + (pos.0 + (-pos.0 / width + 1) * width) as usize % self.width() + } else { + pos.0 as usize % self.width() + }, + if pos.1 < 0 { + (pos.1 + (-pos.1 / height + 1) * height) as usize % self.height() + } else { + pos.1 as usize % self.height() + }, + ) + } fn width(&self) -> usize { self.map[0].len() } @@ -85,80 +85,103 @@ impl GardenMap { self.map.len() } fn at(&self, pos: &Position) -> &MapTile { + let pos = self.wrap_pos(pos); &self.map[pos.1][pos.0] } - fn at_mut(&mut self, pos: &Position) -> &mut MapTile { - &mut self.map[pos.1][pos.0] - } // return the valid 'moves' from pos fn adjacent_to(&self, pos: &Position) -> Vec { ADJACENCIES .iter() .filter_map(|ofs| self.offset_pos(pos, ofs)) - .filter(|pos| self.at(pos).c == '.' || self.at(pos).c == 'S') + .filter(|pos| self.at(pos).c != '#') .collect() } fn offset_pos(&self, pos: &Position, ofs: &Offset) -> Option { let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1); - if new_pos.0 < 0 || new_pos.1 < 0 || new_pos.0 >= self.width() as isize || new_pos.1 >= self.height() as isize { - return None; - } - return Some((new_pos.0 as usize, new_pos.1 as usize)); + return Some((new_pos.0, new_pos.1)); } fn find_start(&mut self) { for (y, row) in self.map.iter().enumerate() { for (x, tile) in row.iter().enumerate() { if tile.c == 'S' { - self.start = (x, y); + self.start = (x as isize, y as isize); return; } } } panic!("didn't find the start square!"); } - fn build_graph(&mut self) { - for y in 0..self.height() { - for x in 0..self.width() { - for (x2, y2) in self.adjacent_to(&(x, y)) { - self.graph.add_edge(self.at(&(x, y)).idx, self.at(&(x2, y2)).idx, ()); - } - } - } - } - fn reachable_after(&self, from: &Position, n: usize) -> HashSet { + fn reachable_after(&self, from: &Position, n: usize) -> u64 { + let bar = ProgressBar::new(n as u64).with_style( + ProgressStyle::with_template( + "[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}", + ) + .unwrap(), + ); let mut visited_after: Vec> = Vec::new(); visited_after.push(HashSet::from([*from])); - for i in 1..n + 1 { + for i in 1..n+1 { visited_after.push( visited_after[i - 1] .iter() .flat_map(|last| self.adjacent_to(last)) .collect(), ); + bar.inc(1); + // if primes::is_prime(i as u64) { + // println!("count after {} steps: {}", i, visited_after[i].len()); + // } } - visited_after[n].clone() + visited_after[n].len() as u64 } fn reachable_count_after(&self, from: &Position, n: usize) -> u64 { - self.reachable_after(from, n).len() as u64 + let dim = self.width() as f64; + let target_mod = (n % self.width()) as f64; + let x_values:Vec = vec![target_mod, target_mod + dim, target_mod + 2.*dim]; + let y_values:Vec = x_values.iter().map(|n| self.reachable_after(from, *n as usize) as f64).collect(); + + let coeffs = polyfit_rs::polyfit_rs::polyfit( + &x_values, + &y_values, + 2, + ).unwrap(); + println!("values: x: {:?} y: {:?}", x_values, y_values); + println!("coefficients: {:?}", coeffs); + + let f_n= n as f64; + let result = coeffs[0] + coeffs[1] * f_n + coeffs[2] * f_n.powf(2.0); + + result.round() as u64 + } + fn draw_with_bounds(&self, from: &Position, to: &Position) { + for row in from.1..to.1 + 1 { + for col in from.0..to.0 + 1 { + print!("{}", self.at(&(col, row)).c); + } + println!(); + } } } -fn print_visited(map: &GardenMap, visited: &Vec>) { - for (y, row) in visited.iter().enumerate() { - for (x, cell) in row.iter().enumerate() { - print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c }); - } - println!(); - } -} +// fn print_visited(map: &GardenMap, visited: &Vec>) { +// for (y, row) in visited.iter().enumerate() { +// for (x, cell) in row.iter().enumerate() { +// print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c }); +// } +// println!(); +// } +// } // PROBLEM 1 solution fn problem1_impl(input: Lines, n: usize) -> u64 { let map = GardenMap::from(input); // println!("map: {:?} start: {:?}", map.map, &map.start); - + // map.draw_with_bounds( + // &(-(map.width() as isize), -(map.height() as isize)), + // &(map.width() as isize * 2 + 1, map.height() as isize * 2 + 1), + // ); map.reachable_count_after(&map.start, n) } @@ -168,13 +191,14 @@ fn problem1(input: Lines) -> u64 { // PROBLEM 2 solution fn problem2(input: Lines) -> u64 { - 0 + problem1_impl(input, 26501365) } #[cfg(test)] mod tests { use crate::*; use std::io::Cursor; + use test_case::test_case; const EXAMPLE: &str = &"........... .....###.#. @@ -194,9 +218,13 @@ mod tests { assert_eq!(problem1_impl(c.lines(), 6), 16); } - #[test] - fn problem2_example() { + #[test_case(6, 16)] + #[test_case(10, 50)] + #[test_case(50, 1594)] + #[test_case(100, 6536)] + #[test_case(500, 167004)] + fn problem2_example(n: usize, expect: u64) { let c = Cursor::new(EXAMPLE); - assert_eq!(problem2(c.lines()), 0); + assert_eq!(problem1_impl(c.lines(), n), expect); } }