1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! 位数。

use super::carmichael_lambda;
use super::factors_dup;
use super::gcd;
use super::mod_pow;

use carmichael_lambda::CarmichaelLambda;
use factors_dup::FactorsDup;
use gcd::Gcd;
use mod_pow::ModPow;

/// 位数。
///
/// 法を $n$ として $a, a^2, \\dots, a^m$ が互いに異なり、かつ $a^m \\equiv 1$ である
/// $m$ が存在すれば、それを返す。
///
/// $0\\le a\\lt n$ とする。
///
/// # Complexity
/// $\\lambda(n)$ に対する素因数列挙にかかる時間に加え、各素因数に対して
/// $O(\\log(\\lambda(n)))$ 時間。試し割り法では $O(\\sqrt{n})$ 時間。
///
/// # Examples
/// ```
/// use nekolib::math::ModOrd;
///
/// assert_eq!(7_u64.mod_ord(10), Some(4));
/// assert_eq!(1_u64.mod_ord(3), Some(1));
/// assert_eq!(22_u64.mod_ord(30), None);
/// ```
///
/// # Suggestions
/// [`dlog`] と同様、$\\lambda$ 関数と素因数列挙に関して引数で渡したいかも。
///
/// [`dlog`]: fn.dlog.html
pub trait ModOrd: Sized {
    fn mod_ord(self, other: Self) -> Option<Self>;
}

macro_rules! impl_uint {
    ($t:ty) => {
        impl ModOrd for $t {
            fn mod_ord(self, n: Self) -> Option<Self> {
                assert_ne!(n, 0, "modulo must be positive");
                let a = match self {
                    0 => return None,
                    1 => return Some(1),
                    _ => self,
                };

                let g = a.gcd(n);
                if g != 1 {
                    return None;
                }

                let mut q = n.carmichael_lambda();
                for e in q.factors_dup() {
                    if a.mod_pow(q / e, n) == 1 {
                        q /= e;
                    }
                }
                (a.mod_pow(q, n) == 1).then(|| q)
            }
        }
    };
    ( $($t:ty)* ) => { $(impl_uint!($t);)* };
}

impl_uint!(u8 u16 u32 u64 u128 usize);

#[test]
fn test() {
    let n_max = 500_u64;

    for n in 2..=n_max {
        for a in 0..n {
            let actual = a.mod_ord(n);
            let mut x = 1;
            let expected = (1..=n).find_map(|i| {
                x = x * a % n;
                if x == 1 { Some(i) } else { None }
            });
            eprintln!("{:?}", (a, n));
            assert_eq!(actual, expected);
        }
    }
}