amalie 0.1.2

Mathmatical library written for rust and python
Documentation
use crate::{ZZ, zz};
use std::collections::HashMap;


fn smod(a: impl AsRef<ZZ>, m: impl AsRef<ZZ>) -> ZZ {
    let a = a.as_ref();
    let m = m.as_ref();

    (a % m + m) % m
}

pub fn gcd(a: impl AsRef<ZZ>, b: impl AsRef<ZZ>) -> ZZ {
    let a = a.as_ref();
    let b = b.as_ref();

    a.gcd(b)
}
pub fn egcd(a: impl AsRef<ZZ>, b: impl AsRef<ZZ>) -> (ZZ, ZZ, ZZ) {
    let a = a.as_ref();
    let b = b.as_ref();

    a.egcd(b)
}

pub fn crt(v: impl AsRef<Vec<ZZ>>, m: impl AsRef<Vec<ZZ>>) -> (ZZ, ZZ) {
    let v = v.as_ref();
    let m = m.as_ref();

    assert_eq!(v.len(), m.len(), "v.len() != m.len()");

    let mut n = zz!(1);
    let mut x = zz!(0);
    for i in 0..v.len() { n *= &m[i]; }
    for i in 0..v.len() {
        let l = &n/&m[i];
        let (_, _, ty) = egcd(&m[i], &l);
        x += &v[i] * &ty * &l;
    }
    (smod(&x, &n), n)
}

pub fn mod_inv(g: impl AsRef<ZZ>, m: impl AsRef<ZZ>) -> Option<ZZ> {
    let m = m.as_ref();

    let (d, x, _) = egcd(g, m);
    if d == 1 {
        Some(smod(&x, m))
    } else {
        None
    }
}

pub fn totient(primes: impl AsRef<[ZZ]>) -> ZZ {
    let primes = primes.as_ref();


    let mut map = HashMap::new();
    for p in primes.iter() {
        let v = map.entry(p).or_insert(0);
        *v += 1;
    }

    let mut out = zz!(1); 
    for (prime, count) in map.iter() {
        out *= prime.pow(*count-zz!(1));
        out *= *prime - 1;
    }
    out
}

pub fn continued_fraction(x: impl AsRef<ZZ>, y: impl AsRef<ZZ>) -> Vec<(ZZ, ZZ)> {
    let mut x = x.as_ref().clone();
    let mut y = y.as_ref().clone();

    let mut a = &x/&y;
    let mut out = vec![(a.clone(), zz!(1))];

    while &(&a * &y) != &x {
        std::mem::swap(&mut x, &mut y);
        y = &y - &a * &x;
        a = &x / &y;

        if out.len() == 1 {
            let n = &a * &out[0].0 + 1;
            let d = &a * 1;
            out.push((n, d));
        }
        else if out.len() == 2 {
            let n = &a * &out[1].0 + &out[0].0;
            let d = &a * &out[1].1 + 1;
            out.push((n, d));

        }
        else {
            let n = &a * &out[out.len()-1].0 + &out[out.len()-2].0;
            let d = &a * &out[out.len()-1].1 + &out[out.len()-2].1;
            out.push((n, d));
        }
    }
    out
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_mod_inv() {
        let (g, m) = (zz!(123), zz!(937));
        assert_eq!(mod_inv(g, m).unwrap(), zz!(678));
    }

    #[test]
    fn test_crt() {
        let m = vec![zz!(678255406928205283764318788009),
			         zz!(206668822692514401698496953701), 
			         zz!(209389899037793911571890084709)];
        let v = vec![zz!(553595583601907102790863048168),
			         zz!(179936685740203889915724224774),
			         zz!(118574769100120356915050028243)];
        let sol = (zz!(11729333136918336599556614225262019449383649885811789858959155083082618000073636325423824), 
			         zz!(29351071308657422647975598374376392562718985574346423618982742115523312472944466642614081));

        assert_eq!(crt(&v, &m), sol);
    }

    #[test]
    fn test_totient() {
        let factors = &[zz!(61), zz!(61), zz!(2113), zz!(3624601)];

        assert_eq!(totient(factors), zz!(28017868032000));
    }

    #[test]
    fn test_continued_fraction() {
        assert_eq!(continued_fraction(zz!(123), zz!(2)), vec![zz!((61, 1)), zz!((123, 2))]);
        assert_eq!(continued_fraction(zz!(123127308098), zz!(202187)), vec![zz!((608977, 1)), zz!((1217955, 2)), zz!((1826932, 3)), zz!((4871819, 8)), zz!((35929665, 59)), zz!((256379474, 421)), zz!((292309139, 480)), zz!((2594852586, 4261)), zz!((28835687585, 47351)), zz!((31430540171, 51612)), zz!((123127308098, 202187))]);
    }
}