fast_modulo/
lib.rs

1#![no_std]
2
3pub mod reference {
4    #[inline]
5    /// calculate `a * b % m`
6    ///
7    /// ```
8    /// use fast_modulo::reference::mulmod_u64;
9    /// assert_eq!(mulmod_u64(3, 4, 5), 2);
10    /// assert_eq!(mulmod_u64((1 << 63) - 1, (1 << 63) - 3, (1 << 63) + 1), 8);
11    /// ```
12    pub fn mulmod_u64(a: u64, b: u64, m: u64) -> u64 {
13        let aa = a as u128;
14        let bb = b as u128;
15        let mm = m as u128;
16        let rr = aa * bb % mm;
17        rr as _
18    }
19
20    #[inline]
21    /// calcurate `a % m`
22    ///
23    /// ```
24    /// use fast_modulo::reference::mod_u128u64;
25    /// assert_eq!(mod_u128u64(17, 3), 2);
26    /// assert_eq!(mod_u128u64((1 << 127) - 1, (1 << 61) - 1), 31);
27    /// ```
28    pub fn mod_u128u64(a: u128, m: u64) -> u64 {
29        let mm = m as u128;
30        let rr = a % mm;
31        rr as _
32    }
33
34    #[inline]
35    /// calculate $`a ^ p \bmod m`$
36    ///
37    /// ```
38    /// use fast_modulo::reference::powmod_u64;
39    /// assert_eq!(powmod_u64(2, 10, 13), 10);
40    /// assert_eq!(powmod_u64(31, 41, 59), 39);
41    /// ```
42    pub fn powmod_u64(mut a: u64, mut p: u64, m: u64) -> u64 {
43        let mut y = 1;
44        while p > 0 {
45            if p % 2 == 1 {
46                y = mulmod_u64(y, a, m);
47            }
48            a = mulmod_u64(a, a, m);
49            p /= 2;
50        }
51        y
52    }
53}
54
55mod internal {
56    use core::arch::asm;
57    #[inline]
58    #[cfg(target_arch = "x86_64")]
59    pub fn mulmod_u64(a: u64, mut b: u64, m: u64) -> u64 {
60        unsafe {
61            asm!(
62                "mul rdx",
63                "div {}",
64                in(reg) m,
65                inout("rax") a => _,
66                inout("rdx") b,
67            );
68        }
69        b
70    }
71    #[inline]
72    #[cfg(not(target_arch = "x86_64"))]
73    pub fn mulmod_u64(a: u64, b: u64, m: u64) -> u64 {
74        // fallback
75        super::reference::mulmod_u64(a, b, m)
76    }
77
78    #[inline]
79    #[cfg(target_arch = "x86_64")]
80    pub fn mod_u128u64(a: u128, m: u64) -> u64 {
81        let qb = 65 + m.leading_zeros() - a.leading_zeros();
82        if qb > 64 {
83            let s = qb - 64;
84            let mask = (1 << s) - 1;
85            let r = mod_u128u64_unchecked(a >> s, m) as u128;
86            let na = (r << s) + (a & mask);
87            mod_u128u64_unchecked(na, m)
88        } else {
89            mod_u128u64_unchecked(a, m)
90        }
91    }
92    #[inline]
93    #[cfg(not(target_arch = "x86_64"))]
94    pub fn mod_u128u64(a: u128, m: u64) -> u64 {
95        // fallback
96        super::reference::mod_u128u64(a, m)
97    }
98
99    #[inline]
100    #[cfg(target_arch = "x86_64")]
101    pub fn mod_u128u64_unchecked(a: u128, m: u64) -> u64 {
102        let hi = (a >> 64) as u64;
103        let lo = (a & 0xFFFFFFFFFFFFFFFF) as u64;
104        let r;
105        unsafe {
106            asm!(
107                "div {}",
108                in(reg) m,
109                inout("rax") lo => _,
110                inout("rdx") hi => r,
111            );
112        }
113        r
114    }
115    #[inline]
116    #[cfg(not(target_arch = "x86_64"))]
117    pub fn mod_u128u64_unchecked(a: u128, m: u64) -> u64 {
118        // fallback
119        super::reference::mod_u128u64(a, m)
120    }
121}
122
123#[inline]
124/// calculate `a * b % m`
125///
126/// required `a < m && b < m`.
127/// ```
128/// use fast_modulo::mulmod_u64;
129/// assert_eq!(mulmod_u64(3, 4, 5), 2);
130/// assert_eq!(mulmod_u64((1 << 63) - 1, (1 << 63) - 3, (1 << 63) + 1), 8);
131/// ```
132pub fn mulmod_u64(a: u64, b: u64, m: u64) -> u64 {
133    internal::mulmod_u64(a, b, m)
134}
135
136#[inline]
137/// calcurate `a % m`
138///
139/// ```
140/// use fast_modulo::mod_u128u64;
141/// assert_eq!(mod_u128u64(17, 3), 2);
142/// assert_eq!(mod_u128u64((1 << 127) - 1, (1 << 61) - 1), 31);
143/// ```
144pub fn mod_u128u64(a: u128, m: u64) -> u64 {
145    internal::mod_u128u64(a, m)
146}
147
148#[inline]
149/// calcurate `a % m`
150///
151/// This function doesn't check quotient less than $`2^{64}`$.
152/// required $`a < 2^{64}m`$
153/// ```
154/// use fast_modulo::mod_u128u64_unchecked;
155/// assert_eq!(mod_u128u64_unchecked(17, 3), 2);
156/// assert_eq!(mod_u128u64_unchecked((1 << 107) - 1, (1 << 61) - 1), 70368744177663);
157/// ```
158pub fn mod_u128u64_unchecked(a: u128, m: u64) -> u64 {
159    internal::mod_u128u64_unchecked(a, m)
160}
161
162#[inline]
163/// calculate $`a ^ p \bmod m`$
164///
165/// required `a < m`.
166/// ```
167/// use fast_modulo::powmod_u64;
168/// assert_eq!(powmod_u64(2, 10, 13), 10);
169/// assert_eq!(powmod_u64(31, 41, 59), 39);
170/// ```
171pub fn powmod_u64(mut a: u64, mut p: u64, m: u64) -> u64 {
172    let mut y = 1;
173    while p > 0 {
174        if p % 2 == 1 {
175            y = mulmod_u64(y, a, m);
176        }
177        a = mulmod_u64(a, a, m);
178        p /= 2;
179    }
180    y
181}
182
183#[cfg(test)]
184mod tests {
185    use crate::*;
186    #[test]
187    fn modulo_u64_mul() {
188        use rand::prelude::*;
189        let mut rng = rand::thread_rng();
190        for _ in 0..1_000_000 {
191            let m = rng.gen();
192            let a = rng.gen::<u64>() % m;
193            let b = rng.gen::<u64>() % m;
194            assert_eq!(reference::mulmod_u64(a, b, m), mulmod_u64(a, b, m));
195        }
196    }
197    #[test]
198    fn modulo_u128u64() {
199        use rand::prelude::*;
200        let mut rng = rand::thread_rng();
201        for _ in 0..1_000_000 {
202            let m = rng.gen();
203            let a = rng.gen();
204            assert_eq!(reference::mod_u128u64(a, m), mod_u128u64(a, m));
205        }
206    }
207    #[test]
208    fn modulo_u128u64_small_divisor() {
209        use rand::prelude::*;
210        let mut rng = rand::thread_rng();
211        for _ in 0..1_000_000 {
212            let m = rng.gen::<u32>().into();
213            let a = rng.gen();
214            assert_eq!(reference::mod_u128u64(a, m), mod_u128u64(a, m));
215        }
216    }
217    #[test]
218    fn modulo_u64_pow() {
219        use rand::prelude::*;
220        let mut rng = rand::thread_rng();
221        for _ in 0..1_000_000 {
222            let m = rng.gen();
223            let a = rng.gen::<u64>() % m;
224            let p = rng.gen::<u64>();
225            assert_eq!(reference::powmod_u64(a, p, m), powmod_u64(a, p, m));
226        }
227    }
228}