oxiz_math/rational/mod.rs
1//! Arbitrary precision rational arithmetic utilities.
2//!
3//! This module provides utility functions for working with rational numbers
4//! beyond what the `num_rational` crate offers.
5
6#[allow(unused_imports)]
7use crate::prelude::*;
8use core::cmp::Ordering;
9use num_bigint::BigInt;
10use num_integer::Integer;
11use num_rational::BigRational;
12use num_traits::{One, Signed, Zero};
13
14/// Compare two rationals and return their ordering.
15#[inline]
16pub fn cmp(a: &BigRational, b: &BigRational) -> Ordering {
17 a.cmp(b)
18}
19
20/// Compute the absolute value of a rational.
21#[inline]
22pub fn abs(r: &BigRational) -> BigRational {
23 r.abs()
24}
25
26/// Compute the floor of a rational (greatest integer <= r).
27pub fn floor(r: &BigRational) -> BigInt {
28 if r.is_integer() {
29 r.numer().clone()
30 } else if r.is_positive() {
31 r.numer() / r.denom()
32 } else {
33 // For negative non-integers, subtract 1
34 (r.numer() / r.denom()) - BigInt::one()
35 }
36}
37
38/// Compute the ceiling of a rational (smallest integer >= r).
39pub fn ceil(r: &BigRational) -> BigInt {
40 if r.is_integer() {
41 r.numer().clone()
42 } else if r.is_positive() {
43 // For positive non-integers, add 1
44 (r.numer() / r.denom()) + BigInt::one()
45 } else {
46 r.numer() / r.denom()
47 }
48}
49
50/// Round a rational to the nearest integer (ties go to even).
51pub fn round(r: &BigRational) -> BigInt {
52 let floor_val = floor(r);
53 let frac = r - BigRational::from_integer(floor_val.clone());
54
55 let half = BigRational::new(BigInt::one(), BigInt::from(2));
56
57 if frac < half {
58 floor_val
59 } else if frac > half {
60 floor_val + BigInt::one()
61 } else {
62 // Tie: round to even
63 if floor_val.is_even() {
64 floor_val
65 } else {
66 floor_val + BigInt::one()
67 }
68 }
69}
70
71/// Compute the fractional part of a rational (r - floor(r)).
72pub fn frac(r: &BigRational) -> BigRational {
73 r - BigRational::from_integer(floor(r))
74}
75
76/// Check if a rational is an integer.
77#[inline]
78pub fn is_integer(r: &BigRational) -> bool {
79 r.is_integer()
80}
81
82/// Compute the greatest common divisor of two rationals.
83/// Returns the largest positive rational that divides both a and b.
84pub fn gcd(a: &BigRational, b: &BigRational) -> BigRational {
85 if a.is_zero() {
86 return b.abs();
87 }
88 if b.is_zero() {
89 return a.abs();
90 }
91
92 // GCD of rationals: gcd(a/b, c/d) = gcd(a*d, b*c) / (b*d)
93 // But we can simplify using the integer GCD
94 let gcd_num = gcd_bigint(a.numer().clone(), b.numer().clone());
95 let lcm_denom = lcm_bigint(a.denom().clone(), b.denom().clone());
96
97 BigRational::new(gcd_num, lcm_denom)
98}
99
100/// Compute the least common multiple of two rationals.
101pub fn lcm(a: &BigRational, b: &BigRational) -> BigRational {
102 if a.is_zero() || b.is_zero() {
103 return BigRational::zero();
104 }
105
106 let g = gcd(a, b);
107 (a * b / g).abs()
108}
109
110/// Compute GCD of two BigInts using Euclidean algorithm.
111pub fn gcd_bigint(mut a: BigInt, mut b: BigInt) -> BigInt {
112 while !b.is_zero() {
113 let t = &a % &b;
114 a = b;
115 b = t;
116 }
117 a.abs()
118}
119
120/// Extended Euclidean algorithm for BigInts.
121/// Returns (gcd, x, y) such that gcd = a*x + b*y (Bézout's identity).
122///
123/// This is useful for:
124/// - Solving linear Diophantine equations
125/// - Computing modular inverses
126/// - Finding rational solutions to systems
127///
128/// # Examples
129/// ```
130/// use num_bigint::BigInt;
131/// use oxiz_math::rational::gcd_extended;
132///
133/// let a = BigInt::from(240);
134/// let b = BigInt::from(46);
135/// let (gcd, x, y) = gcd_extended(a.clone(), b.clone());
136/// assert_eq!(gcd, BigInt::from(2));
137/// assert_eq!(&a * &x + &b * &y, gcd);
138/// ```
139pub fn gcd_extended(a: BigInt, b: BigInt) -> (BigInt, BigInt, BigInt) {
140 if b.is_zero() {
141 return (
142 a.abs(),
143 if a >= BigInt::zero() {
144 BigInt::one()
145 } else {
146 -BigInt::one()
147 },
148 BigInt::zero(),
149 );
150 }
151
152 let (mut old_r, mut r) = (a, b);
153 let (mut old_s, mut s) = (BigInt::one(), BigInt::zero());
154 let (mut old_t, mut t) = (BigInt::zero(), BigInt::one());
155
156 while !r.is_zero() {
157 let quotient = &old_r / &r;
158 let new_r = &old_r - "ient * &r;
159 old_r = r;
160 r = new_r;
161
162 let new_s = &old_s - "ient * &s;
163 old_s = s;
164 s = new_s;
165
166 let new_t = &old_t - "ient * &t;
167 old_t = t;
168 t = new_t;
169 }
170
171 (old_r, old_s, old_t)
172}
173
174/// Compute LCM of two BigInts.
175pub fn lcm_bigint(a: BigInt, b: BigInt) -> BigInt {
176 if a.is_zero() || b.is_zero() {
177 return BigInt::zero();
178 }
179 let g = gcd_bigint(a.clone(), b.clone());
180 (a * b / g).abs()
181}
182
183/// Compute the power of a rational number with integer exponent.
184pub fn pow_int(base: &BigRational, exp: i32) -> BigRational {
185 if exp == 0 {
186 return BigRational::one();
187 }
188 if exp > 0 {
189 pow_uint(base, exp as u32)
190 } else {
191 let p = pow_uint(base, (-exp) as u32);
192 BigRational::one() / p
193 }
194}
195
196/// Compute the power of a rational number with unsigned integer exponent.
197pub fn pow_uint(base: &BigRational, exp: u32) -> BigRational {
198 if exp == 0 {
199 return BigRational::one();
200 }
201 if exp == 1 {
202 return base.clone();
203 }
204
205 // Binary exponentiation
206 let mut result = BigRational::one();
207 let mut b = base.clone();
208 let mut e = exp;
209
210 while e > 0 {
211 if e & 1 == 1 {
212 result = &result * &b;
213 }
214 b = &b * &b;
215 e >>= 1;
216 }
217
218 result
219}
220
221/// Normalize a rational to have positive denominator.
222pub fn normalize(r: &BigRational) -> BigRational {
223 if r.denom().is_negative() {
224 BigRational::new(-r.numer(), -r.denom())
225 } else {
226 r.clone()
227 }
228}
229
230/// Compare two rationals with a tolerance.
231pub fn approx_eq(a: &BigRational, b: &BigRational, epsilon: &BigRational) -> bool {
232 (a - b).abs() <= *epsilon
233}
234
235/// Compute the sign of a rational: -1, 0, or 1.
236pub fn sign(r: &BigRational) -> i8 {
237 if r.is_positive() {
238 1
239 } else if r.is_negative() {
240 -1
241 } else {
242 0
243 }
244}
245
246/// Clamp a rational to a range [min, max].
247pub fn clamp(val: &BigRational, min: &BigRational, max: &BigRational) -> BigRational {
248 if val < min {
249 min.clone()
250 } else if val > max {
251 max.clone()
252 } else {
253 val.clone()
254 }
255}
256
257/// Compute the minimum of two rationals.
258#[inline]
259pub fn min(a: &BigRational, b: &BigRational) -> BigRational {
260 if a < b { a.clone() } else { b.clone() }
261}
262
263/// Compute the maximum of two rationals.
264#[inline]
265pub fn max(a: &BigRational, b: &BigRational) -> BigRational {
266 if a > b { a.clone() } else { b.clone() }
267}
268
269/// Create a rational from a numerator and denominator.
270#[inline]
271pub fn from_integers(num: i64, den: i64) -> BigRational {
272 BigRational::new(BigInt::from(num), BigInt::from(den))
273}
274
275/// Create a rational from an integer.
276#[inline]
277pub fn from_integer(n: i64) -> BigRational {
278 BigRational::from_integer(BigInt::from(n))
279}
280
281/// Get the numerator of a rational as a reference.
282#[inline]
283pub fn numer(r: &BigRational) -> &BigInt {
284 r.numer()
285}
286
287/// Get the denominator of a rational as a reference.
288#[inline]
289pub fn denom(r: &BigRational) -> &BigInt {
290 r.denom()
291}
292
293/// Convert a rational to f64 (may lose precision).
294pub fn to_f64(r: &BigRational) -> f64 {
295 if r.denom().is_one() {
296 r.numer().to_string().parse().unwrap_or(f64::NAN)
297 } else {
298 let num_f64: f64 = r.numer().to_string().parse().unwrap_or(f64::NAN);
299 let den_f64: f64 = r.denom().to_string().parse().unwrap_or(f64::NAN);
300 num_f64 / den_f64
301 }
302}
303
304/// Compute the mediant of two rationals: (a.num + b.num) / (a.den + b.den).
305/// This is used in continued fraction approximations.
306pub fn mediant(a: &BigRational, b: &BigRational) -> BigRational {
307 let num = a.numer() + b.numer();
308 let den = a.denom() + b.denom();
309 BigRational::new(num, den)
310}
311
312/// Compute the continued fraction representation of a rational number.
313/// Returns a vector of coefficients [a0, a1, a2, ...] such that:
314/// r = a0 + 1/(a1 + 1/(a2 + ...))
315///
316/// # Examples
317/// ```
318/// use num_bigint::BigInt;
319/// use num_rational::BigRational;
320/// use oxiz_math::rational::continued_fraction;
321///
322/// let r = BigRational::new(BigInt::from(22), BigInt::from(7)); // π ≈ 22/7
323/// let cf = continued_fraction(&r);
324/// assert_eq!(cf, vec![BigInt::from(3), BigInt::from(7)]);
325/// ```
326pub fn continued_fraction(r: &BigRational) -> Vec<BigInt> {
327 let mut result = Vec::new();
328 let mut current = r.clone();
329
330 while !current.is_integer() {
331 let int_part = floor(¤t);
332 result.push(int_part.clone());
333 current = BigRational::one() / (current - BigRational::from_integer(int_part));
334 }
335
336 // Add the final integer part
337 result.push(current.numer().clone());
338 result
339}
340
341/// Reconstruct a rational from its continued fraction representation.
342/// Takes coefficients [a0, a1, a2, ...] and returns the rational number.
343///
344/// # Examples
345/// ```
346/// use num_bigint::BigInt;
347/// use num_rational::BigRational;
348/// use oxiz_math::rational::{continued_fraction, from_continued_fraction};
349///
350/// let r = BigRational::new(BigInt::from(22), BigInt::from(7));
351/// let cf = continued_fraction(&r);
352/// let reconstructed = from_continued_fraction(&cf);
353/// assert_eq!(r, reconstructed);
354/// ```
355pub fn from_continued_fraction(coeffs: &[BigInt]) -> BigRational {
356 if coeffs.is_empty() {
357 return BigRational::zero();
358 }
359
360 if coeffs.len() == 1 {
361 return BigRational::from_integer(coeffs[0].clone());
362 }
363
364 // Start from the end and work backwards
365 let mut result = BigRational::from_integer(coeffs[coeffs.len() - 1].clone());
366
367 for i in (0..coeffs.len() - 1).rev() {
368 result = BigRational::from_integer(coeffs[i].clone()) + BigRational::one() / result;
369 }
370
371 result
372}
373
374/// Compute the convergents (partial sums) of a continued fraction.
375/// Returns a vector of rationals that increasingly approximate the target.
376///
377/// Convergents provide the best rational approximations with denominators
378/// up to a given size, useful for interval refinement in algebraic number solving.
379///
380/// # Examples
381/// ```
382/// use num_bigint::BigInt;
383/// use num_rational::BigRational;
384/// use oxiz_math::rational::{continued_fraction, convergents};
385///
386/// let r = BigRational::new(BigInt::from(22), BigInt::from(7));
387/// let cf = continued_fraction(&r);
388/// let convs = convergents(&cf);
389/// // First convergent is 3/1, second is 22/7
390/// assert_eq!(convs[0], BigRational::new(BigInt::from(3), BigInt::from(1)));
391/// assert_eq!(convs[1], BigRational::new(BigInt::from(22), BigInt::from(7)));
392/// ```
393pub fn convergents(coeffs: &[BigInt]) -> Vec<BigRational> {
394 if coeffs.is_empty() {
395 return vec![];
396 }
397
398 let mut result = Vec::with_capacity(coeffs.len());
399
400 // Build convergents using the recurrence relation:
401 // p_n = a_n * p_{n-1} + p_{n-2}
402 // q_n = a_n * q_{n-1} + q_{n-2}
403 let mut p_prev2 = BigInt::one();
404 let mut p_prev1 = coeffs[0].clone();
405 let mut q_prev2 = BigInt::zero();
406 let mut q_prev1 = BigInt::one();
407
408 result.push(BigRational::from_integer(coeffs[0].clone()));
409
410 for coeff in coeffs.iter().skip(1) {
411 let p = coeff * &p_prev1 + &p_prev2;
412 let q = coeff * &q_prev1 + &q_prev2;
413
414 result.push(BigRational::new(p.clone(), q.clone()));
415
416 p_prev2 = p_prev1;
417 p_prev1 = p;
418 q_prev2 = q_prev1;
419 q_prev1 = q;
420 }
421
422 result
423}
424
425/// Find the best rational approximation to a number within a given tolerance.
426/// Uses continued fractions to find the simplest fraction within epsilon of the target.
427///
428/// # Examples
429/// ```
430/// use num_bigint::BigInt;
431/// use num_rational::BigRational;
432/// use oxiz_math::rational::best_rational_approximation;
433///
434/// let pi_approx = BigRational::new(BigInt::from(31416), BigInt::from(10000));
435/// let epsilon = BigRational::new(BigInt::from(1), BigInt::from(1000));
436/// let approx = best_rational_approximation(&pi_approx, &epsilon);
437/// // Should give 22/7 or 355/113 depending on tolerance
438/// ```
439pub fn best_rational_approximation(target: &BigRational, epsilon: &BigRational) -> BigRational {
440 let cf = continued_fraction(target);
441 let convs = convergents(&cf);
442
443 // Find the first convergent within epsilon
444 for conv in convs {
445 if (target - &conv).abs() <= *epsilon {
446 return conv;
447 }
448 }
449
450 // Fallback to the target itself
451 target.clone()
452}
453
454/// Modular exponentiation: compute (base^exp) mod m efficiently.
455/// Uses binary exponentiation to avoid overflow.
456///
457/// # Examples
458/// ```
459/// use num_bigint::BigInt;
460/// use oxiz_math::rational::mod_pow;
461///
462/// let base = BigInt::from(2);
463/// let exp = BigInt::from(10);
464/// let m = BigInt::from(1000);
465/// let result = mod_pow(&base, &exp, &m);
466/// assert_eq!(result, BigInt::from(24)); // 2^10 mod 1000 = 1024 mod 1000 = 24
467/// ```
468pub fn mod_pow(base: &BigInt, exp: &BigInt, m: &BigInt) -> BigInt {
469 if m.is_one() {
470 return BigInt::zero();
471 }
472
473 let mut result = BigInt::one();
474 let mut base = base % m;
475 let mut exp = exp.clone();
476
477 while exp > BigInt::zero() {
478 if (&exp % BigInt::from(2)).is_one() {
479 result = (result * &base) % m;
480 }
481 exp >>= 1;
482 base = (&base * &base) % m;
483 }
484
485 result
486}
487
488/// Compute the modular multiplicative inverse of a modulo m.
489/// Returns Some(x) where (a * x) ≡ 1 (mod m), or None if no inverse exists.
490///
491/// The inverse exists if and only if gcd(a, m) = 1.
492///
493/// # Examples
494/// ```
495/// use num_bigint::BigInt;
496/// use oxiz_math::rational::mod_inverse;
497///
498/// let a = BigInt::from(3);
499/// let m = BigInt::from(7);
500/// let inv = mod_inverse(&a, &m).unwrap();
501/// assert_eq!((&a * &inv) % &m, BigInt::from(1));
502/// ```
503pub fn mod_inverse(a: &BigInt, m: &BigInt) -> Option<BigInt> {
504 let (gcd, x, _) = gcd_extended(a.clone(), m.clone());
505
506 if !gcd.is_one() {
507 return None; // No inverse exists
508 }
509
510 // Ensure the result is positive
511 let inv = x % m;
512 Some(if inv < BigInt::zero() { inv + m } else { inv })
513}
514
515/// Chinese Remainder Theorem: solve a system of congruences.
516/// Given pairs (a_i, m_i) where x ≡ a_i (mod m_i), find x.
517///
518/// Returns Some(x, M) where x is the solution and M is the product of all moduli,
519/// or None if the moduli are not pairwise coprime.
520///
521/// # Examples
522/// ```
523/// use num_bigint::BigInt;
524/// use oxiz_math::rational::chinese_remainder;
525///
526/// // Solve: x ≡ 2 (mod 3), x ≡ 3 (mod 5), x ≡ 2 (mod 7)
527/// let remainders = vec![
528/// (BigInt::from(2), BigInt::from(3)),
529/// (BigInt::from(3), BigInt::from(5)),
530/// (BigInt::from(2), BigInt::from(7)),
531/// ];
532/// let (x, _m) = chinese_remainder(&remainders).unwrap();
533/// assert_eq!(&x % BigInt::from(3), BigInt::from(2));
534/// assert_eq!(&x % BigInt::from(5), BigInt::from(3));
535/// assert_eq!(&x % BigInt::from(7), BigInt::from(2));
536/// ```
537pub fn chinese_remainder(congruences: &[(BigInt, BigInt)]) -> Option<(BigInt, BigInt)> {
538 if congruences.is_empty() {
539 return None;
540 }
541
542 // Compute the product of all moduli (M in mathematical notation)
543 let mut prod_moduli = BigInt::one();
544 for (_, m_i) in congruences {
545 prod_moduli = &prod_moduli * m_i;
546 }
547
548 let mut result = BigInt::zero();
549
550 for (a_i, m_i) in congruences {
551 // M_i = M / m_i in mathematical notation
552 let partial_prod = &prod_moduli / m_i;
553
554 // Find the modular inverse of partial_prod mod m_i
555 let inv = mod_inverse(&partial_prod, m_i)?;
556
557 // Add to result: a_i * partial_prod * inv
558 result = (result + a_i * &partial_prod * inv) % &prod_moduli;
559 }
560
561 // Ensure result is positive
562 if result < BigInt::zero() {
563 result += &prod_moduli;
564 }
565
566 Some((result, prod_moduli))
567}
568
569/// Solve a linear Diophantine equation ax + by = c.
570/// Returns Some((x, y)) if a solution exists, or None if no solution exists.
571///
572/// A solution exists if and only if gcd(a, b) divides c.
573///
574/// # Examples
575/// ```
576/// use num_bigint::BigInt;
577/// use oxiz_math::rational::solve_linear_diophantine;
578///
579/// let a = BigInt::from(3);
580/// let b = BigInt::from(5);
581/// let c = BigInt::from(1);
582/// let (x, y) = solve_linear_diophantine(&a, &b, &c).unwrap();
583/// assert_eq!(&a * &x + &b * &y, c);
584/// ```
585pub fn solve_linear_diophantine(a: &BigInt, b: &BigInt, c: &BigInt) -> Option<(BigInt, BigInt)> {
586 let (gcd, x0, y0) = gcd_extended(a.clone(), b.clone());
587
588 // Check if c is divisible by gcd(a, b)
589 if (c % &gcd) != BigInt::zero() {
590 return None; // No solution exists
591 }
592
593 // Scale the solution
594 let factor = c / &gcd;
595 let x = x0 * &factor;
596 let y = y0 * factor;
597
598 Some((x, y))
599}
600
601/// Performs the Miller-Rabin primality test.
602///
603/// Returns `true` if `n` is probably prime, `false` if composite.
604/// The parameter `k` controls the number of rounds (higher = more accurate).
605///
606/// # Examples
607/// ```
608/// use num_bigint::BigInt;
609/// use oxiz_math::rational::is_prime;
610///
611/// assert!(is_prime(&BigInt::from(17), 5));
612/// assert!(is_prime(&BigInt::from(97), 5));
613/// assert!(!is_prime(&BigInt::from(15), 5));
614/// ```
615#[cfg(feature = "std")]
616pub fn is_prime(n: &BigInt, k: usize) -> bool {
617 use num_traits::One;
618 use rand::RngExt;
619
620 // Handle small cases
621 if n <= &BigInt::one() {
622 return false;
623 }
624 if n == &BigInt::from(2) || n == &BigInt::from(3) {
625 return true;
626 }
627 if n.is_even() {
628 return false;
629 }
630
631 // Write n-1 as 2^r * d
632 let n_minus_1 = n - BigInt::one();
633 let mut d = n_minus_1.clone();
634 let mut r = 0u32;
635 while d.is_even() {
636 d >>= 1;
637 r += 1;
638 }
639
640 let two = BigInt::from(2);
641 let n_minus_3 = n - &two - BigInt::one();
642 let mut rng = rand::rng();
643
644 // Miller-Rabin test
645 'witness: for _ in 0..k {
646 // Pick random witness a in [2, n-2]
647 let a = if n_minus_3 <= BigInt::zero() {
648 two.clone()
649 } else {
650 // Generate random number by generating random u64 and taking mod
651 let random_u64 = rng.random_range(0u64..u64::MAX);
652 (BigInt::from(random_u64) % &n_minus_3) + &two
653 };
654
655 let mut x = mod_pow(&a, &d, n);
656
657 if x == BigInt::one() || x == n_minus_1 {
658 continue 'witness;
659 }
660
661 for _ in 0..(r - 1) {
662 x = mod_pow(&x, &two, n);
663 if x == n_minus_1 {
664 continue 'witness;
665 }
666 }
667
668 return false; // Composite
669 }
670
671 true // Probably prime
672}
673
674/// Performs trial division to find small prime factors.
675///
676/// Returns a vector of prime factors found up to the limit.
677/// If the input becomes 1, all factors have been found.
678///
679/// # Examples
680/// ```
681/// use num_bigint::BigInt;
682/// use oxiz_math::rational::trial_division;
683///
684/// let n = BigInt::from(60);
685/// let factors = trial_division(&n, 100);
686/// // 60 = 2^2 * 3 * 5
687/// assert!(factors.contains(&BigInt::from(2)));
688/// assert!(factors.contains(&BigInt::from(3)));
689/// assert!(factors.contains(&BigInt::from(5)));
690/// ```
691pub fn trial_division(n: &BigInt, limit: u64) -> Vec<BigInt> {
692 let mut factors = Vec::new();
693 let mut num = n.clone();
694
695 // Check for factor 2
696 while num.is_even() {
697 factors.push(BigInt::from(2));
698 num >>= 1;
699 }
700
701 // Check odd factors up to limit
702 let mut divisor = BigInt::from(3);
703 let limit_big = BigInt::from(limit);
704 let two = BigInt::from(2);
705
706 while &divisor * &divisor <= num && divisor <= limit_big {
707 while &num % &divisor == BigInt::zero() {
708 factors.push(divisor.clone());
709 num /= &divisor;
710 }
711 divisor += &two;
712 }
713
714 // If num > 1, it's a prime factor
715 if num > BigInt::one() && n != &num {
716 factors.push(num);
717 }
718
719 factors
720}
721
722/// Pollard's rho algorithm for integer factorization.
723///
724/// Attempts to find a non-trivial factor of `n`.
725/// Returns `None` if no factor is found within the iteration limit.
726///
727/// # Examples
728/// ```
729/// use num_bigint::BigInt;
730/// use oxiz_math::rational::pollard_rho;
731///
732/// let n = BigInt::from(8051); // 8051 = 83 * 97
733/// if let Some(factor) = pollard_rho(&n) {
734/// assert!(n.clone() % &factor == BigInt::from(0));
735/// assert!(factor > BigInt::from(1) && factor < n);
736/// }
737/// ```
738#[cfg(feature = "std")]
739pub fn pollard_rho(n: &BigInt) -> Option<BigInt> {
740 use rand::RngExt;
741
742 if n <= &BigInt::one() {
743 return None;
744 }
745 if n.is_even() {
746 return Some(BigInt::from(2));
747 }
748
749 let mut rng = rand::rng();
750
751 // Generate random initial values using u64
752 let random_x0 = rng.random_range(0u64..u64::MAX);
753 let random_c = rng.random_range(1u64..u64::MAX);
754
755 let x0 = BigInt::from(random_x0) % n;
756 let c = BigInt::from(random_c) % n;
757
758 let f = |x: &BigInt| -> BigInt { (x * x + &c) % n };
759
760 let mut x = x0.clone();
761 let mut y = x0;
762 let mut d = BigInt::one();
763
764 let max_iterations = 100000;
765 let mut iterations = 0;
766
767 while d == BigInt::one() && iterations < max_iterations {
768 x = f(&x);
769 y = f(&f(&y));
770
771 let diff = if x >= y { &x - &y } else { &y - &x };
772 d = gcd_bigint(diff, n.clone());
773
774 iterations += 1;
775 }
776
777 if d != *n && d != BigInt::one() {
778 Some(d)
779 } else {
780 None
781 }
782}
783
784/// Computes the Jacobi symbol (a/n).
785///
786/// The Jacobi symbol is a generalization of the Legendre symbol.
787/// Returns -1, 0, or 1.
788///
789/// # Examples
790/// ```
791/// use num_bigint::BigInt;
792/// use oxiz_math::rational::jacobi_symbol;
793///
794/// // (2/5) = -1
795/// assert_eq!(jacobi_symbol(&BigInt::from(2), &BigInt::from(5)), -1);
796/// // (3/5) = -1
797/// assert_eq!(jacobi_symbol(&BigInt::from(3), &BigInt::from(5)), -1);
798/// // (4/5) = 1
799/// assert_eq!(jacobi_symbol(&BigInt::from(4), &BigInt::from(5)), 1);
800/// ```
801pub fn jacobi_symbol(a: &BigInt, n: &BigInt) -> i8 {
802 let mut a = a % n;
803 let mut n = n.clone();
804 let mut result = 1i8;
805
806 while a != BigInt::zero() {
807 // Remove factors of 2
808 while a.is_even() {
809 a >>= 1;
810 let n_mod_8 = &n % BigInt::from(8);
811 if n_mod_8 == BigInt::from(3) || n_mod_8 == BigInt::from(5) {
812 result = -result;
813 }
814 }
815
816 // Swap a and n
817 core::mem::swap(&mut a, &mut n);
818
819 // Quadratic reciprocity
820 if &a % BigInt::from(4) == BigInt::from(3) && &n % BigInt::from(4) == BigInt::from(3) {
821 result = -result;
822 }
823
824 a %= &n;
825 }
826
827 if n == BigInt::one() { result } else { 0 }
828}
829
830/// Computes the Legendre symbol (a/p) for prime p.
831///
832/// Returns -1 if a is a quadratic non-residue mod p,
833/// 0 if a ≡ 0 (mod p), and 1 if a is a quadratic residue mod p.
834///
835/// # Examples
836/// ```
837/// use num_bigint::BigInt;
838/// use oxiz_math::rational::legendre_symbol;
839///
840/// // For p = 5:
841/// // 1^2 = 1, 2^2 = 4, 3^2 = 4, 4^2 = 1 (mod 5)
842/// // So quadratic residues are {1, 4}
843/// assert_eq!(legendre_symbol(&BigInt::from(1), &BigInt::from(5)), 1);
844/// assert_eq!(legendre_symbol(&BigInt::from(4), &BigInt::from(5)), 1);
845/// assert_eq!(legendre_symbol(&BigInt::from(2), &BigInt::from(5)), -1);
846/// ```
847pub fn legendre_symbol(a: &BigInt, p: &BigInt) -> i8 {
848 // For prime p, Legendre symbol equals Jacobi symbol
849 jacobi_symbol(a, p)
850}
851
852// ---------------------------------------------------------------------------
853// Verified complete factorization
854//
855// `trial_division` only ever removes factors up to a fixed limit and then
856// blindly assumes whatever remains is prime. That assumption silently
857// misclassifies composite residuals (e.g. a semiprime whose two factors are
858// both above the limit), which in turn made `is_square_free`,
859// `divisor_count`, `divisor_sum`, `mobius` and `euler_totient` produce wrong
860// results for such inputs, and made `euler_totient`'s dedicated fallback
861// loop (bounded only by `sqrt(n)`) stall for ~10^9 iterations on a large
862// 64-bit prime.
863//
864// The helpers below factor completely: trial division peels off small
865// factors, and anything left over is verified with Miller-Rabin and, if
866// composite, split with Pollard's rho. Both primality testing and rho use a
867// self-contained deterministic pseudo-random sequence (no `rand` dependency)
868// so they behave identically with or without the `std` feature and never
869// need an entropy source. Total work is capped by a fixed retry budget
870// rather than by `n`'s magnitude, so factorization always terminates
871// promptly -- including for large primes, which is exactly the case that
872// used to hang.
873// ---------------------------------------------------------------------------
874
875/// Small deterministic PRNG (SplitMix64) used only to vary the starting
876/// parameters of successive Pollard's rho attempts. Not cryptographically
877/// meaningful -- just needs to explore different sequences on retry.
878struct SplitMix64(u64);
879
880impl SplitMix64 {
881 fn new(seed: u64) -> Self {
882 SplitMix64(seed)
883 }
884
885 fn next_u64(&mut self) -> u64 {
886 self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
887 let mut z = self.0;
888 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
889 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
890 z ^ (z >> 31)
891 }
892}
893
894/// FNV-1a hash of a `BigInt`'s bytes, used to seed the deterministic PRNG.
895fn seed_from_bigint(n: &BigInt, salt: u64) -> u64 {
896 let bytes = n.to_signed_bytes_le();
897 let mut h: u64 = 0xcbf2_9ce4_8422_2325 ^ salt;
898 for &b in &bytes {
899 h ^= b as u64;
900 h = h.wrapping_mul(0x0000_0100_0000_01B3);
901 }
902 h
903}
904
905/// First 20 primes, used as fixed Miller-Rabin witnesses. This needs no
906/// entropy source (unlike [`is_prime`]), so it is available unconditionally.
907const SMALL_WITNESS_PRIMES: [u64; 20] = [
908 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
909];
910
911/// Deterministic Miller-Rabin primality test using a fixed witness set.
912///
913/// Unlike [`is_prime`], this does not draw from an entropy source, so it
914/// works identically with or without the `std` feature. False positives are
915/// only theoretically possible against numbers deliberately constructed to
916/// defeat this exact fixed witness set, which cannot arise from ordinary
917/// factorization work.
918fn is_prime_fixed_witnesses(n: &BigInt) -> bool {
919 let zero = BigInt::zero();
920 let one = BigInt::one();
921 let two = BigInt::from(2);
922
923 if n <= &one {
924 return false;
925 }
926 if n == &two {
927 return true;
928 }
929 if n.is_even() {
930 return false;
931 }
932
933 for &p in &SMALL_WITNESS_PRIMES {
934 let bp = BigInt::from(p);
935 if n == &bp {
936 return true;
937 }
938 if n > &bp && (n % &bp) == zero {
939 return false;
940 }
941 }
942
943 let n_minus_1 = n - &one;
944 let mut d = n_minus_1.clone();
945 let mut r: u32 = 0;
946 while d.is_even() {
947 d >>= 1;
948 r += 1;
949 }
950
951 'witness: for &a_val in &SMALL_WITNESS_PRIMES {
952 let a = BigInt::from(a_val);
953 if &a >= n {
954 continue;
955 }
956 let mut x = mod_pow(&a, &d, n);
957 if x == one || x == n_minus_1 {
958 continue 'witness;
959 }
960 for _ in 0..r.saturating_sub(1) {
961 x = mod_pow(&x, &two, n);
962 if x == n_minus_1 {
963 continue 'witness;
964 }
965 }
966 return false;
967 }
968
969 true
970}
971
972/// One attempt at splitting composite `n` via Pollard's rho, using
973/// deterministic pseudo-random parameters derived from `attempt`. Returns
974/// `None` if this particular choice of parameters fails to find a factor
975/// within the per-attempt iteration cap; callers should retry with a
976/// different `attempt` index.
977fn pollard_rho_attempt(n: &BigInt, attempt: u64) -> Option<BigInt> {
978 let one = BigInt::one();
979 if n.is_even() {
980 return Some(BigInt::from(2));
981 }
982
983 let mut rng = SplitMix64::new(seed_from_bigint(n, attempt));
984 let x0 = BigInt::from(rng.next_u64()) % n;
985 let mut c = BigInt::from(rng.next_u64()) % n;
986 if c.is_zero() {
987 c = one.clone();
988 }
989
990 let f = |x: &BigInt| -> BigInt { (x * x + &c) % n };
991
992 let mut x = x0.clone();
993 let mut y = x0;
994 let mut d = one.clone();
995
996 let max_iterations = 100_000;
997 let mut iterations = 0;
998
999 while d == one && iterations < max_iterations {
1000 x = f(&x);
1001 y = f(&f(&y));
1002 let diff = if x >= y { &x - &y } else { &y - &x };
1003 d = gcd_bigint(diff, n.clone());
1004 iterations += 1;
1005 }
1006
1007 if d != *n && d != one { Some(d) } else { None }
1008}
1009
1010/// Completely factor `n` (`n > 1`) into primes, with multiplicity, verifying
1011/// primality with Miller-Rabin instead of assuming a residual is prime.
1012///
1013/// Total Pollard's-rho work is capped by a fixed retry budget rather than by
1014/// `n`'s magnitude, so this always terminates promptly. In the
1015/// astronomically unlikely case that a composite cofactor resists the full
1016/// retry budget -- which would require a deliberately constructed,
1017/// cryptographically hard semiprime (a fundamentally hard problem for any
1018/// classical factoring approach, not specific to this implementation) --
1019/// this returns `Err((partial_factors, unresolved_residual))` instead of
1020/// guessing.
1021fn factorize_verified(n: &BigInt) -> Result<Vec<BigInt>, (Vec<BigInt>, BigInt)> {
1022 let one = BigInt::one();
1023 debug_assert!(n > &one, "factorize_verified expects n > 1");
1024
1025 let mut factors = Vec::new();
1026 let mut remaining = n.clone();
1027
1028 while remaining.is_even() {
1029 factors.push(BigInt::from(2));
1030 remaining >>= 1;
1031 }
1032
1033 let mut d = BigInt::from(3);
1034 let small_limit = BigInt::from(1_000_000u64);
1035 while &d * &d <= remaining && d <= small_limit {
1036 while (&remaining % &d).is_zero() {
1037 factors.push(d.clone());
1038 remaining /= &d;
1039 }
1040 d += BigInt::from(2);
1041 }
1042
1043 const MAX_POLLARD_ATTEMPTS: u32 = 64;
1044 let mut attempts_left = MAX_POLLARD_ATTEMPTS;
1045 let mut stack = vec![remaining];
1046
1047 while let Some(m) = stack.pop() {
1048 if m <= one {
1049 continue;
1050 }
1051 if is_prime_fixed_witnesses(&m) {
1052 factors.push(m);
1053 continue;
1054 }
1055
1056 let mut split = None;
1057 let mut attempt_idx: u64 = 0;
1058 while attempts_left > 0 {
1059 attempts_left -= 1;
1060 attempt_idx += 1;
1061 if let Some(f) = pollard_rho_attempt(&m, attempt_idx)
1062 && f > one
1063 && f < m
1064 {
1065 split = Some(f);
1066 break;
1067 }
1068 }
1069
1070 match split {
1071 Some(f) => {
1072 let cofactor = &m / &f;
1073 stack.push(f);
1074 stack.push(cofactor);
1075 }
1076 None => {
1077 // Budget exhausted on a known-composite (verified above)
1078 // residual: surface it rather than silently treating it as
1079 // prime. See doc comment for when this can occur.
1080 factors.sort();
1081 return Err((factors, m));
1082 }
1083 }
1084 }
1085
1086 factors.sort();
1087 Ok(factors)
1088}
1089
1090/// Complete factorization of `n` (`n > 1`), with multiplicity, falling back
1091/// to treating an unresolved residual (see [`factorize_verified`]) as a
1092/// single factor. This preserves the non-`Result` signatures of the
1093/// number-theoretic helpers below; the fallback path is only reachable for
1094/// deliberately constructed, cryptographically hard semiprimes and is
1095/// documented on [`factorize_verified`]. Callers needing an explicit error
1096/// on that path should use [`factorize_verified`] directly (or the public
1097/// `factorize` wrapper).
1098fn factorize_or_best_effort(n: &BigInt) -> Vec<BigInt> {
1099 match factorize_verified(n) {
1100 Ok(factors) => factors,
1101 Err((mut factors, residual)) => {
1102 factors.push(residual);
1103 factors.sort();
1104 factors
1105 }
1106 }
1107}
1108
1109/// Completely factor `n` into primes, with multiplicity.
1110///
1111/// This is the honest, `Result`-returning counterpart to the internal
1112/// factorization used by [`is_square_free`], [`divisor_count`],
1113/// [`divisor_sum`], [`mobius`] and [`euler_totient`]. Prefer this function
1114/// when you need to know whether factorization fully succeeded rather than
1115/// silently falling back on an unresolved residual.
1116///
1117/// # Errors
1118///
1119/// Returns `Err` describing the unresolved composite residual if the fixed
1120/// retry budget is exhausted before `n` is fully factored -- see
1121/// `factorize_verified` for when this can occur (in practice, only for
1122/// deliberately constructed cryptographically hard semiprimes).
1123pub fn factorize(n: &BigInt) -> Result<Vec<BigInt>, String> {
1124 if n <= &BigInt::one() {
1125 return Ok(Vec::new());
1126 }
1127 factorize_verified(n).map_err(|(_partial, residual)| {
1128 format!(
1129 "factorize: exhausted retry budget without fully factoring residual cofactor {residual}"
1130 )
1131 })
1132}
1133
1134/// Computes Euler's totient function φ(n).
1135///
1136/// φ(n) counts the number of integers from 1 to n that are coprime with n.
1137///
1138/// # Examples
1139/// ```
1140/// use num_bigint::BigInt;
1141/// use oxiz_math::rational::euler_totient;
1142///
1143/// assert_eq!(euler_totient(&BigInt::from(1)), BigInt::from(1));
1144/// assert_eq!(euler_totient(&BigInt::from(9)), BigInt::from(6)); // φ(9) = 6
1145/// assert_eq!(euler_totient(&BigInt::from(10)), BigInt::from(4)); // φ(10) = 4
1146/// ```
1147pub fn euler_totient(n: &BigInt) -> BigInt {
1148 if n <= &BigInt::one() {
1149 return BigInt::one();
1150 }
1151
1152 let mut result = n.clone();
1153 let mut seen_primes = crate::prelude::HashSet::new();
1154
1155 for factor in factorize_or_best_effort(n) {
1156 if seen_primes.insert(factor.clone()) {
1157 // φ(n) = n * (1 - 1/p1) * (1 - 1/p2) * ...
1158 // = n * (p1 - 1)/p1 * (p2 - 1)/p2 * ...
1159 result = result * (&factor - BigInt::one()) / &factor;
1160 }
1161 }
1162
1163 result
1164}
1165
1166/// Tests if n is a perfect power: n = a^b for some a, b > 1.
1167///
1168/// Returns `Some((a, b))` if n is a perfect power, `None` otherwise.
1169///
1170/// # Examples
1171/// ```
1172/// use num_bigint::BigInt;
1173/// use oxiz_math::rational::is_perfect_power;
1174///
1175/// assert_eq!(is_perfect_power(&BigInt::from(8)), Some((BigInt::from(2), 3)));
1176/// assert_eq!(is_perfect_power(&BigInt::from(27)), Some((BigInt::from(3), 3)));
1177/// // 16 = 2^4 or 4^2, both are valid
1178/// let result = is_perfect_power(&BigInt::from(16));
1179/// assert!(result.is_some());
1180/// let (base, exp) = result.unwrap();
1181/// assert_eq!(base.pow(exp), BigInt::from(16));
1182/// assert_eq!(is_perfect_power(&BigInt::from(10)), None);
1183/// ```
1184pub fn is_perfect_power(n: &BigInt) -> Option<(BigInt, u32)> {
1185 if n <= &BigInt::one() {
1186 return None;
1187 }
1188
1189 // Check for each possible exponent b from 2 to log2(n)
1190 let bit_len = n.bits() as u32;
1191
1192 for b in 2..=bit_len {
1193 // Binary search for a such that a^b = n
1194 let mut low = BigInt::one();
1195 let mut high = n.clone();
1196
1197 while low <= high {
1198 let mid = (&low + &high) / BigInt::from(2);
1199 let power = pow_uint(&BigRational::from_integer(mid.clone()), b);
1200
1201 if power.is_integer() {
1202 let power_int = power.numer().clone();
1203
1204 match power_int.cmp(n) {
1205 Ordering::Equal => return Some((mid, b)),
1206 Ordering::Less => low = mid + BigInt::one(),
1207 Ordering::Greater => high = mid - BigInt::one(),
1208 }
1209 } else {
1210 break;
1211 }
1212 }
1213 }
1214
1215 None
1216}
1217
1218/// Tests if n is square-free (not divisible by any perfect square except 1).
1219///
1220/// # Examples
1221/// ```
1222/// use num_bigint::BigInt;
1223/// use oxiz_math::rational::is_square_free;
1224///
1225/// assert!(is_square_free(&BigInt::from(6))); // 6 = 2 * 3
1226/// assert!(is_square_free(&BigInt::from(10))); // 10 = 2 * 5
1227/// assert!(!is_square_free(&BigInt::from(12))); // 12 = 4 * 3, divisible by 4 = 2^2
1228/// assert!(!is_square_free(&BigInt::from(18))); // 18 = 9 * 2, divisible by 9 = 3^2
1229/// ```
1230pub fn is_square_free(n: &BigInt) -> bool {
1231 if n <= &BigInt::one() {
1232 return n == &BigInt::one();
1233 }
1234
1235 let factors = factorize_or_best_effort(n);
1236 let mut prev: Option<BigInt> = None;
1237
1238 for factor in factors {
1239 if let Some(ref p) = prev
1240 && &factor == p
1241 {
1242 return false; // Found repeated factor
1243 }
1244 prev = Some(factor);
1245 }
1246
1247 true
1248}
1249
1250/// Computes the number of divisors of n (tau function, τ(n)).
1251///
1252/// # Examples
1253/// ```
1254/// use num_bigint::BigInt;
1255/// use oxiz_math::rational::divisor_count;
1256///
1257/// assert_eq!(divisor_count(&BigInt::from(12)), BigInt::from(6)); // 1, 2, 3, 4, 6, 12
1258/// assert_eq!(divisor_count(&BigInt::from(28)), BigInt::from(6)); // 1, 2, 4, 7, 14, 28
1259/// assert_eq!(divisor_count(&BigInt::from(1)), BigInt::from(1));
1260/// ```
1261pub fn divisor_count(n: &BigInt) -> BigInt {
1262 if n <= &BigInt::zero() {
1263 return BigInt::zero();
1264 }
1265 if n == &BigInt::one() {
1266 return BigInt::one();
1267 }
1268
1269 let factors = factorize_or_best_effort(n);
1270
1271 let mut count = BigInt::one();
1272 let mut current_prime = None;
1273 let mut current_count = 0u32;
1274
1275 for factor in factors {
1276 if let Some(ref prime) = current_prime {
1277 if &factor == prime {
1278 current_count += 1;
1279 } else {
1280 count *= current_count + 1;
1281 current_prime = Some(factor);
1282 current_count = 1;
1283 }
1284 } else {
1285 current_prime = Some(factor);
1286 current_count = 1;
1287 }
1288 }
1289
1290 if current_count > 0 {
1291 count *= current_count + 1;
1292 }
1293
1294 count
1295}
1296
1297/// Computes the sum of divisors of n (sigma function, σ(n)).
1298///
1299/// # Examples
1300/// ```
1301/// use num_bigint::BigInt;
1302/// use oxiz_math::rational::divisor_sum;
1303///
1304/// assert_eq!(divisor_sum(&BigInt::from(12)), BigInt::from(28)); // 1+2+3+4+6+12
1305/// assert_eq!(divisor_sum(&BigInt::from(6)), BigInt::from(12)); // 1+2+3+6
1306/// assert_eq!(divisor_sum(&BigInt::from(1)), BigInt::from(1));
1307/// ```
1308pub fn divisor_sum(n: &BigInt) -> BigInt {
1309 if n <= &BigInt::zero() {
1310 return BigInt::zero();
1311 }
1312 if n == &BigInt::one() {
1313 return BigInt::one();
1314 }
1315
1316 let factors = factorize_or_best_effort(n);
1317
1318 let mut sum = BigInt::one();
1319 let mut current_prime = None;
1320 let mut current_count = 0u32;
1321
1322 for factor in factors {
1323 if let Some(ref prime) = current_prime {
1324 if &factor == prime {
1325 current_count += 1;
1326 } else {
1327 // σ(p^k) = (p^(k+1) - 1) / (p - 1)
1328 let p_power =
1329 pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
1330 let numerator = p_power.numer() - BigInt::one();
1331 let denominator = prime - BigInt::one();
1332 sum *= numerator / denominator;
1333
1334 current_prime = Some(factor);
1335 current_count = 1;
1336 }
1337 } else {
1338 current_prime = Some(factor);
1339 current_count = 1;
1340 }
1341 }
1342
1343 if let Some(prime) = current_prime {
1344 let p_power = pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
1345 let numerator = p_power.numer() - BigInt::one();
1346 let denominator = prime - BigInt::one();
1347 sum *= numerator / denominator;
1348 }
1349
1350 sum
1351}
1352
1353/// Computes the Möbius function μ(n).
1354///
1355/// Returns:
1356/// - 1 if n is square-free with an even number of prime factors
1357/// - -1 if n is square-free with an odd number of prime factors
1358/// - 0 if n is not square-free
1359///
1360/// # Examples
1361/// ```
1362/// use num_bigint::BigInt;
1363/// use oxiz_math::rational::mobius;
1364///
1365/// assert_eq!(mobius(&BigInt::from(1)), 1);
1366/// assert_eq!(mobius(&BigInt::from(6)), 1); // 6 = 2*3 (2 primes)
1367/// assert_eq!(mobius(&BigInt::from(30)), -1); // 30 = 2*3*5 (3 primes)
1368/// assert_eq!(mobius(&BigInt::from(12)), 0); // 12 = 2^2*3 (not square-free)
1369/// ```
1370pub fn mobius(n: &BigInt) -> i8 {
1371 if n <= &BigInt::zero() {
1372 return 0;
1373 }
1374 if n == &BigInt::one() {
1375 return 1;
1376 }
1377
1378 let factors = factorize_or_best_effort(n);
1379 let mut unique_primes = crate::prelude::HashSet::new();
1380
1381 for factor in factors {
1382 if !unique_primes.insert(factor) {
1383 return 0; // Repeated prime factor => not square-free
1384 }
1385 }
1386
1387 if unique_primes.len() % 2 == 0 { 1 } else { -1 }
1388}
1389
1390/// Computes Carmichael's lambda function λ(n).
1391///
1392/// λ(n) is the exponent of the group (ℤ/nℤ)*.
1393/// For any a coprime to n: a^λ(n) ≡ 1 (mod n).
1394///
1395/// # Examples
1396/// ```
1397/// use num_bigint::BigInt;
1398/// use oxiz_math::rational::carmichael_lambda;
1399///
1400/// assert_eq!(carmichael_lambda(&BigInt::from(1)), BigInt::from(1));
1401/// assert_eq!(carmichael_lambda(&BigInt::from(8)), BigInt::from(2)); // λ(8) = 2
1402/// assert_eq!(carmichael_lambda(&BigInt::from(15)), BigInt::from(4)); // λ(15) = 4
1403/// ```
1404pub fn carmichael_lambda(n: &BigInt) -> BigInt {
1405 if n <= &BigInt::one() {
1406 return BigInt::one();
1407 }
1408
1409 let factors = factorize_or_best_effort(n);
1410 let mut prime_powers: FxHashMap<BigInt, u32> = FxHashMap::default();
1411
1412 for factor in factors {
1413 *prime_powers.entry(factor).or_insert(0) += 1;
1414 }
1415
1416 let mut result = BigInt::one();
1417
1418 for (prime, exp) in prime_powers {
1419 let lambda_p = if prime == BigInt::from(2) && exp >= 3 {
1420 // λ(2^k) = 2^(k-2) for k ≥ 3
1421 BigInt::from(2).pow(exp - 2)
1422 } else if prime == BigInt::from(2) {
1423 // λ(2) = 1, λ(4) = 2
1424 if exp == 1 {
1425 BigInt::one()
1426 } else {
1427 BigInt::from(2)
1428 }
1429 } else {
1430 // λ(p^k) = φ(p^k) = p^(k-1) * (p-1)
1431 let p_power = pow_uint(&BigRational::from_integer(prime.clone()), exp - 1);
1432 p_power.numer() * (&prime - BigInt::one())
1433 };
1434
1435 result = lcm_bigint(result, lambda_p);
1436 }
1437
1438 result
1439}
1440
1441/// Binary GCD (Stein's algorithm) - more efficient than Euclidean GCD.
1442///
1443/// This algorithm uses bitwise operations instead of division,
1444/// making it faster for large integers.
1445///
1446/// # Examples
1447/// ```
1448/// use num_bigint::BigInt;
1449/// use oxiz_math::rational::gcd_binary;
1450///
1451/// assert_eq!(gcd_binary(BigInt::from(48), BigInt::from(18)), BigInt::from(6));
1452/// assert_eq!(gcd_binary(BigInt::from(100), BigInt::from(35)), BigInt::from(5));
1453/// assert_eq!(gcd_binary(BigInt::from(17), BigInt::from(19)), BigInt::from(1));
1454/// ```
1455pub fn gcd_binary(mut a: BigInt, mut b: BigInt) -> BigInt {
1456 if a == BigInt::zero() {
1457 return b.abs();
1458 }
1459 if b == BigInt::zero() {
1460 return a.abs();
1461 }
1462
1463 a = a.abs();
1464 b = b.abs();
1465
1466 // Count common factors of 2
1467 let mut shift = 0u32;
1468 while a.is_even() && b.is_even() {
1469 a >>= 1;
1470 b >>= 1;
1471 shift += 1;
1472 }
1473
1474 // Remove remaining factors of 2 from a
1475 while a.is_even() {
1476 a >>= 1;
1477 }
1478
1479 loop {
1480 // Remove factors of 2 from b
1481 while b.is_even() {
1482 b >>= 1;
1483 }
1484
1485 // Ensure a <= b
1486 if a > b {
1487 core::mem::swap(&mut a, &mut b);
1488 }
1489
1490 b -= &a;
1491
1492 if b == BigInt::zero() {
1493 break;
1494 }
1495 }
1496
1497 a << shift
1498}
1499
1500/// Tonelli-Shanks algorithm for computing modular square roots.
1501///
1502/// Finds x such that x² ≡ n (mod p) for prime p.
1503/// Returns None if n is not a quadratic residue modulo p.
1504///
1505/// # Examples
1506/// ```
1507/// use num_bigint::BigInt;
1508/// use oxiz_math::rational::tonelli_shanks;
1509///
1510/// // 4 is a quadratic residue mod 7 (2^2 = 4)
1511/// let result = tonelli_shanks(&BigInt::from(4), &BigInt::from(7));
1512/// assert!(result.is_some());
1513/// if let Some(x) = result {
1514/// let p = BigInt::from(7);
1515/// assert_eq!((&x * &x) % &p, BigInt::from(4) % &p);
1516/// }
1517/// ```
1518pub fn tonelli_shanks(n: &BigInt, p: &BigInt) -> Option<BigInt> {
1519 // Check if n is a quadratic residue
1520 if legendre_symbol(n, p) != 1 {
1521 return None;
1522 }
1523
1524 // Handle special cases
1525 if p == &BigInt::from(2) {
1526 return Some(n % p);
1527 }
1528
1529 // Factor out powers of 2 from p-1: p-1 = 2^S * Q
1530 let p_minus_1 = p - BigInt::one();
1531 let mut q = p_minus_1.clone();
1532 let mut s = 0u32;
1533 while q.is_even() {
1534 q >>= 1;
1535 s += 1;
1536 }
1537
1538 // Special case: p ≡ 3 (mod 4)
1539 if s == 1 {
1540 let exp = (p + BigInt::one()) / BigInt::from(4);
1541 return Some(mod_pow(n, &exp, p));
1542 }
1543
1544 // Find a quadratic non-residue z
1545 let mut z = BigInt::from(2);
1546 while legendre_symbol(&z, p) != -1 {
1547 z += BigInt::one();
1548 }
1549
1550 let mut m = s;
1551 let mut c = mod_pow(&z, &q, p);
1552 let mut t = mod_pow(n, &q, p);
1553 let mut r = mod_pow(n, &((&q + BigInt::one()) / BigInt::from(2)), p);
1554
1555 loop {
1556 if t == BigInt::zero() {
1557 return Some(BigInt::zero());
1558 }
1559 if t == BigInt::one() {
1560 return Some(r);
1561 }
1562
1563 // Find the least i such that t^(2^i) = 1
1564 let mut i = 1u32;
1565 let mut temp = (&t * &t) % p;
1566 while temp != BigInt::one() && i < m {
1567 temp = (&temp * &temp) % p;
1568 i += 1;
1569 }
1570
1571 let b = mod_pow(&c, &BigInt::from(2u64).pow(m - i - 1), p);
1572 m = i;
1573 c = (&b * &b) % p;
1574 t = (&t * &c) % p;
1575 r = (&r * &b) % p;
1576 }
1577}
1578
1579/// Computes factorial n!
1580///
1581/// # Examples
1582/// ```
1583/// use num_bigint::BigInt;
1584/// use oxiz_math::rational::factorial;
1585///
1586/// assert_eq!(factorial(0), BigInt::from(1));
1587/// assert_eq!(factorial(5), BigInt::from(120));
1588/// assert_eq!(factorial(10), BigInt::from(3628800));
1589/// ```
1590pub fn factorial(n: u32) -> BigInt {
1591 if n == 0 || n == 1 {
1592 return BigInt::one();
1593 }
1594
1595 let mut result = BigInt::one();
1596 for i in 2..=n {
1597 result *= i;
1598 }
1599 result
1600}
1601
1602/// Computes binomial coefficient C(n, k) = n! / (k! * (n-k)!)
1603///
1604/// # Examples
1605/// ```
1606/// use num_bigint::BigInt;
1607/// use oxiz_math::rational::binomial;
1608///
1609/// assert_eq!(binomial(5, 2), BigInt::from(10));
1610/// assert_eq!(binomial(10, 3), BigInt::from(120));
1611/// assert_eq!(binomial(5, 0), BigInt::from(1));
1612/// assert_eq!(binomial(5, 5), BigInt::from(1));
1613/// ```
1614pub fn binomial(n: u32, k: u32) -> BigInt {
1615 if k > n {
1616 return BigInt::zero();
1617 }
1618 if k == 0 || k == n {
1619 return BigInt::one();
1620 }
1621
1622 // Use symmetry: C(n,k) = C(n,n-k)
1623 let k = core::cmp::min(k, n - k);
1624
1625 let mut result = BigInt::one();
1626 for i in 0..k {
1627 result *= n - i;
1628 result /= i + 1;
1629 }
1630 result
1631}
1632
1633#[cfg(test)]
1634mod tests;