malachite_base/num/factorization/
prime_sieve.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MP Library.
4//
5//      `primesieve.c` contributed to the GNU project by Marco Bodrato.
6//
7//      Copyright © 2010-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::num::basic::integers::PrimitiveInt;
16use crate::num::basic::unsigneds::PrimitiveUnsigned;
17use crate::num::conversion::traits::ExactFrom;
18use crate::num::logic::traits::CountOnes;
19
20pub const SIEVE_SEED_U32: u32 = 0x69128480;
21// 70bits pre-sieved mask for primes 5, 7
22pub const SIEVE_MASK1_U32: u32 = 0x12148960;
23pub const SIEVE_MASK2_U32: u32 = 0x44a120cc;
24pub const SIEVE_MASKT_U32: u32 = 0x1a;
25pub const SEED_LIMIT_U32: u64 = 120;
26
27pub const SIEVE_SEED_U64: u64 = 0x3294C9E069128480;
28// 110bits pre-sieved mask for primes 5, 11
29pub const SIEVE_MASK1_U64: u64 = 0x81214a1204892058;
30pub const SIEVE_MASKT_U64: u64 = 0xc8130681244;
31// 182bits pre-sieved mask for primes 7, 13
32pub const SIEVE_2MSK1_U64: u64 = 0x9402180c40230184;
33pub const SIEVE_2MSK2_U64: u64 = 0x0285021088402120;
34pub const SIEVE_2MSKT_U64: u64 = 0xa41210084421;
35pub const SEED_LIMIT_U64: u64 = 210;
36
37const C70M2U32W: u64 = 70 - (u32::WIDTH << 1);
38const C70MU32W: u64 = 70 - u32::WIDTH;
39const C2U32W: u64 = u32::WIDTH << 1;
40const C3U32WM70: u64 = 3 * u32::WIDTH - 70;
41
42// # Worst-case complexity
43// $T(n) = O(n)$
44//
45// $M(n) = O(1)$
46//
47// where $T$ is time, $M$ is additional memory, and $n$ is `bit_array.len()`.
48//
49// This is equivalent to `fill_bitpattern` from `primesieve.c`, GMP 6.2.1.
50fn fill_bitpattern_u32(bit_array: &mut [u32], mut offset: u64) -> u64 {
51    let mut len = bit_array.len();
52    let (mut mask, mut mask2, mut tail) = if offset == 0 {
53        (SIEVE_MASK1_U32, SIEVE_MASK2_U32, SIEVE_MASKT_U32)
54    } else {
55        offset %= 70;
56        if offset != 0 {
57            if offset <= u32::WIDTH {
58                let offset_comp = u32::WIDTH - offset;
59                let mut mask = SIEVE_MASK2_U32 << offset_comp;
60                let mut mask2 = SIEVE_MASKT_U32 << offset_comp;
61                if offset != u32::WIDTH {
62                    mask |= SIEVE_MASK1_U32 >> offset;
63                    mask2 |= SIEVE_MASK2_U32 >> offset;
64                }
65                let tail = if offset <= C70M2U32W {
66                    (SIEVE_MASK1_U32 << (C70M2U32W - offset)) | (SIEVE_MASKT_U32 >> offset)
67                } else {
68                    mask2 |= SIEVE_MASK1_U32 << (C70MU32W - offset);
69                    SIEVE_MASK1_U32 >> (offset - C70M2U32W)
70                };
71                (mask, mask2, tail)
72            } else if offset < C2U32W {
73                let mut mask = (SIEVE_MASK2_U32 >> (offset - u32::WIDTH))
74                    | (SIEVE_MASKT_U32 << (C2U32W - offset));
75                if offset <= C70MU32W {
76                    let mut tail = SIEVE_MASK2_U32 << (C70MU32W - offset);
77                    if offset != C70MU32W {
78                        tail |= SIEVE_MASK1_U32 >> (offset - C70M2U32W);
79                    }
80                    (
81                        mask,
82                        (SIEVE_MASKT_U32 >> (offset - u32::WIDTH))
83                            | (SIEVE_MASK1_U32 << (C70MU32W - offset)),
84                        tail,
85                    )
86                } else {
87                    let d70_offset = 70 - offset;
88                    let width_m_d70_offset = u32::WIDTH - d70_offset;
89                    mask |= SIEVE_MASK1_U32 << d70_offset;
90                    (
91                        mask,
92                        (SIEVE_MASK2_U32 << d70_offset) | (SIEVE_MASK1_U32 >> width_m_d70_offset),
93                        SIEVE_MASK2_U32 >> width_m_d70_offset,
94                    )
95                }
96            } else {
97                let d70_offset = 70 - offset;
98                let width_m_d70_offset = u32::WIDTH - d70_offset;
99                (
100                    (SIEVE_MASK1_U32 << d70_offset) | (SIEVE_MASKT_U32 >> (offset - C2U32W)),
101                    (SIEVE_MASK2_U32 << d70_offset) | (SIEVE_MASK1_U32 >> width_m_d70_offset),
102                    (SIEVE_MASKT_U32 << d70_offset) | (SIEVE_MASK2_U32 >> width_m_d70_offset),
103                )
104            }
105        } else {
106            (SIEVE_MASK1_U32, SIEVE_MASK2_U32, SIEVE_MASKT_U32)
107        }
108    };
109    for xs in bit_array.chunks_mut(2) {
110        xs[0] = mask;
111        len -= 1;
112        if len == 0 {
113            break;
114        }
115        xs[1] = mask2;
116        let temp = mask2 >> C3U32WM70;
117        mask2 = (mask2 << C70M2U32W) | (mask >> C3U32WM70);
118        mask = (mask << C70M2U32W) | tail;
119        tail = temp;
120        len -= 1;
121        if len == 0 {
122            break;
123        }
124    }
125    2
126}
127
128const C110MU64W: u64 = 110 - u64::WIDTH;
129const C2U64WM110: u64 = (u64::WIDTH << 1) - 110;
130const C182MU64W: u64 = 182 - u64::WIDTH;
131const C182M2U64W: u64 = 182 - (u64::WIDTH << 1);
132const C3U64WM182: u64 = 3 * u64::WIDTH - 182;
133const C2U64W: u64 = u64::WIDTH << 1;
134
135// # Worst-case complexity
136// $T(n) = O(n)$
137//
138// $M(n) = O(1)$
139//
140// where $T$ is time, $M$ is additional memory, and $n$ is `bit_array.len()`.
141//
142// This is equivalent to `fill_bitpattern` from `primesieve.c`, GMP 6.2.1.
143fn fill_bitpattern_u64(bit_array: &mut [u64], mut offset: u64) -> u64 {
144    let mut len = bit_array.len();
145    let ((mut m11, mut m12), (mut m21, mut m22, mut m23)) = if offset == 0 {
146        (
147            (SIEVE_MASK1_U64, SIEVE_MASKT_U64),
148            (SIEVE_2MSK1_U64, SIEVE_2MSK2_U64, SIEVE_2MSKT_U64),
149        )
150    } else {
151        // correctly handle offset == 0...
152        let mut m21 = offset % 110;
153        let (m11, m12) = if m21 != 0 {
154            if m21 < u64::WIDTH {
155                let mut m11 = (SIEVE_MASK1_U64 >> m21) | (SIEVE_MASKT_U64 << (u64::WIDTH - m21));
156                if m21 <= C110MU64W {
157                    (
158                        m11,
159                        (SIEVE_MASK1_U64 << (C110MU64W - m21)) | (SIEVE_MASKT_U64 >> m21),
160                    )
161                } else {
162                    m11 |= SIEVE_MASK1_U64 << (110 - m21);
163                    (m11, SIEVE_MASK1_U64 >> (m21 - C110MU64W))
164                }
165            } else {
166                (
167                    (SIEVE_MASK1_U64 << (110 - m21)) | (SIEVE_MASKT_U64 >> (m21 - u64::WIDTH)),
168                    (SIEVE_MASKT_U64 << (110 - m21)) | (SIEVE_MASK1_U64 >> (m21 - C110MU64W)),
169                )
170            }
171        } else {
172            (SIEVE_MASK1_U64, SIEVE_MASKT_U64)
173        };
174        ((m11, m12), {
175            offset %= 182;
176            if offset != 0 {
177                if offset <= u64::WIDTH {
178                    let mut m21 = SIEVE_2MSK2_U64 << (u64::WIDTH - offset);
179                    let mut m22 = SIEVE_2MSKT_U64 << (u64::WIDTH - offset);
180                    if offset != u64::WIDTH {
181                        m21 |= SIEVE_2MSK1_U64 >> offset;
182                        m22 |= SIEVE_2MSK2_U64 >> offset;
183                    }
184                    if offset <= C182M2U64W {
185                        (
186                            m21,
187                            m22,
188                            (SIEVE_2MSK1_U64 << (C182M2U64W - offset))
189                                | (SIEVE_2MSKT_U64 >> offset),
190                        )
191                    } else {
192                        m22 |= SIEVE_2MSK1_U64 << (C182MU64W - offset);
193                        (m21, m22, SIEVE_2MSK1_U64 >> (offset - C182M2U64W))
194                    }
195                } else if offset < C2U64W {
196                    m21 = (SIEVE_2MSK2_U64 >> (offset - u64::WIDTH))
197                        | (SIEVE_2MSKT_U64 << (C2U64W - offset));
198                    if offset <= C182MU64W {
199                        let mut m23 = SIEVE_2MSK2_U64 << (C182MU64W - offset);
200                        if offset != C182MU64W {
201                            m23 |= SIEVE_2MSK1_U64 >> (offset - C182M2U64W);
202                        }
203                        (
204                            m21,
205                            (SIEVE_2MSKT_U64 >> (offset - u64::WIDTH))
206                                | (SIEVE_2MSK1_U64 << (C182MU64W - offset)),
207                            m23,
208                        )
209                    } else {
210                        m21 |= SIEVE_2MSK1_U64 << (182 - offset);
211                        (
212                            m21,
213                            (SIEVE_2MSK2_U64 << (182 - offset))
214                                | (SIEVE_2MSK1_U64 >> (offset - C182MU64W)),
215                            SIEVE_2MSK2_U64 >> (offset - C182MU64W),
216                        )
217                    }
218                } else {
219                    (
220                        (SIEVE_2MSK1_U64 << (182 - offset))
221                            | (SIEVE_2MSKT_U64 >> (offset - C2U64W)),
222                        (SIEVE_2MSK2_U64 << (182 - offset))
223                            | (SIEVE_2MSK1_U64 >> (offset - C182MU64W)),
224                        (SIEVE_2MSKT_U64 << (182 - offset))
225                            | (SIEVE_2MSK2_U64 >> (offset - C182MU64W)),
226                    )
227                }
228            } else {
229                (SIEVE_2MSK1_U64, SIEVE_2MSK2_U64, SIEVE_2MSKT_U64)
230            }
231        })
232    };
233    for xs in bit_array.chunks_mut(2) {
234        xs[0] = m11 | m21;
235        len -= 1;
236        if len == 0 {
237            break;
238        }
239        let temp = m11 >> C2U64WM110;
240        m11 = (m11 << C110MU64W) | m12;
241        m12 = temp;
242        xs[1] = m11 | m22;
243        let temp = m11 >> C2U64WM110;
244        m11 = (m11 << C110MU64W) | m12;
245        m12 = temp;
246        let temp = m22 >> C3U64WM182;
247        m22 = (m22 << C182M2U64W) | (m21 >> C3U64WM182);
248        m21 = (m21 << C182M2U64W) | m23;
249        m23 = temp;
250        len -= 1;
251        if len == 0 {
252            break;
253        }
254    }
255    4
256}
257
258#[doc(hidden)]
259// This is equivalent to `n_to_bit` from `primesieve.c`, GMP 6.2.1.
260pub const fn n_to_bit(n: u64) -> u64 {
261    ((n - 5) | 1) / 3
262}
263
264#[doc(hidden)]
265// This is equivalent to `id_to_n` from `primesieve.c`, GMP 6.2.1.
266pub const fn id_to_n(id: u64) -> u64 {
267    id * 3 + 1 + (id & 1)
268}
269
270// # Worst-case complexity
271// $T(n) = O(n\log\log n)$
272//
273// $M(n) = O(1)$
274//
275// where $T$ is time, $M$ is additional memory, and $n$ is `bit_array.len()`.
276//
277// This is equivalent to `first_block_primesieve` from `primesieve.c`, GMP 6.2.1.
278fn first_block_primesieve<T: PrimitiveUnsigned, F: Fn(&mut [T], u64) -> u64>(
279    bit_array: &mut [T],
280    n: u64,
281    fill_bitpattern: F,
282    sieve_seed: T,
283    seed_limit: u64,
284) {
285    assert!(n > 4);
286    let bits = n_to_bit(n);
287    let limbs = usize::exact_from(bits >> T::LOG_WIDTH);
288    let mut i = if limbs == 0 {
289        0
290    } else {
291        fill_bitpattern(&mut bit_array[1..=limbs], 0)
292    };
293    bit_array[0] = sieve_seed;
294    if (bits + 1) & T::WIDTH_MASK != 0 {
295        bit_array[limbs] |= T::MAX << ((bits + 1) & T::WIDTH_MASK);
296    }
297    if n > seed_limit {
298        assert!(i < T::WIDTH);
299        if n_to_bit(seed_limit + 1) < T::WIDTH {
300            i = 0;
301        }
302        let mut mask = T::power_of_2(i);
303        let mut index = 0;
304        for i in i + 1.. {
305            if bit_array[index] & mask == T::ZERO {
306                let mut step = id_to_n(i);
307                // lindex = n_to_bit(id_to_n(i) * id_to_n(i));
308                let mut lindex = i * (step + 1) - 1 + ((i & 1).wrapping_neg() & (i + 1));
309                // lindex = i * (step + 1 + (i & 1)) - 1 + (i & 1);
310                if lindex > bits {
311                    break;
312                }
313                step <<= 1;
314                let maskrot = step & T::WIDTH_MASK;
315                let mut lmask = T::power_of_2(lindex & T::WIDTH_MASK);
316                while lindex <= bits {
317                    bit_array[usize::exact_from(lindex >> T::LOG_WIDTH)] |= lmask;
318                    lmask.rotate_left_assign(maskrot);
319                    lindex += step;
320                }
321                // lindex = n_to_bit(id_to_n(i) * bit_to_n(i));
322                lindex = i * (i * 3 + 6) + (i & 1);
323                lmask = T::power_of_2(lindex & T::WIDTH_MASK);
324                while lindex <= bits {
325                    bit_array[usize::exact_from(lindex >> T::LOG_WIDTH)] |= lmask;
326                    lmask.rotate_left_assign(maskrot);
327                    lindex += step;
328                }
329            }
330            mask <<= 1;
331            if mask == T::ZERO {
332                mask = T::ONE;
333                index += 1;
334            }
335        }
336    }
337}
338
339// # Worst-case complexity
340// $T(n) = O(n\log\log n)$
341//
342// $M(n) = O(1)$
343//
344// where $T$ is time, $M$ is additional memory, and $n$ is `bit_array.len()`.
345//
346// This is equivalent to `block_resieve` from `primesieve.c`, GMP 6.2.1.
347fn block_resieve<T: PrimitiveUnsigned, F: Fn(&mut [T], u64) -> u64>(
348    bit_array: &mut [T],
349    offset: u64,
350    sieve: &[T],
351    fill_bitpattern: &F,
352) {
353    let limbs = bit_array.len();
354    let off = offset;
355    assert_ne!(limbs, 0);
356    assert!(offset >= T::WIDTH);
357    let bits = u64::exact_from(limbs << T::LOG_WIDTH) - 1;
358    let i = fill_bitpattern(&mut bit_array[..limbs], offset - T::WIDTH);
359    assert!(i < T::WIDTH);
360    let mut mask = T::power_of_2(i);
361    let mut index = 0;
362    for i in i + 1.. {
363        if sieve[index] & mask == T::ZERO {
364            let mut step = id_to_n(i);
365            // lindex = n_to_bit(id_to_n(i)*id_to_n(i));
366            let mut lindex = i * (step + 1) - 1 + ((i & 1).wrapping_neg() & (i + 1));
367            // lindex = i*(step+1+(i&1))-1+(i&1);
368            if lindex > bits + off {
369                break;
370            }
371            step <<= 1;
372            let maskrot = step & T::WIDTH_MASK;
373            if lindex < off {
374                lindex += step * ((off - lindex - 1) / step + 1);
375            }
376            lindex -= off;
377            let mut lmask = T::power_of_2(lindex & T::WIDTH_MASK);
378            while lindex <= bits {
379                bit_array[usize::exact_from(lindex >> T::LOG_WIDTH)] |= lmask;
380                lmask.rotate_left_assign(maskrot);
381                lindex += step;
382            }
383            // lindex = n_to_bit(id_to_n(i)*bit_to_n(i));
384            lindex = i * (i * 3 + 6) + (i & 1);
385            if lindex < off {
386                lindex += step * ((off - lindex - 1) / step + 1);
387            }
388            lindex -= off;
389            lmask = T::power_of_2(lindex & T::WIDTH_MASK);
390            while lindex <= bits {
391                bit_array[usize::exact_from(lindex >> T::LOG_WIDTH)] |= lmask;
392                lmask.rotate_left_assign(maskrot);
393                lindex += step;
394            }
395        }
396        mask <<= 1;
397        if mask == T::ZERO {
398            mask = T::ONE;
399            index += 1;
400        }
401    }
402}
403
404#[doc(hidden)]
405#[inline]
406// This is equivalent to `primesieve_size` from `primesieve.c`, GMP 6.2.1.
407pub fn limbs_prime_sieve_size<T: PrimitiveUnsigned>(n: u64) -> usize {
408    assert!(n >= 5);
409    usize::exact_from((n_to_bit(n) >> T::LOG_WIDTH) + 1)
410}
411
412const BLOCK_SIZE: usize = 2048;
413
414pub_test! {limbs_count_ones<T: PrimitiveUnsigned>(xs: &[T]) -> u64 {
415    xs.iter().map(|&x| CountOnes::count_ones(x)).sum()
416}}
417
418// Fills bit_array with the characteristic function of composite numbers up to the parameter n. I.e.
419// a bit set to "1" represent a composite, a "0" represent a prime.
420//
421// The primesieve_size(n) limbs pointed to by bit_array are overwritten. The returned value counts
422// prime integers in the interval [4, n]. Note that n > 4.
423//
424// Even numbers and multiples of 3 are excluded "a priori", only numbers equivalent to +/- 1 mod 6
425// have their bit in the array.
426//
427// Once sieved, if the bit b is ZERO it represent a prime, the represented prime is bit_to_n(b), if
428// the LSbit is bit 0, or id_to_n(b), if you call "1" the first bit.
429//
430// # Worst-case complexity
431// $T(n) = O(n\log\log n)$
432//
433// $M(n) = O(1)$
434fn limbs_prime_sieve_generic<T: PrimitiveUnsigned, F: Fn(&mut [T], u64) -> u64>(
435    bit_array: &mut [T],
436    n: u64,
437    fill_bitpattern: F,
438    sieve_seed: T,
439    seed_limit: u64,
440) -> u64 {
441    assert!(n > 4);
442    let bits = n_to_bit(n);
443    let size = usize::exact_from((bits >> T::LOG_WIDTH) + 1);
444    if size > BLOCK_SIZE << 1 {
445        let mut off = BLOCK_SIZE + (size % BLOCK_SIZE);
446        first_block_primesieve(
447            bit_array,
448            id_to_n(u64::exact_from(off) << T::LOG_WIDTH),
449            &fill_bitpattern,
450            sieve_seed,
451            seed_limit,
452        );
453        let (sieve, bit_array) = bit_array.split_at_mut(off);
454        for xs in bit_array.chunks_mut(BLOCK_SIZE) {
455            block_resieve(
456                xs,
457                u64::exact_from(off) << T::LOG_WIDTH,
458                sieve,
459                &fill_bitpattern,
460            );
461            off += BLOCK_SIZE;
462            if off >= size {
463                break;
464            }
465        }
466    } else {
467        first_block_primesieve(bit_array, n, &fill_bitpattern, sieve_seed, seed_limit);
468    }
469    if (bits + 1) & T::WIDTH_MASK != 0 {
470        bit_array[size - 1] |= T::MAX << ((bits + 1) & T::WIDTH_MASK);
471    }
472    (u64::exact_from(size) << T::LOG_WIDTH) - limbs_count_ones(&bit_array[..size])
473}
474
475#[doc(hidden)]
476// This is equivalent to `gmp_primesieve` from `primesieve.c`, GMP 6.2.1.
477#[inline]
478pub fn limbs_prime_sieve_u32(bit_array: &mut [u32], n: u64) -> u64 {
479    limbs_prime_sieve_generic(
480        bit_array,
481        n,
482        fill_bitpattern_u32,
483        SIEVE_SEED_U32,
484        SEED_LIMIT_U32,
485    )
486}
487
488#[doc(hidden)]
489// This is equivalent to `gmp_primesieve` from `primesieve.c`, GMP 6.2.1.
490#[inline]
491pub fn limbs_prime_sieve_u64(bit_array: &mut [u64], n: u64) -> u64 {
492    limbs_prime_sieve_generic(
493        bit_array,
494        n,
495        fill_bitpattern_u64,
496        SIEVE_SEED_U64,
497        SEED_LIMIT_U64,
498    )
499}