Skip to main content

malachite_nz/natural/arithmetic/
primorial.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MP Library.
4//
5//      Contributed to the GNU project by Marco Bodrato.
6//
7//      Copyright © 2012, 2015, 2016 Free Software Foundation, Inc.
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::natural::arithmetic::factorial::log_n_max;
16use crate::natural::arithmetic::mul::product_of_limbs::limbs_product;
17use crate::natural::{Natural, bit_to_limb_count_floor};
18use crate::platform::Limb;
19use alloc::vec::Vec;
20use malachite_base::num::arithmetic::traits::{PowerOf2, Primorial, RotateLeftAssign};
21use malachite_base::num::basic::integers::PrimitiveInt;
22use malachite_base::num::conversion::traits::{ConvertibleFrom, ExactFrom, WrappingFrom};
23#[cfg(feature = "32_bit_limbs")]
24use malachite_base::num::factorization::prime_sieve::limbs_prime_sieve_u32;
25#[cfg(not(feature = "32_bit_limbs"))]
26use malachite_base::num::factorization::prime_sieve::limbs_prime_sieve_u64;
27use malachite_base::num::factorization::prime_sieve::{id_to_n, limbs_prime_sieve_size, n_to_bit};
28use malachite_base::num::factorization::traits::Primes;
29
30#[cfg(feature = "32_bit_limbs")]
31const SMALL_PRIMORIAL_LIMIT: u64 = 29;
32#[cfg(not(feature = "32_bit_limbs"))]
33const SMALL_PRIMORIAL_LIMIT: u64 = 53;
34
35// This is equivalent to `mpz_primorial_ui` from `mpz/primorial_ui.c`, GMP 6.2.1, where n is too
36// large for the primorial of n to fit in a single limb.
37#[allow(clippy::useless_conversion)]
38fn limbs_primorial(n: Limb) -> Vec<Limb> {
39    let n_u64 = u64::from(n);
40    let size = usize::exact_from(n >> Limb::LOG_WIDTH);
41    let size = size + (size >> 1) + 1;
42    assert!(size >= limbs_prime_sieve_size::<Limb>(n_u64));
43    let mut sieve = vec![0; size];
44    #[cfg(feature = "32_bit_limbs")]
45    let count = limbs_prime_sieve_u32(&mut sieve, n_u64);
46    #[cfg(not(feature = "32_bit_limbs"))]
47    let count = limbs_prime_sieve_u64(&mut sieve, n);
48    let size = usize::exact_from((count + 1) / log_n_max(n) + 1);
49    let mut factors = vec![0; size];
50    let mut j = 0;
51    let mut prod = 6;
52    // Store primes from 5 to n
53    let max_prod = Limb::MAX / n;
54    let i = n_to_bit(5);
55    let mut index = bit_to_limb_count_floor(i);
56    let mut mask = Limb::power_of_2(i & Limb::WIDTH_MASK);
57    for i in i + 1..=n_to_bit(n_u64) + 1 {
58        if sieve[index] & mask == 0 {
59            let prime = Limb::wrapping_from(id_to_n(i));
60            if prod > max_prod {
61                factors[j] = prod;
62                j += 1;
63                prod = prime;
64            } else {
65                prod *= prime;
66            }
67        }
68        mask.rotate_left_assign(1);
69        if mask == 1 {
70            index += 1;
71        }
72    }
73    // j != 0
74    factors[j] = prod;
75    j += 1;
76    sieve.resize(j, 0);
77    let (out_len, new_sieve) = limbs_product(&mut sieve, &mut factors[..j]);
78    assert!(new_sieve.is_none());
79    sieve.truncate(out_len);
80    sieve
81}
82
83#[cfg(feature = "32_bit_limbs")]
84const SMALL_PRODUCT_OF_FIRST_N_PRIMES_LIMIT: u64 = 10;
85#[cfg(not(feature = "32_bit_limbs"))]
86const SMALL_PRODUCT_OF_FIRST_N_PRIMES_LIMIT: u64 = 16;
87
88fn limbs_product_of_first_n_primes(n: usize) -> Vec<Limb> {
89    let mut prod: Limb = 1;
90    let mut factors = Vec::new();
91    for prime in Limb::primes().take(n) {
92        if let Some(p) = prod.checked_mul(prime) {
93            prod = p;
94        } else {
95            factors.push(prod);
96            prod = prime;
97        }
98    }
99    factors.push(prod);
100    let mut out = Vec::new();
101    let (out_len, new_out) = limbs_product(&mut out, &mut factors);
102    out = new_out.unwrap();
103    out.truncate(out_len);
104    out
105}
106
107impl Primorial for Natural {
108    /// Computes the primorial of a [`Natural`]: the product of all primes less than or equal to it.
109    ///
110    /// The [`product_of_first_n_primes`](Natural::product_of_first_n_primes) function is similar;
111    /// it computes the primorial of the $n$th prime.
112    ///
113    /// $$
114    /// f(n) = n\\# =prod_{pleq natop p\\text {prime}} p.
115    /// $$
116    ///
117    /// $n\\# = O(e^{(1+o(1))n})$.
118    ///
119    /// # Worst-case complexity
120    /// $T(n) = O(n \log n \log\log n)$
121    ///
122    /// $M(n) = O(n \log n)$
123    ///
124    /// # Examples
125    /// ```
126    /// use malachite_base::num::arithmetic::traits::Primorial;
127    /// use malachite_nz::natural::Natural;
128    ///
129    /// assert_eq!(Natural::primorial(0), 1);
130    /// assert_eq!(Natural::primorial(1), 1);
131    /// assert_eq!(Natural::primorial(2), 2);
132    /// assert_eq!(Natural::primorial(3), 6);
133    /// assert_eq!(Natural::primorial(4), 6);
134    /// assert_eq!(Natural::primorial(5), 30);
135    /// assert_eq!(
136    ///     Natural::primorial(100).to_string(),
137    ///     "2305567963945518424753102147331756070"
138    /// );
139    /// ```
140    ///
141    /// This is equivalent to `mpz_primorial_ui` from `mpz/primorial_ui.c`, GMP 6.2.1.
142    #[inline]
143    fn primorial(n: u64) -> Self {
144        assert!(Limb::convertible_from(n));
145        if n < SMALL_PRIMORIAL_LIMIT {
146            Self::from(Limb::primorial(n))
147        } else {
148            Self::from_owned_limbs_asc(limbs_primorial(Limb::wrapping_from(n)))
149        }
150    }
151
152    /// Computes the product of the first $n$ primes.
153    ///
154    /// The [`primorial`](Natural::primorial) function is similar; it computes the product of all
155    /// primes less than or equal to $n$.
156    ///
157    /// $$
158    /// f(n) = p_n\\# = \prod_{k=1}^n p_n,
159    /// $$
160    /// where $p_n$ is the $n$th prime number.
161    ///
162    /// $p_n\\# = O\left (\left (\frac{1}{e}k\log k\left (\frac{\log k}{e^2}k \right )^{1/\log
163    /// k}\right )^k\omega(1)\right )$.
164    ///
165    /// This asymptotic approximation is due to [Bart
166    /// Michels](https://math.stackexchange.com/a/1594930).
167    ///
168    /// # Worst-case complexity
169    /// $T(n) = O(n (\log n)^2 \log\log n)$
170    ///
171    /// $M(n) = O(n \log n)$
172    ///
173    /// # Examples
174    /// ```
175    /// use malachite_base::num::arithmetic::traits::Primorial;
176    /// use malachite_nz::natural::Natural;
177    ///
178    /// assert_eq!(Natural::product_of_first_n_primes(0), 1);
179    /// assert_eq!(Natural::product_of_first_n_primes(1), 2);
180    /// assert_eq!(Natural::product_of_first_n_primes(2), 6);
181    /// assert_eq!(Natural::product_of_first_n_primes(3), 30);
182    /// assert_eq!(Natural::product_of_first_n_primes(4), 210);
183    /// assert_eq!(Natural::product_of_first_n_primes(5), 2310);
184    /// assert_eq!(
185    ///     Natural::product_of_first_n_primes(100).to_string(),
186    ///     "4711930799906184953162487834760260422020574773409675520188634839616415335845034221205\
187    ///     28925670554468197243910409777715799180438028421831503871944494399049257903072063599053\
188    ///     8452312528339864352999310398481791730017201031090"
189    /// );
190    /// ```
191    #[inline]
192    fn product_of_first_n_primes(n: u64) -> Self {
193        assert!(Limb::convertible_from(n));
194        if n < SMALL_PRODUCT_OF_FIRST_N_PRIMES_LIMIT {
195            Self::from(Limb::product_of_first_n_primes(n))
196        } else {
197            Self::from_owned_limbs_asc(limbs_product_of_first_n_primes(usize::exact_from(n)))
198        }
199    }
200}