1#![no_std]
2
3pub mod reference {
4 #[inline]
5 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 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 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 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 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 super::reference::mod_u128u64(a, m)
120 }
121}
122
123#[inline]
124pub fn mulmod_u64(a: u64, b: u64, m: u64) -> u64 {
133 internal::mulmod_u64(a, b, m)
134}
135
136#[inline]
137pub fn mod_u128u64(a: u128, m: u64) -> u64 {
145 internal::mod_u128u64(a, m)
146}
147
148#[inline]
149pub fn mod_u128u64_unchecked(a: u128, m: u64) -> u64 {
159 internal::mod_u128u64_unchecked(a, m)
160}
161
162#[inline]
163pub 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}