diff --git a/euler51/src/combination.rs b/euler51/src/combination.rs index 92c7935..c246174 100644 --- a/euler51/src/combination.rs +++ b/euler51/src/combination.rs @@ -1,5 +1,6 @@ -struct PositionCombinations { +pub struct PositionCombinations { min_length: usize, + max_length: usize, state: Vec, finished: bool, } @@ -8,13 +9,14 @@ impl Iterator for PositionCombinations { type Item = Vec; fn next(&mut self) -> Option { - if self.finished { - return None; - } loop { let result = self.state.clone(); self.increment(); - if result.iter().filter(|x| **x).count() >= self.min_length { + if self.finished { + return None; + } + if (self.min_length..=self.max_length).contains(&result.iter().filter(|x| **x).count()) + { return Some(result); } } @@ -22,11 +24,12 @@ impl Iterator for PositionCombinations { } impl PositionCombinations { - fn new(min_length: usize, max_length: usize) -> Self { - let state = vec![false; max_length]; + pub fn new(min_length: usize, max_length: usize, length: usize) -> Self { + let state = vec![false; length]; let finished = false; Self { min_length, + max_length, state, finished, } @@ -46,15 +49,25 @@ impl PositionCombinations { } } +pub trait ToString { + fn to_string(&self) -> String; +} + +impl ToString for Vec { + fn to_string(&self) -> String { + self.iter().map(|b| if *b { 'x' } else { '.' }).collect() + } +} + #[cfg(test)] mod test { - #[test] - fn test_combination() { - use crate::combination::PositionCombinations; - use std::collections::HashSet; + use crate::combination::PositionCombinations; + use std::collections::HashSet; - let combinator = PositionCombinations::new(2, 4); - let result: HashSet<_> = combinator.into_iter().collect(); + #[test] + fn test_combination_2_3_4() { + let combinator = PositionCombinations::new(2, 3, 4); + let result: Vec<_> = combinator.into_iter().collect(); let mut number_solutions = result.len(); let mut solution = HashSet::new(); solution.insert(vec![false, false, true, true]); @@ -67,7 +80,22 @@ mod test { solution.insert(vec![true, false, true, true]); solution.insert(vec![true, true, false, true]); solution.insert(vec![true, true, true, false]); - solution.insert(vec![true, true, true, true]); + for item in solution { + assert!(result.contains(&item)); + number_solutions -= 1; + } + assert_eq!(number_solutions, 0); + } + + #[test] + fn test_combination_2_2_3() { + let combinator = PositionCombinations::new(2, 2, 3); + let result: Vec<_> = combinator.into_iter().collect(); + let mut number_solutions = result.len(); + let mut solution = HashSet::new(); + solution.insert(vec![false, true, true]); + solution.insert(vec![true, false, true]); + solution.insert(vec![true, true, false]); for item in solution { assert!(result.contains(&item)); number_solutions -= 1; diff --git a/euler51/src/macros.rs b/euler51/src/macros.rs new file mode 100644 index 0000000..aa57676 --- /dev/null +++ b/euler51/src/macros.rs @@ -0,0 +1,3 @@ +macro_rules! dbg_println { + ($($arg:tt)*) => (#[cfg(debug_assertions)] println!($($arg)*)); +} diff --git a/euler51/src/main.rs b/euler51/src/main.rs index 09f0359..c8c7530 100644 --- a/euler51/src/main.rs +++ b/euler51/src/main.rs @@ -1,79 +1,133 @@ mod combination; mod primes; +#[macro_use] +mod macros; + +use crate::combination::{PositionCombinations, ToString}; use crate::primes::Primes; -type Int = i64; +type Int = u64; -const MIN_VALUE: Int = 10_000; -const MAX_VALUE: Int = 99_999; +// EXAMPLE 1 +// const MIN_VALUE: Int = 10; +// const MAX_VALUE: Int = 99; +// const LENGTH: usize = 2; +// const MIN_LENGTH: usize = 1; -const DIGITS: [Int; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; -// const MIN_VALUE: Int = 10_000_000; -// const MAX_VALUE: Int = 10_010_000; -// const MAX_VALUE: Int = 99_999_999; - -// const LENGTH_OF_NUMBERS: usize = 5; +// OWN TEST +// const MIN_VALUE: Int = 100; +// const MAX_VALUE: Int = 999; +// const LENGTH: usize = 3; // const MIN_LENGTH: usize = 2; +// EXAMPLE 2 +// const MIN_VALUE: Int = 10_000; +// const MAX_VALUE: Int = 99_999; +// const LENGTH: usize = 5; +// const MIN_LENGTH: usize = 2; + +// FINAL +const MIN_VALUE: Int = 100_000; +const MAX_VALUE: Int = 999_999; +const LENGTH: usize = 6; +const MIN_LENGTH: usize = 2; + +const MAX_LENGTH: usize = LENGTH - 1; +const DIGITS: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + fn main() { - let primes = Primes::get_between(MIN_VALUE, MAX_VALUE); - // let mut new_primes = primes.clone(); - for p in primes.vector.iter() { - let mut candidates = vec![]; - let digits = num_to_digits(p); - for d in DIGITS { - let mut these_digits = digits.clone(); - these_digits[3] = d; - these_digits[4] = d; - let num = digits_to_num(these_digits); - if primes.set.contains(&num) { - candidates.push(num) + let all_primes = Primes::get_between(MIN_VALUE, MAX_VALUE); + let mut best_of_all_combinations = vec![]; + for pattern in PositionCombinations::new(MIN_LENGTH, MAX_LENGTH, LENGTH) { + dbg_println!("{}", pattern.to_string()); + let mut best_this_combination = vec![]; + let mut primes_to_check = all_primes.clone(); + while let Some(first) = primes_to_check.pop() { + let mut primes_matching_pattern = vec![]; + let digits = first.to_digits(); + for digit in DIGITS { + let could_be_prime = digits + .iter() + .zip(pattern.clone()) + .map(|(d, b)| if b { digit } else { *d }) + .collect::>() + .to_num(); + if primes_to_check.contains(&could_be_prime) || could_be_prime == first { + primes_matching_pattern.push(could_be_prime); + } + } + if primes_matching_pattern.len() > 2 { + best_this_combination.push(primes_matching_pattern) } } - if candidates.len() > 4 { - println!("candidates"); - for c in &candidates { - // new_primes.remove(c); - println!("{c}"); - } + if !best_this_combination.is_empty() { + best_of_all_combinations.push(get_longest(best_this_combination, false)); } } - // println!("new primes"); - // for p in new_primes.vector.iter() { - // println!("{p}"); - // } -} -fn num_to_digits(n: &Int) -> Vec { - n.to_string() - .chars() - .map(|x| x.to_digit(10).unwrap() as Int) - .collect() + println!("{:#?}", get_longest(best_of_all_combinations, false)); } -fn digits_to_num(vec: Vec) -> Int { - let mut result = 0; - let mut position = 1; - for digit in vec.iter().rev() { - result += digit * position; - position *= 10; +fn get_longest(mut vec: Vec>, print: bool) -> Vec { + if vec.is_empty() { + panic!("cannot give the longest element of an empty vector") + } + if print { + println!("{:#?}", vec); + } + vec.sort_by_key(|c| c.len()); + let length = vec.last().unwrap().len(); + vec.into_iter().find(|v| v.len() == length).unwrap() +} + +impl ToString for Vec { + fn to_string(&self) -> String { + self.iter().map(|d| d.to_string()).collect() + } +} + +trait ToDigits { + fn to_digits(&self) -> Vec; +} + +trait ToNum { + fn to_num(&self) -> Int; +} + +impl ToDigits for Int { + fn to_digits(&self) -> Vec { + self.to_string() + .chars() + .map(|x| x.to_digit(10).unwrap() as u8) + .collect() + } +} + +impl ToNum for Vec { + fn to_num(&self) -> Int { + let mut result = 0; + let mut position = 1; + for digit in self.iter().rev() { + result += *digit as Int * position; + position *= 10; + } + result } - result } #[cfg(test)] mod test { - use crate::{digits_to_num, num_to_digits}; + use crate::{ToDigits, ToNum}; #[test] fn test_digits_to_num() { let digits = vec![5, 7, 3, 8, 1]; - assert_eq!(digits_to_num(digits), 57381); + assert_eq!(digits.to_num(), 57381); } #[test] fn test_num_to_digits() { let num = 83371; - assert_eq!(num_to_digits(&num), vec![8, 3, 3, 7, 1]); + assert_eq!(num.to_digits(), vec![8, 3, 3, 7, 1]); } } diff --git a/euler51/src/primes.rs b/euler51/src/primes.rs index b26d409..9b8af4a 100644 --- a/euler51/src/primes.rs +++ b/euler51/src/primes.rs @@ -7,9 +7,9 @@ use std::collections::HashSet; use std::hash::Hash; #[derive(Clone)] -pub struct Primes { - pub vector: Vec, - pub set: HashSet, +pub struct Primes { + vector: Vec, + set: HashSet, } impl Primes @@ -38,6 +38,7 @@ where set.insert(prime); } } + vector.reverse(); // so that self.pop removes the smallest prime Primes { vector, set } } @@ -70,6 +71,23 @@ where self.set.remove(num); }; } + + // pub fn len(&self) -> usize { + // debug_assert_eq!(self.vector.len(), self.set.len()); + // self.vector.len() + // } + + pub fn pop(&mut self) -> Option { + let result = self.vector.pop(); + if let Some(value) = result { + self.set.remove(&value); + } + result + } + + pub fn contains(&self, val: &T) -> bool { + self.set.contains(val) + } } #[cfg(test)] @@ -80,6 +98,23 @@ mod test { fn test_example_prime() { let primes = Primes::get_between(2, 99_999); assert!(primes.set.contains(&56003)); + assert!(primes.set.contains(&56993)); assert!(!primes.set.contains(&56002)); } + + #[test] + fn test_remove() { + let mut primes = Primes::get_between(2, 9); + assert!(primes.contains(&7)); + primes.remove(&7); + assert!(!primes.contains(&7)); + } + + #[test] + fn test_pop() { + let mut primes = Primes::get_between(10, 20); + assert!(primes.contains(&11)); + primes.pop(); + assert!(!primes.contains(&11)); + } }