Skip to main content

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 - &quotient * &r;
159        old_r = r;
160        r = new_r;
161
162        let new_s = &old_s - &quotient * &s;
163        old_s = s;
164        s = new_s;
165
166        let new_t = &old_t - &quotient * &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(&current);
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;