Skip to main content

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);
283macro_rules! impl_const_powm {
284    ($name:ident, $T:ty, $D:ty) => {
285        /// Const modular exponentiation using binary exponentiation.
286        pub const fn $name(base: $T, exp: $T, m: $T) -> $T {
287            if m <= 1 {
288                return 0;
289            }
290            let mut base = base % m;
291            let mut result: $T = 1;
292            let mut exp = exp;
293            while exp > 0 {
294                if exp & 1 == 1 {
295                    result = (((result as $D) * (base as $D)) % (m as $D)) as $T;
296                }
297                exp >>= 1;
298                base = (((base as $D) * (base as $D)) % (m as $D)) as $T;
299            }
300            result
301        }
302    };
303}
304impl_const_powm!(powm_u32, u32, u64);
305impl_const_powm!(powm_u64, u64, u128);
306
307// forward modular operations to valye by value
308macro_rules! impl_mod_ops_by_deref {
309    ($($T:ty)*) => {$(
310        // core ops
311        impl ModularCoreOps<$T, &$T> for &$T {
312            type Output = $T;
313            #[inline]
314            fn addm(self, rhs: $T, m: &$T) -> $T {
315                (*self).addm(rhs, &m)
316            }
317            #[inline]
318            fn subm(self, rhs: $T, m: &$T) -> $T {
319                (*self).subm(rhs, &m)
320            }
321            #[inline]
322            fn mulm(self, rhs: $T, m: &$T) -> $T {
323                (*self).mulm(rhs, &m)
324            }
325        }
326        impl ModularCoreOps<&$T, &$T> for $T {
327            type Output = $T;
328            #[inline]
329            fn addm(self, rhs: &$T, m: &$T) -> $T {
330                self.addm(*rhs, &m)
331            }
332            #[inline]
333            fn subm(self, rhs: &$T, m: &$T) -> $T {
334                self.subm(*rhs, &m)
335            }
336            #[inline]
337            fn mulm(self, rhs: &$T, m: &$T) -> $T {
338                self.mulm(*rhs, &m)
339            }
340        }
341        impl ModularCoreOps<&$T, &$T> for &$T {
342            type Output = $T;
343            #[inline]
344            fn addm(self, rhs: &$T, m: &$T) -> $T {
345                (*self).addm(*rhs, &m)
346            }
347            #[inline]
348            fn subm(self, rhs: &$T, m: &$T) -> $T {
349                (*self).subm(*rhs, &m)
350            }
351            #[inline]
352            fn mulm(self, rhs: &$T, m: &$T) -> $T {
353                (*self).mulm(*rhs, &m)
354            }
355        }
356
357        // pow
358        impl ModularPow<$T, &$T> for &$T {
359            type Output = $T;
360            #[inline]
361            fn powm(self, exp: $T, m: &$T) -> $T {
362                (*self).powm(exp, &m)
363            }
364        }
365        impl ModularPow<&$T, &$T> for $T {
366            type Output = $T;
367            #[inline]
368            fn powm(self, exp: &$T, m: &$T) -> $T {
369                self.powm(*exp, &m)
370            }
371        }
372        impl ModularPow<&$T, &$T> for &$T {
373            type Output = $T;
374            #[inline]
375            fn powm(self, exp: &$T, m: &$T) -> $T {
376                (*self).powm(*exp, &m)
377            }
378        }
379
380        // unary ops
381        impl ModularUnaryOps<&$T> for &$T {
382            type Output = $T;
383
384            #[inline]
385            fn negm(self, m: &$T) -> $T {
386                ModularUnaryOps::<&$T>::negm(*self, m)
387            }
388            #[inline]
389            fn invm(self, m: &$T) -> Option<$T> {
390                ModularUnaryOps::<&$T>::invm(*self, m)
391            }
392            #[inline]
393            fn dblm(self, m: &$T) -> $T {
394                ModularUnaryOps::<&$T>::dblm(*self, m)
395            }
396            #[inline]
397            fn sqm(self, m: &$T) -> $T {
398                ModularUnaryOps::<&$T>::sqm(*self, m)
399            }
400        }
401    )*};
402}
403
404impl_mod_ops_by_deref!(u8 u16 u32 u64 u128 usize);
405
406macro_rules! impl_absm_for_prim {
407    ($($signed:ty => $unsigned:ty;)*) => {$(
408        impl ModularAbs<$unsigned> for $signed {
409            fn absm(self, m: &$unsigned) -> $unsigned {
410                if self >= 0 {
411                    (self as $unsigned) % m
412                } else {
413                    (-self as $unsigned).negm(m)
414                }
415            }
416        }
417    )*};
418}
419
420impl_absm_for_prim! {
421    i8 => u8; i16 => u16; i32 => u32; i64 => u64; i128 => u128; isize => usize;
422}
423
424macro_rules! impl_div_exact_for_prim {
425    ($($t:ty)*) => {$(
426        impl DivExact<$t, ()> for $t {
427            type Output = $t;
428            #[inline]
429            fn div_exact(self, d: $t, _: &()) -> Option<Self::Output> {
430                let (q, r) = (self / d, self % d);
431                if r == 0 {
432                    Some(q)
433                } else {
434                    None
435                }
436            }
437        }
438    )*};
439}
440
441impl_div_exact_for_prim!(u8 u16 u32 u64 u128);
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446    use core::ops::Neg;
447    use rand::random;
448
449    const NRANDOM: u32 = 10; // number of random tests to run
450
451    #[test]
452    fn addm_test() {
453        // fixed cases
454        const CASES: [(u8, u8, u8, u8); 10] = [
455            // [m, x, y, rem]: x + y = rem (mod m)
456            (5, 0, 0, 0),
457            (5, 1, 2, 3),
458            (5, 2, 1, 3),
459            (5, 2, 2, 4),
460            (5, 3, 2, 0),
461            (5, 2, 3, 0),
462            (5, 6, 1, 2),
463            (5, 1, 6, 2),
464            (5, 11, 7, 3),
465            (5, 7, 11, 3),
466        ];
467
468        for &(m, x, y, r) in CASES.iter() {
469            assert_eq!(x.addm(y, &m), r);
470            assert_eq!((x as u16).addm(y as u16, &(m as _)), r as _);
471            assert_eq!((x as u32).addm(y as u32, &(m as _)), r as _);
472            assert_eq!((x as u64).addm(y as u64, &(m as _)), r as _);
473            assert_eq!((x as u128).addm(y as u128, &(m as _)), r as _);
474        }
475
476        // random cases for u64 and u128
477        for _ in 0..NRANDOM {
478            let a = random::<u32>() as u64;
479            let b = random::<u32>() as u64;
480            let m = random::<u32>() as u64;
481            assert_eq!(a.addm(b, &m), (a + b) % m);
482            assert_eq!(
483                a.addm(b, &(1u64 << 32)) as u32,
484                (a as u32).wrapping_add(b as u32)
485            );
486
487            let a = random::<u64>() as u128;
488            let b = random::<u64>() as u128;
489            let m = random::<u64>() as u128;
490            assert_eq!(a.addm(b, &m), (a + b) % m);
491            assert_eq!(
492                a.addm(b, &(1u128 << 64)) as u64,
493                (a as u64).wrapping_add(b as u64)
494            );
495        }
496    }
497
498    #[test]
499    fn subm_test() {
500        // fixed cases
501        const CASES: [(u8, u8, u8, u8); 10] = [
502            // [m, x, y, rem]: x - y = rem (mod m)
503            (7, 0, 0, 0),
504            (7, 11, 9, 2),
505            (7, 5, 2, 3),
506            (7, 2, 5, 4),
507            (7, 6, 7, 6),
508            (7, 1, 7, 1),
509            (7, 7, 1, 6),
510            (7, 0, 6, 1),
511            (7, 15, 1, 0),
512            (7, 1, 15, 0),
513        ];
514
515        for &(m, x, y, r) in CASES.iter() {
516            assert_eq!(x.subm(y, &m), r);
517            assert_eq!((x as u16).subm(y as u16, &(m as _)), r as _);
518            assert_eq!((x as u32).subm(y as u32, &(m as _)), r as _);
519            assert_eq!((x as u64).subm(y as u64, &(m as _)), r as _);
520            assert_eq!((x as u128).subm(y as u128, &(m as _)), r as _);
521        }
522
523        // random cases for u64 and u128
524        for _ in 0..NRANDOM {
525            let a = random::<u32>() as u64;
526            let b = random::<u32>() as u64;
527            let m = random::<u32>() as u64;
528            assert_eq!(
529                a.subm(b, &m),
530                (a as i64 - b as i64).rem_euclid(m as i64) as u64
531            );
532            assert_eq!(
533                a.subm(b, &(1u64 << 32)) as u32,
534                (a as u32).wrapping_sub(b as u32)
535            );
536
537            let a = random::<u64>() as u128;
538            let b = random::<u64>() as u128;
539            let m = random::<u64>() as u128;
540            assert_eq!(
541                a.subm(b, &m),
542                (a as i128 - b as i128).rem_euclid(m as i128) as u128
543            );
544            assert_eq!(
545                a.subm(b, &(1u128 << 64)) as u64,
546                (a as u64).wrapping_sub(b as u64)
547            );
548        }
549    }
550
551    #[test]
552    fn negm_and_absm_test() {
553        // fixed cases
554        const CASES: [(u8, u8, u8); 5] = [
555            // [m, x, rem]: -x = rem (mod m)
556            (5, 0, 0),
557            (5, 2, 3),
558            (5, 1, 4),
559            (5, 5, 0),
560            (5, 12, 3),
561        ];
562
563        for &(m, x, r) in CASES.iter() {
564            assert_eq!(x.negm(&m), r);
565            assert_eq!((x as i8).neg().absm(&m), r);
566            assert_eq!((x as u16).negm(&(m as _)), r as _);
567            assert_eq!((x as i16).neg().absm(&(m as u16)), r as _);
568            assert_eq!((x as u32).negm(&(m as _)), r as _);
569            assert_eq!((x as i32).neg().absm(&(m as u32)), r as _);
570            assert_eq!((x as u64).negm(&(m as _)), r as _);
571            assert_eq!((x as i64).neg().absm(&(m as u64)), r as _);
572            assert_eq!((x as u128).negm(&(m as _)), r as _);
573            assert_eq!((x as i128).neg().absm(&(m as u128)), r as _);
574        }
575
576        // random cases for u64 and u128
577        for _ in 0..NRANDOM {
578            let a = random::<u32>() as u64;
579            let m = random::<u32>() as u64;
580            assert_eq!(a.negm(&m), (a as i64).neg().rem_euclid(m as i64) as u64);
581            assert_eq!(a.negm(&(1u64 << 32)) as u32, (a as u32).wrapping_neg());
582
583            let a = random::<u64>() as u128;
584            let m = random::<u64>() as u128;
585            assert_eq!(a.negm(&m), (a as i128).neg().rem_euclid(m as i128) as u128);
586            assert_eq!(a.negm(&(1u128 << 64)) as u64, (a as u64).wrapping_neg());
587        }
588    }
589
590    #[test]
591    fn mulm_test() {
592        // fixed cases
593        const CASES: [(u8, u8, u8, u8); 10] = [
594            // [m, x, y, rem]: x*y = rem (mod m)
595            (7, 0, 0, 0),
596            (7, 11, 9, 1),
597            (7, 5, 2, 3),
598            (7, 2, 5, 3),
599            (7, 6, 7, 0),
600            (7, 1, 7, 0),
601            (7, 7, 1, 0),
602            (7, 0, 6, 0),
603            (7, 15, 1, 1),
604            (7, 1, 15, 1),
605        ];
606
607        for &(m, x, y, r) in CASES.iter() {
608            assert_eq!(x.mulm(y, &m), r);
609            assert_eq!((x as u16).mulm(y as u16, &(m as _)), r as _);
610            assert_eq!((x as u32).mulm(y as u32, &(m as _)), r as _);
611            assert_eq!((x as u64).mulm(y as u64, &(m as _)), r as _);
612            assert_eq!((x as u128).mulm(y as u128, &(m as _)), r as _);
613        }
614
615        // random cases for u64 and u128
616        for _ in 0..NRANDOM {
617            let a = random::<u32>() as u64;
618            let b = random::<u32>() as u64;
619            let m = random::<u32>() as u64;
620            assert_eq!(a.mulm(b, &m), (a * b) % m);
621            assert_eq!(
622                a.mulm(b, &(1u64 << 32)) as u32,
623                (a as u32).wrapping_mul(b as u32)
624            );
625
626            let a = random::<u64>() as u128;
627            let b = random::<u64>() as u128;
628            let m = random::<u64>() as u128;
629            assert_eq!(a.mulm(b, &m), (a * b) % m);
630            assert_eq!(
631                a.mulm(b, &(1u128 << 32)) as u32,
632                (a as u32).wrapping_mul(b as u32)
633            );
634        }
635    }
636
637    #[test]
638    fn powm_test() {
639        // fixed cases
640        const CASES: [(u8, u8, u8, u8); 12] = [
641            // [m, x, y, rem]: x^y = rem (mod m)
642            (7, 0, 0, 1),
643            (7, 11, 9, 1),
644            (7, 5, 2, 4),
645            (7, 2, 5, 4),
646            (7, 6, 7, 6),
647            (7, 1, 7, 1),
648            (7, 7, 1, 0),
649            (7, 0, 6, 0),
650            (7, 15, 1, 1),
651            (7, 1, 15, 1),
652            (7, 255, 255, 6),
653            (10, 255, 255, 5),
654        ];
655
656        for &(m, x, y, r) in CASES.iter() {
657            assert_eq!(x.powm(y, &m), r);
658            assert_eq!((x as u16).powm(y as u16, &(m as _)), r as _);
659            assert_eq!((x as u32).powm(y as u32, &(m as _)), r as _);
660            assert_eq!((x as u64).powm(y as u64, &(m as _)), r as _);
661            assert_eq!((x as u128).powm(y as u128, &(m as _)), r as _);
662        }
663    }
664
665    #[test]
666    fn invm_test() {
667        // fixed cases
668        const CASES: [(u64, u64, u64); 8] = [
669            // [a, m, x] s.t. a*x = 1 (mod m) is satisfied
670            (5, 11, 9),
671            (8, 11, 7),
672            (10, 11, 10),
673            (3, 5000, 1667),
674            (1667, 5000, 3),
675            (999, 5000, 3999),
676            (999, 9_223_372_036_854_775_807, 3_619_181_019_466_538_655),
677            (
678                9_223_372_036_854_775_804,
679                9_223_372_036_854_775_807,
680                3_074_457_345_618_258_602,
681            ),
682        ];
683
684        for &(a, m, x) in CASES.iter() {
685            assert_eq!(a.invm(&m).unwrap(), x);
686        }
687
688        // random cases for u64 and u128
689        for _ in 0..NRANDOM {
690            let a = random::<u32>() as u64;
691            let m = random::<u32>() as u64;
692            if let Some(ia) = a.invm(&m) {
693                assert_eq!(a.mulm(ia, &m), 1);
694            }
695
696            let a = random::<u64>() as u128;
697            let m = random::<u64>() as u128;
698            if let Some(ia) = a.invm(&m) {
699                assert_eq!(a.mulm(ia, &m), 1);
700            }
701        }
702    }
703
704    #[test]
705    fn const_powm_test() {
706        // Verify const powm matches the trait-based powm
707        for _ in 0..NRANDOM {
708            let a = random::<u32>();
709            let m = random::<u32>().max(2);
710            let exp = random::<u32>() % 32;
711            assert_eq!(powm_u32(a, exp, m), a.powm(exp, &m));
712
713            let a = random::<u64>();
714            let m = random::<u64>().max(2);
715            let exp = random::<u64>() % 32;
716            assert_eq!(powm_u64(a, exp, m), a.powm(exp, &m));
717        }
718    }
719
720    #[test]
721    fn dblm_and_sqm_test() {
722        // random cases for u64 and u128
723        for _ in 0..NRANDOM {
724            let a = random::<u64>();
725            let m = random::<u64>();
726            assert_eq!(a.addm(a, &m), a.dblm(&m));
727            assert_eq!(a.mulm(2, &m), a.dblm(&m));
728            assert_eq!(a.mulm(a, &m), a.sqm(&m));
729            assert_eq!(a.powm(2, &m), a.sqm(&m));
730
731            let a = random::<u128>();
732            let m = random::<u128>();
733            assert_eq!(a.addm(a, &m), a.dblm(&m));
734            assert_eq!(a.mulm(2, &m), a.dblm(&m));
735            assert_eq!(a.mulm(a, &m), a.sqm(&m));
736            assert_eq!(a.powm(2, &m), a.sqm(&m));
737        }
738    }
739
740    #[test]
741    fn legendre_test() {
742        const CASES: [(u8, u8, i8); 18] = [
743            (0, 11, 0),
744            (1, 11, 1),
745            (2, 11, -1),
746            (4, 11, 1),
747            (7, 11, -1),
748            (10, 11, -1),
749            (0, 17, 0),
750            (1, 17, 1),
751            (2, 17, 1),
752            (4, 17, 1),
753            (9, 17, 1),
754            (10, 17, -1),
755            (0, 101, 0),
756            (1, 101, 1),
757            (2, 101, -1),
758            (4, 101, 1),
759            (9, 101, 1),
760            (10, 101, -1),
761        ];
762
763        for &(a, n, res) in CASES.iter() {
764            assert_eq!(a.legendre(&n), res);
765            assert_eq!((a as u16).legendre(&(n as u16)), res);
766            assert_eq!((a as u32).legendre(&(n as u32)), res);
767            assert_eq!((a as u64).legendre(&(n as u64)), res);
768            assert_eq!((a as u128).legendre(&(n as u128)), res);
769        }
770
771        const SIGNED_CASES: [(i8, i8, i8); 15] = [
772            (-10, 11, 1),
773            (-7, 11, 1),
774            (-4, 11, -1),
775            (-2, 11, 1),
776            (-1, 11, -1),
777            (-10, 17, -1),
778            (-9, 17, 1),
779            (-4, 17, 1),
780            (-2, 17, 1),
781            (-1, 17, 1),
782            (-10, 101, -1),
783            (-9, 101, 1),
784            (-4, 101, 1),
785            (-2, 101, -1),
786            (-1, 101, 1),
787        ];
788
789        for &(a, n, res) in SIGNED_CASES.iter() {
790            assert_eq!(a.legendre(&n), res);
791            assert_eq!((a as i16).legendre(&(n as i16)), res);
792            assert_eq!((a as i32).legendre(&(n as i32)), res);
793            assert_eq!((a as i64).legendre(&(n as i64)), res);
794            assert_eq!((a as i128).legendre(&(n as i128)), res);
795        }
796    }
797
798    #[test]
799    fn jacobi_test() {
800        const CASES: [(u8, u8, i8); 15] = [
801            (1, 1, 1),
802            (15, 1, 1),
803            (2, 3, -1),
804            (29, 9, 1),
805            (4, 11, 1),
806            (17, 11, -1),
807            (19, 29, -1),
808            (10, 33, -1),
809            (11, 33, 0),
810            (12, 33, 0),
811            (14, 33, -1),
812            (15, 33, 0),
813            (15, 37, -1),
814            (29, 59, 1),
815            (30, 59, -1),
816        ];
817
818        for &(a, n, res) in CASES.iter() {
819            assert_eq!(a.jacobi(&n), res, "{}, {}", a, n);
820            assert_eq!((a as u16).jacobi(&(n as u16)), res);
821            assert_eq!((a as u32).jacobi(&(n as u32)), res);
822            assert_eq!((a as u64).jacobi(&(n as u64)), res);
823            assert_eq!((a as u128).jacobi(&(n as u128)), res);
824        }
825
826        const SIGNED_CASES: [(i8, i8, i8); 15] = [
827            (-10, 15, 0),
828            (-7, 15, 1),
829            (-4, 15, -1),
830            (-2, 15, -1),
831            (-1, 15, -1),
832            (-10, 13, 1),
833            (-9, 13, 1),
834            (-4, 13, 1),
835            (-2, 13, -1),
836            (-1, 13, 1),
837            (-10, 11, 1),
838            (-9, 11, -1),
839            (-4, 11, -1),
840            (-2, 11, 1),
841            (-1, 11, -1),
842        ];
843
844        for &(a, n, res) in SIGNED_CASES.iter() {
845            assert_eq!(a.jacobi(&n), res);
846            assert_eq!((a as i16).jacobi(&(n as i16)), res);
847            assert_eq!((a as i32).jacobi(&(n as i32)), res);
848            assert_eq!((a as i64).jacobi(&(n as i64)), res);
849            assert_eq!((a as i128).jacobi(&(n as i128)), res);
850        }
851    }
852
853    #[test]
854    fn kronecker_test() {
855        const CASES: [(u8, u8, i8); 18] = [
856            (0, 15, 0),
857            (1, 15, 1),
858            (2, 15, 1),
859            (4, 15, 1),
860            (7, 15, -1),
861            (10, 15, 0),
862            (0, 14, 0),
863            (1, 14, 1),
864            (2, 14, 0),
865            (4, 14, 0),
866            (9, 14, 1),
867            (10, 14, 0),
868            (0, 11, 0),
869            (1, 11, 1),
870            (2, 11, -1),
871            (4, 11, 1),
872            (9, 11, 1),
873            (10, 11, -1),
874        ];
875
876        for &(a, n, res) in CASES.iter() {
877            assert_eq!(a.kronecker(&n), res);
878            assert_eq!((a as u16).kronecker(&(n as u16)), res);
879            assert_eq!((a as u32).kronecker(&(n as u32)), res);
880            assert_eq!((a as u64).kronecker(&(n as u64)), res);
881            assert_eq!((a as u128).kronecker(&(n as u128)), res);
882        }
883
884        const SIGNED_CASES: [(i8, i8, i8); 37] = [
885            (-10, 15, 0),
886            (-7, 15, 1),
887            (-4, 15, -1),
888            (-2, 15, -1),
889            (-1, 15, -1),
890            (-10, 14, 0),
891            (-9, 14, -1),
892            (-4, 14, 0),
893            (-2, 14, 0),
894            (-1, 14, -1),
895            (-10, 11, 1),
896            (-9, 11, -1),
897            (-4, 11, -1),
898            (-2, 11, 1),
899            (-1, 11, -1),
900            (-10, -11, -1),
901            (-9, -11, 1),
902            (-4, -11, 1),
903            (-2, -11, -1),
904            (-1, -11, 1),
905            (0, -11, 0),
906            (1, -11, 1),
907            (2, -11, -1),
908            (4, -11, 1),
909            (9, -11, 1),
910            (10, -11, -1),
911            (-10, 32, 0),
912            (-9, 32, 1),
913            (-4, 32, 0),
914            (-2, 32, 0),
915            (-1, 32, 1),
916            (0, 32, 0),
917            (1, 32, 1),
918            (2, 32, 0),
919            (4, 32, 0),
920            (9, 32, 1),
921            (10, 32, 0),
922        ];
923
924        for &(a, n, res) in SIGNED_CASES.iter() {
925            assert_eq!(a.kronecker(&n), res, "{}, {}", a, n);
926            assert_eq!((a as i16).kronecker(&(n as i16)), res);
927            assert_eq!((a as i32).kronecker(&(n as i32)), res);
928            assert_eq!((a as i64).kronecker(&(n as i64)), res);
929            assert_eq!((a as i128).kronecker(&(n as i128)), res);
930        }
931    }
932}