geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
use crate::algebra::blade_new::grade;
use crate::algebra::mv::Mv;
use crate::algebra::signature::Signature;
use crate::governance::construction::{Construction, ConstructionError};
use crate::governance::field::ProbeSpec;
use crate::governance::geom_class::GeomClass;
use crate::governance::reading::VariableMap;
use crate::governance::rule::TransformRule;
use crate::scalar::{Rat, Scalar};

/// The central data structure: grade constraints + polynomial equations
/// declared on a Signature. Free-standing — exists without any Mv.
///
/// Prefer building via `GovernanceBuilder` for the ergonomic API.
/// Fields are public for first-party crates (`geoit-lang`, `geoit-render`)
/// and advanced usage.
#[derive(Clone, Debug)]
pub struct Governance {
    pub sig: Signature,
    pub derived_gens: Vec<Mv>,
    pub geom_classes: Vec<GeomClass>,
    pub constructions: Vec<Construction>,
    /// Which construction builds probe points for rendering, if any.
    pub probe: Option<ProbeSpec>,
    /// v0.0.3: Declared transformation rules for compositional governance.
    pub transform_rules: Vec<TransformRule>,
}

impl Governance {
    /// Construct a Governance directly from its components.
    ///
    /// Prefer `GovernanceBuilder` for the ergonomic API.
    /// This constructor is for advanced usage, testing, and first-party crates.
    pub fn from_parts(
        sig: Signature,
        derived_gens: Vec<Mv>,
        geom_classes: Vec<GeomClass>,
        constructions: Vec<Construction>,
        probe: Option<ProbeSpec>,
    ) -> Self {
        Governance {
            sig,
            derived_gens,
            geom_classes,
            constructions,
            probe,
            transform_rules: vec![],
        }
    }

    /// The signature this governance is defined on.
    pub fn sig(&self) -> &Signature {
        &self.sig
    }

    /// Named derived generators (e.g., e_o, e_inf in CGA).
    pub fn derived_gens(&self) -> &[Mv] {
        &self.derived_gens
    }

    /// Number of geometric classes.
    pub fn num_classes(&self) -> usize {
        self.geom_classes.len()
    }

    /// Number of constructions.
    pub fn num_constructions(&self) -> usize {
        self.constructions.len()
    }

    /// Build an Mv from a construction formula.
    pub fn construct(
        &self,
        construction_index: usize,
        params: &[Scalar],
    ) -> Result<Mv, ConstructionError> {
        if construction_index >= self.constructions.len() {
            return Err(ConstructionError::IndexOutOfRange {
                index: construction_index,
                len: self.constructions.len(),
            });
        }
        self.constructions[construction_index].construct(
            params,
            &self.sig,
            &self.derived_gens,
            &self.constructions,
        )
    }

    /// Is the Mv valid for the given class?
    pub fn is_valid(&self, mv: &Mv, class_index: usize) -> bool {
        let class = &self.geom_classes[class_index];

        // Grade check
        for (mask, coeff) in mv.blades() {
            if !coeff.is_zero() && !class.grade_permitted(grade(mask)) {
                return false;
            }
        }

        // Equation check
        let var_map = VariableMap::for_grade_mask(&self.sig, class.grade_mask);
        let values: Vec<Rat> = var_map
            .var_to_mask
            .iter()
            .map(|&mask| mv.coefficient(mask).try_as_rat().unwrap_or(Rat::ZERO))
            .collect();

        for eq in &class.equations {
            if !eq.eval(&values).is_zero() {
                return false;
            }
        }

        // Inequality check
        for ineq in &class.inequalities {
            if ineq.eval(&values).is_zero() {
                return false;
            }
        }

        true
    }
}

impl Default for Governance {
    fn default() -> Self {
        Governance {
            sig: Signature::new(0, 0, 0).unwrap(),
            derived_gens: vec![],
            geom_classes: vec![],
            constructions: vec![],
            probe: None,
            transform_rules: vec![],
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::governance::expr::Expr;

    fn vga3_governance() -> Governance {
        Governance {
            sig: Signature::new(0, 0, 3).unwrap(),
            derived_gens: vec![],
            geom_classes: vec![GeomClass::grades_only(&[1])],
            constructions: vec![Construction {
                class_index: 0,
                arity: 3,
                body: Expr::Add(
                    Expr::add(
                        Expr::mul(Expr::param(0), Expr::gen(0)),
                        Expr::mul(Expr::param(1), Expr::gen(1)),
                    ),
                    Expr::mul(Expr::param(2), Expr::gen(2)),
                ),
            }],
            probe: None,
            transform_rules: vec![],
        }
    }

    #[test]
    fn governance_exists_without_mv() {
        let gov = vga3_governance();
        assert_eq!(gov.sig.n(), 3);
        assert_eq!(gov.geom_classes.len(), 1);
        assert_eq!(gov.constructions.len(), 1);
    }

    #[test]
    fn construct_and_validate() {
        let gov = vga3_governance();
        let params = vec![Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)];
        let mv = gov.construct(0, &params).unwrap();
        assert!(gov.is_valid(&mv, 0));
    }

    #[test]
    fn invalid_mv() {
        let gov = vga3_governance();
        let bv = Mv::from_rat_terms(&[(0b011, crate::scalar::Rat::from(1))]);
        assert!(!gov.is_valid(&bv, 0));
    }
}