use geoit::algebra::ops;
use geoit::algebra::*;
use geoit::codec;
use geoit::governance::{
Construction, Expr, ExtractionMap, FieldOp, GeneratorProfile, GeomClass, Governance, Phase,
Predicate, ProbeSpec, ProofTerm, ReadingRules,
};
use geoit::scalar::*;
use geoit::GeoitSnapshot;
#[test]
fn codec_bad_magic() {
let err = codec::decode_governance(b"BADXX\x01").unwrap_err();
assert!(format!("{}", err).contains("magic"));
}
#[test]
fn codec_truncated_empty() {
let err = codec::decode_governance(b"").unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("unexpected") || msg.contains("EOF") || msg.contains("end"));
}
#[test]
fn codec_truncated_after_magic() {
let err = codec::decode_governance(b"GEOIT").unwrap_err();
let msg = format!("{}", err);
assert!(
msg.contains("unexpected")
|| msg.contains("EOF")
|| msg.contains("end")
|| msg.contains("version")
);
}
#[test]
fn codec_wrong_type_decode() {
let gov = Governance {
sig: Signature::new(0, 0, 2).unwrap(),
derived_gens: vec![],
geom_classes: vec![],
constructions: vec![],
probe: None,
transform_rules: vec![],
};
let bytes = codec::encode_governance(&gov);
let _ = codec::decode(&bytes);
}
#[test]
fn codec_governance_with_equations() {
use geoit::governance::poly::Poly;
let mut eq = Poly::zero(3);
eq.add_term(vec![2, 0, 0], Rat::ONE);
eq.add_term(vec![0, 2, 0], Rat::ONE);
eq.add_term(vec![0, 0, 0], Rat::from(-1));
let gov = Governance {
sig: Signature::new(0, 0, 3).unwrap(),
derived_gens: vec![Mv::from_rat_terms(&[
(0b001, Rat::from(1)),
(0b010, Rat::from(1)),
])],
geom_classes: vec![GeomClass {
grade_mask: 0b010,
equations: vec![eq],
inequalities: vec![],
field_op: FieldOp::OuterProduct,
expected_profile: None,
}],
constructions: vec![Construction {
class_index: 0,
arity: 3,
body: Expr::Add(
Box::new(Expr::Mul(
Box::new(Expr::Param(0)),
Box::new(Expr::Generator(0)),
)),
Box::new(Expr::Mul(
Box::new(Expr::Param(1)),
Box::new(Expr::Generator(1)),
)),
),
}],
probe: Some(ProbeSpec {
construction_index: 0,
arity: 2,
}),
transform_rules: vec![],
};
let bytes = codec::encode_governance(&gov);
let decoded = codec::decode_governance(&bytes).unwrap();
assert_eq!(decoded.sig, gov.sig);
assert_eq!(decoded.derived_gens.len(), 1);
assert_eq!(decoded.geom_classes.len(), 1);
assert_eq!(decoded.geom_classes[0].equations.len(), 1);
assert_eq!(decoded.constructions.len(), 1);
assert_eq!(decoded.constructions[0].arity, 3);
assert!(decoded.probe.is_some());
}
#[test]
fn codec_snapshot_full_roundtrip() {
let snap = GeoitSnapshot {
mv: Mv::from_rat_terms(&[
(0b001, Rat::from(3)),
(0b010, Rat::from(4)),
(0b100, Rat::new(-1, 2)),
]),
governance: Governance {
sig: Signature::new(0, 0, 3).unwrap(),
derived_gens: vec![],
geom_classes: vec![GeomClass {
grade_mask: 0b010,
equations: vec![],
inequalities: vec![],
field_op: FieldOp::ScalarProduct,
expected_profile: None,
}],
constructions: vec![],
probe: None,
transform_rules: vec![],
},
predicate: Predicate::new(0, &[], &[], vec![true, true], vec![true], false),
phase: Phase::Commitment,
readings: ReadingRules {
extractions: vec![
ExtractionMap::Coefficient(0b001),
ExtractionMap::Coefficient(0b010),
ExtractionMap::Linear(vec![(0b100, Scalar::from(-2i64))]),
],
},
profile: GeneratorProfile::EMPTY,
proof: ProofTerm::Checked { class_index: 0 },
};
let bytes = codec::encode(&snap);
let decoded = codec::decode(&bytes).unwrap();
assert_eq!(decoded.mv, snap.mv);
assert_eq!(decoded.phase, Phase::Commitment);
assert_eq!(decoded.readings.extractions.len(), 3);
assert_eq!(decoded.predicate.equations_satisfied.len(), 2);
}
#[test]
fn codec_mv_scalar_variants() {
let mut mv = Mv::new();
mv.add_term(0b001, Scalar::Rat(Rat::new(355, 113)));
mv.add_term(0b010, Scalar::from(42i64));
let bytes = codec::encode_mv(&mv);
let decoded = codec::decode_mv(&bytes).unwrap();
assert_eq!(mv.coefficient(0b001), decoded.coefficient(0b001));
assert_eq!(mv.coefficient(0b010), decoded.coefficient(0b010));
}
#[test]
fn eval_error_param_out_of_range() {
use geoit::governance::expr::ContextShape;
let expr = Expr::Param(5);
let shape = ContextShape::for_construction(3, 0, 0);
let errors = expr.validate(&shape).unwrap_err();
assert_eq!(errors.len(), 1);
assert!(format!("{}", errors[0]).contains("Param(5)"));
}
#[test]
fn eval_error_derived_gen_out_of_range() {
use geoit::governance::expr::ContextShape;
let expr = Expr::DerivedGen(10);
let shape = ContextShape::for_construction(0, 2, 0);
let errors = expr.validate(&shape).unwrap_err();
assert!(format!("{}", errors[0]).contains("DerivedGen(10)"));
}
#[test]
fn eval_error_nested_multiple() {
use geoit::governance::expr::ContextShape;
let expr = Expr::Add(Box::new(Expr::Param(99)), Box::new(Expr::DerivedGen(99)));
let shape = ContextShape::for_construction(2, 2, 0);
let errors = expr.validate(&shape).unwrap_err();
assert_eq!(errors.len(), 2);
}
#[test]
fn eval_valid_expr_passes() {
use geoit::governance::expr::ContextShape;
let expr = Expr::Add(
Box::new(Expr::Mul(
Box::new(Expr::Param(0)),
Box::new(Expr::Generator(1)),
)),
Box::new(Expr::Mul(
Box::new(Expr::Param(1)),
Box::new(Expr::Generator(2)),
)),
);
let shape = ContextShape::for_construction(3, 0, 0);
assert!(expr.validate(&shape).is_ok());
}
#[test]
fn groebner_iteration_limit_format() {
use geoit::governance::GroebnerError;
let err = GroebnerError::IterationLimit {
pairs_processed: 100,
basis_size: 50,
};
let msg = format!("{}", err);
assert!(msg.contains("100"));
assert!(msg.contains("50"));
}
#[test]
fn radical_add_commutative() {
let a = RadicalElement::sqrt(Rat::from(2));
let b = RadicalElement::sqrt(Rat::from(3));
let ab = a.add(&b);
let ba = b.add(&a);
assert_eq!(ab, ba);
}
#[test]
fn radical_mul_commutative() {
let a = RadicalElement::sqrt(Rat::from(2));
let b = RadicalElement::sqrt(Rat::from(3));
let ab = a.mul(&b);
let ba = b.mul(&a);
assert_eq!(ab, ba);
}
#[test]
fn radical_add_associative() {
let a = RadicalElement::from_rat(Rat::from(3));
let b = RadicalElement::sqrt(Rat::from(2));
let c = RadicalElement::sqrt(Rat::from(5));
let ab_c = a.add(&b).add(&c);
let a_bc = a.add(&b.add(&c));
assert_eq!(ab_c, a_bc);
}
#[test]
fn radical_mul_distributes_over_add() {
let a = RadicalElement::sqrt(Rat::from(2));
let b = RadicalElement::from_rat(Rat::from(3));
let c = RadicalElement::from_rat(Rat::from(5));
let lhs = a.mul(&b.add(&c));
let rhs = a.mul(&b).add(&a.mul(&c));
assert_eq!(lhs, rhs);
}
#[test]
fn radical_additive_inverse() {
let a = RadicalElement::sqrt(Rat::from(7));
let neg_a = a.neg();
let zero = a.add(&neg_a);
assert!(zero.is_zero());
}
#[test]
fn radical_multiplicative_inverse() {
let a = RadicalElement::sqrt(Rat::from(3));
let inv = a.div(&a);
assert!(inv.is_rational());
assert_eq!(inv.to_rat(), Some(Rat::ONE));
}
#[test]
fn radical_rat_embeds_correctly() {
let r = RadicalElement::from_rat(Rat::new(7, 3));
assert!(r.is_rational());
assert_eq!(r.to_rat().unwrap(), Rat::new(7, 3));
}
#[test]
fn radical_zero_annihilates() {
let a = RadicalElement::sqrt(Rat::from(2));
let zero = RadicalElement::from_rat(Rat::ZERO);
let product = a.mul(&zero);
assert!(product.is_zero());
}
#[test]
fn radical_one_is_identity() {
let a = RadicalElement::sqrt(Rat::from(5));
let one = RadicalElement::from_rat(Rat::ONE);
let product = a.mul(&one);
assert_eq!(product, a);
}
#[test]
fn scalar_radical_neg_roundtrip() {
let s = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
let neg = -s.clone();
let sum = s + neg;
assert!(sum.is_zero());
}
#[test]
fn scalar_radical_sub() {
let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
let b = Scalar::Radical(RadicalElement::sqrt(Rat::from(2)));
let diff = a - b;
assert!(diff.is_zero());
}
#[test]
fn scalar_radical_div_self() {
let a = Scalar::Radical(RadicalElement::sqrt(Rat::from(3)));
let one = a.clone() / a;
assert_eq!(one, Scalar::from(1i64));
}
#[test]
fn bladekey_sym_diff_commutative() {
let a = BladeKey::from_sorted(&[0, 2, 4]);
let b = BladeKey::from_sorted(&[1, 2, 3]);
assert_eq!(
BladeKey::symmetric_difference(&a, &b),
BladeKey::symmetric_difference(&b, &a)
);
}
#[test]
fn bladekey_intersection_commutative() {
let a = BladeKey::from_sorted(&[0, 1, 3]);
let b = BladeKey::from_sorted(&[1, 2, 3]);
assert_eq!(
BladeKey::intersection(&a, &b),
BladeKey::intersection(&b, &a)
);
}
#[test]
fn bladekey_sym_diff_self_is_scalar() {
let a = BladeKey::from_sorted(&[0, 1, 2, 3]);
assert!(BladeKey::symmetric_difference(&a, &a).is_scalar());
}
#[test]
fn bladekey_generator_roundtrip_all() {
for k in 0..16u16 {
let bk = BladeKey::generator(k);
assert_eq!(bk.grade(), 1);
assert!(bk.contains_generator(k));
for j in 0..16u16 {
if j != k {
assert!(!bk.contains_generator(j));
}
}
}
}
#[test]
fn mv_blades_bk_matches_legacy() {
let mv = Mv::from_rat_terms(&[
(0b001, Rat::from(3)),
(0b010, Rat::from(4)),
(0b100, Rat::from(5)),
]);
let bk_count = mv.blades_bk().count();
let legacy_count = mv.blades().count();
assert_eq!(bk_count, legacy_count);
assert_eq!(bk_count, 3);
}
#[test]
fn mv_term_bk_equivalence() {
let mask_mv = Mv::term(0b101, Scalar::from(7i64));
let bk_mv = Mv::term_bk(BladeKey::from_sorted(&[0, 2]), Scalar::from(7i64));
assert_eq!(mask_mv, bk_mv);
}
#[test]
fn ga_scalar_commutes_with_everything() {
let sig = Signature::new(0, 0, 3).unwrap();
let s = Mv::scalar(Scalar::from(5i64));
let v = Mv::from_rat_terms(&[(0b001, Rat::from(3)), (0b010, Rat::from(4))]);
let sv = ops::geometric(&s, &v, &sig);
let vs = ops::geometric(&v, &s, &sig);
assert_eq!(sv, vs);
}
#[test]
fn ga_reverse_is_anti_automorphism() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(1)), (0b010, Rat::from(2))]);
let b = Mv::from_rat_terms(&[(0b010, Rat::from(3)), (0b100, Rat::from(1))]);
let ab = ops::geometric(&a, &b, &sig);
let rev_ab = ops::reverse(&ab);
let rev_b_rev_a = ops::geometric(&ops::reverse(&b), &ops::reverse(&a), &sig);
assert_eq!(rev_ab, rev_b_rev_a);
}
#[test]
fn ga_grade_involution_is_automorphism() {
let sig = Signature::new(0, 0, 3).unwrap();
let a = Mv::from_rat_terms(&[(0b001, Rat::from(2)), (0b010, Rat::from(3))]);
let b = Mv::from_rat_terms(&[(0b001, Rat::from(1)), (0b100, Rat::from(4))]);
let ab = ops::geometric(&a, &b, &sig);
let hat_ab = ops::grade_involution(&ab);
let hat_a_hat_b = ops::geometric(&ops::grade_involution(&a), &ops::grade_involution(&b), &sig);
assert_eq!(hat_ab, hat_a_hat_b);
}
#[test]
fn ga_inner_product_vector_bivector_grade() {
let sig = Signature::new(0, 0, 3).unwrap();
let v = Mv::from_rat_terms(&[(0b001, Rat::from(1))]);
let bv = Mv::from_rat_terms(&[(0b011, Rat::from(1))]);
let result = ops::inner(&v, &bv, &sig);
for (mask, coeff) in result.blades() {
if !coeff.is_zero() {
assert_eq!(geoit::algebra::blade_new::grade(mask), 1);
}
}
}