From b96c2f92439c181e5e1e0255318d45011386bb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= <44257381+JSorngard@users.noreply.github.com> Date: Fri, 31 May 2024 06:03:03 +0200 Subject: [PATCH] Add `prime_factors` function to `Primes` (#35) * Add prime_factors function to Primes * Bump version number, add changes to changelog * More illustrative example in prime_factorization * Add test --- CHANGELOG.md | 4 ++ Cargo.toml | 2 +- src/cache.rs | 137 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 135 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09e9233..367d38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ This file contains the changes to the crate since version 0.4.8. +# 0.7.3 + + - Add `Primes::prime_factors` function that returns an iterator over the prime factors of the given number (and not their multiplicities). + # 0.7.2 - Add `Primes::prime_factorization` function that returns an iterator over the prime factors of the given number and their multiplicities. diff --git a/Cargo.toml b/Cargo.toml index d4972ca..f22dab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "const-primes" authors = ["Johanna Sörngård "] -version = "0.7.2" +version = "0.7.3" edition = "2021" license = "MIT OR Apache-2.0" keywords = ["const", "primes"] diff --git a/src/cache.rs b/src/cache.rs index 5ee05ba..68908d2 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -145,6 +145,9 @@ impl Primes { /// they will not be yielded by the iterator, but their product can be retrieved by calling /// [`remainder`](PrimeFactorization::remainder) on the iterator once it is exhausted. /// + /// If you do not need to know the multiplicity of each prime factor, + /// it may be faster to use [`prime_factors`](Self::prime_factors). + /// /// # Examples /// /// Basic usage: @@ -162,24 +165,61 @@ impl Primes { /// // 1024 = 2^10 /// assert_eq!(CACHE.prime_factorization(1024).next(), Some((2, 10))); /// ``` - /// 42 has 7 as a prime factor, but 7 is not in the cache: + /// 294 has 7 as a prime factor, but 7 is not in the cache: /// ``` /// # use const_primes::Primes; /// # const CACHE: Primes<3> = Primes::new(); - /// // 42 = 2*3*7 - /// let mut factorization_of_42 = CACHE.prime_factorization(42); + /// // 294 = 2*3*7*7 + /// let mut factorization_of_294 = CACHE.prime_factorization(294); /// /// // only 2 and 3 are in the cache: - /// assert_eq!(factorization_of_42.by_ref().collect::>(), &[(2, 1), (3, 1)]); + /// assert_eq!(factorization_of_294.by_ref().collect::>(), &[(2, 1), (3, 1)]); /// - /// // the factor of 7 can be found with the remainder function: - /// assert_eq!(factorization_of_42.remainder(), Some(7)); + /// // the factor of 7*7 can be found with the remainder function: + /// assert_eq!(factorization_of_294.remainder(), Some(49)); /// ``` #[inline] pub fn prime_factorization(&self, number: Underlying) -> PrimeFactorization<'_> { PrimeFactorization::new(&self.primes, number) } + /// Returns an iterator over all the prime factors of the given number in increasing order. + /// + /// If a number contains prime factors larger than the largest prime in `self`, + /// they will not be yielded by the iterator, but their product can be retrieved by calling + /// [`remainder`](PrimeFactorization::remainder) on the iterator. + /// + /// If you also wish to know the multiplicity of each prime factor of the number, + /// take a look at [`prime_factorization`](Self::prime_factorization). + /// + /// # Examples + /// + /// ``` + /// # use const_primes::Primes; + /// // Contains [2, 3, 5] + /// const CACHE: Primes<3> = Primes::new(); + /// + /// assert_eq!(CACHE.prime_factors(3*5).collect::>(), &[3, 5]); + /// assert_eq!(CACHE.prime_factors(2*2*2*2*3).collect::>(), &[2, 3]); + /// ``` + /// 294 has 7 as a prime factor, but 7 is not in the cache: + /// ``` + /// # use const_primes::Primes; + /// # const CACHE: Primes<3> = Primes::new(); + /// // 294 = 2*3*7*7 + /// let mut factors_of_294 = CACHE.prime_factors(294); + /// + /// // only 2 and 3 are in the cache + /// assert_eq!(factors_of_294.by_ref().collect::>(), &[2, 3]); + /// + /// // the factor of 7*7 can be found with the remainder function + /// assert_eq!(factors_of_294.remainder(), Some(49)); + /// ``` + #[inline] + pub fn prime_factors(&self, number: Underlying) -> PrimeFactors<'_> { + PrimeFactors::new(&self.primes, number) + } + // region: Next prime /// Returns the largest prime less than `n`. @@ -448,7 +488,7 @@ impl AsRef<[Underlying; N]> for Primes { // endregion: AsRef -pub use prime_factors::PrimeFactorization; +pub use prime_factors::{PrimeFactorization, PrimeFactors}; mod prime_factors { use super::Underlying; @@ -517,6 +557,68 @@ mod prime_factors { } impl<'a> FusedIterator for PrimeFactorization<'a> {} + + /// An iterator over the prime factors of a given number. + /// + /// Created by the [`prime_factors`](super::Primes::prime_factors) + /// function on [`Primes`](super::Primes), see it for more information. + #[derive(Debug, Clone)] + #[must_use = "iterators are lazy and do nothing unless consumed"] + pub struct PrimeFactors<'a> { + primes_cache: &'a [Underlying], + cache_index: usize, + number: Underlying, + } + + impl<'a> PrimeFactors<'a> { + #[inline] + pub(crate) const fn new(primes_cache: &'a [Underlying], number: Underlying) -> Self { + Self { + primes_cache, + cache_index: 0, + number, + } + } + + /// If the number contains prime factors that are larger than the largest prime + /// in the cache, this function returns their product. + /// + /// It does this by doing all the work that [`PrimeFactorization`] would have done, + /// so the performance advantage of this iterator over that one dissapears if this function is called. + #[inline] + #[must_use = "`self` will be dropped if the result is not used"] + pub fn remainder(self) -> Option { + // We haven't actually divided out any of the factors to save work, + // so we do that by just delegating to PrimeFactorization, + // which does the work in its implementation of this function. + PrimeFactorization::new(self.primes_cache, self.number).remainder() + } + } + + impl<'a> Iterator for PrimeFactors<'a> { + type Item = Underlying; + fn next(&mut self) -> Option { + if self.number == 1 { + return None; + } + + while let Some(prime) = self.primes_cache.get(self.cache_index) { + self.cache_index += 1; + if self.number % prime == 0 { + return Some(*prime); + } + } + + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.primes_cache.len() - self.cache_index)) + } + } + + impl<'a> FusedIterator for PrimeFactors<'a> {} } // region: IntoIterator @@ -868,6 +970,27 @@ mod test { ); } + #[test] + fn check_prime_factors() { + const CACHE: Primes<3> = Primes::new(); + + let mut factors_of_14 = CACHE.prime_factors(14); + + assert_eq!(factors_of_14.next(), Some(2)); + assert_eq!(factors_of_14.next(), None); + assert_eq!(factors_of_14.remainder(), Some(7)); + + let mut factors_of_15 = CACHE.prime_factors(15); + + assert_eq!(factors_of_15.by_ref().collect::>(), &[3, 5]); + assert!(factors_of_15.remainder().is_none()); + + assert_eq!( + CACHE.prime_factors(2 * 3 * 3 * 3 * 5).collect::>(), + &[2, 3, 5] + ); + } + #[test] fn check_next_prime() { const CACHE: Primes<100> = Primes::new();