yagi 0.1.3

Batteries-included DSP library
Documentation
use crate::error::{Error, Result};

const MAX_FACTORS: usize = 40;

/// Determine if number is prime (slow, simple method)
///
/// # Arguments
///
/// * `n` - Number to check for primality
///
/// # Returns
///
/// `true` if prime, `false` otherwise
pub fn is_prime(n: u32) -> bool {
    // check base cases (0, 1, 2, 3, divisible by 2, divisible by 3)
    if n <= 1 {
        return false;
    } else if n <= 3 {
        return true;
    } else if n % 2 == 0 {
        return false; // divisible by 2
    } else if n % 3 == 0 {
        return false; // divisible by 3
    }

    let mut r = 5;
    while r * r <= n {
        if n % r == 0 || n % (r + 2) == 0 {
            return false;
        }
        r += 6;
    }
    true
}

/// Compute number's prime factors
///
/// # Arguments
///
/// * `n` - Number to factor
/// * `factors` - Output slice of factors [size: MAX_FACTORS x 1]
///
/// # Returns
///
/// A tuple containing:
/// - The number of factors found
pub fn factor(n: u32, factors: &mut Vec<u32>) -> Result<usize> {
    let mut num = n;
    let mut num_factors = 0;

    while num > 1 && num_factors < MAX_FACTORS {
        for k in 2..=num {
            if num % k == 0 {
                // k factors n; append to list
                factors.push(k);
                num_factors += 1;
                num /= k;
                break;
            }
        }
    }

    if num > 1 && num_factors == MAX_FACTORS {
        return Err(Error::NoConvergence("could not factor number in MAX_FACTORS numbers".to_string()));
    }

    factors.shrink_to_fit();
    Ok(num_factors)
}

/// Compute number's unique prime factors
///
/// # Arguments
///
/// * `n` - Number to factor
/// * `factors` - Output slice of factors [size: MAX_FACTORS x 1]
///
/// # Returns
/// 
/// A tuple containing:
/// - The number of unique factors found
pub fn unique_factor(n: u32, factors: &mut Vec<u32>) -> Result<usize> {
    let mut num = n;
    let mut num_factors = 0;

    while num > 1 && num_factors < MAX_FACTORS {
        for k in 2..=num {
            if num % k == 0 {
                // k factors n; append to list
                if num_factors == 0 || factors[num_factors - 1] != k {
                    factors.push(k);
                    num_factors += 1;
                }

                num /= k;

                break;
            }
        }
    }

    if num > 1 && num_factors == MAX_FACTORS {
        return Err(Error::NoConvergence("could not factor number in MAX_FACTORS numbers".to_string()));
    }

    Ok(num_factors)
}

/// Compute greatest common divisor between two integers p and q
///
/// # Arguments
///
/// * `p` - First integer
/// * `q` - Second integer
///
/// # Returns
///
/// Greatest common divisor
pub fn gcd(mut p: u32, mut q: u32) -> Result<u32> {
    // check base cases
    if p == 0 || q == 0 {
        return Err(Error::Value("input cannot be zero".to_string()));
    } else if p == 1 || q == 1 {
        return Ok(1);
    } else if p == q {
        return Ok(p);
    } else if p < q {
        return gcd(q, p);
    }

    // dumb, slow method
    let mut gcd = 1;
    let mut r = 2; // root
    while r <= q {
        while p % r == 0 && q % r == 0 {
            p /= r;
            q /= r;
            gcd *= r;
        }
        r += if r == 2 { 1 } else { 2 };
    }
    
    Ok(gcd)
}

/// Compute c = base^exp (mod n)
///
/// # Arguments
///
/// * `base` - Base
/// * `exp` - Exponent
/// * `n` - Modulus
///
/// # Returns
///
/// Result of modular exponentiation
pub fn modpow(base: u32, exp: u32, n: u32) -> u32 {
    let mut c = 1;
    for _ in 0..exp {
        c = (c * base) % n;
    }
    c
}

/// Find smallest primitive root of n
///
/// # Arguments
///
/// * `n` - Number to find primitive root for
///
/// # Returns
///
/// Smallest primitive root
pub fn primitive_root(_n: u32) -> u32 {
    unimplemented!()
}

/// Find smallest primitive root of n (assuming n is prime)
///
/// # Arguments
///
/// * `n` - Prime number to find primitive root for
///
/// # Returns
///
/// Smallest primitive root
pub fn primitive_root_prime(n: u32) -> Result<u32> {
    // find unique factors of n-1
    let mut unique_factors = Vec::with_capacity(MAX_FACTORS);
    let num_unique_factors = unique_factor(n - 1, &mut unique_factors).unwrap();

    // search for minimum integer for which
    //   g^( (n-1)/m ) != 1 (mod n)
    // for all unique roots 'm'
    for g in 2..n {
        let mut is_root = true;
        for &factor in &unique_factors[0..num_unique_factors] {
            let e = (n - 1) / factor;
            if modpow(g, e, n) == 1 {
                // not a root
                is_root = false;
                break;
            }
        }

        if is_root {
            return Ok(g);
        }
    }

    // This should never happen for prime n
    Err(Error::Value("No primitive root found for prime".to_string()))
}

/// Euler's totient function
///
/// # Arguments
///
/// * `n` - Number to compute totient for
///
/// # Returns
///
/// Euler's totient
pub fn totient(x: u32) -> u32 {
    let mut t = x;
    let mut n = x;
    let mut p = 0;
    loop {
        for k in 2..=n {
            if n % k == 0 {
                n /= k;

                if p != k {
                    // factor is unique
                    t *= k - 1;
                    t /= k;
                }
                p = k;
                break;
            }
        }

        if n < 2 {
            break;
        }
    }

    t
}

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

    #[test]
    #[autotest_annotate(autotest_prime_small)]
    fn test_prime_small() {
        for (n, &expected_is_prime) in IS_PRIME_ARRAY.iter().enumerate() {
            assert_eq!(is_prime(n as u32), expected_is_prime != 0);
        }
    }

    #[test]
    #[autotest_annotate(autotest_factors)]
    fn test_factors() {
        const FACTORS_280: [u32; 5] = [2, 2, 2, 5, 7];
        const FACTORS_280_UNIQUE: [u32; 3] = [2, 5, 7];

        let mut factors = Vec::with_capacity(MAX_FACTORS);
        let num_factors = factor(280, &mut factors).unwrap();
        assert_eq!(num_factors, 5);
        assert_eq!(factors, FACTORS_280);

        let mut factors = Vec::with_capacity(MAX_FACTORS);
        let num_unique_factors = unique_factor(280, &mut factors).unwrap();
        assert_eq!(num_unique_factors, 3);
        assert_eq!(factors, FACTORS_280_UNIQUE);
    }

    #[test]
    #[autotest_annotate(autotest_totient)]
    fn test_totient() {
        assert_eq!(totient(9), 6);
        assert_eq!(totient(20), 8);
        assert_eq!(totient(100), 40);
        assert_eq!(totient(1200), 320);
        assert_eq!(totient(1201), 1200);
    }

    fn testbench_gcd(gcd_expected: u32, p: u32, q: u32) {
        let p = p * gcd_expected;
        let q = q * gcd_expected;
        let gcd_test = gcd(p, q).unwrap();
        
        assert_eq!(gcd_test, gcd_expected, "gcd({}, {}) = {} (expected {})", p, q, gcd_test, gcd_expected);
    }

    #[test]
    #[autotest_annotate(autotest_gcd_one)]
    fn test_gcd_one() {
        testbench_gcd(1, 2, 3);
        testbench_gcd(1, 2, 5);
        testbench_gcd(1, 2, 7);
        testbench_gcd(1, 7, 2);
        testbench_gcd(1, 17, 19);
        testbench_gcd(1, 23, 31);
        testbench_gcd(1, 2*2*2*2*2, 3*5*7*19);
    }

    #[test]
    #[autotest_annotate(autotest_gcd_edge_cases)]
    fn test_gcd_edge_cases() {
        testbench_gcd(1, 1, 1);
        testbench_gcd(1, 1, 2);
        testbench_gcd(1, 1, 2400);
        testbench_gcd(12345, 1, 1);    // P==Q
        testbench_gcd((1<<17)-1, 1, 1);    // P==Q
    }

    #[test]
    #[autotest_annotate(autotest_gcd_base)]
    fn test_gcd_base() {
        testbench_gcd(2*2*3*5*7, 2*3, 17);
        testbench_gcd(2*2*3*5*7, 2*3, 17*17);
        testbench_gcd(2*2*3*5*7, 2*3, 17*17*17);
        testbench_gcd(11, 3, 1);
        testbench_gcd(3, 127, 131);
        testbench_gcd(131, 127, 3);
        testbench_gcd(127, 131, 3);
    }

    #[test]
    fn test_modpow() {
        assert_eq!(modpow(2, 3, 5), 3);
        assert_eq!(modpow(3, 4, 7), 4);
    }

    #[test]
    fn test_primitive_root_prime() {
        assert_eq!(primitive_root_prime(5).unwrap(), 2);
        assert_eq!(primitive_root_prime(7).unwrap(), 3);
    }

    const IS_PRIME_ARRAY: [u8; 2500] = [
        0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,
		0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,
		0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,
		0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,
		0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,
		0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
		0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,
		0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,
		0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,
		0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,
		0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,
		0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,
		0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,
		0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,
		0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
		0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,
		0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,
		0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,
		0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,
		0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,
		0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,
		0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
		0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,
		0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,
		0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,
		0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
		0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,
		0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,
		0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,
		0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
		0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
		0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,
		0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,
		0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,
		0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,
		0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
		0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,
		0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,
		0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,
		0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,
		0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    ];
}