geoit 0.0.2

Exact geometric algebra with governed multivectors
Documentation
//! Governance category: a collection of governances connected by morphisms
//! that preserve class structure across algebra embeddings.

use crate::algebra::mv::Mv;
use crate::governance::composition::Embedding;
use crate::governance::geoit::Geoit;
use crate::governance::governance::Governance;
use crate::governance::phase;
use crate::governance::predicate::Predicate;
use crate::governance::profile::GeneratorProfile;
use crate::governance::reading::ReadingRules;
use crate::governance::rule::ProofTerm;
use crate::governance::GovernanceError;
use std::sync::Arc;

/// A governance-aware morphism: maps Mvs AND preserves class structure.
#[derive(Clone, Debug)]
pub struct GovernedMorphism {
    /// Human-readable label: "CGA(2)→CGA(3)", "VGA→CGA", etc.
    pub name: String,
    /// The underlying algebra embedding (generator-to-generator map).
    pub embedding: Embedding,
    /// Source governance index in the category.
    pub source_gov_index: usize,
    /// Target governance index in the category.
    pub target_gov_index: usize,
    /// Class preservation map: source class i maps to target class class_map[i].
    /// None means the class has no image.
    pub class_map: Vec<Option<usize>>,
}

impl GovernedMorphism {
    /// Embed an Mv from the source algebra into the target algebra.
    pub fn embed_mv(&self, mv: &Mv) -> Mv {
        self.embedding.embed_mv(mv)
    }
}

/// The category: a collection of governances connected by governed morphisms.
#[derive(Clone, Debug, Default)]
pub struct GovernanceCategory {
    /// All governances in the category, each wrapped in Arc for sharing.
    pub governances: Vec<Arc<Governance>>,
    /// Named governance entries for lookup.
    pub names: Vec<String>,
    /// All governed morphisms between them.
    pub morphisms: Vec<GovernedMorphism>,
}

impl GovernanceCategory {
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a governance to the category, returning its index.
    pub fn add_governance(&mut self, name: impl Into<String>, gov: Governance) -> usize {
        let idx = self.governances.len();
        self.governances.push(Arc::new(gov));
        self.names.push(name.into());
        idx
    }

    /// Add a governed morphism to the category.
    pub fn add_morphism(&mut self, morphism: GovernedMorphism) {
        self.morphisms.push(morphism);
    }

    /// Look up a governance by name.
    pub fn governance_by_name(&self, name: &str) -> Option<(usize, &Arc<Governance>)> {
        self.names
            .iter()
            .enumerate()
            .find(|(_, n)| n.as_str() == name)
            .map(|(i, _)| (i, &self.governances[i]))
    }

    /// Find a morphism from source to target governance indices.
    pub fn find_morphism(&self, source: usize, target: usize) -> Option<&GovernedMorphism> {
        self.morphisms
            .iter()
            .find(|m| m.source_gov_index == source && m.target_gov_index == target)
    }

    /// Find a morphism by name.
    pub fn morphism_by_name(&self, name: &str) -> Option<&GovernedMorphism> {
        self.morphisms.iter().find(|m| m.name == name)
    }

    /// Embed a Geoit through a governed morphism, producing a target-algebra Geoit.
    pub fn embed_geoit(
        &self,
        geoit: &Geoit,
        morphism_name: &str,
    ) -> Result<Geoit, GovernanceError> {
        let morph = self
            .morphism_by_name(morphism_name)
            .ok_or(GovernanceError::ClassOutOfRange { index: 0, len: 0 })?;

        let target_gov = &self.governances[morph.target_gov_index];

        // Map the Mv
        let target_mv = morph.embed_mv(geoit.mv());

        // Map the class
        let source_class = geoit.predicate().class_index;
        let target_class = morph.class_map.get(source_class).and_then(|c| *c).ok_or(
            GovernanceError::ClassOutOfRange {
                index: source_class,
                len: morph.class_map.len(),
            },
        )?;

        // Compute phase and profile in target algebra
        let ph = phase::compute_phase(&target_mv, &target_gov.sig);
        let prof = GeneratorProfile::compute(&target_mv, &target_gov.sig);

        // Derive readings in target
        let class = &target_gov.geom_classes[target_class];
        let readings = if let Some(constr) = target_gov
            .constructions
            .iter()
            .find(|c| c.class_index == target_class)
        {
            ReadingRules::derive_from_probing(constr, &target_gov.sig, &target_gov.derived_gens)
                .unwrap_or_else(|_| ReadingRules::derive_from_grade_mask(class, 0, &target_gov.sig))
        } else {
            ReadingRules::derive_from_grade_mask(class, 0, &target_gov.sig)
        };

        // Build predicate (derived via morphism, not re-checked)
        let pred = Predicate::new(
            target_class,
            &[],
            &[],
            vec![true; class.equations.len()],
            vec![true; class.inequalities.len()],
            false,
        );

        Ok(Geoit {
            mv: target_mv,
            governance: Arc::clone(target_gov),
            predicate: pred,
            phase: ph,
            readings,
            profile: prof,
            proof: ProofTerm::Derived {
                rule_name: format!("embed({})", morph.name),
                input_proofs: vec![Box::new(geoit.proof.clone())],
            },
        })
    }

    /// Compose two morphisms: if f: A→B and g: B→C, produce g∘f: A→C.
    pub fn compose_morphisms(&self, f_name: &str, g_name: &str) -> Option<GovernedMorphism> {
        let f = self.morphism_by_name(f_name)?;
        let g = self.morphism_by_name(g_name)?;

        // Verify composability: f.target == g.source
        if f.target_gov_index != g.source_gov_index {
            return None;
        }

        // Compose embeddings: for each source gen, f maps it to intermediate,
        // then g maps intermediate to target
        let source_sig = &f.embedding.source_sig;
        let target_sig = &g.embedding.target_sig;
        let composed_map: Vec<u8> = f
            .embedding
            .generator_map
            .iter()
            .map(|&mid_gen| g.embedding.generator_map[mid_gen as usize])
            .collect();

        let embedding = Embedding::new(*source_sig, *target_sig, composed_map).ok()?;

        // Compose class maps
        let class_map: Vec<Option<usize>> = f
            .class_map
            .iter()
            .map(|fc| fc.and_then(|mid_class| g.class_map.get(mid_class).and_then(|gc| *gc)))
            .collect();

        Some(GovernedMorphism {
            name: format!("{}{}", g_name, f_name),
            embedding,
            source_gov_index: f.source_gov_index,
            target_gov_index: g.target_gov_index,
            class_map,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn empty_category() {
        let cat = GovernanceCategory::new();
        assert_eq!(cat.governances.len(), 0);
        assert_eq!(cat.morphisms.len(), 0);
    }

    #[test]
    fn add_governance() {
        let mut cat = GovernanceCategory::new();
        let gov = Governance::default();
        let idx = cat.add_governance("test", gov);
        assert_eq!(idx, 0);
        assert!(cat.governance_by_name("test").is_some());
        assert!(cat.governance_by_name("missing").is_none());
    }
}