neberu 0.0.0

Exact geometric algebra from the balanced ternary axiom. Governed rewriting, self-certifying canonicalization via the Kase Optimality Theorem.
Documentation
// ============================================================
// TRIT — THE AXIOM
// ============================================================
//
// T = { Z, N, P } with multiplication.
// This is the irreducible primitive. Everything else is derived.
//
// Bit encoding (2 bits per trit):
//   00 = Z   (zero / annihilator)       — memset-safe, falsy, propagates via AND
//   01 = N   (negative / imaginary)
//   10 = P   (positive / hyperbolic)
//   11 = INV (invalid / NaN / sentinel) — propagates via OR
//
// The encoding was chosen so that:
//   - A packed word of all-zeros is all-Z (additive identity, memset-safe)
//   - INV (11) propagates via OR across packed words
//   - Z annihilates via AND across packed words
//   - Negation is a clean bitwise operation on non-zero valid pairs
//
// The multiplication table IS the axiom.
// Every other operation in this system derives from it.

// Named arithmetic methods (neg, mul) are intentional — the operator
// trait impls delegate to them, enabling chained exact arithmetic.
#![allow(clippy::should_implement_trait)]

/// A single balanced ternary trit.
/// Only the lower 2 bits are used. Upper 6 bits are always zero.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Trit(pub(crate) u8);

// The three valid states and the sentinel.
pub const Z: Trit = Trit(0b00); // zero / annihilator
pub const N: Trit = Trit(0b01); // negative (-1)
pub const P: Trit = Trit(0b10); // positive (+1)
pub const INV: Trit = Trit(0b11); // invalid — NaN sentinel

impl Trit {
    /// Is this a valid (non-INV) trit?
    #[inline]
    pub fn is_valid(self) -> bool {
        self.0 != 0b11
    }

    /// Is this the zero trit?
    #[inline]
    pub fn is_zero(self) -> bool {
        self.0 == 0b00
    }

    /// Is this the invalid sentinel?
    #[inline]
    pub fn is_inv(self) -> bool {
        self.0 == 0b11
    }

    /// Negation: N↔P, Z stays Z, INV stays INV.
    #[inline]
    pub fn neg(self) -> Trit {
        // N=01 → P=10, P=10 → N=01: swap the two bits for non-zero valid trits.
        // Z=00 → 00 (unchanged), INV=11 → 11 (unchanged).
        // Only 01 and 10 need swapping; both have exactly one bit set.
        match self.0 {
            0b01 => P,
            0b10 => N,
            _ => self, // Z and INV unchanged
        }
    }

    /// THE MULTIPLICATION TABLE — the axiom.
    ///
    /// ```text
    ///      Z   N   P  INV
    ///  Z [ Z   Z   Z  INV ]   Z annihilates (except INV wins)
    ///  N [ Z   P   N  INV ]   N*N=P (sign rule)
    ///  P [ Z   N   P  INV ]   P is identity
    /// INV[INV INV INV INV ]   INV propagates
    /// ```
    ///
    /// INV propagation: if either operand is INV, result is INV.
    /// Z annihilation: if either operand is Z (and neither is INV), result is Z.
    /// Sign product: N*N=P, N*P=N, P*N=N, P*P=P — the sign rule.
    #[inline]
    pub fn mul(self, rhs: Trit) -> Trit {
        // INV propagates: check via OR on raw bits (11 | anything ≥ 11 for relevant cases)
        // More precisely: if either is 11, return 11.
        if self.0 == 0b11 || rhs.0 == 0b11 {
            return INV;
        }
        // Z annihilates.
        if self.0 == 0b00 || rhs.0 == 0b00 {
            return Z;
        }
        // Both are N(01) or P(10). Sign product.
        // P*P=P, N*N=P, N*P=N, P*N=N.
        // High bit of result: high_a XOR high_b.
        // In our encoding: P=10 (high=1,low=0), N=01 (high=0,low=1).
        // high(P)=1, high(N)=0. Product is P iff same sign.
        // Same sign: (self == rhs). In bits: self.0 == rhs.0.
        if self.0 == rhs.0 {
            P
        } else {
            N
        }
    }

    /// The sign of this trit (same as the trit itself — a trit IS a sign).
    #[inline]
    pub fn sign(self) -> Trit {
        self
    }

    /// Convert to i8. Returns None for INV.
    pub fn to_i8(self) -> Option<i8> {
        match self.0 {
            0b00 => Some(0),
            0b01 => Some(-1),
            0b10 => Some(1),
            0b11 => None,
            _ => None, // unreachable: Trit only uses lower 2 bits
        }
    }

    /// From i8. Only -1, 0, 1 are valid.
    pub fn from_i8(v: i8) -> Trit {
        match v {
            -1 => N,
            0 => Z,
            1 => P,
            _ => INV,
        }
    }

    /// The raw 2-bit encoding.
    #[inline]
    pub fn bits(self) -> u8 {
        self.0
    }
}

impl std::ops::Mul for Trit {
    type Output = Trit;
    #[inline]
    fn mul(self, rhs: Trit) -> Trit {
        Trit::mul(self, rhs)
    }
}

impl std::ops::Neg for Trit {
    type Output = Trit;
    #[inline]
    fn neg(self) -> Trit {
        Trit::neg(self)
    }
}

impl std::fmt::Debug for Trit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.0 {
            0b00 => write!(f, "Z"),
            0b01 => write!(f, "N"),
            0b10 => write!(f, "P"),
            0b11 => write!(f, "INV"),
            _ => write!(f, "?"),
        }
    }
}

impl std::fmt::Display for Trit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.0 {
            0b00 => write!(f, "0"),
            0b01 => write!(f, "-"),
            0b10 => write!(f, "+"),
            0b11 => write!(f, "!"),
            _ => write!(f, "?"),
        }
    }
}

// ── Tests ────────────────────────────────────────────────────────────────────

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

    // THE TRUTH TABLE — if these 9 pass, the axiom is correct.
    #[test]
    fn mul_z_z() {
        assert_eq!(Z * Z, Z);
    }
    #[test]
    fn mul_z_n() {
        assert_eq!(Z * N, Z);
    }
    #[test]
    fn mul_z_p() {
        assert_eq!(Z * P, Z);
    }
    #[test]
    fn mul_n_z() {
        assert_eq!(N * Z, Z);
    }
    #[test]
    fn mul_n_n() {
        assert_eq!(N * N, P);
    } // THE sign rule
    #[test]
    fn mul_n_p() {
        assert_eq!(N * P, N);
    }
    #[test]
    fn mul_p_z() {
        assert_eq!(P * Z, Z);
    }
    #[test]
    fn mul_p_n() {
        assert_eq!(P * N, N);
    }
    #[test]
    fn mul_p_p() {
        assert_eq!(P * P, P);
    } // P is multiplicative identity

    // INV propagation
    #[test]
    fn inv_propagates_left() {
        assert_eq!(INV * P, INV);
    }
    #[test]
    fn inv_propagates_right() {
        assert_eq!(P * INV, INV);
    }
    #[test]
    fn inv_times_z() {
        assert_eq!(INV * Z, INV);
    }
    #[test]
    fn inv_times_inv() {
        assert_eq!(INV * INV, INV);
    }

    // Negation
    #[test]
    fn neg_n() {
        assert_eq!(-N, P);
    }
    #[test]
    fn neg_p() {
        assert_eq!(-P, N);
    }
    #[test]
    fn neg_z() {
        assert_eq!(-Z, Z);
    }
    #[test]
    fn neg_inv() {
        assert_eq!(-INV, INV);
    }
    #[test]
    fn double_neg_n() {
        assert_eq!(-(-N), N);
    }
    #[test]
    fn double_neg_p() {
        assert_eq!(-(-P), P);
    }

    // Encoding properties
    #[test]
    fn z_is_zero_bits() {
        assert_eq!(Z.bits(), 0b00);
    }
    #[test]
    fn n_is_01_bits() {
        assert_eq!(N.bits(), 0b01);
    }
    #[test]
    fn p_is_10_bits() {
        assert_eq!(P.bits(), 0b10);
    }
    #[test]
    fn inv_is_11_bits() {
        assert_eq!(INV.bits(), 0b11);
    }
    #[test]
    fn z_is_zero_i8() {
        assert_eq!(Z.to_i8(), Some(0));
    }
    #[test]
    fn n_is_neg1_i8() {
        assert_eq!(N.to_i8(), Some(-1));
    }
    #[test]
    fn p_is_pos1_i8() {
        assert_eq!(P.to_i8(), Some(1));
    }
    #[test]
    fn inv_is_none_i8() {
        assert_eq!(INV.to_i8(), None);
    }

    // Algebraic properties
    #[test]
    fn p_is_multiplicative_identity() {
        for t in [Z, N, P] {
            assert_eq!(t * P, t);
            assert_eq!(P * t, t);
        }
    }
    #[test]
    fn z_is_annihilator() {
        for t in [Z, N, P] {
            assert_eq!(t * Z, Z);
            assert_eq!(Z * t, Z);
        }
    }
    #[test]
    fn commutativity() {
        let trits = [Z, N, P, INV];
        for &a in &trits {
            for &b in &trits {
                assert_eq!(a * b, b * a);
            }
        }
    }
    #[test]
    fn associativity() {
        let trits = [Z, N, P]; // INV excluded (propagation tested separately)
        for &a in &trits {
            for &b in &trits {
                for &c in &trits {
                    assert_eq!((a * b) * c, a * (b * c));
                }
            }
        }
    }

    // Encoding: Z as memset-safe identity
    #[test]
    fn zero_bits_is_z() {
        // A packed word of all-zeros is all-Z (memset-safe).
        // This verifies the encoding choice is correct.
        assert!(Z.is_zero());
        assert!(!Z.is_inv());
        assert_eq!(Z.bits(), 0b00); // zero bits = zero trit
    }

    // Encoding: INV propagates via OR
    #[test]
    fn inv_is_all_bits_set() {
        // INV=11. For packed words: any_pair | 11 = 11.
        // This verifies INV propagation via OR is inherent in the encoding.
        assert_eq!(INV.bits(), 0b11);
        assert_eq!(INV.bits() | Z.bits(), 0b11);
        assert_eq!(INV.bits() | N.bits(), 0b11);
        assert_eq!(INV.bits() | P.bits(), 0b11);
    }

    // The Clifford basis group {imag, dual, hyp} ≡ {N, Z, P}
    #[test]
    fn clifford_basis_group_via_trit() {
        // imaginary: x*x = N (squares to -1 → N)
        assert_eq!(N * N, P); // but we need N in the governance sense:
                              // In Clifford governance: gen.sig = N means e*e = -1 = Expr(-1)
                              // The Trit N IS the imaginary type. Verified by: the sign of -1 is N.
        assert_eq!(Trit::from_i8(-1), N);
        assert_eq!(Trit::from_i8(0), Z);
        assert_eq!(Trit::from_i8(1), P);
    }
}