geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
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::{govern, Construction, Expr, GeomClass, Governance, Phase, VariableMap};
use geoit::scalar::{Rat, Scalar};

fn cga3_governance() -> Governance {
    let sig = Signature::new(1, 0, 4).unwrap();
    let grade_mask = 0b10u64; // grade 1

    let e_origin = Mv::from_rat_terms(&[(0b00001, Rat::new(1, 2)), (0b00010, Rat::new(1, 2))]);
    let e_infinity = Mv::from_rat_terms(&[(0b00001, Rat::from(-1)), (0b00010, Rat::from(1))]);
    let derived_gens = vec![e_origin, e_infinity.clone()];

    // Build polynomial constraints
    let var_map = VariableMap::for_grade_mask(&sig, grade_mask);
    let null_eq = norm_poly(&sig, grade_mask, var_map.num_vars, &var_map.mask_to_var);
    let ip_eq = inner_product_poly(
        &e_infinity,
        &sig,
        grade_mask,
        Rat::ONE,
        var_map.num_vars,
        &var_map.mask_to_var,
    );

    let point_class = GeomClass {
        grade_mask,
        equations: vec![null_eq, ip_eq],
        inequalities: vec![],
        field_op: FieldOp::default(),
        expected_profile: None,
    };

    // Construction
    let euclidean = Expr::Add(
        Expr::add(
            Expr::mul(Expr::param(0), Expr::gen(2)),
            Expr::mul(Expr::param(1), Expr::gen(3)),
        ),
        Expr::mul(Expr::param(2), Expr::gen(4)),
    );
    let r_squared = Expr::Add(
        Expr::add(
            Expr::mul(Expr::param(0), Expr::param(0)),
            Expr::mul(Expr::param(1), Expr::param(1)),
        ),
        Expr::mul(Expr::param(2), Expr::param(2)),
    );
    let neg_half_r_sq = Expr::mul(
        Box::new(Expr::Literal(Scalar::Rat(Rat::new(-1, 2)))),
        Box::new(r_squared),
    );
    let conformal = Expr::Mul(neg_half_r_sq, Expr::dgen(1));
    let body = Expr::Add(
        Box::new(Expr::Add(Box::new(euclidean), Box::new(conformal))),
        Expr::dgen(0),
    );

    Governance {
        sig,
        derived_gens,
        geom_classes: vec![point_class],
        constructions: vec![Construction {
            class_index: 0,
            arity: 3,
            body,
        }],
        probe: None,
        transform_rules: vec![],
    }
}

#[test]
fn cga_derived_gen_properties() {
    let gov = cga3_governance();
    let eo = &gov.derived_gens[0];
    let einf = &gov.derived_gens[1];
    assert!(ops::norm_squared(eo, &gov.sig).is_zero());
    assert!(ops::norm_squared(einf, &gov.sig).is_zero());
    assert_eq!(
        ops::scalar_product_value(eo, einf, &gov.sig),
        Scalar::from(1i64)
    );
}

#[test]
fn cga_point_is_null() {
    let gov = cga3_governance();
    let mv = gov
        .construct(
            0,
            &[Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)],
        )
        .unwrap();
    assert!(ops::norm_squared(&mv, &gov.sig).is_zero());
}

#[test]
fn cga_circuit_closes() {
    let gov = cga3_governance();
    let params = vec![Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)];
    let mv = gov.construct(0, &params).unwrap();
    let geoit = govern(&mv, &gov, 0).unwrap();
    assert!(geoit.is_satisfied());
    assert_eq!(geoit.phase(), Phase::Evaluation);
    assert_eq!(geoit.read_all().unwrap(), params);
}

#[test]
fn cga_circuit_origin() {
    let gov = cga3_governance();
    let params = vec![Scalar::from(0i64), Scalar::from(0i64), Scalar::from(0i64)];
    let mv = gov.construct(0, &params).unwrap();
    let geoit = govern(&mv, &gov, 0).unwrap();
    assert_eq!(geoit.read_all().unwrap(), params);
}

#[test]
fn cga_circuit_unit_points() {
    let gov = cga3_governance();
    for case in &[
        vec![1, 0, 0],
        vec![0, 1, 0],
        vec![0, 0, 1],
        vec![-1, 0, 0],
        vec![1, 1, 1],
    ] {
        let params: Vec<Scalar> = case.iter().map(|&n| Scalar::from(n as i64)).collect();
        let mv = gov.construct(0, &params).unwrap();
        let geoit = govern(&mv, &gov, 0).unwrap();
        assert_eq!(
            geoit.read_all().unwrap(),
            params,
            "CGA failed for {:?}",
            case
        );
    }
}

#[test]
fn cga_circuit_rational() {
    let gov = cga3_governance();
    let params = vec![
        Scalar::Rat(Rat::new(1, 2)),
        Scalar::Rat(Rat::new(1, 3)),
        Scalar::Rat(Rat::new(1, 7)),
    ];
    let mv = gov.construct(0, &params).unwrap();
    let geoit = govern(&mv, &gov, 0).unwrap();
    assert_eq!(geoit.read_all().unwrap(), params);
}

#[test]
fn cga_point_construction() {
    let gov = cga3_governance();
    let mv = gov
        .construct(
            0,
            &[Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)],
        )
        .unwrap();
    assert_eq!(mv.coefficient(0b00001), Scalar::Rat(Rat::new(51, 2)));
    assert_eq!(mv.coefficient(0b00010), Scalar::Rat(Rat::new(-49, 2)));
    assert_eq!(mv.coefficient(0b00100), Scalar::from(3i64));
}

#[test]
fn cga_point_normalization() {
    let gov = cga3_governance();
    let mv = gov
        .construct(
            0,
            &[Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)],
        )
        .unwrap();
    let ip = ops::scalar_product_value(&mv, &gov.derived_gens[1], &gov.sig);
    assert_eq!(ip, Scalar::from(1i64));
}

#[test]
fn cga_non_point_rejected() {
    let gov = cga3_governance();
    let mv = Mv::from_rat_terms(&[(0b00100, Rat::from(1))]);
    assert!(govern(&mv, &gov, 0).is_err());
}