1use std::sync::OnceLock;
10
11const POLYNOMIAL: u32 = 0x1100B;
14
15const FIELD_SIZE: usize = 65536;
17
18const FIELD_ORDER: usize = 65535;
20
21struct GfTables {
23 log_table: Vec<u16>,
25 exp_table: Vec<u16>,
27}
28
29static TABLES: OnceLock<GfTables> = OnceLock::new();
30
31fn tables() -> &'static GfTables {
32 TABLES.get_or_init(|| {
33 let mut log_table = vec![0u16; FIELD_SIZE];
34 let mut exp_table = vec![0u16; FIELD_ORDER * 2];
35
36 let mut val: u32 = 1;
38 for entry in exp_table.iter_mut().take(FIELD_ORDER) {
39 *entry = val as u16;
40 val <<= 1;
42 if val & 0x10000 != 0 {
43 val ^= POLYNOMIAL;
44 }
45 }
46
47 for i in 0..FIELD_ORDER {
49 exp_table[i + FIELD_ORDER] = exp_table[i];
50 }
51
52 for i in 0..FIELD_ORDER {
54 log_table[exp_table[i] as usize] = i as u16;
55 }
56 log_table[0] = 0;
58
59 GfTables {
60 log_table,
61 exp_table,
62 }
63 })
64}
65
66#[inline]
68pub fn add(a: u16, b: u16) -> u16 {
69 a ^ b
70}
71
72#[inline]
74pub fn sub(a: u16, b: u16) -> u16 {
75 a ^ b
76}
77
78#[inline]
81pub fn mul(a: u16, b: u16) -> u16 {
82 if a == 0 || b == 0 {
83 return 0;
84 }
85 let t = tables();
86 let log_sum = t.log_table[a as usize] as usize + t.log_table[b as usize] as usize;
87 t.exp_table[log_sum]
88}
89
90#[inline]
93pub fn div(a: u16, b: u16) -> u16 {
94 assert!(b != 0, "GF(2^16) division by zero");
95 if a == 0 {
96 return 0;
97 }
98 let t = tables();
99 let log_a = t.log_table[a as usize] as usize;
100 let log_b = t.log_table[b as usize] as usize;
101 let idx = log_a + FIELD_ORDER - log_b;
103 t.exp_table[idx]
104}
105
106#[inline]
109pub fn inv(a: u16) -> u16 {
110 assert!(a != 0, "GF(2^16) inverse of zero");
111 let t = tables();
112 let log_a = t.log_table[a as usize] as usize;
113 t.exp_table[FIELD_ORDER - log_a]
114}
115
116#[inline]
118pub fn pow(a: u16, exp: u32) -> u16 {
119 if a == 0 {
120 return 0;
121 }
122 if exp == 0 {
123 return 1;
124 }
125 let t = tables();
126 let log_a = t.log_table[a as usize] as u64;
127 let log_result = (log_a * exp as u64) % FIELD_ORDER as u64;
128 t.exp_table[log_result as usize]
129}
130
131#[inline]
133pub fn exp2(exp: u32) -> u16 {
134 let t = tables();
135 t.exp_table[exp as usize % FIELD_ORDER]
136}
137
138#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_add_is_xor() {
148 assert_eq!(add(0, 0), 0);
149 assert_eq!(add(1, 1), 0);
150 assert_eq!(add(0xFFFF, 0), 0xFFFF);
151 assert_eq!(add(0xAAAA, 0x5555), 0xFFFF);
152 }
153
154 #[test]
155 fn test_mul_identity() {
156 for x in [0u16, 1, 2, 100, 1000, 65535] {
158 assert_eq!(mul(x, 1), x, "mul({x}, 1) should be {x}");
159 assert_eq!(mul(1, x), x, "mul(1, {x}) should be {x}");
160 }
161 }
162
163 #[test]
164 fn test_mul_zero() {
165 for x in [0u16, 1, 2, 100, 65535] {
166 assert_eq!(mul(x, 0), 0);
167 assert_eq!(mul(0, x), 0);
168 }
169 }
170
171 #[test]
172 fn test_mul_commutative() {
173 for a in [2u16, 3, 100, 1000, 32768, 65535] {
174 for b in [2u16, 3, 100, 1000, 32768, 65535] {
175 assert_eq!(mul(a, b), mul(b, a), "mul({a}, {b}) should be commutative");
176 }
177 }
178 }
179
180 #[test]
181 fn test_inverse() {
182 for a in [1u16, 2, 3, 100, 1000, 32768, 65535] {
184 let a_inv = inv(a);
185 assert_eq!(
186 mul(a, a_inv),
187 1,
188 "a={a}, inv(a)={a_inv}: a*inv(a) should be 1"
189 );
190 }
191 }
192
193 #[test]
194 fn test_div_inverse_of_mul() {
195 for a in [1u16, 2, 42, 1000, 65535] {
196 for b in [1u16, 2, 42, 1000, 65535] {
197 let product = mul(a, b);
198 assert_eq!(div(product, b), a, "div(mul({a},{b}), {b}) should be {a}");
199 }
200 }
201 }
202
203 #[test]
204 fn test_pow_basics() {
205 assert_eq!(pow(42, 0), 1);
207 assert_eq!(pow(42, 1), 42);
209 assert_eq!(pow(0, 5), 0);
211 assert_eq!(pow(1, 999), 1);
213 }
214
215 #[test]
216 fn test_pow_consistent_with_mul() {
217 for a in [2u16, 3, 100, 65535] {
219 assert_eq!(pow(a, 2), mul(a, a));
220 assert_eq!(pow(a, 3), mul(mul(a, a), a));
221 }
222 }
223
224 #[test]
225 fn test_generator_order() {
226 assert_eq!(pow(2, FIELD_ORDER as u32), 1);
229 assert_ne!(pow(2, 32768), 1);
231 }
232
233 #[test]
234 fn test_exp2() {
235 assert_eq!(exp2(0), 1);
236 assert_eq!(exp2(1), 2);
237 assert_eq!(exp2(2), 4);
238 assert_eq!(exp2(FIELD_ORDER as u32), 1);
240 }
241
242 #[test]
243 fn test_field_closure() {
244 let a = 42u16;
247 let mut seen = vec![false; FIELD_SIZE];
248 for x in 1..FIELD_SIZE as u16 {
249 let product = mul(a, x);
250 assert!(
251 !seen[product as usize],
252 "duplicate product for a={a}, x={x}"
253 );
254 seen[product as usize] = true;
255 }
256 assert!(!seen[0]);
258 }
259
260 #[test]
263 fn test_known_par2_values() {
264 assert_eq!(pow(2, 0), 1);
266 assert_eq!(pow(2, 1), 2);
267 assert_eq!(pow(2, 8), 256);
268 assert_eq!(pow(2, 16), 0x100B); }
270}