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/// Computes Euler's totient function φ(n).
853///
854/// φ(n) counts the number of integers from 1 to n that are coprime with n.
855///
856/// # Examples
857/// ```
858/// use num_bigint::BigInt;
859/// use oxiz_math::rational::euler_totient;
860///
861/// assert_eq!(euler_totient(&BigInt::from(1)), BigInt::from(1));
862/// assert_eq!(euler_totient(&BigInt::from(9)), BigInt::from(6)); // φ(9) = 6
863/// assert_eq!(euler_totient(&BigInt::from(10)), BigInt::from(4)); // φ(10) = 4
864/// ```
865#[allow(dead_code)]
866pub fn euler_totient(n: &BigInt) -> BigInt {
867 if n <= &BigInt::one() {
868 return BigInt::one();
869 }
870
871 let mut result = n.clone();
872
873 // Find all prime factors using trial division
874 let factors = trial_division(n, 1000000);
875 let mut seen_primes = crate::prelude::HashSet::new();
876
877 for factor in factors {
878 if seen_primes.insert(factor.clone()) {
879 // φ(n) = n * (1 - 1/p1) * (1 - 1/p2) * ...
880 // = n * (p1 - 1)/p1 * (p2 - 1)/p2 * ...
881 result = result * (&factor - BigInt::one()) / &factor;
882 }
883 }
884
885 // If num is still > 1 after trial division, it's a large prime
886 let mut temp = n.clone();
887 let mut divisor = BigInt::from(2);
888 while &divisor * &divisor <= temp {
889 if &temp % &divisor == BigInt::zero() {
890 while &temp % &divisor == BigInt::zero() {
891 temp /= &divisor;
892 }
893 if seen_primes.insert(divisor.clone()) {
894 result = result * (&divisor - BigInt::one()) / &divisor;
895 }
896 }
897 divisor += BigInt::one();
898 }
899
900 if temp > BigInt::one() && !seen_primes.contains(&temp) {
901 result = result * (&temp - BigInt::one()) / &temp;
902 }
903
904 result
905}
906
907/// Tests if n is a perfect power: n = a^b for some a, b > 1.
908///
909/// Returns `Some((a, b))` if n is a perfect power, `None` otherwise.
910///
911/// # Examples
912/// ```
913/// use num_bigint::BigInt;
914/// use oxiz_math::rational::is_perfect_power;
915///
916/// assert_eq!(is_perfect_power(&BigInt::from(8)), Some((BigInt::from(2), 3)));
917/// assert_eq!(is_perfect_power(&BigInt::from(27)), Some((BigInt::from(3), 3)));
918/// // 16 = 2^4 or 4^2, both are valid
919/// let result = is_perfect_power(&BigInt::from(16));
920/// assert!(result.is_some());
921/// let (base, exp) = result.unwrap();
922/// assert_eq!(base.pow(exp), BigInt::from(16));
923/// assert_eq!(is_perfect_power(&BigInt::from(10)), None);
924/// ```
925pub fn is_perfect_power(n: &BigInt) -> Option<(BigInt, u32)> {
926 if n <= &BigInt::one() {
927 return None;
928 }
929
930 // Check for each possible exponent b from 2 to log2(n)
931 let bit_len = n.bits() as u32;
932
933 for b in 2..=bit_len {
934 // Binary search for a such that a^b = n
935 let mut low = BigInt::one();
936 let mut high = n.clone();
937
938 while low <= high {
939 let mid = (&low + &high) / BigInt::from(2);
940 let power = pow_uint(&BigRational::from_integer(mid.clone()), b);
941
942 if power.is_integer() {
943 let power_int = power.numer().clone();
944
945 match power_int.cmp(n) {
946 Ordering::Equal => return Some((mid, b)),
947 Ordering::Less => low = mid + BigInt::one(),
948 Ordering::Greater => high = mid - BigInt::one(),
949 }
950 } else {
951 break;
952 }
953 }
954 }
955
956 None
957}
958
959/// Tests if n is square-free (not divisible by any perfect square except 1).
960///
961/// # Examples
962/// ```
963/// use num_bigint::BigInt;
964/// use oxiz_math::rational::is_square_free;
965///
966/// assert!(is_square_free(&BigInt::from(6))); // 6 = 2 * 3
967/// assert!(is_square_free(&BigInt::from(10))); // 10 = 2 * 5
968/// assert!(!is_square_free(&BigInt::from(12))); // 12 = 4 * 3, divisible by 4 = 2^2
969/// assert!(!is_square_free(&BigInt::from(18))); // 18 = 9 * 2, divisible by 9 = 3^2
970/// ```
971pub fn is_square_free(n: &BigInt) -> bool {
972 if n <= &BigInt::one() {
973 return n == &BigInt::one();
974 }
975
976 let factors = trial_division(n, 100000);
977 let mut prev: Option<BigInt> = None;
978
979 for factor in factors {
980 if let Some(ref p) = prev
981 && &factor == p
982 {
983 return false; // Found repeated factor
984 }
985 prev = Some(factor);
986 }
987
988 true
989}
990
991/// Computes the number of divisors of n (tau function, τ(n)).
992///
993/// # Examples
994/// ```
995/// use num_bigint::BigInt;
996/// use oxiz_math::rational::divisor_count;
997///
998/// assert_eq!(divisor_count(&BigInt::from(12)), BigInt::from(6)); // 1, 2, 3, 4, 6, 12
999/// assert_eq!(divisor_count(&BigInt::from(28)), BigInt::from(6)); // 1, 2, 4, 7, 14, 28
1000/// assert_eq!(divisor_count(&BigInt::from(1)), BigInt::from(1));
1001/// ```
1002pub fn divisor_count(n: &BigInt) -> BigInt {
1003 if n <= &BigInt::zero() {
1004 return BigInt::zero();
1005 }
1006 if n == &BigInt::one() {
1007 return BigInt::one();
1008 }
1009
1010 let factors = trial_division(n, 1000000);
1011
1012 // If factors is empty, n is prime (has exactly 2 divisors: 1 and n)
1013 if factors.is_empty() {
1014 return BigInt::from(2);
1015 }
1016
1017 let mut count = BigInt::one();
1018 let mut current_prime = None;
1019 let mut current_count = 0u32;
1020
1021 for factor in factors {
1022 if let Some(ref prime) = current_prime {
1023 if &factor == prime {
1024 current_count += 1;
1025 } else {
1026 count *= current_count + 1;
1027 current_prime = Some(factor);
1028 current_count = 1;
1029 }
1030 } else {
1031 current_prime = Some(factor);
1032 current_count = 1;
1033 }
1034 }
1035
1036 if current_count > 0 {
1037 count *= current_count + 1;
1038 }
1039
1040 count
1041}
1042
1043/// Computes the sum of divisors of n (sigma function, σ(n)).
1044///
1045/// # Examples
1046/// ```
1047/// use num_bigint::BigInt;
1048/// use oxiz_math::rational::divisor_sum;
1049///
1050/// assert_eq!(divisor_sum(&BigInt::from(12)), BigInt::from(28)); // 1+2+3+4+6+12
1051/// assert_eq!(divisor_sum(&BigInt::from(6)), BigInt::from(12)); // 1+2+3+6
1052/// assert_eq!(divisor_sum(&BigInt::from(1)), BigInt::from(1));
1053/// ```
1054pub fn divisor_sum(n: &BigInt) -> BigInt {
1055 if n <= &BigInt::zero() {
1056 return BigInt::zero();
1057 }
1058 if n == &BigInt::one() {
1059 return BigInt::one();
1060 }
1061
1062 let factors = trial_division(n, 1000000);
1063
1064 // If factors is empty, n is prime (divisors are 1 and n, so sum = 1 + n)
1065 if factors.is_empty() {
1066 return BigInt::one() + n;
1067 }
1068
1069 let mut sum = BigInt::one();
1070 let mut current_prime = None;
1071 let mut current_count = 0u32;
1072
1073 for factor in factors {
1074 if let Some(ref prime) = current_prime {
1075 if &factor == prime {
1076 current_count += 1;
1077 } else {
1078 // σ(p^k) = (p^(k+1) - 1) / (p - 1)
1079 let p_power =
1080 pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
1081 let numerator = p_power.numer() - BigInt::one();
1082 let denominator = prime - BigInt::one();
1083 sum *= numerator / denominator;
1084
1085 current_prime = Some(factor);
1086 current_count = 1;
1087 }
1088 } else {
1089 current_prime = Some(factor);
1090 current_count = 1;
1091 }
1092 }
1093
1094 if let Some(prime) = current_prime {
1095 let p_power = pow_uint(&BigRational::from_integer(prime.clone()), current_count + 1);
1096 let numerator = p_power.numer() - BigInt::one();
1097 let denominator = prime - BigInt::one();
1098 sum *= numerator / denominator;
1099 }
1100
1101 sum
1102}
1103
1104/// Computes the Möbius function μ(n).
1105///
1106/// Returns:
1107/// - 1 if n is square-free with an even number of prime factors
1108/// - -1 if n is square-free with an odd number of prime factors
1109/// - 0 if n is not square-free
1110///
1111/// # Examples
1112/// ```
1113/// use num_bigint::BigInt;
1114/// use oxiz_math::rational::mobius;
1115///
1116/// assert_eq!(mobius(&BigInt::from(1)), 1);
1117/// assert_eq!(mobius(&BigInt::from(6)), 1); // 6 = 2*3 (2 primes)
1118/// assert_eq!(mobius(&BigInt::from(30)), -1); // 30 = 2*3*5 (3 primes)
1119/// assert_eq!(mobius(&BigInt::from(12)), 0); // 12 = 2^2*3 (not square-free)
1120/// ```
1121pub fn mobius(n: &BigInt) -> i8 {
1122 if n <= &BigInt::zero() {
1123 return 0;
1124 }
1125 if n == &BigInt::one() {
1126 return 1;
1127 }
1128
1129 if !is_square_free(n) {
1130 return 0;
1131 }
1132
1133 let factors = trial_division(n, 1000000);
1134 let mut unique_primes = crate::prelude::HashSet::new();
1135
1136 for factor in factors {
1137 unique_primes.insert(factor);
1138 }
1139
1140 if unique_primes.len() % 2 == 0 { 1 } else { -1 }
1141}
1142
1143/// Computes Carmichael's lambda function λ(n).
1144///
1145/// λ(n) is the exponent of the group (ℤ/nℤ)*.
1146/// For any a coprime to n: a^λ(n) ≡ 1 (mod n).
1147///
1148/// # Examples
1149/// ```
1150/// use num_bigint::BigInt;
1151/// use oxiz_math::rational::carmichael_lambda;
1152///
1153/// assert_eq!(carmichael_lambda(&BigInt::from(1)), BigInt::from(1));
1154/// assert_eq!(carmichael_lambda(&BigInt::from(8)), BigInt::from(2)); // λ(8) = 2
1155/// assert_eq!(carmichael_lambda(&BigInt::from(15)), BigInt::from(4)); // λ(15) = 4
1156/// ```
1157#[allow(dead_code)]
1158pub fn carmichael_lambda(n: &BigInt) -> BigInt {
1159 if n <= &BigInt::one() {
1160 return BigInt::one();
1161 }
1162
1163 let factors = trial_division(n, 1000000);
1164 let mut prime_powers: FxHashMap<BigInt, u32> = FxHashMap::default();
1165
1166 for factor in factors {
1167 *prime_powers.entry(factor).or_insert(0) += 1;
1168 }
1169
1170 let mut result = BigInt::one();
1171
1172 for (prime, exp) in prime_powers {
1173 let lambda_p = if prime == BigInt::from(2) && exp >= 3 {
1174 // λ(2^k) = 2^(k-2) for k ≥ 3
1175 BigInt::from(2).pow(exp - 2)
1176 } else if prime == BigInt::from(2) {
1177 // λ(2) = 1, λ(4) = 2
1178 if exp == 1 {
1179 BigInt::one()
1180 } else {
1181 BigInt::from(2)
1182 }
1183 } else {
1184 // λ(p^k) = φ(p^k) = p^(k-1) * (p-1)
1185 let p_power = pow_uint(&BigRational::from_integer(prime.clone()), exp - 1);
1186 p_power.numer() * (&prime - BigInt::one())
1187 };
1188
1189 result = lcm_bigint(result, lambda_p);
1190 }
1191
1192 result
1193}
1194
1195/// Binary GCD (Stein's algorithm) - more efficient than Euclidean GCD.
1196///
1197/// This algorithm uses bitwise operations instead of division,
1198/// making it faster for large integers.
1199///
1200/// # Examples
1201/// ```
1202/// use num_bigint::BigInt;
1203/// use oxiz_math::rational::gcd_binary;
1204///
1205/// assert_eq!(gcd_binary(BigInt::from(48), BigInt::from(18)), BigInt::from(6));
1206/// assert_eq!(gcd_binary(BigInt::from(100), BigInt::from(35)), BigInt::from(5));
1207/// assert_eq!(gcd_binary(BigInt::from(17), BigInt::from(19)), BigInt::from(1));
1208/// ```
1209pub fn gcd_binary(mut a: BigInt, mut b: BigInt) -> BigInt {
1210 if a == BigInt::zero() {
1211 return b.abs();
1212 }
1213 if b == BigInt::zero() {
1214 return a.abs();
1215 }
1216
1217 a = a.abs();
1218 b = b.abs();
1219
1220 // Count common factors of 2
1221 let mut shift = 0u32;
1222 while a.is_even() && b.is_even() {
1223 a >>= 1;
1224 b >>= 1;
1225 shift += 1;
1226 }
1227
1228 // Remove remaining factors of 2 from a
1229 while a.is_even() {
1230 a >>= 1;
1231 }
1232
1233 loop {
1234 // Remove factors of 2 from b
1235 while b.is_even() {
1236 b >>= 1;
1237 }
1238
1239 // Ensure a <= b
1240 if a > b {
1241 core::mem::swap(&mut a, &mut b);
1242 }
1243
1244 b -= &a;
1245
1246 if b == BigInt::zero() {
1247 break;
1248 }
1249 }
1250
1251 a << shift
1252}
1253
1254/// Tonelli-Shanks algorithm for computing modular square roots.
1255///
1256/// Finds x such that x² ≡ n (mod p) for prime p.
1257/// Returns None if n is not a quadratic residue modulo p.
1258///
1259/// # Examples
1260/// ```
1261/// use num_bigint::BigInt;
1262/// use oxiz_math::rational::tonelli_shanks;
1263///
1264/// // 4 is a quadratic residue mod 7 (2^2 = 4)
1265/// let result = tonelli_shanks(&BigInt::from(4), &BigInt::from(7));
1266/// assert!(result.is_some());
1267/// if let Some(x) = result {
1268/// let p = BigInt::from(7);
1269/// assert_eq!((&x * &x) % &p, BigInt::from(4) % &p);
1270/// }
1271/// ```
1272pub fn tonelli_shanks(n: &BigInt, p: &BigInt) -> Option<BigInt> {
1273 // Check if n is a quadratic residue
1274 if legendre_symbol(n, p) != 1 {
1275 return None;
1276 }
1277
1278 // Handle special cases
1279 if p == &BigInt::from(2) {
1280 return Some(n % p);
1281 }
1282
1283 // Factor out powers of 2 from p-1: p-1 = 2^S * Q
1284 let p_minus_1 = p - BigInt::one();
1285 let mut q = p_minus_1.clone();
1286 let mut s = 0u32;
1287 while q.is_even() {
1288 q >>= 1;
1289 s += 1;
1290 }
1291
1292 // Special case: p ≡ 3 (mod 4)
1293 if s == 1 {
1294 let exp = (p + BigInt::one()) / BigInt::from(4);
1295 return Some(mod_pow(n, &exp, p));
1296 }
1297
1298 // Find a quadratic non-residue z
1299 let mut z = BigInt::from(2);
1300 while legendre_symbol(&z, p) != -1 {
1301 z += BigInt::one();
1302 }
1303
1304 let mut m = s;
1305 let mut c = mod_pow(&z, &q, p);
1306 let mut t = mod_pow(n, &q, p);
1307 let mut r = mod_pow(n, &((&q + BigInt::one()) / BigInt::from(2)), p);
1308
1309 loop {
1310 if t == BigInt::zero() {
1311 return Some(BigInt::zero());
1312 }
1313 if t == BigInt::one() {
1314 return Some(r);
1315 }
1316
1317 // Find the least i such that t^(2^i) = 1
1318 let mut i = 1u32;
1319 let mut temp = (&t * &t) % p;
1320 while temp != BigInt::one() && i < m {
1321 temp = (&temp * &temp) % p;
1322 i += 1;
1323 }
1324
1325 let b = mod_pow(&c, &BigInt::from(2u64).pow(m - i - 1), p);
1326 m = i;
1327 c = (&b * &b) % p;
1328 t = (&t * &c) % p;
1329 r = (&r * &b) % p;
1330 }
1331}
1332
1333/// Computes factorial n!
1334///
1335/// # Examples
1336/// ```
1337/// use num_bigint::BigInt;
1338/// use oxiz_math::rational::factorial;
1339///
1340/// assert_eq!(factorial(0), BigInt::from(1));
1341/// assert_eq!(factorial(5), BigInt::from(120));
1342/// assert_eq!(factorial(10), BigInt::from(3628800));
1343/// ```
1344pub fn factorial(n: u32) -> BigInt {
1345 if n == 0 || n == 1 {
1346 return BigInt::one();
1347 }
1348
1349 let mut result = BigInt::one();
1350 for i in 2..=n {
1351 result *= i;
1352 }
1353 result
1354}
1355
1356/// Computes binomial coefficient C(n, k) = n! / (k! * (n-k)!)
1357///
1358/// # Examples
1359/// ```
1360/// use num_bigint::BigInt;
1361/// use oxiz_math::rational::binomial;
1362///
1363/// assert_eq!(binomial(5, 2), BigInt::from(10));
1364/// assert_eq!(binomial(10, 3), BigInt::from(120));
1365/// assert_eq!(binomial(5, 0), BigInt::from(1));
1366/// assert_eq!(binomial(5, 5), BigInt::from(1));
1367/// ```
1368pub fn binomial(n: u32, k: u32) -> BigInt {
1369 if k > n {
1370 return BigInt::zero();
1371 }
1372 if k == 0 || k == n {
1373 return BigInt::one();
1374 }
1375
1376 // Use symmetry: C(n,k) = C(n,n-k)
1377 let k = core::cmp::min(k, n - k);
1378
1379 let mut result = BigInt::one();
1380 for i in 0..k {
1381 result *= n - i;
1382 result /= i + 1;
1383 }
1384 result
1385}
1386
1387#[cfg(test)]
1388mod tests;