Skip to main content

num_modular/
prim.rs

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