geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
//! Scalar tower exhaustive tests.
//!
//! Verifies exact arithmetic across all scalar types, promotion/demotion
//! boundaries, and ordering consistency.

use geoit::scalar::*;

// ═══════════════════════════════════════════════════════════
// RAT128
// ═══════════════════════════════════════════════════════════

#[test]
fn rat_add_large_denominators() {
    // 1/i64::MAX + 1/i64::MAX should work in i128
    let a = Rat::new(1, i64::MAX as i128);
    let b = Rat::new(1, i64::MAX as i128);
    let c = a + b;
    assert_eq!(c, Rat::new(2, i64::MAX as i128));
}

#[test]
fn rat_mul_near_boundary() {
    // Cross-reduction should prevent overflow
    let a = Rat::new(i64::MAX as i128, 7);
    let b = Rat::new(7, i64::MAX as i128);
    let c = a * b;
    assert_eq!(c, Rat::ONE);
}

#[test]
fn rat_ordering_comprehensive() {
    let values = vec![
        Rat::new(-100, 1),
        Rat::new(-1, 2),
        Rat::ZERO,
        Rat::new(1, 1000),
        Rat::new(1, 2),
        Rat::ONE,
        Rat::new(100, 1),
    ];
    for i in 0..values.len() {
        for j in (i + 1)..values.len() {
            assert!(
                values[i] < values[j],
                "{} should be < {}",
                values[i],
                values[j]
            );
        }
    }
}

#[test]
fn rat_arithmetic_identities() {
    let half = Rat::new(1, 2);
    let third = Rat::new(1, 3);
    // 1/2 + 1/3 = 5/6
    assert_eq!(half + third, Rat::new(5, 6));
    // 1/2 * 1/3 = 1/6
    assert_eq!(half * third, Rat::new(1, 6));
    // 1/2 / 1/3 = 3/2
    assert_eq!(half / third, Rat::new(3, 2));
    // -1/2 + 1/2 = 0
    assert_eq!(-half + half, Rat::ZERO);
}

// ═══════════════════════════════════════════════════════════
// BIGINT
// ═══════════════════════════════════════════════════════════

#[test]
fn bigint_from_i128_extremes() {
    let max = BigInt::from(i128::MAX);
    let min = BigInt::from(i128::MIN);
    assert_eq!(max.to_i128(), Some(i128::MAX));
    assert_eq!(min.to_i128(), Some(i128::MIN));
}

#[test]
fn bigint_arithmetic_beyond_i128() {
    let max = BigInt::from(i128::MAX);
    let one = BigInt::from(1i128);
    let beyond = max.add(&one);
    assert!(beyond.to_i128().is_none());
    // But subtracting 1 gets us back
    assert_eq!(beyond.sub(&one).to_i128(), Some(i128::MAX));
}

#[test]
fn bigint_mul_large() {
    // (2^64) * (2^64) = 2^128
    let a = BigInt::from(1u128 << 64);
    let b = BigInt::from(1u128 << 64);
    let c = a.mul(&b);
    assert!(c.to_i128().is_none()); // 2^128 doesn't fit
    assert!(c.is_positive());
}

#[test]
fn bigint_gcd_large() {
    let a = BigInt::from(i128::MAX);
    let b = BigInt::from(i128::MAX - 1);
    let g = a.gcd(&b);
    assert_eq!(g.to_i128(), Some(1)); // consecutive integers are coprime
}

#[test]
fn biguint_div_roundtrip_large() {
    let a = BigUint::from_u128(u128::MAX);
    let b = BigUint::from_u128(12345678901234567890u128);
    let (q, r) = a.div_rem(&b);
    let reconstructed = q.mul(&b).add(&r);
    assert_eq!(reconstructed, a);
}

// ═══════════════════════════════════════════════════════════
// BIGRAT
// ═══════════════════════════════════════════════════════════

#[test]
fn bigrat_promotion_demotion_cycle() {
    // Start with Rat, promote to BigRat, do arithmetic, demote back
    let r = Rat::new(355, 113);
    let b = BigRat::from_rat(r);
    let b2 = b.mul(&b);
    let b2_back = b2.to_rat();
    assert!(b2_back.is_some());
    let r2 = b2_back.unwrap();
    assert_eq!(r2, r * r);
}

#[test]
fn bigrat_stays_canonical() {
    let a = BigRat::from_i128_pair(6, 4);
    let b = BigRat::from_i128_pair(3, 2);
    assert_eq!(a, b);
}

// ═══════════════════════════════════════════════════════════
// SCALAR PROMOTION/DEMOTION
// ═══════════════════════════════════════════════════════════

#[test]
fn scalar_rat_plus_rat() {
    let a = Scalar::from(3i64);
    let b = Scalar::from(4i64);
    let c = a + b;
    assert!(matches!(c, Scalar::Rat(_)));
    assert_eq!(c.try_as_rat(), Some(Rat::from(7)));
}

#[test]
fn scalar_rat_plus_radical() {
    let a = Scalar::from(1i64);
    let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let c = a + b;
    assert!(matches!(c, Scalar::Radical(_)));
    assert!(!c.is_zero());
}

#[test]
fn scalar_overflow_promotes_to_big() {
    let big = Scalar::Rat(Rat::new(i128::MAX / 2 + 1, 1));
    let result = big.clone() + big;
    assert!(result.is_big());
}

#[test]
fn scalar_big_demotes_on_cancellation() {
    let a = Scalar::Big(BigRat::from_i128(42));
    let b = Scalar::Big(BigRat::from_i128(42));
    let c = a - b;
    assert!(c.is_zero());
    assert!(matches!(c, Scalar::Rat(_)));
}

#[test]
fn scalar_big_times_zero_is_zero() {
    let big = Scalar::Big(BigRat::from_i128(999));
    let zero = Scalar::from(0i64);
    let result = big * zero;
    assert!(result.is_zero());
}

// ═══════════════════════════════════════════════════════════
// SCALAR ORD
// ═══════════════════════════════════════════════════════════

#[test]
fn scalar_ord_rat_comprehensive() {
    let vals: Vec<Scalar> = vec![
        Scalar::from(-10i64),
        Scalar::from(-1i64),
        Scalar::from(0i64),
        Scalar::from(1i64),
        Scalar::from(10i64),
    ];
    for i in 0..vals.len() {
        for j in (i + 1)..vals.len() {
            assert!(vals[i] < vals[j], "{} should be < {}", vals[i], vals[j]);
        }
    }
}

#[test]
fn scalar_ord_transitivity() {
    let a = Scalar::from(-5i64);
    let b = Scalar::from(0i64);
    let c = Scalar::from(5i64);
    assert!(a < b && b < c && a < c);
}

#[test]
fn scalar_ord_antisymmetry() {
    let a = Scalar::from(3i64);
    let b = Scalar::from(7i64);
    assert!(a < b);
    assert!(b > a);
    assert!(!(a > b));
    assert!(!(b < a));
}

#[test]
fn scalar_ord_big_vs_rat_equal() {
    let big = Scalar::Big(BigRat::from_i128(42));
    let rat = Scalar::from(42i64);
    assert_eq!(big, rat);
    assert!(!(big < rat));
    assert!(!(big > rat));
}

#[test]
fn scalar_ord_radical_vs_rat() {
    // √2 ≈ 1.414, so 1 < √2 < 2
    let sqrt2 = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    assert!(Scalar::from(1i64) < sqrt2);
    assert!(sqrt2 < Scalar::from(2i64));
}

// ═══════════════════════════════════════════════════════════
// RADICAL TOWER INTEGRATION (via Scalar)
// ═══════════════════════════════════════════════════════════

#[test]
fn scalar_radical_cross_tower_add() {
    // √2 + √3 — the original motivating case
    let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(3)));
    let sum = a + b;
    assert!(!sum.is_zero());
    assert!(matches!(sum, Scalar::Radical(_)));
}

#[test]
fn scalar_radical_same_tower_cancels() {
    // (1+√2) + (2-√2) = 3
    use std::sync::Arc;
    let tower = Arc::new(RadicalTower::single(RadicalLayer::sqrt(Rat::from(2))));
    let a = Scalar::Radical(RadicalElement::new(
        vec![Coeff::ONE, Coeff::ONE],
        Arc::clone(&tower),
    ));
    let b = Scalar::Radical(RadicalElement::new(
        vec![Coeff::Rat(Rat::from(2)), Coeff::Rat(-Rat::ONE)],
        tower,
    ));
    let sum = a + b;
    assert_eq!(sum, Scalar::from(3i64));
}

#[test]
fn scalar_radical_cross_tower_mul() {
    // √2 * √3 = √6 — cross-tower multiplication
    let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(3)));
    let product = a * b;
    assert!(!product.is_zero());
    assert!(matches!(product, Scalar::Radical(_)));
}

#[test]
fn scalar_radical_from_trait() {
    let r = RadicalElement::sqrt(Rat::from(5));
    let s = Scalar::from(r);
    assert!(matches!(s, Scalar::Radical(_)));
    assert!(!s.is_zero());
}

#[test]
fn scalar_radical_rat_demotion() {
    // RadicalElement that's actually rational should demote to Scalar::Rat
    let r = RadicalElement::from_rat(Rat::from(42));
    let s = Scalar::from(r);
    assert_eq!(s, Scalar::from(42i64));
    assert!(matches!(s, Scalar::Rat(_)));
}

#[test]
fn scalar_radical_add_rat() {
    // 3 + √2 via Scalar arithmetic
    let three = Scalar::from(3i64);
    let sqrt2 = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let sum = three + sqrt2;
    assert!(!sum.is_zero());
    assert!(matches!(sum, Scalar::Radical(_)));
}

#[test]
fn scalar_radical_mul_produces_rat() {
    // √2 * √2 = 2 — should demote to Rat
    let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let product = a * b;
    assert_eq!(product, Scalar::from(2i64));
}

#[test]
fn scalar_radical_equality_cross_type() {
    // Scalar::Rat(3) == Scalar::Radical(from_rat(3))
    let a = Scalar::from(3i64);
    let b = Scalar::Radical(RadicalElement::from_rat(Rat::from(3)));
    assert_eq!(a, b);
}

#[test]
fn scalar_radical_ordering() {
    // √2 ≈ 1.414, so Rat(1) < Radical(√2) < Rat(2)
    let one = Scalar::from(1i64);
    let sqrt2 = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let two = Scalar::from(2i64);
    assert!(one < sqrt2);
    assert!(sqrt2 < two);
}

#[test]
fn scalar_radical_cross_tower_div() {
    // √2 / √3 — cross-tower division
    let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
    let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(3)));
    let quotient = a / b;
    assert!(!quotient.is_zero());
    assert!(matches!(quotient, Scalar::Radical(_)));
}