neberu 0.0.0

Exact geometric algebra from the balanced ternary axiom. Governed rewriting, self-certifying canonicalization via the Kase Optimality Theorem.
Documentation
// ============================================================
// GEN — TYPED GENERATOR
// ============================================================
//
// A generator is its Trit type plus a disambiguation index.
// Gen = (Trit, u32): self-relationship type + address.
//
// The Trit IS the type:
//   N → imaginary  (x*x = -1)
//   Z → degenerate (x*x =  0)
//   P → hyperbolic (x*x = +1)
//
// This closes the Gen=u32 concession permanently.
// signature(gen) = gen.sig — O(1), one field access.
// The type information is IN the generator, not in the governance lookup.

use crate::trit::{Trit, N, P, Z};

/// A typed generator: self-relationship type + index.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Gen {
    /// The square signature: what x*x produces.
    pub sig: Trit,
    /// Disambiguation index within generators of the same type.
    pub idx: u32,
}

impl Gen {
    #[inline]
    pub fn new(sig: Trit, idx: u32) -> Gen {
        Gen { sig, idx }
    }
    #[inline]
    pub fn imaginary(idx: u32) -> Gen {
        Gen { sig: N, idx }
    }
    #[inline]
    pub fn degenerate(idx: u32) -> Gen {
        Gen { sig: Z, idx }
    }
    #[inline]
    pub fn hyperbolic(idx: u32) -> Gen {
        Gen { sig: P, idx }
    }

    /// Is this generator valid (non-INV type)?
    pub fn is_valid(self) -> bool {
        self.sig.is_valid()
    }

    /// The square signature. One field access. O(1). No lookup.
    pub fn signature(self) -> Trit {
        self.sig
    }
}

impl PartialOrd for Gen {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for Gen {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        // Order by type first (N < Z < P), then by index.
        self.sig.0.cmp(&other.sig.0).then(self.idx.cmp(&other.idx))
    }
}

impl std::fmt::Debug for Gen {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let type_char = match self.sig {
            N => 'i',
            Z => 'd',
            P => 'h',
            _ => '?',
        };
        write!(f, "e{}{}", self.idx + 1, type_char)
    }
}

impl std::fmt::Display for Gen {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "e{}", self.idx + 1)
    }
}

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

    #[test]
    fn imaginary_signature() {
        assert_eq!(Gen::imaginary(0).signature(), N);
    }
    #[test]
    fn degenerate_signature() {
        assert_eq!(Gen::degenerate(0).signature(), Z);
    }
    #[test]
    fn hyperbolic_signature() {
        assert_eq!(Gen::hyperbolic(0).signature(), P);
    }
    #[test]
    fn ordering_by_type_then_index() {
        // N(0b01) < Z(0b00)? No: Z=0b00 < N=0b01 < P=0b10
        // So ordering: degenerate < imaginary < hyperbolic by raw Trit bits
        let d0 = Gen::degenerate(0);
        let n0 = Gen::imaginary(0);
        let p0 = Gen::hyperbolic(0);
        assert!(d0 < n0);
        assert!(n0 < p0);
        // Within same type, by index
        assert!(Gen::imaginary(0) < Gen::imaginary(1));
    }
    #[test]
    fn signature_is_one_field_access() {
        // This test documents the design: no lookup, no scan, just .sig
        let g = Gen::new(N, 3);
        assert_eq!(g.signature(), g.sig); // literally the same thing
    }
    #[test]
    fn relational_type() {
        // Same index, different algebra → different type.
        // Gen::imaginary(0) ≠ Gen::hyperbolic(0) even though idx is the same.
        assert_ne!(Gen::imaginary(0), Gen::hyperbolic(0));
        assert_ne!(
            Gen::imaginary(0).signature(),
            Gen::hyperbolic(0).signature()
        );
    }
}