1use super::carmichael_lambda;
4use super::factors_dup;
5use super::gcd;
6use super::mod_pow;
7
8use carmichael_lambda::CarmichaelLambda;
9use factors_dup::FactorsDup;
10use gcd::Gcd;
11use mod_pow::ModPow;
12
13pub trait ModOrd: Sized {
38 fn mod_ord(self, other: Self) -> Option<Self>;
39}
40
41macro_rules! impl_uint {
42 ($t:ty) => {
43 impl ModOrd for $t {
44 fn mod_ord(self, n: Self) -> Option<Self> {
45 assert_ne!(n, 0, "modulo must be positive");
46 let a = match self {
47 0 => return None,
48 1 => return Some(1),
49 _ => self,
50 };
51
52 let g = a.gcd(n);
53 if g != 1 {
54 return None;
55 }
56
57 let mut q = n.carmichael_lambda();
58 for e in q.factors_dup() {
59 if a.mod_pow(q / e, n) == 1 {
60 q /= e;
61 }
62 }
63 (a.mod_pow(q, n) == 1).then(|| q)
64 }
65 }
66 };
67 ( $($t:ty)* ) => { $(impl_uint!($t);)* };
68}
69
70impl_uint!(u8 u16 u32 u64 u128 usize);
71
72#[test]
73fn test() {
74 let n_max = 500_u64;
75
76 for n in 2..=n_max {
77 for a in 0..n {
78 let actual = a.mod_ord(n);
79 let mut x = 1;
80 let expected = (1..=n).find_map(|i| {
81 x = x * a % n;
82 if x == 1 { Some(i) } else { None }
83 });
84 eprintln!("{:?}", (a, n));
85 assert_eq!(actual, expected);
86 }
87 }
88}