diff --git a/.gitignore b/.gitignore index 1313560..b8f3ecb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ target/ *.gcda # Specific days -!day10/src/input.rs +!day10/src/*.rs diff --git a/day10/Cargo.toml b/day10/Cargo.toml index 416ae24..7ac6edf 100644 --- a/day10/Cargo.toml +++ b/day10/Cargo.toml @@ -8,10 +8,12 @@ edition = "2018" [features] -part2=[] +part2=["num_cpus", "semaphore"] test = [] [dependencies] ad-hoc-iter = "0.2.2" cfg-if = "1.0.0" +num_cpus = {version = "1.13.0", optional = true} +semaphore = {version = "0.4.0", optional = true} smallmap = "1.2.1" diff --git a/day10/src/part2.rs b/day10/src/part2.rs new file mode 100644 index 0000000..846281c --- /dev/null +++ b/day10/src/part2.rs @@ -0,0 +1,136 @@ +use super::*; +use std::{ + sync::{ + Arc, + RwLock, + }, + thread, +}; +use semaphore::Semaphore; + +#[derive(Debug)] +pub struct Cache(RwLock>); + +impl Cache +{ + pub fn new() -> Self + { + Self(RwLock::new(Map::new())) + } + pub fn get_or_insert_with_owned(self: Arc, key: u8, with: F) -> usize + where F: FnOnce() -> usize + { + match Arc::try_unwrap(self) + { + Ok(this) => { + debug_assert!(key>0); + let key = unsafe {NonZeroU8::new_unchecked(key)}; + + let map = this.0.into_inner().unwrap(); + if let Some(&value) = map.get(&key) { + value + } else { + // No need to insert, the cache will be dropped after this + with() + } + }, + Err(this) => this.get_or_insert_with(key, with) + } + } + #[inline] pub fn insert(&self, key: u8, value: usize) -> usize + { + debug_assert!(key>0); + let key = unsafe {NonZeroU8::new_unchecked(key)}; + self.0.write().unwrap().insert(key, value); + value + } + pub fn get(&self, key: u8) -> Option + { + debug_assert!(key>0); + let key = unsafe {NonZeroU8::new_unchecked(key)}; + self.0.read().unwrap().get(&key).copied() + } + pub fn get_or_insert_with(&self, key: u8, with: F) -> usize + where F: FnOnce() -> usize + { + debug_assert!(key>0); + let key = unsafe {NonZeroU8::new_unchecked(key)}; + + { + let read = self.0.read().unwrap(); + if let Some(&value) = read.get(&key) { + return value; + } + } + // Insert needed + { + let value = with(); + let mut lock = self.0.write().unwrap(); + lock.insert(key, value); + value + } + } +} + +#[derive(Debug)] +enum Deffered +{ + Known(usize), + Yielded(thread::JoinHandle), +} + +impl Deffered +{ + #[inline] pub fn into_value(self) -> usize + { + match self { + Self::Known(v) => v, + Self::Yielded(v) => v.join().unwrap(), + } + } +} + +fn rec_part2(map: Arc, cache: Arc, lock: Semaphore<()>, max: u8, f: u8) -> usize +{ + if f == max { + return 1; + } + debug_assert!(f < max); + + match iterate_adaptor_chain(&map, None, f).map(|next| { + + if next == max { + Deffered::Known(1) + } else { + match cache.get(next) { + Some(value) => Deffered::Known(value), + None => { + let map = Arc::clone(&map); + let cache = Arc::clone(&cache); + let lock = lock.clone(); + if let Ok(_guard) = lock.try_access() { + Deffered::Yielded(thread::spawn(move || { + cache.clone().insert(next, rec_part2(map, cache, lock, max, next)) + })) + } else { + Deffered::Known(cache.clone().insert(next, rec_part2(map, cache, lock, max, next))) + } + } + } + } + }).map(Deffered::into_value) + .sum::() { + #[cold] 0 => panic!("eh"), + x => x, + } +} + +fn gensem() -> Semaphore<()> +{ + Semaphore::new(num_cpus::get()+1, ()) +} + +#[inline] pub fn solve(a: Adaptors, max: u8) -> usize +{ + rec_part2(Arc::new(a), Arc::new(Cache::new()), gensem(), max, 0) +}