use super::crt;
use crate::Integer;
pub fn crt_many(congruences: &[(Integer, Integer)]) -> Option<(Integer, Integer)> {
let mut iter = congruences.iter();
let first = iter.next()?;
let mut r = first.0.mod_floor(&first.1);
let mut m = first.1.clone();
for (ri, mi) in iter {
let (r2, m2) = crt(&r, &m, ri, mi)?;
r = r2;
m = m2;
}
Some((r, m))
}
#[cfg(test)]
mod tests {
use super::*;
fn b(n: i64) -> Integer {
Integer::from(n)
}
#[test]
fn crt_many_basic() {
let cs = [(b(2), b(3)), (b(3), b(5)), (b(2), b(7))];
let (r, m) = crt_many(&cs).unwrap();
assert_eq!(r, b(23));
assert_eq!(m, b(105));
}
#[test]
fn crt_many_single_and_empty() {
assert!(crt_many(&[]).is_none());
let (r, m) = crt_many(&[(b(7), b(11))]).unwrap();
assert_eq!(r, b(7));
assert_eq!(m, b(11));
let (r, _) = crt_many(&[(b(-4), b(11))]).unwrap();
assert_eq!(r, b(7));
}
#[test]
fn crt_many_inconsistent() {
assert!(crt_many(&[(b(1), b(4)), (b(2), b(4))]).is_none());
let cs = [(b(1), b(2)), (b(2), b(4)), (b(3), b(5))];
assert!(crt_many(&cs).is_none());
}
#[test]
fn crt_many_non_coprime() {
let (r, m) = crt_many(&[(b(1), b(4)), (b(3), b(6))]).unwrap();
assert_eq!(m, b(12));
assert_eq!(r, b(9));
}
#[test]
fn crt_many_recovers_value() {
let x = b(12345);
let moduli = [b(97), b(101), b(103), b(107)];
let cs: Vec<(Integer, Integer)> =
moduli.iter().map(|m| (x.mod_floor(m), m.clone())).collect();
let (r, m) = crt_many(&cs).unwrap();
let expected_m: Integer = moduli.iter().fold(b(1), |a, m| &a * m);
assert_eq!(m, expected_m);
assert_eq!(r, x.mod_floor(&m));
}
#[test]
fn crt_many_large_moduli() {
let x: Integer = "1234567890123456789012345678901234567890"
.parse::<num_bigint::BigInt>()
.unwrap()
.into();
let primes = [
1000003u64, 1000033, 1000037, 1000039, 1000081, 1000099, 1000117, 1000121,
];
let cs: Vec<(Integer, Integer)> = primes
.iter()
.map(|&p| {
let pb = Integer::new(p);
(x.mod_floor(&pb), pb)
})
.collect();
let (r, m) = crt_many(&cs).unwrap();
assert_eq!(r, x.mod_floor(&m));
assert_eq!(r, x); }
}