diff --git a/lib/dijkstra/src/lib.rs b/lib/dijkstra/src/lib.rs index bc1eed1..8e87a8b 100644 --- a/lib/dijkstra/src/lib.rs +++ b/lib/dijkstra/src/lib.rs @@ -96,18 +96,18 @@ where while !(self.unvisited_without_distance.is_empty() && self.unvisited_with_distance.is_empty()) { - let coord = self.get_unvisited_coord_with_smallest_distance_from_start(); - let neighbours = self.get_neighbours(&coord); - let previous_distance = *self.matrix.get(coord).unwrap().get_distance().unwrap(); + let to_visit = self.get_unvisited_coord_with_smallest_distance_from_start(); + self.visit(to_visit); + let neighbours = self.get_neighbours(&to_visit); + let previous_distance = *self.matrix.get(to_visit).unwrap().get_distance().unwrap(); for neighbour in neighbours { - self.update_neighbour(neighbour, coord, previous_distance); + self.update_neighbour(neighbour, to_visit, previous_distance); } } } fn get_unvisited_coord_with_smallest_distance_from_start(&mut self) -> Coordinate { - let coord = self - .unvisited_with_distance + self.unvisited_with_distance .iter() .filter_map(|c| { let node = self.matrix.get(*c).unwrap(); @@ -115,9 +115,11 @@ where }) .reduce(|(c0, v0), (c1, v1)| if v0 < v1 { (c0, v0) } else { (c1, v1) }) .map(|(c, _)| *c) - .unwrap_or(self.start); + .unwrap_or(self.start) + } + + fn visit(&mut self, coord: (usize, usize)) { self.unvisited_with_distance.remove(&coord); - coord } fn get_neighbours(&self, coordinate: &Coordinate) -> Vec { diff --git a/lib/primes/src/lib.rs b/lib/primes/src/lib.rs index dd5f505..040a978 100644 --- a/lib/primes/src/lib.rs +++ b/lib/primes/src/lib.rs @@ -6,12 +6,13 @@ use std::fs::File; use std::hash::Hash; use std::io::Write; use std::slice::Iter; +use std::vec::IntoIter; use num::FromPrimitive; use num::Num; use num::ToPrimitive; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Primes { vector: Vec, set: HashSet, @@ -110,6 +111,10 @@ where pub fn into_vec(self) -> Vec { self.vector } + + pub fn to_cloned_iter(&self) -> IntoIter { + self.vector.clone().into_iter() + } } #[cfg(test)] diff --git a/src/euler200/src/main.rs b/src/euler200/src/main.rs index 2797ffa..40f328b 100644 --- a/src/euler200/src/main.rs +++ b/src/euler200/src/main.rs @@ -13,39 +13,45 @@ fn main() { squbes.iterate(); squbes.iterate(); squbes.iterate(); - println!("{:?}", squbes.output()); + println!("{:?}", squbes.get_output()); } #[derive(Debug)] struct Squbes { squbes: HashSet, - new_squbes: Vec, - primes: IntoIter, + primes: Primes, + primes_iter: IntoIter, used_primes: Vec, current_prime: u64, } impl Squbes { pub fn new(max: u64) -> Self { - let mut primes = Primes::get_between(2, max).into_vec().into_iter(); - let first = primes.next().unwrap(); - let second = primes.next().unwrap(); + let primes = Primes::get_between(2, max); + let mut primes_iter = primes.to_cloned_iter(); + let first = primes_iter.next().unwrap(); + let second = primes_iter.next().unwrap(); Self { squbes: HashSet::new(), - new_squbes: vec![], primes, + primes_iter, used_primes: vec![first], current_prime: second, } } pub fn iterate(&mut self) { - self.calculate_squbes_with_current_prime(); + let squbes = self.calculate_squbes_with_current_prime(); self.save_current_prime(); self.update_current_prime(); + self.save_new_squbes(); } - pub fn output(&self) -> Vec { + fn save_new_squbes(&mut self) { + todo!() + } + + pub fn get_output(&self) -> Vec { let mut result: Vec<_> = self.squbes.iter().copied().collect(); result.sort_unstable(); result @@ -56,27 +62,27 @@ impl Squbes { } fn update_current_prime(&mut self) { - self.current_prime = self.primes.next().unwrap(); + self.current_prime = self.primes_iter.next().unwrap(); } - fn calculate_squbes_with_current_prime(&mut self) { - assert_ne!(self.current_prime, 0); - let iter = self - .used_primes - .iter() - .copied() - .zip([self.current_prime].into_iter().cycle()) - .chain( - [self.current_prime] - .into_iter() - .cycle() - .zip(self.used_primes.iter().copied()), - ) - .map(|(p, q)| Self::core(p, q)); - self.new_squbes.extend(iter); + fn calculate_squbes_with_current_prime(&mut self) -> Vec { + get_iterator_over_pairs_both_ways(&self.used_primes, self.current_prime) + .map(|(p, q)| Self::core(p, q)) + .collect() } fn core(p: u64, q: u64) -> u64 { p.pow(2) * q.pow(3) } } + +fn get_iterator_over_pairs_both_ways( + vec: &[u64], + value: u64, +) -> impl Iterator + '_ { + iterate_vec_and_scalar(vec, value).flat_map(|(x, y)| [(x, y), (y, x)].into_iter()) +} + +fn iterate_vec_and_scalar(vec: &[u64], value: u64) -> impl Iterator + '_ { + vec.iter().copied().zip([value].into_iter().cycle()) +} diff --git a/src/euler85/Cargo.toml b/src/euler85/Cargo.toml new file mode 100644 index 0000000..d5b1234 --- /dev/null +++ b/src/euler85/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "euler85" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/euler85/src/main.rs b/src/euler85/src/main.rs new file mode 100644 index 0000000..b26c2ab --- /dev/null +++ b/src/euler85/src/main.rs @@ -0,0 +1,66 @@ +const TARGET: usize = 2_000_000; + +fn main() { + let mut x = 1_000; + let mut y = 2; + + let mut grid = Grid::new(x, y); + let mut number; + let mut best_grid = grid.clone(); + let mut best_diff = 5_000_000; + loop { + number = grid.number_rectangles(); + let diff = number.abs_diff(TARGET); + if diff < best_diff { + best_diff = diff; + best_grid = grid.clone(); + } + if number < TARGET { + y += 1; + } else { + x -= 1; + } + if x == 1 { + break; + } + + grid = Grid::new(x, y); + } + + let area = best_grid.area(); + println!("{area}"); +} + +#[derive(Debug, Clone)] +struct Grid { + x: usize, + y: usize, +} + +impl Grid { + fn new(x: usize, y: usize) -> Self { + Self { x, y } + } + + fn number_rectangles(&self) -> usize { + let mut result = 0; + + for x in 1..=self.x { + for y in 1..=self.y { + result += self.fits(x, y); + } + } + + result + } + + fn fits(&self, x: usize, y: usize) -> usize { + let fits_in_x = self.x - (x - 1); + let fits_in_y = self.y - (y - 1); + fits_in_x * fits_in_y + } + + fn area(&self) -> usize { + self.x * self.y + } +} diff --git a/src/euler86/Cargo.toml b/src/euler86/Cargo.toml new file mode 100644 index 0000000..4e96566 --- /dev/null +++ b/src/euler86/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "euler86" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = "fat" +debug = true + diff --git a/src/euler86/src/main.rs b/src/euler86/src/main.rs new file mode 100644 index 0000000..9334990 --- /dev/null +++ b/src/euler86/src/main.rs @@ -0,0 +1,60 @@ +use std::f64::EPSILON; + +const TARGET: u32 = 1_000_000; + +fn main() { + let mut m = 1; + let mut distinct = 0; + + loop { + println!("trying: {m}"); + + for x in 1..m + 1 { + for y in x..m + 1 { + let z = m; + let c = Cuboid::new(x, y, z); + if c.shortest_path_is_integer() { + distinct += 1; + }; + } + } + + if distinct > TARGET { + break; + } + + m += 1; + } + + println!("\n{m}"); +} + +struct Cuboid { + x: u32, + y: u32, + z: u32, +} + +impl Cuboid { + fn new(x: u32, y: u32, z: u32) -> Self { + Self { x, y, z } + } + + fn shortest_path_is_integer(&self) -> bool { + // because of the way we iterator over x, y and z, these are guaranteed to be in order + // and therefore produces the shortest path + let shortest_path = pythagorean(self.x + self.y, self.z); + + is_integer(shortest_path) + } +} + +fn pythagorean(x: u32, y: u32) -> f64 { + ((x.pow(2) + y.pow(2)) as f64).sqrt() +} + +fn is_integer(result: f64) -> bool { + let rounded = result.round(); + + (result - rounded).abs() <= EPSILON +} diff --git a/src/euler92/Cargo.toml b/src/euler92/Cargo.toml new file mode 100644 index 0000000..93ea1b7 --- /dev/null +++ b/src/euler92/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "euler92" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = "fat" diff --git a/src/euler92/src/main.rs b/src/euler92/src/main.rs new file mode 100644 index 0000000..24c4785 --- /dev/null +++ b/src/euler92/src/main.rs @@ -0,0 +1,38 @@ +const MAXIMUM: usize = 10_000_000; + +fn main() { + let mut arrives_at_89 = 0; + + for i in 1..MAXIMUM { + let mut number = i; + loop { + let sum: usize = number + .to_digits() + .into_iter() + .map(|d| d.pow(2) as usize) + .sum(); + if sum == 89 { + arrives_at_89 += 1; + break; + } else if sum == 1 { + break; + } + number = sum; + } + } + + println!("{arrives_at_89}"); +} + +trait ToDigits { + fn to_digits(&self) -> Vec; +} + +impl ToDigits for usize { + fn to_digits(&self) -> Vec { + self.to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as _) + .collect() + } +} diff --git a/src/euler93/Cargo.toml b/src/euler93/Cargo.toml new file mode 100644 index 0000000..69a13a5 --- /dev/null +++ b/src/euler93/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "euler93" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +itertools = "*" diff --git a/src/euler93/src/main.rs b/src/euler93/src/main.rs new file mode 100644 index 0000000..e84eb7a --- /dev/null +++ b/src/euler93/src/main.rs @@ -0,0 +1,141 @@ +use std::cmp::Ordering; + +use itertools::Itertools; + +type Num = f64; +type Quadruple = [Num; 4]; + +const ALL_DIGITS: [Num; 10] = [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]; + +fn main() { + let mut digits_length = vec![]; + + for quadruple_vec in ALL_DIGITS.into_iter().permutations(4) { + let quadruple = vec_to_quadruple(quadruple_vec); + + let digits = Digits(quadruple); + + let permutations = digits.permutations(); + + let mut results: Vec<_> = permutations + .into_iter() + .flat_map(find_integer_results) + .collect(); + + results.sort_unstable_by(|n1, n2| { + if n1 < n2 { + Ordering::Less + } else { + Ordering::Greater + } + }); + + let result = results + .into_iter() + .tuple_windows() + .filter(|(v0, v1)| (v1 - v0) > Num::EPSILON) + .map(|(n1, _)| n1) // TODO last number may be missing? + .collect_vec(); + + let length = number_consecutive_positive_integers(result); + + digits_length.push((quadruple, length)); + } + + digits_length.sort_unstable_by(|a, b| a.1.cmp(&b.1)); + + let mut result = digits_length.last().unwrap().0; + + result.sort_by(Num::total_cmp); + + let s: String = result + .into_iter() + .map(|d| char::from_digit(d as u32, 10).unwrap()) + .collect(); + + println!("{s}"); +} + +fn number_consecutive_positive_integers(result: Vec) -> usize { + let length = result.len(); + + result + .into_iter() + .tuple_windows() + .filter(|(a, b)| b - 1.5 > *a) + .map(|(a, _)| a.round() as usize) + .next() + .unwrap_or(length) +} + +fn find_integer_results(quadruple: Quadruple) -> Vec { + let [i0, i1, i2, i3] = quadruple; + + let mut result = vec![]; + + for op1 in Operations::iter() { + for op2 in Operations::iter() { + for op3 in Operations::iter() { + let num = op3(op2(op1(i0, i1), i2), i3); + if num > 0. && is_integer(num) { + result.push(num); + } + } + } + } + + result +} + +fn is_integer(num: Num) -> bool { + (num.round() - num).abs() <= Num::EPSILON +} + +struct Digits(Quadruple); + +impl Digits { + fn permutations(&self) -> Vec { + self.0 + .into_iter() + .permutations(4) + .map(vec_to_quadruple) + .collect() + } +} + +fn vec_to_quadruple(v: Vec) -> Quadruple { + unsafe { + let i0 = *v.get_unchecked(0); + let i1 = *v.get_unchecked(1); + let i2 = *v.get_unchecked(2); + let i3 = *v.get_unchecked(3); + [i0, i1, i2, i3] + } +} + +struct Operations { + last: usize, +} + +impl Iterator for Operations { + type Item = Box Num>; + + fn next(&mut self) -> Option { + self.last += 1; + match self.last { + 1 => Some(Box::new(|l, r| l + r)), + 2 => Some(Box::new(|l, r| l - r)), + 3 => Some(Box::new(|l, r| r - l)), + 4 => Some(Box::new(|l, r| l * r)), + 5 => Some(Box::new(|l, r| l / r)), + 6 => Some(Box::new(|l, r| r / l)), + _ => None, + } + } +} + +impl Operations { + fn iter() -> Self { + Self { last: 0 } + } +} diff --git a/src/euler94/Cargo.toml b/src/euler94/Cargo.toml new file mode 100644 index 0000000..0f099c5 --- /dev/null +++ b/src/euler94/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "euler94" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +f128 = "0.2.9" +num-traits = "0.2.15" + +[profile.release] +lto = "fat" +codegen-units = 1 diff --git a/src/euler94/src/main.rs b/src/euler94/src/main.rs new file mode 100644 index 0000000..f6a0fa2 --- /dev/null +++ b/src/euler94/src/main.rs @@ -0,0 +1,55 @@ +type Num = usize; + +fn main() { + let mut sum = 0; + + let mut i = 5; + 'outer: loop { + if i > 333_333_334 { + break; + } + let a = i; + + if a % 2 == 0 { + i += 1; + continue; + } + + for c in [a - 1, a + 1] { + let t = Triangle::new(a, c); + + if t.has_integer_area() { + println!("{:.10}, {:.10}", t.a, t.c); + sum += t.perimeter(); + i = ((i as f64) * 3.59).trunc() as usize; + continue 'outer; + } + } + + i += 1; + } + + println!("{sum:.15}"); +} + +struct Triangle { + a: Num, + c: Num, +} + +impl Triangle { + fn new(a: Num, c: Num) -> Self { + Self { a, c } + } + + fn perimeter(&self) -> Num { + 2 * self.a + self.c + } + + fn has_integer_area(&self) -> bool { + let h = 4 * self.a.pow(2) - self.c.pow(2); + let root = (h as f64).sqrt().trunc() as usize; + + root * root == h + } +} diff --git a/src/euler95/Cargo.toml b/src/euler95/Cargo.toml new file mode 100644 index 0000000..269262f --- /dev/null +++ b/src/euler95/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "euler95" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +rstest = "0.17" + +[profile.release] +debug = true +lto = "fat" diff --git a/src/euler95/src/main.rs b/src/euler95/src/main.rs new file mode 100644 index 0000000..6decb83 --- /dev/null +++ b/src/euler95/src/main.rs @@ -0,0 +1,118 @@ +use std::collections::HashSet; + +const MAX: usize = 1_000_000; + +fn main() { + let mut chains = vec![]; + + let mut part_of_any_chain = HashSet::new(); + + for current in 1..MAX { + if part_of_any_chain.contains(¤t) { + continue; + } + try_build_chain_from_number(MAX, current, &mut chains, &mut part_of_any_chain); + } + + chains.sort_by_key(|chain| chain.len()); + + let longest = chains.last().unwrap(); + + let smallest = longest.iter().min().unwrap().to_owned(); + + println!("{smallest}"); +} + +fn build_proper_divisors(max: usize) -> Vec> { + todo!() +} + +fn try_build_chain_from_number( + max: usize, + mut current: usize, + chains: &mut Vec>, + part_of_any_chain: &mut HashSet, +) { + let mut maybe_chain = vec![]; + + loop { + maybe_chain.push(current); + + let next: usize = get_proper_divisors(current).into_iter().sum(); + + if next > max || next == 0 { + break; + } + + if maybe_chain.contains(&next) { + if maybe_chain.len() > 1 && *maybe_chain.first().unwrap() == next { + for number in &maybe_chain { + part_of_any_chain.insert(*number); + } + println!("{}", maybe_chain.first().unwrap()); + chains.push(maybe_chain); + } + break; + } + + current = next; + } +} + +fn get_proper_divisors(x: usize) -> Vec { + let mut result = vec![1]; + + let limit = x / 2 + 1; + + for i in 2..limit { + if result.contains(&i) { + break; + } + + if x % i == 0 { + let div_1 = i; + let div_2 = x / i; + result.push(div_1); + + if div_2 != div_1 { + result.push(div_2); + } + } + } + + result.sort_unstable(); + result +} + +#[cfg(test)] +mod tests { + + use rstest::rstest; + + #[rstest] + #[case(6, vec![1, 2, 3])] + #[case(28, vec![1, 2, 4, 7, 14])] + #[case(12_496, vec![1, 2, 4, 8, 11, 16, 22, 44, 71, 88, 142, 176, 284, 568, 781, 1136, 1562, 3124, 6248])] + fn proper_divisor(#[case] input: usize, #[case] expected: Vec) { + use crate::get_proper_divisors; + + assert_eq!(get_proper_divisors(input), expected); + } + + #[rstest] + #[case(220, vec![vec![220, 284]])] + #[case(12_496, vec![vec![12_496, 14_288, 15_472, 14_536, 14_264]])] + fn chain(#[case] input: usize, #[case] expected: Vec>) { + use std::collections::HashSet; + + use crate::try_build_chain_from_number; + + let max = expected.iter().flatten().max().unwrap().to_owned(); + let mut part_of_any_chain = HashSet::new(); + + let mut chains = vec![]; + try_build_chain_from_number(max, input, &mut chains, &mut part_of_any_chain); + + assert_eq!(chains, expected); + } +}