num_modular/
prim.rs

1//! Implementations for modular operations on primitive integers
2
3use crate::{udouble, Reducer, Vanilla};
4use crate::{DivExact, ModularAbs, ModularCoreOps, ModularPow, ModularSymbols, ModularUnaryOps};
5
6// FIXME: implement the modular functions as const after https://github.com/rust-lang/rust/pull/68847
7
8macro_rules! impl_core_ops_uu {
9    ($($T:ty => $Tdouble:ty;)*) => ($(
10        impl ModularCoreOps<$T, &$T> for $T {
11            type Output = $T;
12            #[inline(always)]
13            fn addm(self, rhs: $T, m: &$T) -> $T {
14                (((self as $Tdouble) + (rhs as $Tdouble)) % (*m as $Tdouble)) as $T
15            }
16            #[inline]
17            fn subm(self, rhs: $T, m: &$T) -> $T {
18                if self >= rhs {
19                    (self - rhs) % m
20                } else {
21                    ((rhs - self) % m).negm(m)
22                }
23            }
24            #[inline(always)]
25            fn mulm(self, rhs: $T, m: &$T) -> $T {
26                (((self as $Tdouble) * (rhs as $Tdouble)) % (*m as $Tdouble)) as $T
27            }
28        }
29    )*);
30}
31impl_core_ops_uu! { u8 => u16; u16 => u32; u32 => u64; u64 => u128; }
32
33#[cfg(target_pointer_width = "16")]
34impl_core_ops_uu! { usize => u32; }
35#[cfg(target_pointer_width = "32")]
36impl_core_ops_uu! { usize => u64; }
37#[cfg(target_pointer_width = "64")]
38impl_core_ops_uu! { usize => u128; }
39
40impl ModularCoreOps<u128, &u128> for u128 {
41    type Output = u128;
42
43    #[inline]
44    fn addm(self, rhs: u128, m: &u128) -> u128 {
45        if let Some(ab) = self.checked_add(rhs) {
46            ab % m
47        } else {
48            udouble::widening_add(self, rhs) % *m
49        }
50    }
51
52    #[inline]
53    fn subm(self, rhs: u128, m: &u128) -> u128 {
54        if self >= rhs {
55            (self - rhs) % m
56        } else {
57            ((rhs - self) % m).negm(m)
58        }
59    }
60
61    #[inline]
62    fn mulm(self, rhs: u128, m: &u128) -> u128 {
63        if let Some(ab) = self.checked_mul(rhs) {
64            ab % m
65        } else {
66            udouble::widening_mul(self, rhs) % *m
67        }
68    }
69}
70
71macro_rules! impl_powm_uprim {
72    ($($T:ty)*) => ($(
73        impl ModularPow<$T, &$T> for $T {
74            type Output = $T;
75            #[inline(always)]
76            fn powm(self, exp: $T, m: &$T) -> $T {
77                Vanilla::<$T>::new(&m).pow(self % m, &exp)
78            }
79        }
80    )*);
81}
82impl_powm_uprim!(u8 u16 u32 u64 u128 usize);
83
84macro_rules! impl_symbols_uprim {
85    ($($T:ty)*) => ($(
86        impl ModularSymbols<&$T> for $T {
87            #[inline]
88            fn checked_legendre(&self, n: &$T) -> Option<i8> {
89                match self.powm((n - 1)/2, &n) {
90                    0 => Some(0),
91                    1 => Some(1),
92                    x if x == n - 1 => Some(-1),
93                    _ => None,
94                }
95            }
96
97            fn checked_jacobi(&self, n: &$T) -> Option<i8> {
98                if n % 2 == 0 {
99                    return None;
100                }
101                if self == &0 {
102                    return Some(if n == &1 {
103                        1
104                    } else {
105                        0
106                    });
107                }
108                if self == &1 {
109                    return Some(1);
110                }
111
112                let mut a = self % n;
113                let mut n = *n;
114                let mut t = 1;
115                while a > 0 {
116                    while a % 2 == 0 {
117                        a /= 2;
118                        if n % 8 == 3 || n % 8 == 5 {
119                            t *= -1;
120                        }
121                    }
122                    core::mem::swap(&mut a, &mut n);
123                    if a % 4 == 3 && n % 4 == 3 {
124                        t *= -1;
125                    }
126                    a %= n;
127                }
128                Some(if n == 1 {
129                    t
130                } else {
131                    0
132                })
133            }
134
135            fn kronecker(&self, n: &$T) -> i8 {
136                match n {
137                    0 => {
138                        if self == &1 {
139                            1
140                        } else {
141                            0
142                        }
143                    }
144                    1 => 1,
145                    2 => {
146                        if self % 2 == 0 {
147                            0
148                        } else if self % 8 == 1 || self % 8 == 7 {
149                            1
150                        } else {
151                            -1
152                        }
153                    }
154                    _ => {
155                        let f = n.trailing_zeros();
156                        let n = n >> f;
157                        self.kronecker(&2).pow(f)
158                            * self.jacobi(&n)
159                    }
160                }
161            }
162        }
163    )*);
164}
165impl_symbols_uprim!(u8 u16 u32 u64 u128 usize);
166
167macro_rules! impl_symbols_iprim {
168    ($($T:ty, $U:ty;)*) => ($(
169        impl ModularSymbols<&$T> for $T {
170            #[inline]
171            fn checked_legendre(&self, n: &$T) -> Option<i8> {
172                if n < &1 {
173                    return None;
174                }
175                let a = self.rem_euclid(*n) as $U;
176                a.checked_legendre(&(*n as $U))
177            }
178
179            #[inline]
180            fn checked_jacobi(&self, n: &$T) -> Option<i8> {
181                if n < &1 {
182                    return None;
183                }
184                let a = self.rem_euclid(*n) as $U;
185                a.checked_jacobi(&(*n as $U))
186            }
187
188            #[inline]
189            fn kronecker(&self, n: &$T) -> i8 {
190                match n {
191                    -1 => {
192                        if self < &0 {
193                            -1
194                        } else {
195                            1
196                        }
197                    }
198                    0 => {
199                        if self == &1 {
200                            1
201                        } else {
202                            0
203                        }
204                    }
205                    1 => 1,
206                    2 => {
207                        if self % 2 == 0 {
208                            0
209                        } else if self.rem_euclid(8) == 1 || self.rem_euclid(8) == 7 {
210                            1
211                        } else {
212                            -1
213                        }
214                    },
215                    i if i < &-1 => {
216                        self.kronecker(&-1) * self.kronecker(&-i)
217                    },
218                    _ => {
219                        let f = n.trailing_zeros();
220                        self.kronecker(&2).pow(f)
221                            * self.jacobi(&(n >> f))
222                    }
223                }
224            }
225        }
226    )*);
227}
228
229impl_symbols_iprim!(i8, u8; i16, u16; i32, u32; i64, u64; i128, u128; isize, usize;);
230
231macro_rules! impl_unary_uprim {
232    ($($T:ty)*) => ($(
233        impl ModularUnaryOps<&$T> for $T {
234            type Output = $T;
235            #[inline]
236            fn negm(self, m: &$T) -> $T {
237                let x = self % m;
238                if x == 0 {
239                    0
240                } else {
241                    m - x
242                }
243            }
244
245            // inverse mod using extended euclidean algorithm
246            fn invm(self, m: &$T) -> Option<$T> {
247                // TODO: optimize using https://eprint.iacr.org/2020/972.pdf
248                let x = if &self >= m { self % m } else { self.clone() };
249
250                let (mut last_r, mut r) = (m.clone(), x);
251                let (mut last_t, mut t) = (0, 1);
252
253                while r > 0 {
254                    let (quo, rem) = (last_r / r, last_r % r);
255                    last_r = r;
256                    r = rem;
257
258                    let new_t = last_t.subm(quo.mulm(t, m), m);
259                    last_t = t;
260                    t = new_t;
261                }
262
263                // if r = gcd(self, m) > 1, then inverse doesn't exist
264                if last_r > 1 {
265                    None
266                } else {
267                    Some(last_t)
268                }
269            }
270
271            #[inline(always)]
272            fn dblm(self, m: &$T) -> $T {
273                self.addm(self, m)
274            }
275            #[inline(always)]
276            fn sqm(self, m: &$T) -> $T {
277                self.mulm(self, m)
278            }
279        }
280    )*);
281}
282impl_unary_uprim!(u8 u16 u32 u64 u128 usize);
283
284// forward modular operations to valye by value
285macro_rules! impl_mod_ops_by_deref {
286    ($($T:ty)*) => {$(
287        // core ops
288        impl ModularCoreOps<$T, &$T> for &$T {
289            type Output = $T;
290            #[inline]
291            fn addm(self, rhs: $T, m: &$T) -> $T {
292                (*self).addm(rhs, &m)
293            }
294            #[inline]
295            fn subm(self, rhs: $T, m: &$T) -> $T {
296                (*self).subm(rhs, &m)
297            }
298            #[inline]
299            fn mulm(self, rhs: $T, m: &$T) -> $T {
300                (*self).mulm(rhs, &m)
301            }
302        }
303        impl ModularCoreOps<&$T, &$T> for $T {
304            type Output = $T;
305            #[inline]
306            fn addm(self, rhs: &$T, m: &$T) -> $T {
307                self.addm(*rhs, &m)
308            }
309            #[inline]
310            fn subm(self, rhs: &$T, m: &$T) -> $T {
311                self.subm(*rhs, &m)
312            }
313            #[inline]
314            fn mulm(self, rhs: &$T, m: &$T) -> $T {
315                self.mulm(*rhs, &m)
316            }
317        }
318        impl ModularCoreOps<&$T, &$T> for &$T {
319            type Output = $T;
320            #[inline]
321            fn addm(self, rhs: &$T, m: &$T) -> $T {
322                (*self).addm(*rhs, &m)
323            }
324            #[inline]
325            fn subm(self, rhs: &$T, m: &$T) -> $T {
326                (*self).subm(*rhs, &m)
327            }
328            #[inline]
329            fn mulm(self, rhs: &$T, m: &$T) -> $T {
330                (*self).mulm(*rhs, &m)
331            }
332        }
333
334        // pow
335        impl ModularPow<$T, &$T> for &$T {
336            type Output = $T;
337            #[inline]
338            fn powm(self, exp: $T, m: &$T) -> $T {
339                (*self).powm(exp, &m)
340            }
341        }
342        impl ModularPow<&$T, &$T> for $T {
343            type Output = $T;
344            #[inline]
345            fn powm(self, exp: &$T, m: &$T) -> $T {
346                self.powm(*exp, &m)
347            }
348        }
349        impl ModularPow<&$T, &$T> for &$T {
350            type Output = $T;
351            #[inline]
352            fn powm(self, exp: &$T, m: &$T) -> $T {
353                (*self).powm(*exp, &m)
354            }
355        }
356
357        // unary ops
358        impl ModularUnaryOps<&$T> for &$T {
359            type Output = $T;
360
361            #[inline]
362            fn negm(self, m: &$T) -> $T {
363                ModularUnaryOps::<&$T>::negm(*self, m)
364            }
365            #[inline]
366            fn invm(self, m: &$T) -> Option<$T> {
367                ModularUnaryOps::<&$T>::invm(*self, m)
368            }
369            #[inline]
370            fn dblm(self, m: &$T) -> $T {
371                ModularUnaryOps::<&$T>::dblm(*self, m)
372            }
373            #[inline]
374            fn sqm(self, m: &$T) -> $T {
375                ModularUnaryOps::<&$T>::sqm(*self, m)
376            }
377        }
378    )*};
379}
380
381impl_mod_ops_by_deref!(u8 u16 u32 u64 u128 usize);
382
383macro_rules! impl_absm_for_prim {
384    ($($signed:ty => $unsigned:ty;)*) => {$(
385        impl ModularAbs<$unsigned> for $signed {
386            fn absm(self, m: &$unsigned) -> $unsigned {
387                if self >= 0 {
388                    (self as $unsigned) % m
389                } else {
390                    (-self as $unsigned).negm(m)
391                }
392            }
393        }
394    )*};
395}
396
397impl_absm_for_prim! {
398    i8 => u8; i16 => u16; i32 => u32; i64 => u64; i128 => u128; isize => usize;
399}
400
401macro_rules! impl_div_exact_for_prim {
402    ($($t:ty)*) => {$(
403        impl DivExact<$t, ()> for $t {
404            type Output = $t;
405            #[inline]
406            fn div_exact(self, d: $t, _: &()) -> Option<Self::Output> {
407                let (q, r) = (self / d, self % d);
408                if r == 0 {
409                    Some(q)
410                } else {
411                    None
412                }
413            }
414        }
415    )*};
416}
417
418impl_div_exact_for_prim!(u8 u16 u32 u64 u128);
419
420#[cfg(test)]
421mod tests {
422    use super::*;
423    use core::ops::Neg;
424    use rand::random;
425
426    const NRANDOM: u32 = 10; // number of random tests to run
427
428    #[test]
429    fn addm_test() {
430        // fixed cases
431        const CASES: [(u8, u8, u8, u8); 10] = [
432            // [m, x, y, rem]: x + y = rem (mod m)
433            (5, 0, 0, 0),
434            (5, 1, 2, 3),
435            (5, 2, 1, 3),
436            (5, 2, 2, 4),
437            (5, 3, 2, 0),
438            (5, 2, 3, 0),
439            (5, 6, 1, 2),
440            (5, 1, 6, 2),
441            (5, 11, 7, 3),
442            (5, 7, 11, 3),
443        ];
444
445        for &(m, x, y, r) in CASES.iter() {
446            assert_eq!(x.addm(y, &m), r);
447            assert_eq!((x as u16).addm(y as u16, &(m as _)), r as _);
448            assert_eq!((x as u32).addm(y as u32, &(m as _)), r as _);
449            assert_eq!((x as u64).addm(y as u64, &(m as _)), r as _);
450            assert_eq!((x as u128).addm(y as u128, &(m as _)), r as _);
451        }
452
453        // random cases for u64 and u128
454        for _ in 0..NRANDOM {
455            let a = random::<u32>() as u64;
456            let b = random::<u32>() as u64;
457            let m = random::<u32>() as u64;
458            assert_eq!(a.addm(b, &m), (a + b) % m);
459            assert_eq!(
460                a.addm(b, &(1u64 << 32)) as u32,
461                (a as u32).wrapping_add(b as u32)
462            );
463
464            let a = random::<u64>() as u128;
465            let b = random::<u64>() as u128;
466            let m = random::<u64>() as u128;
467            assert_eq!(a.addm(b, &m), (a + b) % m);
468            assert_eq!(
469                a.addm(b, &(1u128 << 64)) as u64,
470                (a as u64).wrapping_add(b as u64)
471            );
472        }
473    }
474
475    #[test]
476    fn subm_test() {
477        // fixed cases
478        const CASES: [(u8, u8, u8, u8); 10] = [
479            // [m, x, y, rem]: x - y = rem (mod m)
480            (7, 0, 0, 0),
481            (7, 11, 9, 2),
482            (7, 5, 2, 3),
483            (7, 2, 5, 4),
484            (7, 6, 7, 6),
485            (7, 1, 7, 1),
486            (7, 7, 1, 6),
487            (7, 0, 6, 1),
488            (7, 15, 1, 0),
489            (7, 1, 15, 0),
490        ];
491
492        for &(m, x, y, r) in CASES.iter() {
493            assert_eq!(x.subm(y, &m), r);
494            assert_eq!((x as u16).subm(y as u16, &(m as _)), r as _);
495            assert_eq!((x as u32).subm(y as u32, &(m as _)), r as _);
496            assert_eq!((x as u64).subm(y as u64, &(m as _)), r as _);
497            assert_eq!((x as u128).subm(y as u128, &(m as _)), r as _);
498        }
499
500        // random cases for u64 and u128
501        for _ in 0..NRANDOM {
502            let a = random::<u32>() as u64;
503            let b = random::<u32>() as u64;
504            let m = random::<u32>() as u64;
505            assert_eq!(
506                a.subm(b, &m),
507                (a as i64 - b as i64).rem_euclid(m as i64) as u64
508            );
509            assert_eq!(
510                a.subm(b, &(1u64 << 32)) as u32,
511                (a as u32).wrapping_sub(b as u32)
512            );
513
514            let a = random::<u64>() as u128;
515            let b = random::<u64>() as u128;
516            let m = random::<u64>() as u128;
517            assert_eq!(
518                a.subm(b, &m),
519                (a as i128 - b as i128).rem_euclid(m as i128) as u128
520            );
521            assert_eq!(
522                a.subm(b, &(1u128 << 64)) as u64,
523                (a as u64).wrapping_sub(b as u64)
524            );
525        }
526    }
527
528    #[test]
529    fn negm_and_absm_test() {
530        // fixed cases
531        const CASES: [(u8, u8, u8); 5] = [
532            // [m, x, rem]: -x = rem (mod m)
533            (5, 0, 0),
534            (5, 2, 3),
535            (5, 1, 4),
536            (5, 5, 0),
537            (5, 12, 3),
538        ];
539
540        for &(m, x, r) in CASES.iter() {
541            assert_eq!(x.negm(&m), r);
542            assert_eq!((x as i8).neg().absm(&m), r);
543            assert_eq!((x as u16).negm(&(m as _)), r as _);
544            assert_eq!((x as i16).neg().absm(&(m as u16)), r as _);
545            assert_eq!((x as u32).negm(&(m as _)), r as _);
546            assert_eq!((x as i32).neg().absm(&(m as u32)), r as _);
547            assert_eq!((x as u64).negm(&(m as _)), r as _);
548            assert_eq!((x as i64).neg().absm(&(m as u64)), r as _);
549            assert_eq!((x as u128).negm(&(m as _)), r as _);
550            assert_eq!((x as i128).neg().absm(&(m as u128)), r as _);
551        }
552
553        // random cases for u64 and u128
554        for _ in 0..NRANDOM {
555            let a = random::<u32>() as u64;
556            let m = random::<u32>() as u64;
557            assert_eq!(a.negm(&m), (a as i64).neg().rem_euclid(m as i64) as u64);
558            assert_eq!(a.negm(&(1u64 << 32)) as u32, (a as u32).wrapping_neg());
559
560            let a = random::<u64>() as u128;
561            let m = random::<u64>() as u128;
562            assert_eq!(a.negm(&m), (a as i128).neg().rem_euclid(m as i128) as u128);
563            assert_eq!(a.negm(&(1u128 << 64)) as u64, (a as u64).wrapping_neg());
564        }
565    }
566
567    #[test]
568    fn mulm_test() {
569        // fixed cases
570        const CASES: [(u8, u8, u8, u8); 10] = [
571            // [m, x, y, rem]: x*y = rem (mod m)
572            (7, 0, 0, 0),
573            (7, 11, 9, 1),
574            (7, 5, 2, 3),
575            (7, 2, 5, 3),
576            (7, 6, 7, 0),
577            (7, 1, 7, 0),
578            (7, 7, 1, 0),
579            (7, 0, 6, 0),
580            (7, 15, 1, 1),
581            (7, 1, 15, 1),
582        ];
583
584        for &(m, x, y, r) in CASES.iter() {
585            assert_eq!(x.mulm(y, &m), r);
586            assert_eq!((x as u16).mulm(y as u16, &(m as _)), r as _);
587            assert_eq!((x as u32).mulm(y as u32, &(m as _)), r as _);
588            assert_eq!((x as u64).mulm(y as u64, &(m as _)), r as _);
589            assert_eq!((x as u128).mulm(y as u128, &(m as _)), r as _);
590        }
591
592        // random cases for u64 and u128
593        for _ in 0..NRANDOM {
594            let a = random::<u32>() as u64;
595            let b = random::<u32>() as u64;
596            let m = random::<u32>() as u64;
597            assert_eq!(a.mulm(b, &m), (a * b) % m);
598            assert_eq!(
599                a.mulm(b, &(1u64 << 32)) as u32,
600                (a as u32).wrapping_mul(b as u32)
601            );
602
603            let a = random::<u64>() as u128;
604            let b = random::<u64>() as u128;
605            let m = random::<u64>() as u128;
606            assert_eq!(a.mulm(b, &m), (a * b) % m);
607            assert_eq!(
608                a.mulm(b, &(1u128 << 32)) as u32,
609                (a as u32).wrapping_mul(b as u32)
610            );
611        }
612    }
613
614    #[test]
615    fn powm_test() {
616        // fixed cases
617        const CASES: [(u8, u8, u8, u8); 12] = [
618            // [m, x, y, rem]: x^y = rem (mod m)
619            (7, 0, 0, 1),
620            (7, 11, 9, 1),
621            (7, 5, 2, 4),
622            (7, 2, 5, 4),
623            (7, 6, 7, 6),
624            (7, 1, 7, 1),
625            (7, 7, 1, 0),
626            (7, 0, 6, 0),
627            (7, 15, 1, 1),
628            (7, 1, 15, 1),
629            (7, 255, 255, 6),
630            (10, 255, 255, 5),
631        ];
632
633        for &(m, x, y, r) in CASES.iter() {
634            assert_eq!(x.powm(y, &m), r);
635            assert_eq!((x as u16).powm(y as u16, &(m as _)), r as _);
636            assert_eq!((x as u32).powm(y as u32, &(m as _)), r as _);
637            assert_eq!((x as u64).powm(y as u64, &(m as _)), r as _);
638            assert_eq!((x as u128).powm(y as u128, &(m as _)), r as _);
639        }
640    }
641
642    #[test]
643    fn invm_test() {
644        // fixed cases
645        const CASES: [(u64, u64, u64); 8] = [
646            // [a, m, x] s.t. a*x = 1 (mod m) is satisfied
647            (5, 11, 9),
648            (8, 11, 7),
649            (10, 11, 10),
650            (3, 5000, 1667),
651            (1667, 5000, 3),
652            (999, 5000, 3999),
653            (999, 9_223_372_036_854_775_807, 3_619_181_019_466_538_655),
654            (
655                9_223_372_036_854_775_804,
656                9_223_372_036_854_775_807,
657                3_074_457_345_618_258_602,
658            ),
659        ];
660
661        for &(a, m, x) in CASES.iter() {
662            assert_eq!(a.invm(&m).unwrap(), x);
663        }
664
665        // random cases for u64 and u128
666        for _ in 0..NRANDOM {
667            let a = random::<u32>() as u64;
668            let m = random::<u32>() as u64;
669            if let Some(ia) = a.invm(&m) {
670                assert_eq!(a.mulm(ia, &m), 1);
671            }
672
673            let a = random::<u64>() as u128;
674            let m = random::<u64>() as u128;
675            if let Some(ia) = a.invm(&m) {
676                assert_eq!(a.mulm(ia, &m), 1);
677            }
678        }
679    }
680
681    #[test]
682    fn dblm_and_sqm_test() {
683        // random cases for u64 and u128
684        for _ in 0..NRANDOM {
685            let a = random::<u64>();
686            let m = random::<u64>();
687            assert_eq!(a.addm(a, &m), a.dblm(&m));
688            assert_eq!(a.mulm(2, &m), a.dblm(&m));
689            assert_eq!(a.mulm(a, &m), a.sqm(&m));
690            assert_eq!(a.powm(2, &m), a.sqm(&m));
691
692            let a = random::<u128>();
693            let m = random::<u128>();
694            assert_eq!(a.addm(a, &m), a.dblm(&m));
695            assert_eq!(a.mulm(2, &m), a.dblm(&m));
696            assert_eq!(a.mulm(a, &m), a.sqm(&m));
697            assert_eq!(a.powm(2, &m), a.sqm(&m));
698        }
699    }
700
701    #[test]
702    fn legendre_test() {
703        const CASES: [(u8, u8, i8); 18] = [
704            (0, 11, 0),
705            (1, 11, 1),
706            (2, 11, -1),
707            (4, 11, 1),
708            (7, 11, -1),
709            (10, 11, -1),
710            (0, 17, 0),
711            (1, 17, 1),
712            (2, 17, 1),
713            (4, 17, 1),
714            (9, 17, 1),
715            (10, 17, -1),
716            (0, 101, 0),
717            (1, 101, 1),
718            (2, 101, -1),
719            (4, 101, 1),
720            (9, 101, 1),
721            (10, 101, -1),
722        ];
723
724        for &(a, n, res) in CASES.iter() {
725            assert_eq!(a.legendre(&n), res);
726            assert_eq!((a as u16).legendre(&(n as u16)), res);
727            assert_eq!((a as u32).legendre(&(n as u32)), res);
728            assert_eq!((a as u64).legendre(&(n as u64)), res);
729            assert_eq!((a as u128).legendre(&(n as u128)), res);
730        }
731
732        const SIGNED_CASES: [(i8, i8, i8); 15] = [
733            (-10, 11, 1),
734            (-7, 11, 1),
735            (-4, 11, -1),
736            (-2, 11, 1),
737            (-1, 11, -1),
738            (-10, 17, -1),
739            (-9, 17, 1),
740            (-4, 17, 1),
741            (-2, 17, 1),
742            (-1, 17, 1),
743            (-10, 101, -1),
744            (-9, 101, 1),
745            (-4, 101, 1),
746            (-2, 101, -1),
747            (-1, 101, 1),
748        ];
749
750        for &(a, n, res) in SIGNED_CASES.iter() {
751            assert_eq!(a.legendre(&n), res);
752            assert_eq!((a as i16).legendre(&(n as i16)), res);
753            assert_eq!((a as i32).legendre(&(n as i32)), res);
754            assert_eq!((a as i64).legendre(&(n as i64)), res);
755            assert_eq!((a as i128).legendre(&(n as i128)), res);
756        }
757    }
758
759    #[test]
760    fn jacobi_test() {
761        const CASES: [(u8, u8, i8); 15] = [
762            (1, 1, 1),
763            (15, 1, 1),
764            (2, 3, -1),
765            (29, 9, 1),
766            (4, 11, 1),
767            (17, 11, -1),
768            (19, 29, -1),
769            (10, 33, -1),
770            (11, 33, 0),
771            (12, 33, 0),
772            (14, 33, -1),
773            (15, 33, 0),
774            (15, 37, -1),
775            (29, 59, 1),
776            (30, 59, -1),
777        ];
778
779        for &(a, n, res) in CASES.iter() {
780            assert_eq!(a.jacobi(&n), res, "{}, {}", a, n);
781            assert_eq!((a as u16).jacobi(&(n as u16)), res);
782            assert_eq!((a as u32).jacobi(&(n as u32)), res);
783            assert_eq!((a as u64).jacobi(&(n as u64)), res);
784            assert_eq!((a as u128).jacobi(&(n as u128)), res);
785        }
786
787        const SIGNED_CASES: [(i8, i8, i8); 15] = [
788            (-10, 15, 0),
789            (-7, 15, 1),
790            (-4, 15, -1),
791            (-2, 15, -1),
792            (-1, 15, -1),
793            (-10, 13, 1),
794            (-9, 13, 1),
795            (-4, 13, 1),
796            (-2, 13, -1),
797            (-1, 13, 1),
798            (-10, 11, 1),
799            (-9, 11, -1),
800            (-4, 11, -1),
801            (-2, 11, 1),
802            (-1, 11, -1),
803        ];
804
805        for &(a, n, res) in SIGNED_CASES.iter() {
806            assert_eq!(a.jacobi(&n), res);
807            assert_eq!((a as i16).jacobi(&(n as i16)), res);
808            assert_eq!((a as i32).jacobi(&(n as i32)), res);
809            assert_eq!((a as i64).jacobi(&(n as i64)), res);
810            assert_eq!((a as i128).jacobi(&(n as i128)), res);
811        }
812    }
813
814    #[test]
815    fn kronecker_test() {
816        const CASES: [(u8, u8, i8); 18] = [
817            (0, 15, 0),
818            (1, 15, 1),
819            (2, 15, 1),
820            (4, 15, 1),
821            (7, 15, -1),
822            (10, 15, 0),
823            (0, 14, 0),
824            (1, 14, 1),
825            (2, 14, 0),
826            (4, 14, 0),
827            (9, 14, 1),
828            (10, 14, 0),
829            (0, 11, 0),
830            (1, 11, 1),
831            (2, 11, -1),
832            (4, 11, 1),
833            (9, 11, 1),
834            (10, 11, -1),
835        ];
836
837        for &(a, n, res) in CASES.iter() {
838            assert_eq!(a.kronecker(&n), res);
839            assert_eq!((a as u16).kronecker(&(n as u16)), res);
840            assert_eq!((a as u32).kronecker(&(n as u32)), res);
841            assert_eq!((a as u64).kronecker(&(n as u64)), res);
842            assert_eq!((a as u128).kronecker(&(n as u128)), res);
843        }
844
845        const SIGNED_CASES: [(i8, i8, i8); 37] = [
846            (-10, 15, 0),
847            (-7, 15, 1),
848            (-4, 15, -1),
849            (-2, 15, -1),
850            (-1, 15, -1),
851            (-10, 14, 0),
852            (-9, 14, -1),
853            (-4, 14, 0),
854            (-2, 14, 0),
855            (-1, 14, -1),
856            (-10, 11, 1),
857            (-9, 11, -1),
858            (-4, 11, -1),
859            (-2, 11, 1),
860            (-1, 11, -1),
861            (-10, -11, -1),
862            (-9, -11, 1),
863            (-4, -11, 1),
864            (-2, -11, -1),
865            (-1, -11, 1),
866            (0, -11, 0),
867            (1, -11, 1),
868            (2, -11, -1),
869            (4, -11, 1),
870            (9, -11, 1),
871            (10, -11, -1),
872            (-10, 32, 0),
873            (-9, 32, 1),
874            (-4, 32, 0),
875            (-2, 32, 0),
876            (-1, 32, 1),
877            (0, 32, 0),
878            (1, 32, 1),
879            (2, 32, 0),
880            (4, 32, 0),
881            (9, 32, 1),
882            (10, 32, 0),
883        ];
884
885        for &(a, n, res) in SIGNED_CASES.iter() {
886            assert_eq!(a.kronecker(&n), res, "{}, {}", a, n);
887            assert_eq!((a as i16).kronecker(&(n as i16)), res);
888            assert_eq!((a as i32).kronecker(&(n as i32)), res);
889            assert_eq!((a as i64).kronecker(&(n as i64)), res);
890            assert_eq!((a as i128).kronecker(&(n as i128)), res);
891        }
892    }
893}