Skip to main content

ternlang_core/
trit.rs

1use std::fmt;
2use std::ops::{Add, Mul, Neg};
3use serde::{Serialize, Deserialize};
4
5use std::sync::LazyLock;
6
7static ADD_TABLE: LazyLock<[[u8; 243]; 243]> = LazyLock::new(|| {
8    let mut table = [[0u8; 243]; 243];
9    for i in 0..243 {
10        for j in 0..243 {
11            let t1 = unpack_5_trits(i as u8);
12            let t2 = unpack_5_trits(j as u8);
13            let mut res = [Trit::Tend; 5];
14            for k in 0..5 {
15                let (sum, _) = t1[k] + t2[k];
16                res[k] = sum;
17            }
18            table[i][j] = pack_5_trits(res);
19        }
20    }
21    table
22});
23
24static CONSENSUS_TABLE: LazyLock<[[u8; 243]; 243]> = LazyLock::new(|| {
25    let mut table = [[0u8; 243]; 243];
26    for i in 0..243 {
27        for j in 0..243 {
28            let t1 = unpack_5_trits(i as u8);
29            let t2 = unpack_5_trits(j as u8);
30            let mut res = [Trit::Tend; 5];
31            for k in 0..5 {
32                res[k] = match (t1[k], t2[k]) {
33                    (Trit::Affirm, Trit::Affirm) => Trit::Affirm,
34                    (Trit::Reject, Trit::Reject) => Trit::Reject,
35                    (Trit::Tend, x) => x,
36                    (x, Trit::Tend) => x,
37                    _ => Trit::Tend,
38                };
39            }
40            table[i][j] = pack_5_trits(res);
41        }
42    }
43    table
44});
45
46static NEG_TABLE: LazyLock<[u8; 243]> = LazyLock::new(|| {
47    let mut table = [0u8; 243];
48    for i in 0..243 {
49        let t = unpack_5_trits(i as u8);
50        let mut res = [Trit::Tend; 5];
51        for k in 0..5 {
52            res[k] = -t[k];
53        }
54        table[i] = pack_5_trits(res);
55    }
56    table
57});
58
59pub fn packed_neg(a: u8) -> u8 {
60    NEG_TABLE[a as usize]
61}
62
63pub fn packed_add(a: u8, b: u8) -> u8 {
64    ADD_TABLE[a as usize][b as usize]
65}
66
67pub fn packed_consensus(a: u8, b: u8) -> u8 {
68    CONSENSUS_TABLE[a as usize][b as usize]
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
72pub enum Trit {
73    Reject = -1,   // logical -1 — conflict, negation
74    Tend   =  0,   // logical  0 — hold, uncertainty
75    Affirm =  1,   // logical +1 — truth, confirmation
76}
77
78impl From<i8> for Trit {
79    fn from(val: i8) -> Self {
80        // VM-PANIC-001: saturate to the nearest trit instead of panicking.
81        // Values outside {-1, 0, +1} (e.g. from arithmetic overflow in balanced-ternary
82        // tensor ops) previously hard-crashed the VM. Saturate: positive → Affirm,
83        // negative → Reject, zero → Tend.
84        if val > 0 { Trit::Affirm }
85        else if val < 0 { Trit::Reject }
86        else { Trit::Tend }
87    }
88}
89
90impl fmt::Display for Trit {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Trit::Reject => write!(f, "reject"),
94            Trit::Tend   => write!(f, "tend"),
95            Trit::Affirm => write!(f, "affirm"),
96        }
97    }
98}
99
100impl Neg for Trit {
101    type Output = Self;
102
103    fn neg(self) -> Self::Output {
104        match self {
105            Trit::Reject => Trit::Affirm,
106            Trit::Tend => Trit::Tend,
107            Trit::Affirm => Trit::Reject,
108        }
109    }
110}
111
112impl Add for Trit {
113    type Output = (Self, Self); // (Sum, Carry)
114
115    fn add(self, rhs: Self) -> Self::Output {
116        match (self, rhs) {
117            (Trit::Reject, Trit::Reject) => (Trit::Affirm, Trit::Reject),
118            (Trit::Reject, Trit::Tend) => (Trit::Reject, Trit::Tend),
119            (Trit::Reject, Trit::Affirm) => (Trit::Tend, Trit::Tend),
120            (Trit::Tend, Trit::Reject) => (Trit::Reject, Trit::Tend),
121            (Trit::Tend, Trit::Tend) => (Trit::Tend, Trit::Tend),
122            (Trit::Tend, Trit::Affirm) => (Trit::Affirm, Trit::Tend),
123            (Trit::Affirm, Trit::Reject) => (Trit::Tend, Trit::Tend),
124            (Trit::Affirm, Trit::Tend) => (Trit::Affirm, Trit::Tend),
125            (Trit::Affirm, Trit::Affirm) => (Trit::Reject, Trit::Affirm),
126        }
127    }
128}
129
130impl Mul for Trit {
131    type Output = Self;
132
133    fn mul(self, rhs: Self) -> Self::Output {
134        match (self, rhs) {
135            (Trit::Tend, _) | (_, Trit::Tend) => Trit::Tend,
136            (Trit::Affirm, Trit::Affirm) | (Trit::Reject, Trit::Reject) => Trit::Affirm,
137            (Trit::Affirm, Trit::Reject) | (Trit::Reject, Trit::Affirm) => Trit::Reject,
138        }
139    }
140}
141
142/// Packs 5 trits into a single u8 using a base-3 scheme.
143/// 3^5 = 243, which fits in 0-255.
144pub fn pack_5_trits(trits: [Trit; 5]) -> u8 {
145    let mut val: u8 = 0;
146    let mut multiplier: u8 = 1;
147    for &t in &trits {
148        let t_val = match t {
149            Trit::Reject => 0,
150            Trit::Tend   => 1,
151            Trit::Affirm => 2,
152        };
153        val += t_val * multiplier;
154        multiplier *= 3;
155    }
156    val
157}
158
159/// Unpacks a u8 into 5 trits.
160pub fn unpack_5_trits(mut val: u8) -> [Trit; 5] {
161    let mut trits = [Trit::Tend; 5];
162    for i in 0..5 {
163        let t_val = val % 3;
164        trits[i] = match t_val {
165            0 => Trit::Reject,
166            1 => Trit::Tend,
167            2 => Trit::Affirm,
168            _ => unreachable!(),
169        };
170        val /= 3;
171    }
172    trits
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_packing() {
181        let test_cases = [
182            [Trit::Reject; 5],
183            [Trit::Tend; 5],
184            [Trit::Affirm; 5],
185            [Trit::Reject, Trit::Tend, Trit::Affirm, Trit::Reject, Trit::Tend],
186            [Trit::Affirm, Trit::Reject, Trit::Tend, Trit::Affirm, Trit::Reject],
187        ];
188
189        for &original in &test_cases {
190            let packed = pack_5_trits(original);
191            let unpacked = unpack_5_trits(packed);
192            assert_eq!(original, unpacked);
193        }
194    }
195
196    #[test]
197    fn test_negation() {
198        assert_eq!(-Trit::Reject, Trit::Affirm);
199        assert_eq!(-Trit::Tend, Trit::Tend);
200        assert_eq!(-Trit::Affirm, Trit::Reject);
201    }
202
203    #[test]
204    fn test_addition() {
205        assert_eq!(Trit::Reject + Trit::Reject, (Trit::Affirm, Trit::Reject));
206        assert_eq!(Trit::Reject + Trit::Tend, (Trit::Reject, Trit::Tend));
207        assert_eq!(Trit::Reject + Trit::Affirm, (Trit::Tend, Trit::Tend));
208        assert_eq!(Trit::Tend + Trit::Reject, (Trit::Reject, Trit::Tend));
209        assert_eq!(Trit::Tend + Trit::Tend, (Trit::Tend, Trit::Tend));
210        assert_eq!(Trit::Tend + Trit::Affirm, (Trit::Affirm, Trit::Tend));
211        assert_eq!(Trit::Affirm + Trit::Reject, (Trit::Tend, Trit::Tend));
212        assert_eq!(Trit::Affirm + Trit::Tend, (Trit::Affirm, Trit::Tend));
213        assert_eq!(Trit::Affirm + Trit::Affirm, (Trit::Reject, Trit::Affirm));
214    }
215
216    #[test]
217    fn test_multiplication() {
218        assert_eq!(Trit::Reject * Trit::Reject, Trit::Affirm);
219        assert_eq!(Trit::Reject * Trit::Tend, Trit::Tend);
220        assert_eq!(Trit::Reject * Trit::Affirm, Trit::Reject);
221        assert_eq!(Trit::Tend * Trit::Reject, Trit::Tend);
222        assert_eq!(Trit::Tend * Trit::Tend, Trit::Tend);
223        assert_eq!(Trit::Tend * Trit::Affirm, Trit::Tend);
224        assert_eq!(Trit::Affirm * Trit::Reject, Trit::Reject);
225        assert_eq!(Trit::Affirm * Trit::Tend, Trit::Tend);
226        assert_eq!(Trit::Affirm * Trit::Affirm, Trit::Affirm);
227    }
228}