diff --git a/src/utils.rs b/src/utils.rs index ef85c82..6515f83 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -234,9 +234,6 @@ pub fn isqrt(mut num: i64) -> i64 { result } -/****************************************************************************** - * Objects. - *****************************************************************************/ mod objects; pub use objects::long::Long; pub use objects::pandigital_checker::PandigitalChecker; @@ -245,384 +242,19 @@ pub use objects::poker_hand::PokerHand; pub use objects::fraction::Fraction; -/****************************************************************************** - * Iterators. - *****************************************************************************/ - -/// Fibonacci sequence iterator. -pub struct Fibonacci { - a: i64, - b: i64, -} -impl Fibonacci { - pub fn new(a: i64, b: i64) -> Fibonacci { - Fibonacci { a, b } - } -} -impl Iterator for Fibonacci { - type Item = i64; - fn next(&mut self) -> Option { - let a = self.a; - (self.a, self.b) = (self.b, self.a + self.b); - Some(a) - } -} - -/// Triangular, pentagonal, hexagonal, etc. number iterator. Specify the -/// number of sides of the polygon as the argument to the constructor. -pub struct Polygonal { - increment: i64, - offset: i64, - num: i64, -} -impl Polygonal { - pub fn new(sides: i64) -> Polygonal { - Polygonal { - increment: sides - 2, - offset: 1, - num: 0, - } - } - /// Find the index at which the given number would appear in a sequence of - /// polygonal numbers (if it is a polygonal number). - /// - /// * `sides` - Number of sides of the polygon the sequence is based on. - /// * `num` - Number whose index is to be found. - /// - /// -> Index. - pub fn invert(sides: i64, num: i64) -> Result { - // A polygonal number is a quadratic function of the index it appears - // at. Solve for the positive root of the corresponding quadratic - // equation. - let a = sides - 2; - let b = 4 - sides; - let c = -2 * num; - let discriminant = b.pow(2) - 4 * a * c; - let idx = (-b as f64 + (discriminant as f64).sqrt()) / (2.0 * a as f64); - let idx_rounded = idx.round() as i64; - if idx.fract() == 0.0 { - Ok(idx_rounded) - } else { - Err(idx_rounded) - } - } -} -impl Iterator for Polygonal { - type Item = i64; - fn next(&mut self) -> Option { - self.num += self.offset; - self.offset += self.increment; - Some(self.num) - } -} - -/// Cubes iterator. Generates cubes of integers without multiplication or -/// exponentiation. -pub struct Cubes { - increment: i64, - offset: i64, - num: i64, -} -impl Cubes { - pub fn new() -> Cubes { - Cubes { - increment: 6, - offset: 1, - num: 0, - } - } -} -impl Iterator for Cubes { - type Item = i64; - fn next(&mut self) -> Option { - self.num += self.offset; - self.offset += self.increment; - self.increment += 6; - Some(self.num) - } -} - -/// Collatz sequence iterator. -pub struct Collatz { - num: i64, - done: bool, -} -impl Collatz { - pub fn new(num: i64) -> Collatz { - Collatz { num, done: false } - } -} -impl Iterator for Collatz { - type Item = i64; - fn next(&mut self) -> Option { - if self.done { - return None; - } - let num = self.num; - self.num = if self.num % 2 == 0 { - self.num / 2 - } else { - 3 * self.num + 1 - }; - self.done = num == 1; - Some(num) - } -} - -/// Divisors iterator. Generates all divisors of a number in an unspecified -/// order. Positive numbers only! -pub struct Divisors { - dividend: i64, - limit: i64, - current: i64, - other: i64, -} -impl Divisors { - pub fn new(dividend: i64) -> Divisors { - Divisors { - dividend, - limit: isqrt(dividend), - current: 0, - other: 0, - } - } -} -impl Iterator for Divisors { - type Item = i64; - fn next(&mut self) -> Option { - if self.other > 0 { - let other = self.other; - self.other = 0; - return Some(other); - } - loop { - self.current += 1; - if self.current > self.limit { - return None; - } - if self.dividend % self.current == 0 { - let other = self.dividend / self.current; - if other != self.current { - self.other = other; - } - return Some(self.current); - } - } - } -} +mod iterators; +pub use iterators::fibonacci::Fibonacci; +pub use iterators::polygonal::Polygonal; +pub use iterators::cubes::Cubes; +pub use iterators::collatz::Collatz; +pub use iterators::divisors::Divisors; +pub use iterators::prime_divisors::PrimeDivisors; +pub use iterators::digits::Digits; +pub use iterators::bits::Bits; +pub use iterators::pythagorean_triplets::PythagoreanTriplets; +pub use iterators::potential_primes::PotentialPrimes; +pub use iterators::continued_fraction::ContinuedFraction; -/// Prime divisors iterator. Generates all prime divisors of a number in -/// ascending order. Positive numbers only! -pub struct PrimeDivisors { - // If I actually find all prime numbers to iterate over (instead of just - // using potential prime numbers), performance drops significantly. - potential_primes: PotentialPrimes, - num: i64, -} -impl PrimeDivisors { - pub fn new(num: i64) -> PrimeDivisors { - PrimeDivisors { - potential_primes: PotentialPrimes::new(num), - num, - } - } -} -impl Iterator for PrimeDivisors { - type Item = (i64, u32); - fn next(&mut self) -> Option<(i64, u32)> { - loop { - let potential_prime = match self.potential_primes.next() { - Some(potential_prime) if potential_prime <= self.num => potential_prime, - _ => return None, - }; - let mut power = 0; - while self.num % potential_prime == 0 { - self.num /= potential_prime; - power += 1; - } - // Since I divide out the number by all potential primes I find, - // the potential primes I do find are actually prime. - if power > 0 { - return Some((potential_prime, power)); - } - } - } -} - -/// Digits iterator. Generates the decimal digits of a number from least -/// significant to most significant. Positive numbers only! -pub struct Digits { - num: i64, -} -impl Digits { - pub fn new(num: i64) -> Digits { - Digits { num } - } -} -impl Iterator for Digits { - type Item = i64; - fn next(&mut self) -> Option { - if self.num == 0 { - None - } else { - let digit = self.num % 10; - self.num /= 10; - Some(digit) - } - } -} - -/// Bits iterator. Generates the binary digits of a number from least -/// significant to most significant. Positive numbers only! -pub struct Bits { - num: i64, -} -impl Bits { - pub fn new(num: i64) -> Bits { - Bits { num } - } -} -impl Iterator for Bits { - type Item = i64; - fn next(&mut self) -> Option { - if self.num == 0 { - None - } else { - let bit = self.num & 1; - self.num >>= 1; - Some(bit) - } - } -} - -/// Pythagorean triplets iterator. Generates all Pythagorean triplets with the -/// given sum. The triplets are generated using a well-known parametrisation -/// which can represent any primitive triplet. Positive numbers only! -pub struct PythagoreanTriplets { - semiperimeter: i64, - m_ub: i64, - m: i64, -} -impl PythagoreanTriplets { - pub fn new(perimeter: i64) -> PythagoreanTriplets { - let semiperimeter = perimeter / 2; - // Non-strict upper bound for the parameter `m`. - let m_ub = isqrt(semiperimeter); - PythagoreanTriplets { - semiperimeter, - m_ub, - m: 1, - } - } -} -impl Iterator for PythagoreanTriplets { - type Item = (i64, i64, i64); - fn next(&mut self) -> Option<(i64, i64, i64)> { - loop { - self.m += 1; - if self.m > self.m_ub { - return None; - } - if self.semiperimeter % self.m != 0 { - continue; - } - let m = self.m; - let remaining_term = self.semiperimeter / m; - let remaining_odd = remaining_term >> remaining_term.trailing_zeros(); - let m_plus_n_lb = m + 1 + m % 2; - for m_plus_n in (m_plus_n_lb..) - .step_by(2) - .take_while(|&m_plus_n| m_plus_n < 2 * m && m_plus_n <= remaining_odd) - { - if remaining_odd % m_plus_n == 0 && gcd(m_plus_n, m) == 1 { - let d = remaining_term / m_plus_n; - let n = m_plus_n - m; - let a = (m.pow(2) - n.pow(2)) * d; - let b = 2 * m * n * d; - let c = (m.pow(2) + n.pow(2)) * d; - return Some((a, b, c)); - } - } - } - } -} - -/// Potential prime numbers. Generates some small prime numbers and numbers -/// coprime to 30. Used for wheel factorisation with 2, 3 and 5. -pub struct PotentialPrimes { - limit: i64, - num: i64, - offset: std::iter::Cycle>, -} -impl PotentialPrimes { - pub fn new(limit: i64) -> PotentialPrimes { - PotentialPrimes { - limit, - num: 1, - offset: [4, 2, 4, 2, 4, 6, 2, 6].into_iter().cycle(), - } - } -} -impl Iterator for PotentialPrimes { - type Item = i64; - fn next(&mut self) -> Option { - self.num += match self.num { - 1 => 1, - 2 => 1, - 3 | 5 => 2, - _ => self.offset.next().unwrap(), - }; - if self.num > self.limit { - None - } else { - Some(self.num) - } - } -} - -/// Generate the continued fraction representation of the square root of a -/// number. The elements generated after the first shall constitute the -/// repeating terms in the continued fraction. -pub struct ContinuedFraction { - num: i64, - a0: i64, - numerator_addend: i64, - denominator: i64, -} -impl ContinuedFraction { - pub fn new(num: i64) -> ContinuedFraction { - ContinuedFraction { - num, - a0: isqrt(num), - numerator_addend: 0, - denominator: 1, - } - } -} -impl Iterator for ContinuedFraction { - type Item = i64; - fn next(&mut self) -> Option { - // This will happen if the given number was a perfect square. We will - // also use this as the condition for detecting repeating terms. - if self.denominator == 0 { - return None; - } - - // If a part of the continued fraction is - // (num.sqrt() + numerator_addend) / denominator - // then the next term of the continued fraction, `numerator_addend` and - // `denominator` can be found using a recurrence relation. - let a = (self.a0 + self.numerator_addend) / self.denominator; - self.numerator_addend = a * self.denominator - self.numerator_addend; - self.denominator = (self.num - self.numerator_addend.pow(2)) / self.denominator; - - // When this happens, the terms will start repeating. - if a == self.a0 * 2 { - self.denominator = 0; - } - Some(a) - } -} #[cfg(test)] mod tests { diff --git a/src/utils/iterators.rs b/src/utils/iterators.rs new file mode 100644 index 0000000..0691620 --- /dev/null +++ b/src/utils/iterators.rs @@ -0,0 +1,11 @@ +pub mod fibonacci; +pub mod polygonal; +pub mod cubes; +pub mod collatz; +pub mod divisors; +pub mod prime_divisors; +pub mod digits; +pub mod bits; +pub mod pythagorean_triplets; +pub mod potential_primes; +pub mod continued_fraction; diff --git a/src/utils/iterators/bits.rs b/src/utils/iterators/bits.rs new file mode 100644 index 0000000..37c9f96 --- /dev/null +++ b/src/utils/iterators/bits.rs @@ -0,0 +1,24 @@ +/// Bits iterator. Generates the binary digits of a number from least +/// significant to most significant. Positive numbers only! +pub struct Bits { + num: i64, +} +impl Bits { + pub fn new(num: i64) -> Bits { + Bits { num } + } +} +impl Iterator for Bits { + type Item = i64; + fn next(&mut self) -> Option { + if self.num == 0 { + None + } else { + let bit = self.num & 1; + self.num >>= 1; + Some(bit) + } + } +} + + diff --git a/src/utils/iterators/collatz.rs b/src/utils/iterators/collatz.rs new file mode 100644 index 0000000..cd887c3 --- /dev/null +++ b/src/utils/iterators/collatz.rs @@ -0,0 +1,28 @@ +/// Collatz sequence iterator. +pub struct Collatz { + num: i64, + done: bool, +} +impl Collatz { + pub fn new(num: i64) -> Collatz { + Collatz { num, done: false } + } +} +impl Iterator for Collatz { + type Item = i64; + fn next(&mut self) -> Option { + if self.done { + return None; + } + let num = self.num; + self.num = if self.num % 2 == 0 { + self.num / 2 + } else { + 3 * self.num + 1 + }; + self.done = num == 1; + Some(num) + } +} + + diff --git a/src/utils/iterators/continued_fraction.rs b/src/utils/iterators/continued_fraction.rs new file mode 100644 index 0000000..ff4bb60 --- /dev/null +++ b/src/utils/iterators/continued_fraction.rs @@ -0,0 +1,46 @@ +use crate::utils; + +/// Generate the continued fraction representation of the square root of a +/// number. The elements generated after the first shall constitute the +/// repeating terms in the continued fraction. +pub struct ContinuedFraction { + num: i64, + a0: i64, + numerator_addend: i64, + denominator: i64, +} +impl ContinuedFraction { + pub fn new(num: i64) -> ContinuedFraction { + ContinuedFraction { + num, + a0: utils::isqrt(num), + numerator_addend: 0, + denominator: 1, + } + } +} +impl Iterator for ContinuedFraction { + type Item = i64; + fn next(&mut self) -> Option { + // This will happen if the given number was a perfect square. We will + // also use this as the condition for detecting repeating terms. + if self.denominator == 0 { + return None; + } + + // If a part of the continued fraction is + // (num.sqrt() + numerator_addend) / denominator + // then the next term of the continued fraction, `numerator_addend` and + // `denominator` can be found using a recurrence relation. + let a = (self.a0 + self.numerator_addend) / self.denominator; + self.numerator_addend = a * self.denominator - self.numerator_addend; + self.denominator = (self.num - self.numerator_addend.pow(2)) / self.denominator; + + // When this happens, the terms will start repeating. + if a == self.a0 * 2 { + self.denominator = 0; + } + Some(a) + } +} + diff --git a/src/utils/iterators/cubes.rs b/src/utils/iterators/cubes.rs new file mode 100644 index 0000000..b3c12a1 --- /dev/null +++ b/src/utils/iterators/cubes.rs @@ -0,0 +1,27 @@ + +/// Cubes iterator. Generates cubes of integers without multiplication or +/// exponentiation. +pub struct Cubes { + increment: i64, + offset: i64, + num: i64, +} +impl Cubes { + pub fn new() -> Cubes { + Cubes { + increment: 6, + offset: 1, + num: 0, + } + } +} +impl Iterator for Cubes { + type Item = i64; + fn next(&mut self) -> Option { + self.num += self.offset; + self.offset += self.increment; + self.increment += 6; + Some(self.num) + } +} + diff --git a/src/utils/iterators/digits.rs b/src/utils/iterators/digits.rs new file mode 100644 index 0000000..27a299c --- /dev/null +++ b/src/utils/iterators/digits.rs @@ -0,0 +1,24 @@ +/// Digits iterator. Generates the decimal digits of a number from least +/// significant to most significant. Positive numbers only! +pub struct Digits { + num: i64, +} +impl Digits { + pub fn new(num: i64) -> Digits { + Digits { num } + } +} +impl Iterator for Digits { + type Item = i64; + fn next(&mut self) -> Option { + if self.num == 0 { + None + } else { + let digit = self.num % 10; + self.num /= 10; + Some(digit) + } + } +} + + diff --git a/src/utils/iterators/divisors.rs b/src/utils/iterators/divisors.rs new file mode 100644 index 0000000..e5ce613 --- /dev/null +++ b/src/utils/iterators/divisors.rs @@ -0,0 +1,45 @@ +use crate::utils; + +/// Divisors iterator. Generates all divisors of a number in an unspecified +/// order. Positive numbers only! +pub struct Divisors { + dividend: i64, + limit: i64, + current: i64, + other: i64, +} +impl Divisors { + pub fn new(dividend: i64) -> Divisors { + Divisors { + dividend, + limit: utils::isqrt(dividend), + current: 0, + other: 0, + } + } +} +impl Iterator for Divisors { + type Item = i64; + fn next(&mut self) -> Option { + if self.other > 0 { + let other = self.other; + self.other = 0; + return Some(other); + } + loop { + self.current += 1; + if self.current > self.limit { + return None; + } + if self.dividend % self.current == 0 { + let other = self.dividend / self.current; + if other != self.current { + self.other = other; + } + return Some(self.current); + } + } + } +} + + diff --git a/src/utils/iterators/fibonacci.rs b/src/utils/iterators/fibonacci.rs new file mode 100644 index 0000000..5c0ca99 --- /dev/null +++ b/src/utils/iterators/fibonacci.rs @@ -0,0 +1,18 @@ +/// Fibonacci sequence iterator. +pub struct Fibonacci { + a: i64, + b: i64, +} +impl Fibonacci { + pub fn new(a: i64, b: i64) -> Fibonacci { + Fibonacci { a, b } + } +} +impl Iterator for Fibonacci { + type Item = i64; + fn next(&mut self) -> Option { + let a = self.a; + (self.a, self.b) = (self.b, self.a + self.b); + Some(a) + } +} diff --git a/src/utils/iterators/polygonal.rs b/src/utils/iterators/polygonal.rs new file mode 100644 index 0000000..8959653 --- /dev/null +++ b/src/utils/iterators/polygonal.rs @@ -0,0 +1,49 @@ + +/// Triangular, pentagonal, hexagonal, etc. number iterator. Specify the +/// number of sides of the polygon as the argument to the constructor. +pub struct Polygonal { + increment: i64, + offset: i64, + num: i64, +} +impl Polygonal { + pub fn new(sides: i64) -> Polygonal { + Polygonal { + increment: sides - 2, + offset: 1, + num: 0, + } + } + /// Find the index at which the given number would appear in a sequence of + /// polygonal numbers (if it is a polygonal number). + /// + /// * `sides` - Number of sides of the polygon the sequence is based on. + /// * `num` - Number whose index is to be found. + /// + /// -> Index. + pub fn invert(sides: i64, num: i64) -> Result { + // A polygonal number is a quadratic function of the index it appears + // at. Solve for the positive root of the corresponding quadratic + // equation. + let a = sides - 2; + let b = 4 - sides; + let c = -2 * num; + let discriminant = b.pow(2) - 4 * a * c; + let idx = (-b as f64 + (discriminant as f64).sqrt()) / (2.0 * a as f64); + let idx_rounded = idx.round() as i64; + if idx.fract() == 0.0 { + Ok(idx_rounded) + } else { + Err(idx_rounded) + } + } +} +impl Iterator for Polygonal { + type Item = i64; + fn next(&mut self) -> Option { + self.num += self.offset; + self.offset += self.increment; + Some(self.num) + } +} + diff --git a/src/utils/iterators/potential_primes.rs b/src/utils/iterators/potential_primes.rs new file mode 100644 index 0000000..2011e48 --- /dev/null +++ b/src/utils/iterators/potential_primes.rs @@ -0,0 +1,34 @@ +/// Potential prime numbers. Generates some small prime numbers and numbers +/// coprime to 30. Used for wheel factorisation with 2, 3 and 5. +pub struct PotentialPrimes { + limit: i64, + num: i64, + offset: std::iter::Cycle>, +} +impl PotentialPrimes { + pub fn new(limit: i64) -> PotentialPrimes { + PotentialPrimes { + limit, + num: 1, + offset: [4, 2, 4, 2, 4, 6, 2, 6].into_iter().cycle(), + } + } +} +impl Iterator for PotentialPrimes { + type Item = i64; + fn next(&mut self) -> Option { + self.num += match self.num { + 1 => 1, + 2 => 1, + 3 | 5 => 2, + _ => self.offset.next().unwrap(), + }; + if self.num > self.limit { + None + } else { + Some(self.num) + } + } +} + + diff --git a/src/utils/iterators/prime_divisors.rs b/src/utils/iterators/prime_divisors.rs new file mode 100644 index 0000000..291b650 --- /dev/null +++ b/src/utils/iterators/prime_divisors.rs @@ -0,0 +1,41 @@ +use crate::utils; + +/// Prime divisors iterator. Generates all prime divisors of a number in +/// ascending order. Positive numbers only! +pub struct PrimeDivisors { + // If I actually find all prime numbers to iterate over (instead of just + // using potential prime numbers), performance drops significantly. + potential_primes: utils::PotentialPrimes, + num: i64, +} +impl PrimeDivisors { + pub fn new(num: i64) -> PrimeDivisors { + PrimeDivisors { + potential_primes: utils::PotentialPrimes::new(num), + num, + } + } +} +impl Iterator for PrimeDivisors { + type Item = (i64, u32); + fn next(&mut self) -> Option<(i64, u32)> { + loop { + let potential_prime = match self.potential_primes.next() { + Some(potential_prime) if potential_prime <= self.num => potential_prime, + _ => return None, + }; + let mut power = 0; + while self.num % potential_prime == 0 { + self.num /= potential_prime; + power += 1; + } + // Since I divide out the number by all potential primes I find, + // the potential primes I do find are actually prime. + if power > 0 { + return Some((potential_prime, power)); + } + } + } +} + + diff --git a/src/utils/iterators/pythagorean_triplets.rs b/src/utils/iterators/pythagorean_triplets.rs new file mode 100644 index 0000000..099faa9 --- /dev/null +++ b/src/utils/iterators/pythagorean_triplets.rs @@ -0,0 +1,55 @@ +use crate::utils; + +/// Pythagorean triplets iterator. Generates all Pythagorean triplets with the +/// given sum. The triplets are generated using a well-known parametrisation +/// which can represent any primitive triplet. Positive numbers only! +pub struct PythagoreanTriplets { + semiperimeter: i64, + m_ub: i64, + m: i64, +} +impl PythagoreanTriplets { + pub fn new(perimeter: i64) -> PythagoreanTriplets { + let semiperimeter = perimeter / 2; + // Non-strict upper bound for the parameter `m`. + let m_ub = utils::isqrt(semiperimeter); + PythagoreanTriplets { + semiperimeter, + m_ub, + m: 1, + } + } +} +impl Iterator for PythagoreanTriplets { + type Item = (i64, i64, i64); + fn next(&mut self) -> Option<(i64, i64, i64)> { + loop { + self.m += 1; + if self.m > self.m_ub { + return None; + } + if self.semiperimeter % self.m != 0 { + continue; + } + let m = self.m; + let remaining_term = self.semiperimeter / m; + let remaining_odd = remaining_term >> remaining_term.trailing_zeros(); + let m_plus_n_lb = m + 1 + m % 2; + for m_plus_n in (m_plus_n_lb..) + .step_by(2) + .take_while(|&m_plus_n| m_plus_n < 2 * m && m_plus_n <= remaining_odd) + { + if remaining_odd % m_plus_n == 0 && utils::gcd(m_plus_n, m) == 1 { + let d = remaining_term / m_plus_n; + let n = m_plus_n - m; + let a = (m.pow(2) - n.pow(2)) * d; + let b = 2 * m * n * d; + let c = (m.pow(2) + n.pow(2)) * d; + return Some((a, b, c)); + } + } + } + } +} + +