clock-curve-math 1.1.3

High-performance, constant-time, cryptography-grade number theory library for ClockCurve ecosystem
Documentation
//! Comprehensive tests for extensible arithmetic operations.
//!
//! This module tests the generic field arithmetic framework, variable precision
//! operations, and advanced multiplication algorithms.

use clock_curve_math::BigInt;
use clock_curve_math::extensible::*;

#[test]
fn test_multiplication_algorithm_enum() {
    // Test all variants
    assert_eq!(
        MultiplicationAlgorithm::Standard,
        MultiplicationAlgorithm::Standard
    );
    assert_eq!(
        MultiplicationAlgorithm::Schoolbook,
        MultiplicationAlgorithm::Schoolbook
    );
    assert_eq!(
        MultiplicationAlgorithm::Karatsuba,
        MultiplicationAlgorithm::Karatsuba
    );
    assert_eq!(
        MultiplicationAlgorithm::ToomCook,
        MultiplicationAlgorithm::ToomCook
    );
    assert_eq!(MultiplicationAlgorithm::FFT, MultiplicationAlgorithm::FFT);

    // Test that Standard and Schoolbook are different variants (Schoolbook is an alias but separate variant)
    assert_ne!(
        MultiplicationAlgorithm::Standard,
        MultiplicationAlgorithm::Schoolbook
    );

    // Test Debug formatting
    assert!(format!("{:?}", MultiplicationAlgorithm::Karatsuba).contains("Karatsuba"));
}

#[test]
fn test_mul_karatsuba_standalone_basic() {
    // Test basic Karatsuba multiplication
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let result = mul_karatsuba_standalone(&a, &b);
    let expected = a.mul(&b);

    assert_eq!(result, expected);
}

#[test]
fn test_mul_karatsuba_standalone_edge_cases() {
    // Test with zeros
    let zero = BigInt::from_u64(0);
    let num = BigInt::from_u64(42);

    assert_eq!(mul_karatsuba_standalone(&zero, &num), BigInt::from_u64(0));
    assert_eq!(mul_karatsuba_standalone(&num, &zero), BigInt::from_u64(0));
    assert_eq!(mul_karatsuba_standalone(&zero, &zero), BigInt::from_u64(0));

    // Test with ones
    let one = BigInt::from_u64(1);
    assert_eq!(mul_karatsuba_standalone(&num, &one), num);
    assert_eq!(mul_karatsuba_standalone(&one, &num), num);
}

#[test]
fn test_mul_karatsuba_standalone_large_numbers() {
    // Test with larger numbers
    let a = BigInt::from_limbs(&[u64::MAX, u64::MAX, 0, 0]);
    let b = BigInt::from_limbs(&[u64::MAX, u64::MAX, 0, 0]);

    let result = mul_karatsuba_standalone(&a, &b);
    let expected = a.mul(&b);

    assert_eq!(result, expected);
}

#[test]
fn test_mul_toom_cook_standalone_basic() {
    // Test basic Toom-Cook multiplication
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let result = mul_toom_cook_standalone(&a, &b);
    let expected = a.mul(&b);

    assert_eq!(result, expected);
}

#[test]
fn test_mul_toom_cook_standalone_edge_cases() {
    // Test with zeros and ones
    let zero = BigInt::from_u64(0);
    let one = BigInt::from_u64(1);
    let num = BigInt::from_u64(42);

    assert_eq!(mul_toom_cook_standalone(&zero, &num), BigInt::from_u64(0));
    assert_eq!(mul_toom_cook_standalone(&num, &zero), BigInt::from_u64(0));
    assert_eq!(mul_toom_cook_standalone(&zero, &zero), BigInt::from_u64(0));

    assert_eq!(mul_toom_cook_standalone(&num, &one), num);
    assert_eq!(mul_toom_cook_standalone(&one, &num), num);
}

#[test]
fn test_mul_fft_standalone_basic() {
    // Test basic FFT multiplication
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let result = mul_fft_standalone(&a, &b);
    let expected = a.mul(&b);

    assert_eq!(result, expected);
}

#[test]
fn test_mul_fft_standalone_edge_cases() {
    // Test with zeros and ones
    let zero = BigInt::from_u64(0);
    let one = BigInt::from_u64(1);
    let num = BigInt::from_u64(42);

    assert_eq!(mul_fft_standalone(&zero, &num), BigInt::from_u64(0));
    assert_eq!(mul_fft_standalone(&num, &zero), BigInt::from_u64(0));
    assert_eq!(mul_fft_standalone(&zero, &zero), BigInt::from_u64(0));

    assert_eq!(mul_fft_standalone(&num, &one), num);
    assert_eq!(mul_fft_standalone(&one, &num), num);
}

#[test]
fn test_multiplication_algorithms_consistency() {
    // Test that all algorithms produce the same result
    let test_cases = vec![
        (BigInt::from_u64(123), BigInt::from_u64(456)),
        (BigInt::from_u64(789), BigInt::from_u64(123)),
        (
            BigInt::from_limbs(&[1, 2, 3, 0]),
            BigInt::from_limbs(&[4, 5, 6, 0]),
        ),
    ];

    for (a, b) in test_cases {
        let standard = a.mul(&b);
        let karatsuba = mul_karatsuba_standalone(&a, &b);
        let toom_cook = mul_toom_cook_standalone(&a, &b);
        let fft = mul_fft_standalone(&a, &b);

        assert_eq!(
            karatsuba, standard,
            "Karatsuba failed for {:?} * {:?}",
            a, b
        );
        assert_eq!(
            toom_cook, standard,
            "Toom-Cook failed for {:?} * {:?}",
            a, b
        );
        assert_eq!(fft, standard, "FFT failed for {:?} * {:?}", a, b);
    }
}

#[test]
fn test_multiplication_algorithms_commutativity() {
    // Test that multiplication is commutative for all algorithms
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let karatsuba_ab = mul_karatsuba_standalone(&a, &b);
    let karatsuba_ba = mul_karatsuba_standalone(&b, &a);
    assert_eq!(karatsuba_ab, karatsuba_ba);

    let toom_ab = mul_toom_cook_standalone(&a, &b);
    let toom_ba = mul_toom_cook_standalone(&b, &a);
    assert_eq!(toom_ab, toom_ba);

    let fft_ab = mul_fft_standalone(&a, &b);
    let fft_ba = mul_fft_standalone(&b, &a);
    assert_eq!(fft_ab, fft_ba);
}

#[test]
fn test_multiplication_algorithms_distributivity() {
    // Test distributivity: (a + b) * c = a*c + b*c
    let a = BigInt::from_u64(100);
    let b = BigInt::from_u64(200);
    let c = BigInt::from_u64(50);

    let left = (a.add(&b)).mul(&c);
    let right = a.mul(&c).add(&b.mul(&c));

    assert_eq!(left, right);

    // Test with Karatsuba
    let left_k = mul_karatsuba_standalone(&(a.add(&b)), &c);
    let right_k = mul_karatsuba_standalone(&a, &c).add(&mul_karatsuba_standalone(&b, &c));
    assert_eq!(left_k, right_k);
}

#[test]
fn test_mul_karatsuba_impl_consistency() {
    // Test that the implementation functions match standalone versions
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    // Note: mul_karatsuba_impl is not public, but we can test through other means
    let standalone = mul_karatsuba_standalone(&a, &b);
    let standard = a.mul(&b);

    assert_eq!(standalone, standard);
}

#[test]
fn test_mul_toom_cook_impl_consistency() {
    // Test that Toom-Cook implementation is consistent
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let standalone = mul_toom_cook_standalone(&a, &b);
    let standard = a.mul(&b);

    assert_eq!(standalone, standard);
}

#[test]
fn test_mul_fft_impl_consistency() {
    // Test that FFT implementation is consistent
    let a = BigInt::from_u64(12345);
    let b = BigInt::from_u64(67890);

    let standalone = mul_fft_standalone(&a, &b);
    let standard = a.mul(&b);

    assert_eq!(standalone, standard);
}

// Test the utils module functions
#[test]
fn test_utils_modular_inverse() {
    use clock_curve_math::extensible::utils;

    let modulus = BigInt::from_u64(17); // Prime modulus
    let value = BigInt::from_u64(3);

    // 3 * 6 = 18 ≡ 1 mod 17, so 6 is the inverse of 3
    let inverse = utils::mod_inverse(&value, &modulus);
    assert!(inverse.is_some());
    assert_eq!(inverse.unwrap(), BigInt::from_u64(6));

    // Test that value * inverse ≡ 1 mod modulus
    let product = value.mul(&inverse.unwrap());
    let remainder = product.div_rem(&modulus).1;
    assert_eq!(remainder, BigInt::from_u64(1));
}

#[test]
fn test_utils_modular_inverse_edge_cases() {
    use clock_curve_math::extensible::utils;

    let modulus = BigInt::from_u64(17);

    // Test with 1 (inverse should be 1)
    let inverse_1 = utils::mod_inverse(&BigInt::from_u64(1), &modulus);
    assert!(inverse_1.is_some());
    assert_eq!(inverse_1.unwrap(), BigInt::from_u64(1));

    // Test with modulus - 1
    let inverse_neg1 = utils::mod_inverse(&BigInt::from_u64(16), &modulus);
    assert!(inverse_neg1.is_some());
    assert_eq!(inverse_neg1.unwrap(), BigInt::from_u64(16)); // -1 * -1 = 1 mod 17

    // Test with number that doesn't have an inverse (shares factor with modulus)
    let no_inverse = utils::mod_inverse(&BigInt::from_u64(4), &BigInt::from_u64(6));
    assert!(no_inverse.is_none());
}

#[test]
fn test_utils_modular_exponentiation() {
    use clock_curve_math::extensible::utils;

    let base = BigInt::from_u64(2);
    let exp = BigInt::from_u64(10);
    let modulus = BigInt::from_u64(1000);

    let result = utils::mod_pow(&base, &exp, &modulus);
    assert_eq!(result, BigInt::from_u64(24)); // 2^10 = 1024, 1024 mod 1000 = 24
}

#[test]
fn test_utils_modular_exponentiation_edge_cases() {
    use clock_curve_math::extensible::utils;

    let modulus = BigInt::from_u64(17);

    // Test base^0 = 1
    let result = utils::mod_pow(&BigInt::from_u64(5), &BigInt::from_u64(0), &modulus);
    assert_eq!(result, BigInt::from_u64(1));

    // Test 1^exp = 1
    let result = utils::mod_pow(&BigInt::from_u64(1), &BigInt::from_u64(42), &modulus);
    assert_eq!(result, BigInt::from_u64(1));

    // Test exp = 1
    let result = utils::mod_pow(&BigInt::from_u64(5), &BigInt::from_u64(1), &modulus);
    assert_eq!(result, BigInt::from_u64(5));
}

#[test]
fn test_algorithms_performance_characteristics() {
    // Test that algorithms handle various input sizes appropriately
    // This is more of a smoke test than a performance benchmark

    let small_a = BigInt::from_u64(123);
    let small_b = BigInt::from_u64(456);

    let medium_a = BigInt::from_limbs(&[u64::MAX, u64::MAX, 0, 0]);
    let medium_b = BigInt::from_limbs(&[u64::MAX, u64::MAX, 0, 0]);

    // All algorithms should work for small inputs
    let _ = mul_karatsuba_standalone(&small_a, &small_b);
    let _ = mul_toom_cook_standalone(&small_a, &small_b);
    let _ = mul_fft_standalone(&small_a, &small_b);

    // All algorithms should work for medium inputs
    let _ = mul_karatsuba_standalone(&medium_a, &medium_b);
    let _ = mul_toom_cook_standalone(&medium_a, &medium_b);
    let _ = mul_fft_standalone(&medium_a, &medium_b);
}

#[test]
fn test_multiplication_algorithm_debug_formatting() {
    // Test that algorithms can be debug formatted
    let algo = MultiplicationAlgorithm::Karatsuba;
    let debug_str = format!("{:?}", algo);
    assert!(debug_str.contains("Karatsuba"));

    let algo2 = MultiplicationAlgorithm::ToomCook;
    let debug_str2 = format!("{:?}", algo2);
    assert!(debug_str2.contains("ToomCook"));
}

#[test]
fn test_multiplication_algorithm_equality() {
    // Test equality and inequality
    assert_eq!(
        MultiplicationAlgorithm::Standard,
        MultiplicationAlgorithm::Standard
    );
    assert_ne!(
        MultiplicationAlgorithm::Standard,
        MultiplicationAlgorithm::Schoolbook
    );
    assert_ne!(
        MultiplicationAlgorithm::Karatsuba,
        MultiplicationAlgorithm::ToomCook
    );
    assert_ne!(
        MultiplicationAlgorithm::FFT,
        MultiplicationAlgorithm::Standard
    );
}

#[test]
fn test_multiplication_algorithm_clone() {
    // Test Clone trait
    let algo = MultiplicationAlgorithm::Karatsuba;
    let cloned = algo.clone();
    assert_eq!(algo, cloned);
}

#[test]
fn test_multiplication_algorithm_copy() {
    // Test Copy trait
    let algo = MultiplicationAlgorithm::FFT;
    let copied = algo;
    assert_eq!(algo, copied);
}