geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
use crate::algebra::mv::Mv;
use crate::algebra::signature::Signature;
use crate::governance::expr::{EvalContext, Expr};
use crate::scalar::Scalar;

/// A formula that produces an Mv from scalar parameters.
/// The Mv is intended to satisfy geom_classes\[class_index\].
#[derive(Clone, Debug)]
pub struct Construction {
    /// Which GeomClass this builds objects for.
    pub class_index: usize,
    /// Number of scalar inputs.
    pub arity: usize,
    /// The formula.
    pub body: Expr,
}

/// Error from construction.
#[derive(Clone, Debug)]
pub enum ConstructionError {
    ArityMismatch { expected: usize, got: usize },
    IndexOutOfRange { index: usize, len: usize },
}

impl Construction {
    /// Evaluate the construction formula with given parameters.
    pub fn construct(
        &self,
        params: &[Scalar],
        sig: &Signature,
        derived_gens: &[Mv],
        constructions: &[Construction],
    ) -> Result<Mv, ConstructionError> {
        if params.len() != self.arity {
            return Err(ConstructionError::ArityMismatch {
                expected: self.arity,
                got: params.len(),
            });
        }
        let ctx = EvalContext {
            params,
            sig,
            derived_gens,
            constructions,
            mv_table: &[],
            governances: &[],
            mv_governance_indices: &[],
            embeddings: &[],
            morphisms: &[],
            probe_mv: None,
            object_mv: None,
        };
        Ok(self.body.eval(&ctx))
    }
}

impl std::fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ConstructionError::ArityMismatch { expected, got } => write!(
                f,
                "arity mismatch: expected {} params, got {}",
                expected, got
            ),
            ConstructionError::IndexOutOfRange { index, len } => write!(
                f,
                "construction index {} out of range (have {})",
                index, len
            ),
        }
    }
}

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

    #[test]
    fn vga_vector_construction() {
        let sig = Signature::new(0, 0, 3).unwrap();
        let c = 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)),
            ),
        };

        let params = vec![Scalar::from(3i64), Scalar::from(4i64), Scalar::from(5i64)];
        let mv = c.construct(&params, &sig, &[], &[]).unwrap();
        assert_eq!(mv.coefficient(0b001), Scalar::from(3i64));
        assert_eq!(mv.coefficient(0b010), Scalar::from(4i64));
        assert_eq!(mv.coefficient(0b100), Scalar::from(5i64));
        assert_eq!(mv.len(), 3);
    }

    #[test]
    fn arity_mismatch() {
        let sig = Signature::new(0, 0, 3).unwrap();
        let c = Construction {
            class_index: 0,
            arity: 3,
            body: Expr::Generator(0),
        };
        let params = vec![Scalar::from(1i64)]; // only 1, need 3
        assert!(c.construct(&params, &sig, &[], &[]).is_err());
    }
}