1#![allow(clippy::manual_is_multiple_of)]
2use anyhow::{Context, Result};
3use clap::Subcommand;
4use num_bigint::{BigInt, BigUint};
5use num_integer::Integer;
6use num_traits::One;
7
8#[derive(Subcommand)]
9pub enum MathAction {
10 #[command(about = "Calculate greatest common divisor")]
11 Gcd {
12 #[arg(help = "First number")]
13 a: String,
14 #[arg(help = "Second number")]
15 b: String,
16 },
17 #[command(about = "Calculate least common multiple")]
18 Lcm {
19 #[arg(help = "First number")]
20 a: String,
21 #[arg(help = "Second number")]
22 b: String,
23 },
24 #[command(about = "Calculate modular inverse (a^-1 mod m)")]
25 Modinv {
26 #[arg(help = "Value")]
27 a: String,
28 #[arg(help = "Modulus")]
29 m: String,
30 },
31 #[command(about = "Calculate modular exponentiation (base^exp mod m)")]
32 Modpow {
33 #[arg(help = "Base")]
34 base: String,
35 #[arg(help = "Exponent")]
36 exp: String,
37 #[arg(help = "Modulus")]
38 m: String,
39 },
40}
41
42pub fn run(action: MathAction) -> Result<()> {
43 match action {
44 MathAction::Gcd { a, b } => {
45 let a = a.parse::<u128>().context("Invalid number for a")?;
46 let b = b.parse::<u128>().context("Invalid number for b")?;
47 println!("{}", gcd(a, b));
48 }
49 MathAction::Lcm { a, b } => {
50 let a = a.parse::<u128>().context("Invalid number for a")?;
51 let b = b.parse::<u128>().context("Invalid number for b")?;
52 println!("{}", lcm(a, b)?);
53 }
54 MathAction::Modinv { a, m } => {
55 let a = a.parse::<u128>().context("Invalid number for a")?;
56 let m = m.parse::<u128>().context("Invalid number for m")?;
57 println!("{}", modinv(a, m)?);
58 }
59 MathAction::Modpow { base, exp, m } => {
60 let base = base.parse::<u128>().context("Invalid number for base")?;
61 let exp = exp.parse::<u128>().context("Invalid number for exp")?;
62 let m = m.parse::<u128>().context("Invalid number for m")?;
63 println!("{}", modpow(base, exp, m)?);
64 }
65 }
66 Ok(())
67}
68
69pub fn gcd(mut a: u128, mut b: u128) -> u128 {
71 while b != 0 {
72 let t = b;
73 b = a % b;
74 a = t;
75 }
76 a
77}
78
79pub fn lcm(a: u128, b: u128) -> Result<u128> {
81 if a == 0 || b == 0 {
82 return Ok(0);
83 }
84 let g = gcd(a, b);
85 (a / g)
86 .checked_mul(b)
87 .ok_or_else(|| anyhow::anyhow!("LCM overflow"))
88}
89
90pub fn modinv(a: u128, m: u128) -> Result<u128> {
93 if m == 0 {
94 anyhow::bail!("Modulus must be non-zero");
95 }
96 if m == 1 {
97 return Ok(0);
98 }
99
100 let a_big = BigInt::from(a);
103 let m_big = BigInt::from(m);
104
105 let extended = a_big.extended_gcd(&m_big);
106
107 if extended.gcd != BigInt::one() {
108 anyhow::bail!(
109 "Modular inverse does not exist (gcd({}, {}) = {})",
110 a,
111 m,
112 extended.gcd
113 );
114 }
115
116 let res = (extended.x % &m_big + &m_big) % &m_big;
118
119 Ok(res.try_into().unwrap())
121}
122
123pub fn modpow(base: u128, exp: u128, m: u128) -> Result<u128> {
126 if m == 0 {
127 anyhow::bail!("Modulus must be non-zero");
128 }
129 if m == 1 {
130 return Ok(0);
131 }
132
133 if m <= u64::MAX as u128 {
136 return Ok(modpow_u64(base, exp, m as u64) as u128);
137 }
138
139 if m % 2 != 0 {
142 let mont = Montgomery::new(m)?;
143 let base_mont = mont.transform(base);
144 let res_mont = mont.pow(base_mont, exp);
145 let res = mont.reduce_from(res_mont);
146 return Ok(res);
147 }
148
149 let base_big = BigUint::from(base);
151 let exp_big = BigUint::from(exp);
152 let m_big = BigUint::from(m);
153
154 let res = base_big.modpow(&exp_big, &m_big);
155
156 Ok(res.try_into().unwrap())
158}
159
160fn modpow_u64(base: u128, mut exp: u128, m: u64) -> u64 {
162 if m % 2 != 0 {
164 let mont = Montgomery64::new(m).expect("m is odd");
165 let base_val = (base % (m as u128)) as u64;
167 let base_mont = mont.transform(base_val);
168 let res_mont = mont.pow(base_mont, exp);
169 return mont.reduce_from(res_mont);
170 }
171
172 let m_u128 = m as u128;
173 let mut res: u128 = 1;
174 let mut base = base % m_u128;
175
176 while exp > 0 {
177 if exp & 1 == 1 {
178 res = (res * base) % m_u128;
179 }
180 base = (base * base) % m_u128;
181 exp >>= 1;
182 }
183 res as u64
184}
185
186#[derive(Debug, Clone, Copy)]
187pub struct Montgomery64 {
188 m: u64,
189 m_prime: u64, r2: u64, }
192
193impl Montgomery64 {
194 pub fn new(m: u64) -> Result<Self> {
195 if m % 2 == 0 {
196 anyhow::bail!("Modulus must be odd");
197 }
198
199 let mut inv = 1u64;
204 for _ in 0..6 {
205 inv = inv.wrapping_mul(2u64.wrapping_sub(m.wrapping_mul(inv)));
206 }
207 let m_prime = 0u64.wrapping_sub(inv);
208
209 let r_mod_m = (u64::MAX % m).wrapping_add(1) % m;
212 let r2 = ((r_mod_m as u128 * r_mod_m as u128) % m as u128) as u64;
213
214 Ok(Self { m, m_prime, r2 })
215 }
216
217 #[inline(always)]
220 pub fn reduce(&self, t: u128) -> u64 {
221 let m = self.m;
222 let m_prime = self.m_prime;
223
224 let m_factor = (t as u64).wrapping_mul(m_prime);
226
227 let t_correction = (m_factor as u128) * (m as u128);
229
230 let (val, overflow) = t.overflowing_add(t_correction);
240
241 let mut res = (val >> 64) as u64;
243
244 if overflow {
251 res = res.wrapping_sub(m);
252 } else if res >= m {
253 res -= m;
254 }
255 res
256 }
257
258 #[inline(always)]
259 pub fn mul(&self, a: u64, b: u64) -> u64 {
260 let prod = (a as u128) * (b as u128);
261 self.reduce(prod)
262 }
263
264 pub fn transform(&self, a: u64) -> u64 {
265 self.mul(a, self.r2)
266 }
267
268 #[allow(dead_code)]
269 pub fn reduce_from(&self, a: u64) -> u64 {
270 self.reduce(a as u128)
274 }
275
276 pub fn pow(&self, mut base: u64, mut exp: u128) -> u64 {
277 let mut res = self.transform(1);
278 while exp > 0 {
279 if exp % 2 == 1 {
280 res = self.mul(res, base);
281 }
282 base = self.mul(base, base);
283 exp /= 2;
284 }
285 res
286 }
287}
288
289fn widening_mul_u128(a: u128, b: u128) -> (u128, u128) {
291 let al = a as u64;
295 let ah = (a >> 64) as u64;
296 let bl = b as u64;
297 let bh = (b >> 64) as u64;
298
299 let t0 = (al as u128) * (bl as u128);
300 let t1 = (al as u128) * (bh as u128);
301 let t2 = (ah as u128) * (bl as u128);
302 let t3 = (ah as u128) * (bh as u128);
303
304 let (mid, carry_mid) = t1.overflowing_add(t2);
305 let mid_lo = mid << 64; let mid_hi = mid >> 64; let (lo, carry_lo) = t0.overflowing_add(mid_lo);
310
311 let mut hi = t3 + mid_hi;
312 if carry_mid {
313 hi += 1 << 64;
314 }
315 if carry_lo {
316 hi += 1;
317 }
318
319 (lo, hi)
320}
321
322#[derive(Debug)]
323pub struct Montgomery {
324 m: u128,
325 m_prime: u128, r2: u128, }
328
329impl Montgomery {
330 pub fn new(m: u128) -> Result<Self> {
331 if m % 2 == 0 {
332 anyhow::bail!("Modulus must be odd for Montgomery arithmetic");
333 }
334
335 let mut inv = 1u128;
337 for _ in 0..7 {
338 inv = inv.wrapping_mul(2u128.wrapping_sub(m.wrapping_mul(inv)));
339 }
340 let m_prime = 0u128.wrapping_sub(inv);
341
342 let mut r2 = (u128::MAX % m + 1) % m; for _ in 0..128 {
347 let (mut val, overflow) = r2.overflowing_shl(1);
349 if overflow {
350 val = val.wrapping_add(0u128.wrapping_sub(m));
353 } else if val >= m {
354 val -= m;
355 }
356 r2 = val;
357 }
358
359 Ok(Self { m, m_prime, r2 })
360 }
361
362 pub fn reduce(&self, lo: u128, hi: u128) -> u128 {
364 let m = self.m;
365 let m_prime = self.m_prime;
366
367 let m_factor = lo.wrapping_mul(m_prime);
368 let (prod_lo, prod_hi) = widening_mul_u128(m_factor, m);
369
370 let (_, carry_lo) = lo.overflowing_add(prod_lo);
371 let (sum, carry1) = hi.overflowing_add(prod_hi);
372 let (mut res, carry2) = sum.overflowing_add(if carry_lo { 1 } else { 0 });
373 let carry = carry1 || carry2;
374
375 if carry {
376 res = res.wrapping_sub(m);
377 } else if res >= m {
378 res -= m;
379 }
380 res
381 }
382
383 pub fn mul(&self, a: u128, b: u128) -> u128 {
385 let (lo, hi) = widening_mul_u128(a, b);
386 self.reduce(lo, hi)
387 }
388
389 pub fn transform(&self, a: u128) -> u128 {
391 self.mul(a, self.r2)
392 }
393
394 #[allow(dead_code)]
397 pub fn reduce_from(&self, a: u128) -> u128 {
398 self.reduce(a, 0)
399 }
400
401 pub fn pow(&self, mut base: u128, mut exp: u128) -> u128 {
402 let mut res = self.transform(1);
403 while exp > 0 {
404 if exp % 2 == 1 {
405 res = self.mul(res, base);
406 }
407 base = self.mul(base, base);
408 exp /= 2;
409 }
410 res
411 }
412}