diff --git a/src/euler23/Cargo.toml b/src/euler23/Cargo.toml new file mode 100644 index 0000000..4da28f2 --- /dev/null +++ b/src/euler23/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "euler23" +version = "0.1.0" +edition = "2024" + +[dependencies] +itertools = "0.14.0" +proper_divisors = { version = "*", path = "../../lib/proper_divisors" } + +[dev-dependencies] +rstest = "0.25.0" diff --git a/src/euler23/src/main.rs b/src/euler23/src/main.rs new file mode 100644 index 0000000..f3afb80 --- /dev/null +++ b/src/euler23/src/main.rs @@ -0,0 +1,119 @@ +use itertools::Itertools; +use proper_divisors::ProperDivisors; + +const MIN: usize = 1; +// const MAX: usize = 500; +const MAX: usize = 28_123; + +fn main() { + let pd = ProperDivisors::get_between(MIN, MAX); + let abundant_numbers = AbundantNumbers::get_between(MIN, MAX, pd); + + // dbg!(abundant_numbers.can_be_sum_of_two_abundant_numbers(24)); + + let mut sum = 0; + for number in MIN..=MAX { + if !abundant_numbers.can_be_sum_of_two_abundant_numbers(number) { + //println!("{number}"); + sum += number; + } + } + println!("{sum}"); +} + +struct AbundantNumbers(Vec); + +impl AbundantNumbers { + fn get_between(smaller: usize, larger: usize, pd: ProperDivisors) -> Self { + let result = (smaller..=larger) + .filter(|n| Self::is_abundant_number(*n, &pd)) + .collect(); + + Self(result) + } + + fn can_be_sum_of_two_abundant_numbers(&self, number: usize) -> bool { + let (mut larger_idx, mut larger) = self.get_closest_abundant_number_and_index(number); + let (mut smaller_idx, mut smaller) = (0, 12); + + loop { + if larger < smaller { + break false; + } + let sum = larger + smaller; + if sum == number { + break true; + } + match sum.cmp(&number) { + std::cmp::Ordering::Less => { + smaller_idx += 1; + if let Some(result) = self.0.get(smaller_idx) { + smaller = *result; + } else { + break false; + } + } + std::cmp::Ordering::Equal => (), + std::cmp::Ordering::Greater => { + if larger_idx == 0 { + break false; + } + larger_idx = larger_idx.saturating_sub(1); + larger = self.0[larger_idx]; + } + } + } + } + + fn get_closest_abundant_number_and_index(&self, number: usize) -> (usize, usize) { + let mut offset = 0; + + loop { + let larger = number + offset; + if let Some((index, larger)) = self.0.iter().find_position(|n| **n == larger) { + return (index, *larger); + } + + let smaller = number.saturating_sub(offset); + + if let Some((index, smaller)) = self.0.iter().find_position(|n| **n == smaller) { + return (index, *smaller); + } + + offset += 1; + } + } + + fn is_abundant_number(number: usize, pd: &ProperDivisors) -> bool { + pd.get(number).iter().sum::() > number + } +} + +#[cfg(test)] +mod tests { + use proper_divisors::ProperDivisors; + use rstest::rstest; + + use crate::{can_be_sum_of_two_abundant_numbers, get_abundant_numbers, is_abundant_number}; + + #[rstest] + #[case(28, false)] + #[case(12, true)] + fn test_is_abundant_number(#[case] input: usize, #[case] expected: bool) { + let pd = ProperDivisors::get_between(2, input + 1); + let result = is_abundant_number(input, &pd); + + assert_eq!(result, expected); + } + + #[rstest] + #[case(24, true)] + #[case(23, false)] + fn test_can_be_sum_of_two_abundant_numbers(#[case] input: usize, #[case] expected: bool) { + let pd = ProperDivisors::get_between(2, input + 1); + let abundant_numbers = get_abundant_numbers(pd); + let result = can_be_sum_of_two_abundant_numbers(input, &abundant_numbers); + + assert_eq!(result, expected); + } +}