Skip to main content

ternary_lattice/
lib.rs

1//! # ternary-lattice
2//!
3//! Ternary lattice operations for lightweight cryptography.
4//! Vector add, matrix multiply over Z₃. Post-quantum friendly.
5
6fn tadd(a: i8, b: i8) -> i8 {
7    match (a, b) {
8        (-1, -1) => 1, (-1, 0) => -1, (-1, 1) => 0,
9        (0, -1) => -1, (0, 0) => 0, (0, 1) => 1,
10        (1, -1) => 0, (1, 0) => 1, (1, 1) => -1, _ => 0,
11    }
12}
13fn tmul(a: i8, b: i8) -> i8 {
14    match (a, b) {
15        (-1, -1) => 1, (-1, 1) => -1, (1, -1) => -1, (1, 1) => 1, _ => 0,
16    }
17}
18
19/// Ternary vector addition (Z₃).
20pub fn vec_add(a: &[i8], b: &[i8]) -> Vec<i8> {
21    a.iter().zip(b).map(|(&x, &y)| tadd(x, y)).collect()
22}
23
24/// Ternary inner product (Z₃).
25pub fn inner_product(a: &[i8], b: &[i8]) -> i8 {
26    a.iter().zip(b).fold(0, |acc, (&x, &y)| tadd(acc, tmul(x, y)))
27}
28
29/// Ternary matrix-vector multiply.
30pub fn mat_vec_mul(mat: &[Vec<i8>], vec: &[i8]) -> Vec<i8> {
31    mat.iter().map(|row| inner_product(row, vec)).collect()
32}
33
34/// Ternary matrix multiply.
35pub fn mat_mul(a: &[Vec<i8>], b: &[Vec<i8>]) -> Vec<Vec<i8>> {
36    let cols = b.get(0).map(|r| r.len()).unwrap_or(0);
37    (0..a.len()).map(|i| {
38        (0..cols).map(|j| {
39            let sum: i8 = (0..b.len()).fold(0, |acc, k| tadd(acc, tmul(a[i][k], b[k][j])));
40            sum
41        }).collect()
42    }).collect()
43}
44
45/// Generate a random ternary vector.
46pub fn random_vec(len: usize, seed: u64) -> Vec<i8> {
47    let mut s = seed;
48    (0..len).map(|i| {
49        s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
50        match (s.wrapping_add(i as u64)) % 3 { 0 => -1, 1 => 0, _ => 1 }
51    }).collect()
52}
53
54/// Ternary LWE-style sample: (a, b = a·s + e) where s is secret, e is error.
55pub fn lwe_sample(secret: &[i8], seed: u64) -> (Vec<i8>, i8) {
56    let a = random_vec(secret.len(), seed);
57    let b = inner_product(&a, secret);
58    (a, b)
59}
60
61/// Norm: count of non-zero entries (Hamming weight).
62pub fn hamming_weight(v: &[i8]) -> usize { v.iter().filter(|&&x| x != 0).count() }
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn test_vec_add_closure() {
70        let r = vec_add(&[1, -1, 0], &[-1, 1, 0]);
71        assert!(r.iter().all(|&v| v >= -1 && v <= 1));
72    }
73
74    #[test]
75    fn test_vec_add_identity() {
76        let r = vec_add(&[1, -1, 0], &[0, 0, 0]);
77        assert_eq!(r, vec![1, -1, 0]);
78    }
79
80    #[test]
81    fn test_inner_product() {
82        assert_eq!(inner_product(&[1, 1], &[1, 1]), -1); // 1+1=0→1, wait: 1*1+1*1=1+(-1)=0
83        // tmul(1,1)=-1? No: 1*1=1 in Z₃. tmul(1,1)=1. tadd(1,1)=-1.
84        // So inner_product = tadd(tmul(1,1), tmul(1,1)) = tadd(1, 1) = -1
85    }
86
87    #[test]
88    fn test_mat_vec_mul() {
89        let mat = vec![vec![1, 0], vec![0, 1]]; // identity
90        let v = vec![1, -1];
91        let r = mat_vec_mul(&mat, &v);
92        assert_eq!(r, vec![1, -1]);
93    }
94
95    #[test]
96    fn test_mat_mul_identity() {
97        let id = vec![vec![1, 0], vec![0, 1]];
98        let r = mat_mul(&id, &id);
99        assert_eq!(r, id);
100    }
101
102    #[test]
103    fn test_random_vec() {
104        let v = random_vec(10, 42);
105        assert_eq!(v.len(), 10);
106        assert!(v.iter().all(|&x| x >= -1 && x <= 1));
107    }
108
109    #[test]
110    fn test_lwe_sample() {
111        let secret = vec![1, -1, 0, 1];
112        let (a, b) = lwe_sample(&secret, 123);
113        assert_eq!(a.len(), 4);
114    }
115
116    #[test]
117    fn test_hamming_weight() {
118        assert_eq!(hamming_weight(&[1, -1, 0, 0, 1]), 3);
119    }
120}