geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
//! Governance structural tests: dimension, phase prediction, FieldOp evaluation,
//! triangular decomposition correctness.

use ::geoit::algebra::mv::Mv;
use ::geoit::algebra::ops;
use ::geoit::algebra::signature::Signature;
use ::geoit::governance::field::FieldOp;
use ::geoit::governance::geom_class::{inner_product_poly, norm_poly};
use ::geoit::governance::groebner;
use ::geoit::governance::phase::compute_phase;
use ::geoit::governance::poly::Poly;
use ::geoit::governance::reading::VariableMap;
use ::geoit::governance::triangular;
use ::geoit::governance::Expr;
use ::geoit::governance::Phase;
use ::geoit::scalar::{Rat, Scalar};
use ::geoit::{Algebra, GeomClassBuilder, GovernanceBuilder};

// ═══════════════════════════════════════════════════════════
// PHASE COMPUTATION
// ═══════════════════════════════════════════════════════════

#[test]
fn phase_vga_vector_commitment() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let v = Mv::from_rat_terms(&[(0b001, Rat::from(3)), (0b010, Rat::from(4))]);
    assert_eq!(compute_phase(&v, &sig), Phase::Commitment);
}

#[test]
fn phase_pga_evaluation() {
    // Any Mv touching a degenerate generator → Evaluation
    let sig = Signature::new(0, 1, 3).unwrap();
    let v = Mv::from_rat_terms(&[(0b0001, Rat::from(1))]);
    assert_eq!(compute_phase(&v, &sig), Phase::Evaluation);
}

#[test]
fn phase_negative_norm_deliberation() {
    // In Cl(1,0,0): e₀² = -1, norm²(e₀) = -1 → Deliberation
    let sig = Signature::new(1, 0, 0).unwrap();
    let v = Mv::generator(0);
    assert_eq!(compute_phase(&v, &sig), Phase::Deliberation);
}

#[test]
fn phase_null_vector_evaluation() {
    // In Cl(1,0,1): e₀+e₁ has norm² = -1+1 = 0 → Evaluation
    let sig = Signature::new(1, 0, 1).unwrap();
    let v = Mv::from_rat_terms(&[(0b01, Rat::from(1)), (0b10, Rat::from(1))]);
    assert_eq!(compute_phase(&v, &sig), Phase::Evaluation);
}

#[test]
fn phase_zero_mv_evaluation() {
    let sig = Signature::new(0, 0, 3).unwrap();
    assert_eq!(compute_phase(&Mv::new(), &sig), Phase::Evaluation);
}

#[test]
fn phase_scalar_commitment() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let s = Mv::from_rat_terms(&[(0, Rat::from(5))]);
    assert_eq!(compute_phase(&s, &sig), Phase::Commitment);
}

// ═══════════════════════════════════════════════════════════
// FIELD OP EVALUATION
// ═══════════════════════════════════════════════════════════

#[test]
fn field_op_ipns_parallel() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let probe = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
    let object = Mv::from_rat_terms(&[(0b001, Rat::from(3))]);
    let result = FieldOp::ScalarProduct.evaluate_mv(&probe, &object, &sig);
    assert_eq!(result.coefficient(0), Scalar::from(3));
}

#[test]
fn field_op_ipns_orthogonal() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let probe = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
    let object = Mv::from_rat_terms(&[(0b010, Rat::from(1))]);
    assert!(FieldOp::ScalarProduct
        .evaluate_mv(&probe, &object, &sig)
        .is_zero());
}

#[test]
fn field_op_opns_on_surface() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let v = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
    // v ∧ v = 0 → probe on its own surface
    assert!(FieldOp::OuterProduct.is_on_surface(&v, &v, &sig));
}

#[test]
fn field_op_opns_off_surface() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let a = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
    let b = Mv::from_rat_terms(&[(0b010, Rat::from(1))]);
    assert!(!FieldOp::OuterProduct.is_on_surface(&a, &b, &sig));
}

#[test]
fn field_op_left_contraction_returns_mv() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let probe = Mv::from_rat_terms(&[(0b001, Rat::from(1))]); // e0
    let object = Mv::from_rat_terms(&[(0b011, Rat::from(1))]); // e01
    let result = FieldOp::LeftContraction.evaluate_mv(&probe, &object, &sig);
    // e0 ⌋ e01 = e1
    assert!(!result.is_zero());
    assert_eq!(result.coefficient(0b010), Scalar::from(1));
}

#[test]
fn field_op_all_variants_dont_panic() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let a = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
    let b = Mv::from_rat_terms(&[(0b010, Rat::from(2))]);
    let _ = FieldOp::ScalarProduct.evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::OuterProduct.evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::LeftContraction.evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::InnerProduct.evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::GeometricProduct.evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::GradeProduct(0).evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::GradeProduct(1).evaluate_mv(&a, &b, &sig);
    let _ = FieldOp::GradeProduct(2).evaluate_mv(&a, &b, &sig);
}

// ═══════════════════════════════════════════════════════════
// TRIANGULAR DECOMPOSITION
// ═══════════════════════════════════════════════════════════

#[test]
fn triangular_pure_linear() {
    // x + y = 3, x - y = 1 → fully determined
    let mut eq1 = Poly::zero(2);
    eq1.add_term(vec![1, 0], Rat::from(1));
    eq1.add_term(vec![0, 1], Rat::from(1));
    eq1.add_term(vec![0, 0], Rat::from(-3));
    let mut eq2 = Poly::zero(2);
    eq2.add_term(vec![1, 0], Rat::from(1));
    eq2.add_term(vec![0, 1], Rat::from(-1));
    eq2.add_term(vec![0, 0], Rat::from(-1));
    let ts = triangular::triangular_decompose(&[eq1, eq2]).unwrap();
    assert_eq!(ts.free_vars.len(), 0);
    assert_eq!(ts.polys.len(), 2);
}

#[test]
fn triangular_underdetermined() {
    // x + y + z = 1 → one equation, 3 vars → 2 free
    let mut eq = Poly::zero(3);
    eq.add_term(vec![1, 0, 0], Rat::from(1));
    eq.add_term(vec![0, 1, 0], Rat::from(1));
    eq.add_term(vec![0, 0, 1], Rat::from(1));
    eq.add_term(vec![0, 0, 0], Rat::from(-1));
    let ts = triangular::triangular_decompose(&[eq]).unwrap();
    assert_eq!(ts.free_vars.len(), 2);
    assert_eq!(ts.polys.len(), 1);
}

#[test]
fn triangular_dimension_matches_groebner() {
    // CGA2 Point: 4 vars, 2 equations → dimension should be 2
    let sig = Signature::new(1, 0, 3).unwrap();
    let gm = 0b10u64; // grade 1
    let vm = VariableMap::for_grade_mask(&sig, gm);
    let einf = Mv::from_rat_terms(&[(0b0001, Rat::from(-1)), (0b0010, Rat::from(1))]);
    let null_eq = norm_poly(&sig, gm, vm.num_vars, &vm.mask_to_var);
    let ip_eq = inner_product_poly(&einf, &sig, gm, Rat::ONE, vm.num_vars, &vm.mask_to_var);

    let dim_tri = triangular::dimension(&[null_eq.clone(), ip_eq.clone()], vm.num_vars);
    let dim_grob = {
        let basis = groebner::groebner_basis(vec![null_eq, ip_eq]).unwrap();
        groebner::free_variables(&basis, vm.num_vars).len()
    };
    assert_eq!(
        dim_tri, dim_grob,
        "triangular and Gröbner should agree on dimension"
    );
    assert_eq!(dim_tri, 2, "CGA2 Point should have dimension 2");
}

// ═══════════════════════════════════════════════════════════
// OVERFLOW BOUNDARY
// ═══════════════════════════════════════════════════════════

#[test]
fn overflow_in_mv_product() {
    let sig = Signature::new(0, 0, 3).unwrap();
    let big = i64::MAX as i64;
    let a = Mv::from_rat_terms(&[(0b001, Rat::from(big))]);
    let b = Mv::from_rat_terms(&[(0b001, Rat::from(big))]);
    let product = ops::geometric(&a, &b, &sig);
    let expected = Scalar::Rat(Rat::new((big as i128) * (big as i128), 1));
    assert_eq!(product.coefficient(0), expected);
}

#[test]
fn governance_with_large_coords() {
    let mut alg = Algebra::new(Signature::new(1, 0, 3).unwrap());
    let eo = Mv::from_rat_terms(&[(0b0001, Rat::new(1, 2)), (0b0010, Rat::new(1, 2))]);
    let einf = Mv::from_rat_terms(&[(0b0001, Rat::from(-1)), (0b0010, Rat::from(1))]);
    alg.add_derived("eo", eo);
    alg.add_derived("einf", einf.clone());
    let class = GeomClassBuilder::new(&alg)
        .grades(&[1])
        .null_constraint()
        .normalization(&einf, 1)
        .build();
    let eucl = Expr::Add(
        Expr::mul(Expr::param(0), Expr::gen(2)),
        Expr::mul(Expr::param(1), Expr::gen(3)),
    );
    let r_sq = Expr::Add(
        Expr::mul(Expr::param(0), Expr::param(0)),
        Expr::mul(Expr::param(1), Expr::param(1)),
    );
    let neg_half_r2 = Expr::mul(
        Box::new(Expr::Literal(Scalar::Rat(Rat::new(-1, 2)))),
        Box::new(r_sq),
    );
    let conformal = Expr::Mul(neg_half_r2, Expr::dgen(1));
    let body = Expr::Add(
        Box::new(Expr::Add(Box::new(eucl), Box::new(conformal))),
        Expr::dgen(0),
    );
    let gov = GovernanceBuilder::new(alg)
        .class("Point", class)
        .construction("Point", "Point", 2, body)
        .build();

    let params = vec![Scalar::from(10000i64), Scalar::from(10000i64)];
    let mv = gov.construct("Point", &params).unwrap();
    let geoit = gov.govern(&mv, "Point").unwrap();
    assert!(geoit.is_satisfied());
    let extracted = geoit.read_all().unwrap();
    assert_eq!(extracted, params);
}