malachite_base/num/arithmetic/
primorial.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{CheckedPrimorial, Primorial};
10use crate::num::basic::integers::USIZE_IS_U32;
11use crate::num::conversion::traits::WrappingFrom;
12
13const PRIMORIALS_U8: [u8; 5] = [1, 2, 6, 30, 210];
14const PRIMORIALS_U16: [u16; 7] = [1, 2, 6, 30, 210, 2310, 30030];
15const PRIMORIALS_U32: [u32; 10] = [1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690, 223092870];
16const PRIMORIALS_U64: [u64; 16] = [
17    1,
18    2,
19    6,
20    30,
21    210,
22    2310,
23    30030,
24    510510,
25    9699690,
26    223092870,
27    6469693230,
28    200560490130,
29    7420738134810,
30    304250263527210,
31    13082761331670030,
32    614889782588491410,
33];
34const PRIMORIALS_U128: [u128; 27] = [
35    1,
36    2,
37    6,
38    30,
39    210,
40    2310,
41    30030,
42    510510,
43    9699690,
44    223092870,
45    6469693230,
46    200560490130,
47    7420738134810,
48    304250263527210,
49    13082761331670030,
50    614889782588491410,
51    32589158477190044730,
52    1922760350154212639070,
53    117288381359406970983270,
54    7858321551080267055879090,
55    557940830126698960967415390,
56    40729680599249024150621323470,
57    3217644767340672907899084554130,
58    267064515689275851355624017992790,
59    23768741896345550770650537601358310,
60    2305567963945518424753102147331756070,
61    232862364358497360900063316880507363070,
62];
63
64const PRIMORIAL_PRIMES_U8: [u64; 5] = [2, 3, 5, 7, 11];
65
66const PRIMORIAL_PRIMES_U16: [u64; 7] = [2, 3, 5, 7, 11, 13, 17];
67
68const PRIMORIAL_PRIMES_U32: [u64; 10] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
69
70const PRIMORIAL_PRIMES_U64: [u64; 16] =
71    [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53];
72
73const PRIMORIAL_PRIMES_U128: [u64; 27] = [
74    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
75    101, 103,
76];
77
78macro_rules! impl_primorials_a {
79    ($t:ident, $ps:ident, $pps:ident) => {
80        impl CheckedPrimorial for $t {
81            /// Computes the primorial of a number: the product of all primes less than or equal to
82            /// it.
83            ///
84            /// The
85            /// [`checked_product_of_first_n_primes`](CheckedPrimorial::checked_product_of_first_n_primes)
86            /// function is similar; it computes the primorial of the $n$th prime.
87            ///
88            /// If the input is too large, the function returns `None`.
89            ///
90            /// $$
91            /// f(n) = \\begin{cases}
92            ///     \operatorname{Some}(n\\#) & \text{if} \\quad n\\# < 2^W, \\\\
93            ///     \operatorname{None} & \text{if} \\quad n\\# \geq 2^W,
94            /// \\end{cases}
95            /// $$
96            /// where $W$ is `Self::WIDTH`.
97            ///
98            /// # Worst-case complexity
99            /// Constant time and additional memory.
100            ///
101            /// # Examples
102            /// See [here](super::primorial#checked_primorial).
103            #[inline]
104            fn checked_primorial(n: u64) -> Option<$t> {
105                let i = match $pps.binary_search(&n) {
106                    Ok(i) => i + 1,
107                    Err(i) => i,
108                };
109                $ps.get(i).copied()
110            }
111
112            /// Computes the product of the first $n$ primes.
113            ///
114            /// The [`checked_primorial`](CheckedPrimorial::checked_primorial) function is similar;
115            /// it computes the product of all primes less than or equal to $n$.
116            ///
117            /// If the input is too large, the function returns `None`.
118            ///
119            /// $$
120            /// f(n) = \\begin{cases}
121            ///     \operatorname{Some}(p_n\\#) & \text{if} \\quad p_n\\# < 2^W, \\\\
122            ///     \operatorname{None} & \text{if} \\quad p_n\\# \geq 2^W,
123            /// \\end{cases}
124            /// $$
125            /// where $W$ is `Self::WIDTH`.
126            ///
127            /// # Worst-case complexity
128            /// Constant time and additional memory.
129            ///
130            /// # Examples
131            /// See [here](super::primorial#checked_product_of_first_n_primes).
132            #[inline]
133            fn checked_product_of_first_n_primes(n: u64) -> Option<$t> {
134                $ps.get(usize::try_from(n).ok()?).copied()
135            }
136        }
137    };
138}
139impl_primorials_a!(u8, PRIMORIALS_U8, PRIMORIAL_PRIMES_U8);
140impl_primorials_a!(u16, PRIMORIALS_U16, PRIMORIAL_PRIMES_U16);
141impl_primorials_a!(u32, PRIMORIALS_U32, PRIMORIAL_PRIMES_U32);
142impl_primorials_a!(u64, PRIMORIALS_U64, PRIMORIAL_PRIMES_U64);
143impl_primorials_a!(u128, PRIMORIALS_U128, PRIMORIAL_PRIMES_U128);
144
145impl CheckedPrimorial for usize {
146    /// Computes the primorial of a [`usize`]: the product of all primes less than or equal to it.
147    ///
148    /// The
149    /// [`checked_product_of_first_n_primes`](CheckedPrimorial::checked_product_of_first_n_primes)
150    /// function is similar; it computes the primorial of the $n$th prime.
151    ///
152    /// If the input is too large, the function returns `None`.
153    ///
154    /// $$
155    /// f(n) = \\begin{cases}
156    ///     \operatorname{Some}(n\\#) & \text{if} \\quad n\\# < 2^W, \\\\
157    ///     \operatorname{None} & \text{if} \\quad n\\# \geq 2^W,
158    /// \\end{cases}
159    /// $$
160    /// where $W$ is `usize::WIDTH`.
161    ///
162    /// # Worst-case complexity
163    /// Constant time and additional memory.
164    ///
165    /// # Examples
166    /// See [here](super::primorial#checked_primorial).
167    #[inline]
168    fn checked_primorial(n: u64) -> Option<usize> {
169        if USIZE_IS_U32 {
170            u32::checked_primorial(n).map(usize::wrapping_from)
171        } else {
172            u64::checked_primorial(n).map(usize::wrapping_from)
173        }
174    }
175
176    /// Computes the product of the first $n$ primes.
177    ///
178    /// The [`checked_primorial`](CheckedPrimorial::checked_primorial) function is similar; it
179    /// computes the product of all primes less than or equal to $n$.
180    ///
181    /// If the input is too large, the function returns `None`.
182    ///
183    /// $$
184    /// f(n) = \\begin{cases}
185    ///     \operatorname{Some}(p_n\\#) & \text{if} \\quad p_n\\# < 2^W, \\\\
186    ///     \operatorname{None} & \text{if} \\quad p_n\\# \geq 2^W,
187    /// \\end{cases}
188    /// $$
189    /// where $W$ is `usize::WIDTH`.
190    ///
191    /// # Worst-case complexity
192    /// Constant time and additional memory.
193    ///
194    /// # Examples
195    /// See [here](super::primorial#checked_product_of_first_n_primes).
196    #[inline]
197    fn checked_product_of_first_n_primes(n: u64) -> Option<usize> {
198        if USIZE_IS_U32 {
199            u32::checked_product_of_first_n_primes(n).map(usize::wrapping_from)
200        } else {
201            u64::checked_product_of_first_n_primes(n).map(usize::wrapping_from)
202        }
203    }
204}
205
206macro_rules! impl_primorials_b {
207    ($t:ident) => {
208        impl Primorial for $t {
209            /// Computes the primorial of a number: the product of all primes less than or equal to
210            /// it.
211            ///
212            /// The [`product_of_first_n_primes`](Primorial::product_of_first_n_primes) function is
213            /// similar; it computes the primorial of the $n$th prime.
214            ///
215            /// If the input is too large, the function panics. For a function that returns `None`
216            /// instead, try [`checked_primorial`](CheckedPrimorial::checked_primorial).
217            ///
218            /// $$
219            /// f(n) = n\\# = \prod_{p \leq n \atop p \\ \\text {prime}} p.
220            /// $$
221            ///
222            /// $n\\# = O(e^{(1+o(1))n})$.
223            ///
224            /// # Worst-case complexity
225            /// Constant time and additional memory.
226            ///
227            /// # Panics
228            /// Panics if the output is too large to be represented.
229            ///
230            /// # Examples
231            /// See [here](super::primorial#primorial).
232            #[inline]
233            fn primorial(n: u64) -> $t {
234                $t::checked_primorial(n).unwrap()
235            }
236
237            /// Computes the product of the first $n$ primes.
238            ///
239            /// The [`primorial`](Primorial::primorial) function is similar; it computes the product
240            /// of all primes less than or equal to $n$.
241            ///
242            /// If the input is too large, the function panics. For a function that returns `None`
243            /// instead, try
244            /// [`checked_product_of_first_n_primes`](CheckedPrimorial::checked_product_of_first_n_primes).
245            ///
246            /// $$
247            /// f(n) = p_n\\# = \prod_{k=1}^n p_n,
248            /// $$
249            /// where $p_n$ is the $n$th prime number.
250            ///
251            /// $p_n\\# = O\left ( \left ( \frac{1}{e}k\log k\left ( \frac{\log k}{e^2}k \right
252            /// )^{1/\log k} \right )^k \omega(1)\right )$.
253            ///
254            /// This asymptotic approximation is due to [Bart
255            /// Michels](https://math.stackexchange.com/a/1594930).
256            ///
257            /// # Worst-case complexity
258            /// Constant time and additional memory.
259            ///
260            /// # Panics
261            /// Panics if the output is too large to be represented.
262            ///
263            /// # Examples
264            /// See [here](super::primorial#product_of_first_n_primes).
265            #[inline]
266            fn product_of_first_n_primes(n: u64) -> $t {
267                $t::checked_product_of_first_n_primes(n).unwrap()
268            }
269        }
270    };
271}
272apply_to_unsigneds!(impl_primorials_b);