use crate::expr::Expr;
use crate::gen::Gen;
use crate::governance::{Governance, Relation};
use crate::rat::Rat;
use crate::trace::{KotResult, TraceGen, TraceWalk};
use crate::trit::{Trit, INV, N, P, Z};
use crate::word::Word;
pub fn tracegen_to_word(tg: &TraceGen) -> Word {
let mut trits: Vec<Trit> = Vec::new();
trits.extend(std::iter::repeat_n(P, tg.rule_idx));
trits.push(INV);
trits.extend(std::iter::repeat_n(P, tg.start));
trits.push(INV);
for g in tg.matched.generators() {
trits.push(g.sig);
}
let gens: Vec<Gen> = trits.iter().map(|&t| Gen { sig: t, idx: 0 }).collect();
Word::from_gens(&gens)
}
pub fn word_to_tracegen(word: &Word) -> Option<TraceGen> {
let gens = word.generators();
let sep1 = gens.iter().position(|g| g.sig == INV)?;
let sep2 = gens[sep1 + 1..]
.iter()
.position(|g| g.sig == INV)
.map(|i| i + sep1 + 1)?;
let rule_idx = gens[..sep1].iter().filter(|g| g.sig == P).count();
if gens[..sep1].iter().any(|g| g.sig != P) {
return None;
}
let start = gens[sep1 + 1..sep2].iter().filter(|g| g.sig == P).count();
if gens[sep1 + 1..sep2].iter().any(|g| g.sig != P) {
return None;
}
let matched_gens: Vec<Gen> = gens[sep2 + 1..]
.iter()
.map(|g| Gen { sig: g.sig, idx: 0 })
.collect();
let matched = Word::from_gens(&matched_gens);
Some(TraceGen::new(rule_idx, start, matched))
}
pub fn tracewalk_to_expr(tw: &TraceWalk) -> Expr {
let mut result = Expr::zero();
for step in tw.steps() {
let word = tracegen_to_word(step);
result = result.add(&Expr::term(Rat::one(), word));
}
result
}
pub fn kot_result_to_expr(kr: &KotResult) -> Expr {
let minimal_trit = if kr.minimal { P } else { N };
let complete_trit = if kr.complete { P } else { N };
let consistent_trit = if kr.consistent { P } else { N };
let gens = vec![
Gen {
sig: minimal_trit,
idx: 0,
},
Gen {
sig: complete_trit,
idx: 1,
},
Gen {
sig: consistent_trit,
idx: 2,
},
];
Expr::term(Rat::one(), Word::from_gens(&gens))
}
pub fn q2_governance() -> Governance {
let passing_cert = Word::from_gens(&[
Gen { sig: P, idx: 0 },
Gen { sig: P, idx: 1 },
Gen { sig: P, idx: 2 },
]);
let mut gov = Governance::free().with_relation(Relation::new(passing_cert, Expr::int(1)));
for m in [P, N] {
for c in [P, N] {
for s in [P, N] {
if m == P && c == P && s == P {
continue;
} let word = Word::from_gens(&[
Gen { sig: m, idx: 0 },
Gen { sig: c, idx: 1 },
Gen { sig: s, idx: 2 },
]);
gov = gov.with_relation(Relation::new(word, Expr::int(-1)));
}
}
}
gov
}
pub fn compute_q2(kr: &KotResult) -> Trit {
let cert_expr = kot_result_to_expr(kr);
let gov = q2_governance();
let canonical = gov.canonicalize(&cert_expr);
match canonical.as_scalar() {
Some(r) => r.sign(),
None => Z, }
}
pub fn pack_word(word: &Word) -> u128 {
let mut result = 0u128;
for (i, g) in word.generators().iter().enumerate() {
result |= (g.sig.bits() as u128) << (i * 2);
}
result
}
pub fn pack_walk(walk: &TraceWalk) -> (u128, usize) {
let mut result = 0u128;
let mut offset = 0usize;
for step in walk.steps() {
let word = tracegen_to_word(step);
let val = pack_word(&word);
let bits = word.grade() * 2;
result |= val << offset;
offset += bits;
}
(result, offset)
}
pub fn pack_governance_sources(gov: &Governance) -> (u128, usize) {
let mut result = 0u128;
let mut offset = 0usize;
for rel in gov.relations() {
let val = pack_word(&rel.source);
let bits = rel.source.grade() * 2;
result |= val << offset;
offset += bits;
}
(result, offset)
}
pub fn certificate_bytes(n1_val: u128, n1_bits: usize, n2_val: u128, n2_bits: usize) -> Vec<u8> {
let total_bits = n1_bits + n2_bits;
let total_bytes = total_bits.div_ceil(8);
let combined: u128 = n1_val | (n2_val << n1_bits);
let mut bytes = Vec::with_capacity(total_bytes);
for i in 0..total_bytes {
bytes.push(((combined >> (i * 8)) & 0xFF) as u8);
}
bytes
}
pub fn fmt_hex(val: u128, bits: usize) -> String {
let bytes = bits.div_ceil(8);
format!(
"0x{:0>width$X}",
val & ((1u128 << bits) - 1),
width = bytes * 2
)
}
pub fn fmt_bits(val: u128, bits: usize) -> String {
(0..bits)
.step_by(2)
.map(|i| format!("{:02b}", (val >> i) & 0b11))
.collect::<Vec<_>>()
.join(" ")
}
pub fn trit_word_display(word: &Word) -> String {
if word.is_scalar() {
return "1".to_string();
}
let syms: Vec<&str> = word
.generators()
.iter()
.map(|g| match g.sig {
P => "P",
N => "N",
Z => "Z",
INV => "INV",
_ => "?",
})
.collect();
format!("[{}]", syms.join("·"))
}
pub fn trit_expr_display(expr: &Expr) -> String {
if expr.is_zero() {
return "0".to_string();
}
let mut parts = Vec::new();
for (word, coeff) in expr.terms() {
let word_str = trit_word_display(word);
if coeff.is_one() {
parts.push(word_str);
} else if *coeff == crate::rat::Rat::neg_one() {
parts.push(format!("-{}", word_str));
} else {
parts.push(format!("{}·{}", coeff, word_str));
}
}
parts.join(" + ")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::governance::Governance;
use crate::trace::{verify_kot, walk_from_trace};
fn ei(i: u32) -> Gen {
Gen::imaginary(i)
}
#[test]
fn tracegen_roundtrip_zero_zero_matched_nn() {
let tg = TraceGen::new(0, 0, Word::from_gens(&[ei(0), ei(0)]));
let word = tracegen_to_word(&tg);
assert_eq!(word.grade(), 4);
let gens = word.generators();
assert_eq!(gens[0].sig, INV); assert_eq!(gens[1].sig, INV); assert_eq!(gens[2].sig, N); assert_eq!(gens[3].sig, N); }
#[test]
fn tracegen_encode_decode_roundtrip() {
let tg_orig = TraceGen::new(2, 3, Word::from_gens(&[ei(0), Gen::hyperbolic(0)]));
let word = tracegen_to_word(&tg_orig);
let tg_decoded = word_to_tracegen(&word).unwrap();
assert_eq!(tg_decoded.rule_idx, 2);
assert_eq!(tg_decoded.start, 3);
assert_eq!(tg_decoded.matched.grade(), 2);
assert_eq!(tg_decoded.matched.generators()[0].sig, N);
assert_eq!(tg_decoded.matched.generators()[1].sig, P);
}
#[test]
fn tracegen_rule3_start2_encodes_correctly() {
let tg = TraceGen::new(3, 2, Word::from_gens(&[Gen::hyperbolic(0)]));
let word = tracegen_to_word(&tg);
assert_eq!(word.grade(), 8);
let g = word.generators();
assert!(g[..3].iter().all(|x| x.sig == P));
assert_eq!(g[3].sig, INV); assert!(g[4..6].iter().all(|x| x.sig == P));
assert_eq!(g[6].sig, INV); assert_eq!(g[7].sig, P);
}
#[test]
fn tracewalk_empty_encodes_as_zero() {
use crate::trace::TraceWalk;
let walk = TraceWalk::empty();
let expr = tracewalk_to_expr(&walk);
assert!(expr.is_zero());
}
#[test]
fn tracewalk_one_step_encodes_as_one_term() {
use crate::trace::TraceWalk;
let tg = TraceGen::new(0, 0, Word::from_gens(&[ei(0), ei(0)]));
let walk = TraceWalk::singleton(tg);
let expr = tracewalk_to_expr(&walk);
assert_eq!(expr.num_terms(), 1);
let (_, coeff) = expr.terms().next().unwrap();
assert_eq!(*coeff, Rat::one());
}
#[test]
fn kot_passing_encodes_as_ppp() {
let kr = KotResult {
minimal: true,
complete: true,
consistent: true,
witnesses: vec![],
};
let expr = kot_result_to_expr(&kr);
assert_eq!(expr.num_terms(), 1);
let (word, coeff) = expr.terms().next().unwrap();
assert_eq!(*coeff, Rat::one());
assert_eq!(word.grade(), 3);
assert!(word.generators().iter().all(|g| g.sig == P));
}
#[test]
fn kot_failing_consistency_encodes_with_n() {
let kr = KotResult {
minimal: true,
complete: true,
consistent: false,
witnesses: vec!["test".to_string()],
};
let expr = kot_result_to_expr(&kr);
let (word, coeff) = expr.terms().next().unwrap();
assert_eq!(*coeff, Rat::one());
assert_eq!(word.generators()[2].sig, N);
}
#[test]
fn q2_passing_cert_gives_p() {
let kr = KotResult {
minimal: true,
complete: true,
consistent: true,
witnesses: vec![],
};
assert_eq!(compute_q2(&kr), P);
}
#[test]
fn q2_failing_cert_gives_n() {
let kr = KotResult {
minimal: false,
complete: true,
consistent: true,
witnesses: vec![],
};
assert_eq!(compute_q2(&kr), N);
}
#[test]
fn q2_for_cl100_e1e1_gives_p() {
let gov = Governance::cl(1, 0, 0);
let source = Expr::term(Rat::one(), Word::from_gens(&[ei(0), ei(0)]));
let (_, trace) = gov.canonicalize_traced(&source);
let walk = walk_from_trace(&trace, &gov);
let kr = verify_kot(&walk, &source, &gov);
assert_eq!(kr.as_trit(), P, "Q₁ must be P");
assert_eq!(compute_q2(&kr), P, "Q₂ must be P");
}
#[test]
fn q2_self_reference_fixed_point() {
let kr = KotResult {
minimal: true,
complete: true,
consistent: true,
witnesses: vec![],
};
let q2 = compute_q2(&kr);
let kr2 = KotResult {
minimal: true,
complete: true,
consistent: (q2 == P),
witnesses: vec![],
};
let q3 = compute_q2(&kr2);
assert_eq!(q2, P);
assert_eq!(q3, P);
}
}