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}