From 24f05860a2066943548aeb365173e3848fa07be4 Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Tue, 6 Dec 2022 14:16:21 +0100 Subject: [PATCH] Rust Day 6 - Tidy up; Add tests for correctness --- rust/Cargo.toml | 4 + rust/src/day06/main.rs | 14 +- rust/src/day06/part_1.rs | 570 ++++++++++++++++------------------- rust/src/day06/part_2.rs | 576 +++++++++++++++++------------------- rust/src/day06/solutions.rs | 528 +++++++++++++++++++++++++++++++++ 5 files changed, 1068 insertions(+), 624 deletions(-) create mode 100644 rust/src/day06/solutions.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 832223b..9d50f76 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -46,3 +46,7 @@ path = "src/day05/main.rs" [[bin]] name = "day06" path = "src/day06/main.rs" + +[dependencies] +# Required for one of the day06 solutions (commented out) +# phf = { version = "0.11.1", features = ["macros"] } \ No newline at end of file diff --git a/rust/src/day06/main.rs b/rust/src/day06/main.rs index 9dfdf58..a1f9873 100644 --- a/rust/src/day06/main.rs +++ b/rust/src/day06/main.rs @@ -1,17 +1,17 @@ +#![feature(const_mut_refs)] #![feature(array_windows)] -#![cfg_attr(test, feature(test))] -#![allow(soft_unstable)] -#![cfg(test)] +#![feature(test)] extern crate test; const INPUT: &str = include_str!("input.txt"); +use solutions::*; + mod part_1; -use part_1::part_1_vec; mod part_2; -use part_2::part_2_vec; +mod solutions; pub fn main() { - println!("Part 1: {}", part_1_vec(INPUT)); - println!("Part 2: {}", part_2_vec(INPUT)); + println!("Part 1: {}", solve_vertesians_nodeps_const::<4>(INPUT)); + println!("Part 2: {}", solve_vertesians_nodeps_const::<14>(INPUT)); } diff --git a/rust/src/day06/part_1.rs b/rust/src/day06/part_1.rs index 05fd0a0..7e887fd 100644 --- a/rust/src/day06/part_1.rs +++ b/rust/src/day06/part_1.rs @@ -1,287 +1,7 @@ -const STRING_SLICE_LENGTH: usize = 4; - -pub(crate) fn part_1_vec(input: &'static str) -> usize { - for idx in 0..(input.len() - STRING_SLICE_LENGTH) { - let mut unique_chars = input[idx..idx + STRING_SLICE_LENGTH] - .chars() - .collect::>(); - unique_chars.sort(); - unique_chars.dedup(); - if unique_chars.len() == STRING_SLICE_LENGTH { - return idx + STRING_SLICE_LENGTH; - } - } - unreachable!("We should always find an answer"); -} - -pub(crate) fn part_1_hashset(input: &'static str) -> usize { - for idx in 0..(input.len() - STRING_SLICE_LENGTH) { - let unique_chars = input[idx..idx + STRING_SLICE_LENGTH] - .chars() - .collect::>(); - if unique_chars.len() == STRING_SLICE_LENGTH { - return idx + STRING_SLICE_LENGTH; - } - } - unreachable!("We should always find an answer"); -} - -pub(crate) fn part_1_nicopap_original(input: &'static str) -> usize { - nicopap_original::first_sop(input.as_bytes(), STRING_SLICE_LENGTH).unwrap() -} - -mod nicopap_original { - fn all_distinct(slice: &[T]) -> bool { - let mut slice_copy = slice.to_vec(); - slice_copy.sort_unstable(); - slice_copy.windows(2).all(|pair| pair[0] != pair[1]) - } - // sop stands for "start of packet" - pub(crate) fn first_sop( - datastream: &[T], - required_distinct: usize, - ) -> Option { - datastream - .windows(required_distinct) - .enumerate() - .find_map(|(i, slice)| all_distinct(slice).then_some(i + required_distinct)) - } -} - -pub(crate) fn part_1_nicopap_original_without_windows(input: &'static str) -> usize { - let mut cache = Vec::with_capacity(STRING_SLICE_LENGTH); - for (i, char) in input.chars().enumerate() { - cache.insert(0, char); - if cache.len() >= STRING_SLICE_LENGTH { - let mut sorted_cache = cache.clone(); - sorted_cache.sort_unstable(); - sorted_cache.dedup(); - if sorted_cache.len() == STRING_SLICE_LENGTH { - return i + 1; - } - cache.pop(); - } - } - unreachable!() -} - -pub(crate) fn part_1_nicopap_improved(input: &'static str) -> usize { - nicopap_improved::first_sop(input.as_bytes(), STRING_SLICE_LENGTH).unwrap() -} -mod nicopap_improved { - fn all_distinct(slice: &[T], collect_vec: &mut Vec) -> bool { - collect_vec.clear(); - slice.clone_into(collect_vec); - collect_vec.sort_unstable(); - collect_vec.windows(2).all(|pair| pair[0] != pair[1]) - } - // sop stands for "start of packet" - pub(crate) fn first_sop( - datastream: &[T], - required_distinct: usize, - ) -> Option { - let mut collect_vec = Vec::with_capacity(required_distinct); - datastream - .windows(required_distinct) - .enumerate() - .find_map(|(i, slice)| { - all_distinct(slice, &mut collect_vec).then_some(i + required_distinct) - }) - } -} - -pub(crate) fn part_1_nicopap_improved_again(input: &'static str) -> usize { - nicopap_improved_again::first_sop::(input.as_bytes()).unwrap() -} -mod nicopap_improved_again { - struct AlphabetSet(u32); - impl AlphabetSet { - const fn new() -> Self { - Self(0) - } - /// Add letter to set, return `true` if it is already part of it. - /// `symbol` must be an ascii latin alphabet letter. - fn add_letter_or_contains(&mut self, symbol: u8) -> bool { - let to_set = 1 << (symbol.wrapping_sub(b'a') as u32); - let already_set = self.0 & to_set == to_set; - self.0 |= to_set; - already_set - } - } - fn all_distinct(slice: &[u8]) -> bool { - let mut set = AlphabetSet::new(); - !slice - .iter() - .any(|letter| set.add_letter_or_contains(*letter)) - } - - // sop stands for "start of packet" - pub(crate) fn first_sop(datastream: &[u8]) -> Option { - datastream - .array_windows::() - .enumerate() - .find_map(|(i, slice)| all_distinct(slice).then_some(i + W)) - } -} - -pub(crate) fn part_1_manevillef(input: &'static str) -> usize { - manevillef::find_marker(&input.chars().collect::>(), STRING_SLICE_LENGTH).unwrap() -} - -mod manevillef { - use std::collections::HashSet; - - pub(crate) fn find_marker(chars: &[char], window: usize) -> Option { - chars - .windows(window) - .into_iter() - .position(|items| { - let set: HashSet = items.iter().copied().collect(); - set.len() == window - }) - .map(|p| p + window) - } -} - -pub fn part_1_snaketwix(input: &'static str) -> usize { - let mut number_of_duplicates = 0; - let mut char_map = std::collections::HashMap::new(); - let mut char_deque = std::collections::VecDeque::with_capacity(STRING_SLICE_LENGTH + 1); - - let mut ans = 0; - - for (index, char) in input.char_indices() { - let entry = char_map.entry(char).or_insert(0); - *entry += 1; - - if *entry > 1 { - number_of_duplicates += 1; - } - - char_deque.push_back(char); - - if index > STRING_SLICE_LENGTH - 1 { - let char = char_deque.pop_front().unwrap(); - - // Know that the entry exists, but not sure how to unwrap the entry to get access to the value - let entry = char_map.entry(char).or_default(); - *entry -= 1; - - if *entry >= 1 { - number_of_duplicates -= 1; - } - - if number_of_duplicates == 0 { - ans = index + 1; - break; - } - } - } - - ans -} - -pub fn part_1_snaketwix_modified(input: &'static str) -> usize { - let mut number_of_duplicates = 0; - let mut char_map = std::collections::HashMap::new(); - let mut char_deque = std::collections::VecDeque::with_capacity(STRING_SLICE_LENGTH + 1); - - let mut ans = 0; - - for (index, char) in input.char_indices() { - let entry = char_map.entry(char).or_insert(0); - *entry += 1; - - if *entry > 1 { - number_of_duplicates += 1; - } - - char_deque.push_back(char); - - if index > STRING_SLICE_LENGTH - 1 { - let char = char_deque.pop_front().unwrap(); - - let entry = match char_map.entry(char) { - std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), - std::collections::hash_map::Entry::Vacant(_) => unreachable!(), - }; - *entry -= 1; - - if *entry >= 1 { - number_of_duplicates -= 1; - } - - if number_of_duplicates == 0 { - ans = index + 1; - break; - } - } - } - - ans -} - -fn part_1_harudagondi(input: &'static str) { - harudagondi::solve_part1(input); -} - -mod harudagondi { - use std::collections::{HashSet, VecDeque}; - - #[derive(Default)] - struct Solver { - buffer: VecDeque, - counter: usize, - buffer_size: usize, - } - - impl Solver { - fn new(buffer_size: usize) -> Self { - Self { - buffer: VecDeque::new(), - counter: 0, - buffer_size, - } - } - - fn update(&mut self, c: char) { - if self.buffer.len() < self.buffer_size { - self.buffer.push_back(c); - } else { - self.buffer.pop_front(); - self.buffer.push_back(c); - } - self.counter += 1; - } - - fn starter(&self) -> Option { - let buffer = self.buffer.iter().fold(HashSet::new(), |mut acc, c| { - acc.insert(*c); - acc - }); - - if buffer.len() == self.buffer_size { - Some(self.counter) - } else { - None - } - } - } - - pub fn solve_part1(input: &'static str) -> usize { - let mut solver = Solver::new(super::STRING_SLICE_LENGTH); - for c in input.chars() { - solver.update(c); - if let Some(counter) = solver.starter() { - return counter; - } - } - unreachable!(); - } -} - #[cfg(test)] mod tests { + const WINDOW_SIZE: usize = 4; + use crate::solutions::*; const SAMPLES: [(&str, usize); 5] = [ (include_str!("sample_inputs/1.txt"), 7), @@ -291,65 +11,301 @@ mod tests { (include_str!("sample_inputs/5.txt"), 11), ]; - #[test] - fn test_with_solution() { - assert_eq!(super::part_1_vec(crate::INPUT), 1723); + #[bench] + fn bench_vec(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vec::(crate::INPUT)); } #[test] - fn test_with_sample_solution() { + fn test_vec_with_solution() { + assert_eq!(solve_vec::(crate::INPUT), 1723) + } + + #[test] + fn test_vec_with_sample_solutions() { for (sample_input, sample_answer) in SAMPLES { - assert_eq!(super::part_1_vec(sample_input), sample_answer); + assert_eq!(solve_vec::(sample_input), sample_answer) } } #[bench] - fn bench_part_1_vec(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_vec(crate::INPUT)); + fn bench_hashset(bencher: &mut test::Bencher) { + bencher.iter(|| solve_hashset::(crate::INPUT)); + } + + #[test] + fn test_hashset_with_solution() { + assert_eq!(solve_hashset::(crate::INPUT), 1723) + } + + #[test] + fn test_hashset_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_hashset::(sample_input), sample_answer) + } } #[bench] - fn bench_part_1_hashset(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_hashset(crate::INPUT)); + fn bench_snaketwix(bencher: &mut test::Bencher) { + bencher.iter(|| solve_snaketwix::(crate::INPUT)); + } + + #[test] + fn test_snaketwix_with_solution() { + assert_eq!(solve_snaketwix::(crate::INPUT), 1723) + } + + #[test] + fn test_snaketwix_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_snaketwix::(sample_input), sample_answer) + } } #[bench] - fn bench_part_1_snaketwix(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_snaketwix(crate::INPUT)); + fn bench_snaketwix_modified(bencher: &mut test::Bencher) { + bencher.iter(|| solve_snaketwix_modified::(crate::INPUT)); + } + + #[test] + fn test_snaketwix_modified_with_solution() { + assert_eq!(solve_snaketwix_modified::(crate::INPUT), 1723) + } + + #[test] + fn test_snaketwix_modified_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_snaketwix_modified::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_snaketwix_modified(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_snaketwix_modified(crate::INPUT)); + fn bench_nicopap_original(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_original::(crate::INPUT)); + } + + #[test] + fn test_nicopap_original_with_solution() { + assert_eq!(solve_nicopap_original::(crate::INPUT), 1723) + } + + #[test] + fn test_nicopap_original_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_original::(sample_input), + sample_answer + ) + } + } + #[bench] + fn bench_nicopap_original_without_windows(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_original_without_windows::(crate::INPUT)); + } + + #[test] + fn test_nicopap_original_without_windows_with_solution() { + assert_eq!( + solve_nicopap_original_without_windows::(crate::INPUT), + 1723 + ) + } + + #[test] + fn test_nicopap_original_without_windows_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_original_without_windows::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_nicopap_original(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_nicopap_original(crate::INPUT)); + fn bench_nicopap_improved(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_improved::(crate::INPUT)); + } + + #[test] + fn test_nicopap_improved_with_solution() { + assert_eq!(solve_nicopap_improved::(crate::INPUT), 1723) + } + + #[test] + fn test_nicopap_improved_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_improved::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_nicopap_original_without_windows(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_nicopap_original_without_windows(crate::INPUT)); + fn bench_nicopap_improved_again(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_improved_again::(crate::INPUT)); + } + + #[test] + fn test_nicopap_improved_again_with_solution() { + assert_eq!( + solve_nicopap_improved_again::(crate::INPUT), + 1723 + ) + } + + #[test] + fn test_nicopap_improved_again_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_improved_again::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_nicopap_improved(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_nicopap_improved(crate::INPUT)); + fn bench_manevillef(bencher: &mut test::Bencher) { + bencher.iter(|| solve_manevillef::(crate::INPUT)); + } + + #[test] + fn test_manevillef_with_solution() { + assert_eq!(solve_manevillef::(crate::INPUT), 1723) + } + + #[test] + fn test_manevillef_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_manevillef::(sample_input), sample_answer) + } } #[bench] - fn bench_part_1_nicopap_improved_again(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_nicopap_improved_again(crate::INPUT)); + fn bench_manevillef_again(bencher: &mut test::Bencher) { + bencher.iter(|| solve_manevillef_again::(crate::INPUT)); + } + + #[test] + fn test_manevillef_again_with_solution() { + assert_eq!(solve_manevillef_again::(crate::INPUT), 1723) + } + + #[test] + fn test_manevillef_again_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_manevillef_again::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_manevillef(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_manevillef(crate::INPUT)); + fn bench_harudagondi(bencher: &mut test::Bencher) { + bencher.iter(|| solve_harudagondi::(crate::INPUT)); + } + + #[test] + fn test_harudagondi_with_solution() { + assert_eq!(solve_harudagondi::(crate::INPUT), 1723) + } + + #[test] + fn test_harudagondi_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_harudagondi::(sample_input), + sample_answer + ) + } + } + + /* Requires phf crate + #[bench] + fn bench_vertesians(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians::(crate::INPUT)); + } + + #[test] + fn test_vertesians_with_solution() { + assert_eq!(solve_vertesians::(crate::INPUT), 1723) + } + + #[test] + fn test_vertesians_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_vertesians::(sample_input), sample_answer) + } + } + */ + + #[bench] + fn bench_vertesians_nodeps(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_with_solution() { + assert_eq!(solve_vertesians_nodeps::(crate::INPUT), 1723) + } + + #[test] + fn test_vertesians_nodeps_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_1_harudagondi(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_1_harudagondi(crate::INPUT)); + fn bench_vertesians_nodeps_improved(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps_improved::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_improved_with_solution() { + assert_eq!( + solve_vertesians_nodeps_improved::(crate::INPUT), + 1723 + ) + } + + #[test] + fn test_vertesians_nodeps_improved_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps_improved::(sample_input), + sample_answer + ) + } + } + + #[bench] + fn bench_vertesians_nodeps_const(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps_const::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_const_with_solution() { + assert_eq!( + solve_vertesians_nodeps_const::(crate::INPUT), + 1723 + ) + } + + #[test] + fn test_vertesians_nodeps_const_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps_const::(sample_input), + sample_answer + ) + } } } diff --git a/rust/src/day06/part_2.rs b/rust/src/day06/part_2.rs index b116855..afd5eb6 100644 --- a/rust/src/day06/part_2.rs +++ b/rust/src/day06/part_2.rs @@ -1,289 +1,8 @@ -const STRING_SLICE_LENGTH: usize = 14; - -pub(crate) fn part_2_vec(input: &'static str) -> usize { - for idx in 0..(input.len() - STRING_SLICE_LENGTH) { - let mut unique_chars = input[idx..idx + STRING_SLICE_LENGTH] - .chars() - .collect::>(); - unique_chars.sort(); - unique_chars.dedup(); - if unique_chars.len() == STRING_SLICE_LENGTH { - return idx + STRING_SLICE_LENGTH; - } - } - unreachable!("We should always find an answer"); -} - -pub(crate) fn part_2_hashset(input: &'static str) -> usize { - for idx in 0..(input.len() - STRING_SLICE_LENGTH) { - let unique_chars = input[idx..idx + STRING_SLICE_LENGTH] - .chars() - .collect::>(); - if unique_chars.len() == STRING_SLICE_LENGTH { - return idx + STRING_SLICE_LENGTH; - } - } - unreachable!("We should always find an answer"); -} - -pub(crate) fn part_2_nicopap_original(input: &'static str) -> usize { - nicopap_original::first_sop(input.as_bytes(), STRING_SLICE_LENGTH).unwrap() -} - -mod nicopap_original { - fn all_distinct(slice: &[T]) -> bool { - let mut slice_copy = slice.to_vec(); - slice_copy.sort_unstable(); - slice_copy.windows(2).all(|pair| pair[0] != pair[1]) - } - // sop stands for "start of packet" - pub(crate) fn first_sop( - datastream: &[T], - required_distinct: usize, - ) -> Option { - datastream - .windows(required_distinct) - .enumerate() - .find_map(|(i, slice)| all_distinct(slice).then_some(i + required_distinct)) - } -} - -pub(crate) fn part_2_nicopap_original_without_windows(input: &'static str) -> usize { - let mut cache = Vec::with_capacity(STRING_SLICE_LENGTH); - for (i, char) in input.chars().enumerate() { - cache.insert(0, char); - if cache.len() >= STRING_SLICE_LENGTH { - let mut sorted_cache = cache.clone(); - sorted_cache.sort_unstable(); - sorted_cache.dedup(); - if sorted_cache.len() == STRING_SLICE_LENGTH { - return i + 1; - } - cache.pop(); - } - } - unreachable!() -} - -pub(crate) fn part_2_nicopap_improved(input: &'static str) -> usize { - nicopap_improved::first_sop(input.as_bytes(), STRING_SLICE_LENGTH).unwrap() -} - -mod nicopap_improved { - fn all_distinct(slice: &[T], collect_vec: &mut Vec) -> bool { - collect_vec.clear(); - slice.clone_into(collect_vec); - collect_vec.sort_unstable(); - collect_vec.windows(2).all(|pair| pair[0] != pair[1]) - } - // sop stands for "start of packet" - pub(crate) fn first_sop( - datastream: &[T], - required_distinct: usize, - ) -> Option { - let mut collect_vec = Vec::with_capacity(required_distinct); - datastream - .windows(required_distinct) - .enumerate() - .find_map(|(i, slice)| { - all_distinct(slice, &mut collect_vec).then_some(i + required_distinct) - }) - } -} - -pub(crate) fn part_2_nicopap_improved_again(input: &'static str) -> usize { - nicopap_improved_again::first_sop::(input.as_bytes()).unwrap() -} - -mod nicopap_improved_again { - struct AlphabetSet(u32); - impl AlphabetSet { - const fn new() -> Self { - Self(0) - } - /// Add letter to set, return `true` if it is already part of it. - /// `symbol` must be an ascii latin alphabet letter. - fn add_letter_or_contains(&mut self, symbol: u8) -> bool { - let to_set = 1 << (symbol.wrapping_sub(b'a') as u32); - let already_set = self.0 & to_set == to_set; - self.0 |= to_set; - already_set - } - } - fn all_distinct(slice: &[u8]) -> bool { - let mut set = AlphabetSet::new(); - !slice - .iter() - .any(|letter| set.add_letter_or_contains(*letter)) - } - - // sop stands for "start of packet" - pub(crate) fn first_sop(datastream: &[u8]) -> Option { - datastream - .array_windows::() - .enumerate() - .find_map(|(i, slice)| all_distinct(slice).then_some(i + W)) - } -} - -pub(crate) fn part_2_manevillef(input: &'static str) -> usize { - manevillef::find_marker(&input.chars().collect::>(), STRING_SLICE_LENGTH).unwrap() -} - -mod manevillef { - use std::collections::HashSet; - - pub(crate) fn find_marker(chars: &[char], window: usize) -> Option { - chars - .windows(window) - .into_iter() - .position(|items| { - let set: HashSet = items.iter().copied().collect(); - set.len() == window - }) - .map(|p| p + window) - } -} - -pub fn part_2_snaketwix(input: &'static str) -> usize { - let mut number_of_duplicates = 0; - let mut char_map = std::collections::HashMap::new(); - let mut char_deque = std::collections::VecDeque::with_capacity(STRING_SLICE_LENGTH + 1); - - let mut ans = 0; - - for (index, char) in input.char_indices() { - let entry = char_map.entry(char).or_insert(0); - *entry += 1; - - if *entry > 1 { - number_of_duplicates += 1; - } - - char_deque.push_back(char); - - if index > STRING_SLICE_LENGTH - 1 { - let char = char_deque.pop_front().unwrap(); - - // Know that the entry exists, but not sure how to unwrap the entry to get access to the value - let entry = char_map.entry(char).or_default(); - *entry -= 1; - - if *entry >= 1 { - number_of_duplicates -= 1; - } - - if number_of_duplicates == 0 { - ans = index + 1; - break; - } - } - } - - ans -} - -pub fn part_2_snaketwix_modified(input: &'static str) -> usize { - let mut number_of_duplicates = 0; - let mut char_map = std::collections::HashMap::new(); - let mut char_deque = std::collections::VecDeque::with_capacity(STRING_SLICE_LENGTH + 1); - - let mut ans = 0; - - for (index, char) in input.char_indices() { - let entry = char_map.entry(char).or_insert(0); - *entry += 1; - - if *entry > 1 { - number_of_duplicates += 1; - } - - char_deque.push_back(char); - - if index > STRING_SLICE_LENGTH - 1 { - let char = char_deque.pop_front().unwrap(); - - let entry = match char_map.entry(char) { - std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), - std::collections::hash_map::Entry::Vacant(_) => unreachable!(), - }; - *entry -= 1; - - if *entry >= 1 { - number_of_duplicates -= 1; - } - - if number_of_duplicates == 0 { - ans = index + 1; - break; - } - } - } - - ans -} - -fn part_2_harudagondi(input: &'static str) { - harudagondi::solve_part2(input); -} - -mod harudagondi { - use std::collections::{HashSet, VecDeque}; - - #[derive(Default)] - struct Solver { - buffer: VecDeque, - counter: usize, - buffer_size: usize, - } - - impl Solver { - fn new(buffer_size: usize) -> Self { - Self { - buffer: VecDeque::new(), - counter: 0, - buffer_size, - } - } - - fn update(&mut self, c: char) { - if self.buffer.len() < self.buffer_size { - self.buffer.push_back(c); - } else { - self.buffer.pop_front(); - self.buffer.push_back(c); - } - self.counter += 1; - } - - fn starter(&self) -> Option { - let buffer = self.buffer.iter().fold(HashSet::new(), |mut acc, c| { - acc.insert(*c); - acc - }); - - if buffer.len() == self.buffer_size { - Some(self.counter) - } else { - None - } - } - } - - pub fn solve_part2(input: &'static str) -> usize { - let mut solver = Solver::new(super::STRING_SLICE_LENGTH); - for c in input.chars() { - solver.update(c); - if let Some(counter) = solver.starter() { - return counter; - } - } - unreachable!(); - } -} - #[cfg(test)] mod tests { + const WINDOW_SIZE: usize = 14; + use crate::solutions::*; + const SAMPLES: [(&str, usize); 5] = [ (include_str!("sample_inputs/1.txt"), 19), (include_str!("sample_inputs/2.txt"), 23), @@ -292,64 +11,301 @@ mod tests { (include_str!("sample_inputs/5.txt"), 26), ]; - #[test] - fn test_with_solution() { - assert_eq!(super::part_2_vec(crate::INPUT), 3708); + #[bench] + fn bench_vec(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vec::(crate::INPUT)); } #[test] - fn test_with_sample_solution() { + fn test_vec_with_solution() { + assert_eq!(solve_vec::(crate::INPUT), 3708) + } + + #[test] + fn test_vec_with_sample_solutions() { for (sample_input, sample_answer) in SAMPLES { - assert_eq!(super::part_2_vec(sample_input), sample_answer); + assert_eq!(solve_vec::(sample_input), sample_answer) } } #[bench] - fn bench_part_2_vec(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_vec(crate::INPUT)); + fn bench_hashset(bencher: &mut test::Bencher) { + bencher.iter(|| solve_hashset::(crate::INPUT)); + } + + #[test] + fn test_hashset_with_solution() { + assert_eq!(solve_hashset::(crate::INPUT), 3708) + } + + #[test] + fn test_hashset_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_hashset::(sample_input), sample_answer) + } } #[bench] - fn bench_part_2_hashset(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_hashset(crate::INPUT)); + fn bench_snaketwix(bencher: &mut test::Bencher) { + bencher.iter(|| solve_snaketwix::(crate::INPUT)); + } + + #[test] + fn test_snaketwix_with_solution() { + assert_eq!(solve_snaketwix::(crate::INPUT), 3708) + } + + #[test] + fn test_snaketwix_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_snaketwix::(sample_input), sample_answer) + } } #[bench] - fn bench_part_2_snaketwix(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_snaketwix(crate::INPUT)); + fn bench_snaketwix_modified(bencher: &mut test::Bencher) { + bencher.iter(|| solve_snaketwix_modified::(crate::INPUT)); + } + + #[test] + fn test_snaketwix_modified_with_solution() { + assert_eq!(solve_snaketwix_modified::(crate::INPUT), 3708) + } + + #[test] + fn test_snaketwix_modified_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_snaketwix_modified::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_2_snaketwix_modified(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_snaketwix_modified(crate::INPUT)); + fn bench_nicopap_original(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_original::(crate::INPUT)); + } + + #[test] + fn test_nicopap_original_with_solution() { + assert_eq!(solve_nicopap_original::(crate::INPUT), 3708) + } + + #[test] + fn test_nicopap_original_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_original::(sample_input), + sample_answer + ) + } + } + #[bench] + fn bench_nicopap_original_without_windows(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_original_without_windows::(crate::INPUT)); + } + + #[test] + fn test_nicopap_original_without_windows_with_solution() { + assert_eq!( + solve_nicopap_original_without_windows::(crate::INPUT), + 3708 + ) + } + + #[test] + fn test_nicopap_original_without_windows_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_original_without_windows::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_2_nicopap_original(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_nicopap_original(crate::INPUT)); + fn bench_nicopap_improved(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_improved::(crate::INPUT)); } - #[bench] - fn bench_part_2_nicopap_original_without_windows(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_nicopap_original_without_windows(crate::INPUT)); + + #[test] + fn test_nicopap_improved_with_solution() { + assert_eq!(solve_nicopap_improved::(crate::INPUT), 3708) + } + + #[test] + fn test_nicopap_improved_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_improved::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_2_nicopap_improved(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_nicopap_improved(crate::INPUT)); + fn bench_nicopap_improved_again(bencher: &mut test::Bencher) { + bencher.iter(|| solve_nicopap_improved_again::(crate::INPUT)); + } + + #[test] + fn test_nicopap_improved_again_with_solution() { + assert_eq!( + solve_nicopap_improved_again::(crate::INPUT), + 3708 + ) + } + + #[test] + fn test_nicopap_improved_again_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_nicopap_improved_again::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_2_nicopap_improved_again(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_nicopap_improved_again(crate::INPUT)); + fn bench_manevillef(bencher: &mut test::Bencher) { + bencher.iter(|| solve_manevillef::(crate::INPUT)); + } + + #[test] + fn test_manevillef_with_solution() { + assert_eq!(solve_manevillef::(crate::INPUT), 3708) + } + + #[test] + fn test_manevillef_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_manevillef::(sample_input), sample_answer) + } } #[bench] - fn bench_part_2_manevillef(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_manevillef(crate::INPUT)); + fn bench_manevillef_again(bencher: &mut test::Bencher) { + bencher.iter(|| solve_manevillef_again::(crate::INPUT)); + } + + #[test] + fn test_manevillef_again_with_solution() { + assert_eq!(solve_manevillef_again::(crate::INPUT), 3708) + } + + #[test] + fn test_manevillef_again_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_manevillef_again::(sample_input), + sample_answer + ) + } } #[bench] - fn bench_part_2_harudagondi(bencher: &mut test::Bencher) { - bencher.iter(|| super::part_2_harudagondi(crate::INPUT)); + fn bench_harudagondi(bencher: &mut test::Bencher) { + bencher.iter(|| solve_harudagondi::(crate::INPUT)); + } + + #[test] + fn test_harudagondi_with_solution() { + assert_eq!(solve_harudagondi::(crate::INPUT), 3708) + } + + #[test] + fn test_harudagondi_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_harudagondi::(sample_input), + sample_answer + ) + } + } + + /* Requires phf crate + #[bench] + fn bench_vertesians(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians::(crate::INPUT)); + } + + #[test] + fn test_vertesians_with_solution() { + assert_eq!(solve_vertesians::(crate::INPUT), 3708) + } + + #[test] + fn test_vertesians_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!(solve_vertesians::(sample_input), sample_answer) + } + } + */ + + #[bench] + fn bench_vertesians_nodeps(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_with_solution() { + assert_eq!(solve_vertesians_nodeps::(crate::INPUT), 3708) + } + + #[test] + fn test_vertesians_nodeps_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps::(sample_input), + sample_answer + ) + } + } + + #[bench] + fn bench_vertesians_nodeps_improved(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps_improved::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_improved_with_solution() { + assert_eq!( + solve_vertesians_nodeps_improved::(crate::INPUT), + 3708 + ) + } + + #[test] + fn test_vertesians_nodeps_improved_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps_improved::(sample_input), + sample_answer + ) + } + } + + #[bench] + fn bench_vertesians_nodeps_const(bencher: &mut test::Bencher) { + bencher.iter(|| solve_vertesians_nodeps_const::(crate::INPUT)); + } + + #[test] + fn test_vertesians_nodeps_const_with_solution() { + assert_eq!( + solve_vertesians_nodeps_const::(crate::INPUT), + 3708 + ) + } + + #[test] + fn test_vertesians_nodeps_const_with_sample_solutions() { + for (sample_input, sample_answer) in SAMPLES { + assert_eq!( + solve_vertesians_nodeps_const::(sample_input), + sample_answer + ) + } } } diff --git a/rust/src/day06/solutions.rs b/rust/src/day06/solutions.rs new file mode 100644 index 0000000..3f6039e --- /dev/null +++ b/rust/src/day06/solutions.rs @@ -0,0 +1,528 @@ +#![allow(dead_code)] + +// My original solution +pub(crate) fn solve_vec(input: &'static str) -> usize { + for idx in 0..(input.len() - N) { + let mut unique_chars = input[idx..idx + N].chars().collect::>(); + unique_chars.sort(); + unique_chars.dedup(); + if unique_chars.len() == N { + return idx + N; + } + } + unreachable!("We should always find an answer"); +} + +pub(crate) fn solve_hashset(input: &'static str) -> usize { + for idx in 0..(input.len() - N) { + let unique_chars = input[idx..idx + N] + .chars() + .collect::>(); + if unique_chars.len() == N { + return idx + N; + } + } + unreachable!("We should always find an answer"); +} + +pub(crate) fn solve_nicopap_original(input: &'static str) -> usize { + nicopap_original::first_sop(input.as_bytes(), N).unwrap() +} + +mod nicopap_original { + fn all_distinct(slice: &[T]) -> bool { + let mut slice_copy = slice.to_vec(); + slice_copy.sort_unstable(); + slice_copy.windows(2).all(|pair| pair[0] != pair[1]) + } + // sop stands for "start of packet" + pub(crate) fn first_sop( + datastream: &[T], + required_distinct: usize, + ) -> Option { + datastream + .windows(required_distinct) + .enumerate() + .find_map(|(i, slice)| all_distinct(slice).then_some(i + required_distinct)) + } +} + +pub(crate) fn solve_nicopap_original_without_windows(input: &'static str) -> usize { + let mut cache = Vec::with_capacity(N); + for (i, char) in input.chars().enumerate() { + cache.insert(0, char); + if cache.len() >= N { + let mut sorted_cache = cache.clone(); + sorted_cache.sort_unstable(); + sorted_cache.dedup(); + if sorted_cache.len() == N { + return i + 1; + } + cache.pop(); + } + } + unreachable!() +} + +pub(crate) fn solve_nicopap_improved(input: &'static str) -> usize { + nicopap_improved::first_sop(input.as_bytes(), N).unwrap() +} + +mod nicopap_improved { + fn all_distinct(slice: &[T], collect_vec: &mut Vec) -> bool { + collect_vec.clear(); + slice.clone_into(collect_vec); + collect_vec.sort_unstable(); + collect_vec.windows(2).all(|pair| pair[0] != pair[1]) + } + // sop stands for "start of packet" + pub(crate) fn first_sop( + datastream: &[T], + required_distinct: usize, + ) -> Option { + let mut collect_vec = Vec::with_capacity(required_distinct); + datastream + .windows(required_distinct) + .enumerate() + .find_map(|(i, slice)| { + all_distinct(slice, &mut collect_vec).then_some(i + required_distinct) + }) + } +} + +pub(crate) fn solve_nicopap_improved_again(input: &'static str) -> usize { + nicopap_improved_again::first_sop::(input.as_bytes()).unwrap() +} + +mod nicopap_improved_again { + struct AlphabetSet(u32); + impl AlphabetSet { + const fn new() -> Self { + Self(0) + } + /// Add letter to set, return `true` if it is already part of it. + /// `symbol` must be an ascii latin alphabet letter. + fn add_letter_or_contains(&mut self, symbol: u8) -> bool { + let to_set = 1 << (symbol.wrapping_sub(b'a') as u32); + let already_set = self.0 & to_set == to_set; + self.0 |= to_set; + already_set + } + } + fn all_distinct(slice: &[u8]) -> bool { + let mut set = AlphabetSet::new(); + !slice + .iter() + .any(|letter| set.add_letter_or_contains(*letter)) + } + + // sop stands for "start of packet" + pub(crate) fn first_sop(datastream: &[u8]) -> Option { + datastream + .array_windows::() + .enumerate() + .find_map(|(i, slice)| all_distinct(slice).then_some(i + W)) + } +} + +pub(crate) fn solve_manevillef(input: &'static str) -> usize { + manevillef::find_marker(&input.chars().collect::>(), N).unwrap() +} + +mod manevillef { + use std::collections::HashSet; + + pub(crate) fn find_marker(chars: &[char], window: usize) -> Option { + chars + .windows(window) + .into_iter() + .position(|items| { + let set: HashSet = items.iter().copied().collect(); + set.len() == window + }) + .map(|p| p + window) + } +} + +pub(crate) fn solve_manevillef_again(input: &'static str) -> usize { + let bytes = input.as_bytes(); + let mut cache = bytes[..N].to_owned(); + for (i, byte) in bytes[N..].iter().enumerate() { + let mut sorted = cache.clone(); + sorted.sort_unstable(); + sorted.dedup(); + if sorted.len() == N { + return i + N; + } + cache.rotate_left(1); + cache[N - 1] = *byte; + } + unreachable!() +} + +pub fn solve_snaketwix(input: &'static str) -> usize { + let mut number_of_duplicates = 0; + let mut char_map = std::collections::HashMap::new(); + let mut char_deque = std::collections::VecDeque::with_capacity(N + 1); + + let mut ans = 0; + + for (index, char) in input.char_indices() { + let entry = char_map.entry(char).or_insert(0); + *entry += 1; + + if *entry > 1 { + number_of_duplicates += 1; + } + + char_deque.push_back(char); + + if index > N - 1 { + let char = char_deque.pop_front().unwrap(); + + // Know that the entry exists, but not sure how to unwrap the entry to get access to the value + let entry = char_map.entry(char).or_default(); + *entry -= 1; + + if *entry >= 1 { + number_of_duplicates -= 1; + } + + if number_of_duplicates == 0 { + ans = index + 1; + break; + } + } + } + + ans +} + +pub fn solve_snaketwix_modified(input: &'static str) -> usize { + let mut number_of_duplicates = 0; + let mut char_map = std::collections::HashMap::new(); + let mut char_deque = std::collections::VecDeque::with_capacity(N + 1); + + let mut ans = 0; + + for (index, char) in input.char_indices() { + let entry = char_map.entry(char).or_insert(0); + *entry += 1; + + if *entry > 1 { + number_of_duplicates += 1; + } + + char_deque.push_back(char); + + if index > N - 1 { + let char = char_deque.pop_front().unwrap(); + + let entry = match char_map.entry(char) { + std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), + std::collections::hash_map::Entry::Vacant(_) => unreachable!(), + }; + *entry -= 1; + + if *entry >= 1 { + number_of_duplicates -= 1; + } + + if number_of_duplicates == 0 { + ans = index + 1; + break; + } + } + } + + ans +} + +pub fn solve_harudagondi(input: &'static str) -> usize { + harudagondi::solve_part2::(input) +} + +mod harudagondi { + use std::collections::{HashSet, VecDeque}; + + #[derive(Default)] + struct Solver { + buffer: VecDeque, + counter: usize, + buffer_size: usize, + } + + impl Solver { + fn new(buffer_size: usize) -> Self { + Self { + buffer: VecDeque::new(), + counter: 0, + buffer_size, + } + } + + fn update(&mut self, c: char) { + if self.buffer.len() < self.buffer_size { + self.buffer.push_back(c); + } else { + self.buffer.pop_front(); + self.buffer.push_back(c); + } + self.counter += 1; + } + + fn starter(&self) -> Option { + let buffer = self.buffer.iter().fold(HashSet::new(), |mut acc, c| { + acc.insert(*c); + acc + }); + + if buffer.len() == self.buffer_size { + Some(self.counter) + } else { + None + } + } + } + + pub fn solve_part2(input: &'static str) -> usize { + let mut solver = Solver::new(N); + for c in input.chars() { + solver.update(c); + if let Some(counter) = solver.starter() { + return counter; + } + } + unreachable!(); + } +} + +/* Requires phf crate +pub fn solve_vertesians(input: &'static str) -> usize { + vertesians::main::(input) +} + +mod vertesians { + pub(super) fn main(input: &'static str) -> usize { + let data = input + .chars() + .map(|c| *SYMBOL_MAP.get(&c).expect("Invalid characters in input")) + .collect::>(); + + assert!(!data.is_empty(), "Received empty input"); + + process::(data) + } + + fn process(data: Vec) -> usize { + let mut counter = 0; + + loop { + let combined = { + let mut combined = 0; + + for i in 0..N { + combined |= data[counter + i]; + } + + combined + }; + + if check::(combined) { + return counter + N; + } else { + counter += 1; + } + } + } + + static SYMBOL_MAP: phf::Map = phf::phf_map! { + 'a' => 0b0000_0000_0000_0000_0000_0000_0000_0001, + 'b' => 0b0000_0000_0000_0000_0000_0000_0000_0010, + 'c' => 0b0000_0000_0000_0000_0000_0000_0000_0100, + 'd' => 0b0000_0000_0000_0000_0000_0000_0000_1000, + 'e' => 0b0000_0000_0000_0000_0000_0000_0001_0000, + 'f' => 0b0000_0000_0000_0000_0000_0000_0010_0000, + 'g' => 0b0000_0000_0000_0000_0000_0000_0100_0000, + 'h' => 0b0000_0000_0000_0000_0000_0000_1000_0000, + 'i' => 0b0000_0000_0000_0000_0000_0001_0000_0000, + 'j' => 0b0000_0000_0000_0000_0000_0010_0000_0000, + 'k' => 0b0000_0000_0000_0000_0000_0100_0000_0000, + 'l' => 0b0000_0000_0000_0000_0000_1000_0000_0000, + 'm' => 0b0000_0000_0000_0000_0001_0000_0000_0000, + 'n' => 0b0000_0000_0000_0000_0010_0000_0000_0000, + 'o' => 0b0000_0000_0000_0000_0100_0000_0000_0000, + 'p' => 0b0000_0000_0000_0000_1000_0000_0000_0000, + 'q' => 0b0000_0000_0000_0001_0000_0000_0000_0000, + 'r' => 0b0000_0000_0000_0010_0000_0000_0000_0000, + 's' => 0b0000_0000_0000_0100_0000_0000_0000_0000, + 't' => 0b0000_0000_0000_1000_0000_0000_0000_0000, + 'u' => 0b0000_0000_0001_0000_0000_0000_0000_0000, + 'v' => 0b0000_0000_0010_0000_0000_0000_0000_0000, + 'w' => 0b0000_0000_0100_0000_0000_0000_0000_0000, + 'x' => 0b0000_0000_1000_0000_0000_0000_0000_0000, + 'y' => 0b0000_0001_0000_0000_0000_0000_0000_0000, + 'z' => 0b0000_0010_0000_0000_0000_0000_0000_0000, + }; + + fn check(combined: u32) -> bool { + let mut flipped_count = 0; + + for position in 0..26 { + let filtered = combined & !(1 << position); + + if filtered != combined { + flipped_count += 1; + } + + if flipped_count == N { + return true; + } + } + + false + } +} +*/ + +pub fn solve_vertesians_nodeps(input: &'static str) -> usize { + vertesians_nodeps::main::(input) +} + +mod vertesians_nodeps { + pub(super) fn main(input: &'static str) -> usize { + let data = input + .chars() + .map(|c| 1 << (c as u32 - 0x61)) + .collect::>(); + + assert!(!data.is_empty(), "Received empty input"); + + process::(data) + } + + fn process(data: Vec) -> usize { + let mut counter = 0; + + loop { + let combined = { + let mut combined = 0; + + for i in 0..N { + combined |= data[counter + i]; + } + + combined + }; + + if check::(combined) { + return counter + N; + } else { + counter += 1; + } + } + } + + fn check(combined: u32) -> bool { + let mut flipped_count = 0; + + for position in 0..26 { + let filtered = combined & !(1 << position); + + if filtered != combined { + flipped_count += 1; + } + + if flipped_count == N { + return true; + } + } + + false + } +} + +pub fn solve_vertesians_nodeps_improved(input: &'static str) -> usize { + vertesians_nodeps_improved::main::(input) +} + +mod vertesians_nodeps_improved { + pub(super) fn main(input: &'static str) -> usize { + let data = input + .chars() + .map(|c| 1 << (c as u32 - 0x61)) + .collect::>(); + + assert!(!data.is_empty(), "Received empty input"); + + process::(data) + } + + fn process(data: Vec) -> usize { + let mut counter = 0; + + loop { + let combined = { + let mut combined = 0; + + for i in 0..N { + combined |= data[counter + i]; + } + + combined + }; + + // Thanks to Gibonius for the suggestion to use u32::count_ones() + if (combined & u32::MAX).count_ones() == N as u32 { + return counter + N; + } else { + counter += 1; + } + } + } +} + +pub const fn solve_vertesians_nodeps_const(input: &'static str) -> usize { + // For 0ns results + // match N { + // 4 => nicopap_vertesians_const::RESULT_PART_1, + // 14 => nicopap_vertesians_const::RESULT_PART_2, + // _ => unreachable!(), + // } + nicopap_vertesians_const::process::(input.as_bytes()) +} + +mod nicopap_vertesians_const { + pub(super) const RESULT_PART_1: usize = process::<4>(include_bytes!("./input.txt")); + pub(super) const RESULT_PART_2: usize = process::<14>(include_bytes!("./input.txt")); + + pub(super) const fn process(data: &'static [u8]) -> usize { + let mut counter = 0; + + loop { + let combined = { + let mut combined = 0; + let mut i = 0; + loop { + if i == N { + break; + } + combined |= 1 << (data[counter + i] - b'a') as u32; + i += 1; + } + combined + }; + + if check::(combined) { + return counter + N; + } else { + counter += 1; + } + } + } + + const fn check(combined: u32) -> bool { + let flipped_count = (combined & 0b11_1111_1111_1111_1111_1111_1111).count_ones(); + flipped_count == (N as u32) + } +}