malachite_base/num/arithmetic/
kronecker_symbol.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the GNU MP Library.
4//
5//      Copyright © 1996, 1998, 2000-2004, 2008, 2010 Free Software Foundation, Inc.
6//
7// Uses code adopted from the FLINT Library.
8//
9//      Copyright © 2008 Peter Shrimpton
10//
11//      Copyright © 2009 William Hart
12//
13// This file is part of Malachite.
14//
15// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
16// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
17// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
18
19use crate::fail_on_untested_path;
20use crate::num::arithmetic::traits::{
21    JacobiSymbol, KroneckerSymbol, LegendreSymbol, ModPowerOf2, NegAssign, Parity, UnsignedAbs,
22};
23use crate::num::basic::signeds::PrimitiveSigned;
24use crate::num::basic::unsigneds::PrimitiveUnsigned;
25use crate::num::conversion::traits::SplitInHalf;
26use crate::num::logic::traits::NotAssign;
27use core::mem::swap;
28
29pub_test! {jacobi_symbol_unsigned_simple<T: PrimitiveUnsigned>(mut a: T, mut n: T) -> i8 {
30    assert_ne!(n, T::ZERO);
31    assert!(n.odd());
32    a %= n;
33    let mut t = 1i8;
34    while a != T::ZERO {
35        while a.even() {
36            a >>= 1;
37            let r: u8 = n.mod_power_of_2(3).wrapping_into();
38            if r == 3 || r == 5 {
39                t.neg_assign();
40            }
41        }
42        swap(&mut a, &mut n);
43        if (a & n).get_bit(1) {
44            t.neg_assign();
45        }
46        a %= n;
47    }
48    if n == T::ONE {
49        t
50    } else {
51        0
52    }
53}}
54
55// Computes (a / b) where b is odd, and a and b are otherwise arbitrary two-limb numbers.
56//
57// This is equivalent to `mpn_jacobi_2` from `mpn/jacobi_2.c`, GMP 6.2.1, where `JACOBI_2_METHOD ==
58// 2` and `bit` is 0.
59pub_test! {jacobi_symbol_unsigned_double_fast_2<T: PrimitiveUnsigned>(
60    mut x_1: T,
61    mut x_0: T,
62    mut y_1: T,
63    mut y_0: T,
64) -> i8 {
65    assert!(y_0.odd());
66    if y_1 == T::ZERO && y_0 == T::ONE {
67        // (x|1) = 1
68        return 1;
69    }
70    let mut bit = false;
71    if x_0 == T::ZERO {
72        if x_1 == T::ZERO {
73            // (0|y) = 0, y > 1
74            return 0;
75        }
76        let c = x_1.trailing_zeros();
77        if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
78            bit.not_assign();
79        }
80        x_0 = y_0;
81        y_0 = x_1 >> c;
82        if y_0 == T::ONE {
83            // (1|y) = 1
84            return if bit { -1 } else { 1 };
85        }
86        x_1 = y_1;
87        if (x_0 & y_0).get_bit(1) {
88            bit.not_assign();
89        }
90    } else {
91        if x_0.even() {
92            let c = x_0.trailing_zeros();
93            x_0 = (x_1 << (T::WIDTH - c)) | (x_0 >> c);
94            x_1 >>= c;
95            if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
96                bit.not_assign();
97            }
98        }
99        let mut skip_loop = false;
100        if x_1 == T::ZERO {
101            if y_1 == T::ZERO {
102                assert!(y_0.odd());
103                assert!(y_0 > T::ONE);
104                let j = x_0.jacobi_symbol(y_0);
105                return if bit { -j } else { j };
106            }
107                if (x_0 & y_0).get_bit(1) {
108                    bit.not_assign();
109                }
110                swap(&mut x_0, &mut y_0);
111                x_1 = y_1;
112                skip_loop = true;
113        }
114        if !skip_loop {
115            'outer: while y_1 != T::ZERO {
116                // Compute (x|y)
117                while x_1 > y_1 {
118                    (x_1, x_0) = T::xx_sub_yy_to_zz(x_1, x_0, y_1, y_0);
119                    if x_0 == T::ZERO {
120                        let c = x_1.trailing_zeros();
121                        if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
122                            bit.not_assign();
123                        }
124                        x_0 = y_0;
125                        y_0 = x_1 >> c;
126                        x_1 = y_1;
127                        if (x_0 & y_0).get_bit(1) {
128                            bit.not_assign();
129                        }
130                        break 'outer;
131                    }
132                        let c = x_0.trailing_zeros();
133                        if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
134                            bit.not_assign();
135                        }
136                        x_0 = (x_1 << (T::WIDTH - c)) | (x_0 >> c);
137                        x_1 >>= c;
138                }
139                if x_1 != y_1 {
140                    if x_1 == T::ZERO {
141                        if (x_0 & y_0).get_bit(1) {
142                            bit.not_assign();
143                        }
144                        swap(&mut x_0, &mut y_0);
145                        x_1 = y_1;
146                        break;
147                    }
148                    if (x_0 & y_0).get_bit(1) {
149                        bit.not_assign();
150                    }
151                    // Compute (y|x)
152                    while y_1 > x_1 {
153                        (y_1, y_0) = T::xx_sub_yy_to_zz(y_1, y_0, x_1, x_0);
154                        if y_0 == T::ZERO {
155                            let c = y_1.trailing_zeros();
156                            if c.odd() & (x_0 ^ (x_0 >> 1)).get_bit(1) {
157                                bit.not_assign();
158                            }
159                            y_0 = y_1 >> c;
160                            if (x_0 & y_0).get_bit(1) {
161                                bit.not_assign();
162                            }
163                            break 'outer;
164                        }
165                        let c = y_0.trailing_zeros();
166                        if c.odd() & (x_0 ^ (x_0 >> 1)).get_bit(1) {
167                            bit.not_assign();
168                        }
169                        y_0 = (y_1 << (T::WIDTH - c)) | (y_0 >> c);
170                        y_1 >>= c;
171                    }
172                    if (x_0 & y_0).get_bit(1) {
173                        bit.not_assign();
174                    }
175                }
176                // Compute (x|y)
177                if x_1 == y_1 {
178                    if x_0 < y_0 {
179                        swap(&mut x_0, &mut y_0);
180                        if (x_0 & y_0).get_bit(1) {
181                            bit.not_assign();
182                        }
183                    }
184                    x_0 -= y_0;
185                    if x_0 == T::ZERO {
186                        return 0;
187                    }
188                    let c = x_0.trailing_zeros();
189                    if c.odd() & (y_0 ^ (y_0 >> 1)).get_bit(1) {
190                        bit.not_assign();
191                    }
192                    x_0 >>= c;
193                    if x_0 == T::ONE {
194                        return if bit { -1 } else { 1 };
195                    }
196                    swap(&mut x_0, &mut y_0);
197                    if (x_0 & y_0).get_bit(1) {
198                        bit.not_assign();
199                    }
200                    break;
201                }
202            }
203        }
204    }
205    // Compute (x|y), with y a single limb.
206    assert!(y_0.odd());
207    if y_0 == T::ONE {
208        // (x|1) = 1
209        return if bit { -1 } else { 1 };
210    }
211    while x_1 != T::ZERO {
212        x_1 -= if x_0 < y_0 { T::ONE } else { T::ZERO };
213        x_0.wrapping_sub_assign(y_0);
214        if x_0 == T::ZERO {
215            if x_1 == T::ZERO {
216                fail_on_untested_path(
217                    "jacobi_symbol_unsigned_double_fast_2, x_1 == T::ZERO fourth time",
218                );
219                return 0;
220            }
221            let c = x_1.trailing_zeros();
222            if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
223                bit.not_assign();
224            }
225            x_0 = x_1 >> c;
226            break;
227        }
228        let c = x_0.trailing_zeros();
229        x_0 = (x_1 << (T::WIDTH - c)) | (x_0 >> c);
230        x_1 >>= c;
231        if c.odd() && (y_0 ^ (y_0 >> 1)).get_bit(1) {
232            bit.not_assign();
233        }
234    }
235    assert!(y_0.odd());
236    assert!(y_0 > T::ONE);
237    let j = x_0.jacobi_symbol(y_0);
238    if bit {
239        -j
240    } else {
241        j
242    }
243}}
244
245fn jacobi_symbol_signed<
246    U: PrimitiveUnsigned,
247    S: ModPowerOf2<Output = U> + PrimitiveSigned + UnsignedAbs<Output = U>,
248>(
249    a: S,
250    n: S,
251) -> i8 {
252    assert!(n > S::ZERO);
253    assert!(n.odd());
254    let s = a.unsigned_abs().jacobi_symbol(n.unsigned_abs());
255    if a < S::ZERO && n.get_bit(1) { -s } else { s }
256}
257
258fn kronecker_symbol_unsigned<T: PrimitiveUnsigned>(a: T, b: T) -> i8 {
259    if b == T::ZERO {
260        i8::from(a == T::ONE)
261    } else if a.even() && b.even() {
262        0
263    } else {
264        let b_twos = b.trailing_zeros();
265        let mut s = a.jacobi_symbol(b >> b_twos);
266        if b_twos.odd() {
267            let m: u32 = a.mod_power_of_2(3).wrapping_into();
268            if m == 3 || m == 5 {
269                s.neg_assign();
270            }
271        }
272        s
273    }
274}
275
276fn kronecker_symbol_signed<U: PrimitiveUnsigned, S: ModPowerOf2<Output = U> + PrimitiveSigned>(
277    a: S,
278    b: S,
279) -> i8 {
280    if b == S::ZERO {
281        i8::from(a == S::ONE || a == S::NEGATIVE_ONE)
282    } else if a.even() && b.even() {
283        0
284    } else {
285        let b_twos = b.trailing_zeros();
286        let mut s = a.jacobi_symbol((b >> b_twos).abs());
287        if a < S::ZERO && b < S::ZERO {
288            s.neg_assign();
289        }
290        if b_twos.odd() {
291            let m: u32 = a.mod_power_of_2(3).wrapping_into();
292            if m == 3 || m == 5 {
293                s.neg_assign();
294            }
295        }
296        s
297    }
298}
299
300macro_rules! impl_symbols {
301    ($u:ident, $s:ident) => {
302        impl LegendreSymbol<$u> for $u {
303            /// Computes the Legendre symbol of two numbers.
304            ///
305            /// This implementation is identical to that of [`JacobiSymbol`], since there is no
306            /// computational benefit to requiring that the denominator be prime.
307            ///
308            /// $$
309            /// f(x, y) = \left ( \frac{x}{y} \right ).
310            /// $$
311            ///
312            /// # Worst-case complexity
313            /// $T(n) = O(n^2)$
314            ///
315            /// $M(n) = O(n)$
316            ///
317            /// where $T$ is time, $M$ is additional memory, and $n$ is
318            /// `max(self.significant_bits(), other.significant_bits())`.
319            ///
320            /// # Panics
321            /// Panics if `n` is even.
322            ///
323            /// # Examples
324            /// See [here](super::kronecker_symbol#legendre_symbol).
325            #[inline]
326            fn legendre_symbol(self, n: $u) -> i8 {
327                self.jacobi_symbol(n)
328            }
329        }
330
331        impl LegendreSymbol<$s> for $s {
332            /// Computes the Legendre symbol of two numbers.
333            ///
334            /// This implementation is identical to that of [`JacobiSymbol`], since there is no
335            /// computational benefit to requiring that the denominator be prime.
336            ///
337            /// $$
338            /// f(x, y) = \left ( \frac{x}{y} \right ).
339            /// $$
340            ///
341            /// # Worst-case complexity
342            /// $T(n) = O(n^2)$
343            ///
344            /// $M(n) = O(n)$
345            ///
346            /// where $T$ is time, $M$ is additional memory, and $n$ is
347            /// `max(self.significant_bits(), other.significant_bits())`.
348            ///
349            /// # Panics
350            /// Panics if `n` is even or negative.
351            ///
352            /// # Examples
353            /// See [here](super::kronecker_symbol#legendre_symbol).
354            #[inline]
355            fn legendre_symbol(self, n: $s) -> i8 {
356                self.jacobi_symbol(n)
357            }
358        }
359
360        impl JacobiSymbol<$s> for $s {
361            /// Computes the Jacobi symbol of two numbers.
362            ///
363            /// $$
364            /// f(x, y) = \left ( \frac{x}{y} \right ).
365            /// $$
366            ///
367            /// # Worst-case complexity
368            /// $T(n) = O(n^2)$
369            ///
370            /// $M(n) = O(n)$
371            ///
372            /// where $T$ is time, $M$ is additional memory, and $n$ is
373            /// `max(self.significant_bits(), other.significant_bits())`.
374            ///
375            /// # Panics
376            /// Panics if `n` is even.
377            ///
378            /// # Examples
379            /// See [here](super::kronecker_symbol#jacobi_symbol).
380            #[inline]
381            fn jacobi_symbol(self, n: $s) -> i8 {
382                jacobi_symbol_signed::<$u, $s>(self, n)
383            }
384        }
385
386        impl KroneckerSymbol<$u> for $u {
387            /// Computes the Kronecker symbol of two numbers.
388            ///
389            /// $$
390            /// f(x, y) = \left ( \frac{x}{y} \right ).
391            /// $$
392            ///
393            /// # Worst-case complexity
394            /// $T(n) = O(n^2)$
395            ///
396            /// $M(n) = O(n)$
397            ///
398            /// where $T$ is time, $M$ is additional memory, and $n$ is
399            /// `max(self.significant_bits(), other.significant_bits())`.
400            ///
401            /// # Examples
402            /// See [here](super::kronecker_symbol#kronecker_symbol).
403            #[inline]
404            fn kronecker_symbol(self, n: $u) -> i8 {
405                kronecker_symbol_unsigned(self, n)
406            }
407        }
408
409        impl KroneckerSymbol<$s> for $s {
410            /// Computes the Kronecker symbol of two numbers.
411            ///
412            /// $$
413            /// f(x, y) = \left ( \frac{x}{y} \right ).
414            /// $$
415            ///
416            /// # Worst-case complexity
417            /// $T(n) = O(n^2)$
418            ///
419            /// $M(n) = O(n)$
420            ///
421            /// where $T$ is time, $M$ is additional memory, and $n$ is
422            /// `max(self.significant_bits(), other.significant_bits())`.
423            ///
424            /// # Examples
425            /// See [here](super::kronecker_symbol#kronecker_symbol).
426            #[inline]
427            fn kronecker_symbol(self, n: $s) -> i8 {
428                kronecker_symbol_signed::<$u, $s>(self, n)
429            }
430        }
431    };
432}
433apply_to_unsigned_signed_pairs!(impl_symbols);
434
435macro_rules! impl_jacobi_symbol_unsigned {
436    ($u:ident) => {
437        /// Computes the Jacobi symbol of two numbers.
438        ///
439        /// $$
440        /// f(x, y) = \left ( \frac{x}{y} \right ).
441        /// $$
442        ///
443        /// # Worst-case complexity
444        /// $T(n) = O(n^2)$
445        ///
446        /// $M(n) = O(n)$
447        ///
448        /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
449        /// other.significant_bits())`.
450        ///
451        /// # Panics
452        /// Panics if `n` is even or negative.
453        ///
454        /// # Examples
455        /// See [here](super::kronecker_symbol#jacobi_symbol).
456        impl JacobiSymbol<$u> for $u {
457            #[inline]
458            fn jacobi_symbol(self, n: $u) -> i8 {
459                jacobi_symbol_unsigned_simple(self, n)
460            }
461        }
462    };
463}
464impl_jacobi_symbol_unsigned!(u8);
465impl_jacobi_symbol_unsigned!(u16);
466impl_jacobi_symbol_unsigned!(u32);
467impl_jacobi_symbol_unsigned!(u64);
468impl_jacobi_symbol_unsigned!(usize);
469
470impl JacobiSymbol<u128> for u128 {
471    /// Computes the Jacobi symbol of two `u128`s.
472    ///
473    /// $$
474    /// f(x, y) = \left ( \frac{x}{y} \right ).
475    /// $$
476    ///
477    /// # Worst-case complexity
478    /// $T(n) = O(n^2)$
479    ///
480    /// $M(n) = O(n)$
481    ///
482    /// where $T$ is time, $M$ is additional memory, and $n$ is `max(self.significant_bits(),
483    /// other.significant_bits())`.
484    ///
485    /// # Examples
486    /// See [here](super::kronecker_symbol#jacobi_symbol).
487    #[inline]
488    fn jacobi_symbol(self, n: u128) -> i8 {
489        let (x_1, x_0) = self.split_in_half();
490        let (y_1, y_0) = n.split_in_half();
491        jacobi_symbol_unsigned_double_fast_2(x_1, x_0, y_1, y_0)
492    }
493}