1pub fn poly_mul(a: &[u8], b: &[u8]) -> Vec<u8> {
19 if a.is_empty() || b.is_empty() {
20 return Vec::new();
21 }
22 let mut out = vec![0u8; a.len() + b.len() - 1];
23 for (i, &x) in a.iter().enumerate() {
24 if x == 1 {
25 for (j, &y) in b.iter().enumerate() {
26 out[i + j] ^= y;
27 }
28 }
29 }
30 out
31}
32
33pub fn poly_rem(msg: &[u8], generator: &[u8]) -> Vec<u8> {
49 let r = generator.len().saturating_sub(1);
50 if r == 0 {
51 return Vec::new();
52 }
53 let words = r.div_ceil(64);
55 let mut g = vec![0u64; words];
56 for (d, &b) in generator[..r].iter().enumerate() {
57 if b & 1 == 1 {
58 g[d / 64] |= 1 << (d % 64);
59 }
60 }
61 let mut reg = vec![0u64; words];
62 let top = r - 1;
63 for &b in msg {
64 let fb = ((reg[top / 64] >> (top % 64)) & 1) as u8 ^ (b & 1);
65 for w in (0..words).rev() {
67 let carry = if w > 0 { reg[w - 1] >> 63 } else { 0 };
68 reg[w] = (reg[w] << 1) | carry;
69 }
70 if r % 64 != 0 {
71 reg[words - 1] &= (1u64 << (r % 64)) - 1;
72 }
73 if fb == 1 {
74 for (x, y) in reg.iter_mut().zip(&g) {
75 *x ^= y;
76 }
77 }
78 }
79 (0..r).rev().map(|d| ((reg[d / 64] >> (d % 64)) & 1) as u8).collect()
80}
81
82#[derive(Clone, Debug, PartialEq, Eq)]
84pub struct Matrix {
85 n_cols: usize,
86 rows: Vec<Vec<u64>>,
87}
88
89impl Matrix {
90 pub fn from_rows(n_cols: usize, rows: &[Vec<usize>]) -> Matrix {
92 let words = n_cols.div_ceil(64);
93 let rows = rows
94 .iter()
95 .map(|r| {
96 let mut w = vec![0u64; words];
97 for &c in r {
98 w[c / 64] ^= 1 << (c % 64);
99 }
100 w
101 })
102 .collect();
103 Matrix { n_cols, rows }
104 }
105
106 pub fn n_rows(&self) -> usize {
108 self.rows.len()
109 }
110
111 pub fn n_cols(&self) -> usize {
113 self.n_cols
114 }
115
116 pub fn get(&self, r: usize, c: usize) -> bool {
118 (self.rows[r][c / 64] >> (c % 64)) & 1 == 1
119 }
120
121 pub fn reduce(&mut self) -> Vec<usize> {
125 let mut pivots = Vec::new();
126 let mut r = 0;
127 for c in (0..self.n_cols).rev() {
128 if r == self.rows.len() {
129 break;
130 }
131 let Some(p) = (r..self.rows.len()).find(|&i| self.get(i, c)) else { continue };
132 self.rows.swap(r, p);
133 for i in 0..self.rows.len() {
134 if i != r && self.get(i, c) {
135 let (a, b) = if i < r {
136 let (x, y) = self.rows.split_at_mut(r);
137 (&mut x[i], &y[0])
138 } else {
139 let (x, y) = self.rows.split_at_mut(i);
140 (&mut y[0], &x[r])
141 };
142 for (u, v) in a.iter_mut().zip(b.iter()) {
143 *u ^= v;
144 }
145 }
146 }
147 pivots.push(c);
148 r += 1;
149 }
150 pivots
151 }
152
153 pub fn inverse(&self) -> Option<Matrix> {
165 let n = self.rows.len();
166 if n != self.n_cols {
167 return None;
168 }
169 let words = n.div_ceil(64);
170 let mut a = self.rows.clone();
171 let mut inv: Vec<Vec<u64>> = (0..n)
172 .map(|i| {
173 let mut w = vec![0u64; words];
174 w[i / 64] |= 1 << (i % 64);
175 w
176 })
177 .collect();
178 for c in 0..n {
179 let (word, bit) = (c / 64, c % 64);
180 let p = (c..n).find(|&i| (a[i][word] >> bit) & 1 == 1)?;
181 a.swap(c, p);
182 inv.swap(c, p);
183 let (pivot_a, pivot_inv) = (a[c].clone(), inv[c].clone());
184 for i in 0..n {
185 if i != c && (a[i][word] >> bit) & 1 == 1 {
186 for (u, v) in a[i][word..].iter_mut().zip(&pivot_a[word..]) {
188 *u ^= v;
189 }
190 for (u, v) in inv[i].iter_mut().zip(&pivot_inv) {
191 *u ^= v;
192 }
193 }
194 }
195 }
196 Some(Matrix { n_cols: n, rows: inv })
197 }
198
199 pub fn mul_vec(&self, v: &[u8]) -> Vec<u8> {
202 let mut packed = vec![0u64; self.n_cols.div_ceil(64)];
203 for (c, &b) in v.iter().enumerate().take(self.n_cols) {
204 if b & 1 == 1 {
205 packed[c / 64] |= 1 << (c % 64);
206 }
207 }
208 self.rows
209 .iter()
210 .map(|row| (row.iter().zip(&packed).map(|(a, b)| (a & b).count_ones()).sum::<u32>() & 1) as u8)
211 .collect()
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 fn long_division(msg: &[u8], g: &[u8]) -> Vec<u8> {
221 let r = g.len() - 1;
222 let mut a: Vec<u8> = msg.iter().copied().chain(std::iter::repeat_n(0, r)).collect();
224 for i in 0..msg.len() {
225 if a[i] == 1 {
226 for d in 0..=r {
227 a[i + d] ^= g[r - d];
228 }
229 }
230 }
231 a[msg.len()..].to_vec()
232 }
233
234 #[test]
235 fn the_remainder_is_that_of_the_long_division_at_any_degree() {
236 let mut state = 0x1234_5678_9abc_def1u64;
237 let mut bit = || {
238 state ^= state << 13;
239 state ^= state >> 7;
240 state ^= state << 17;
241 (state & 1) as u8
242 };
243 for r in [1usize, 5, 24, 63, 64, 65, 128, 192] {
244 let mut g: Vec<u8> = (0..r).map(|_| bit()).collect();
245 g.push(1);
246 for len in [0usize, 1, 7, 100] {
247 let msg: Vec<u8> = (0..len).map(|_| bit()).collect();
248 assert_eq!(poly_rem(&msg, &g), long_division(&msg, &g), "r = {r}, {len} bits");
249 }
250 }
251 }
252
253 #[test]
254 fn the_product_with_the_empty_polynomial_is_empty_instead_of_an_underflow() {
255 let none: Vec<u8> = Vec::new();
256 assert_eq!(poly_mul(&[], &[]), none);
257 assert_eq!((poly_mul(&[1, 1], &[]), poly_mul(&[], &[1])), (none.clone(), none));
258 }
259
260 #[test]
261 fn polynomial_products_and_rank() {
262 assert_eq!(poly_mul(&[1, 1, 0, 1], &[1]), vec![1, 1, 0, 1]);
263 assert_eq!(poly_mul(&[1, 1], &[1, 1, 1]), vec![1, 0, 0, 1]);
264 let mut m = Matrix::from_rows(70, &[vec![3, 69], vec![3, 69], vec![0]]);
266 let pivots = m.reduce();
267 assert_eq!(pivots, vec![69, 0]);
268 assert!(m.get(0, 69) && m.get(0, 3) && !m.get(2, 3) && !m.get(2, 69));
269 assert_eq!((m.n_rows(), m.n_cols()), (3, 70));
270 }
271 #[test]
272 fn the_inverse_of_a_random_invertible_matrix_gives_the_identity() {
273 let n = 150;
275 let mut state = 12_345u64;
276 let mut bit = || {
277 state = state.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
278 (state >> 33) & 1 == 1
279 };
280 let lower: Vec<Vec<usize>> = (0..n).map(|i| (0..i).filter(|_| bit()).chain([i]).collect()).collect();
281 let upper: Vec<Vec<usize>> =
282 (0..n).map(|i| [i].into_iter().chain((i + 1..n).filter(|_| bit())).collect()).collect();
283 let l = Matrix::from_rows(n, &lower);
284 let u = Matrix::from_rows(n, &upper);
285 let lu_rows: Vec<Vec<usize>> = (0..n)
287 .map(|i| {
288 let mut acc = vec![0u8; n];
289 for j in (0..n).filter(|&j| l.get(i, j)) {
290 for (c, a) in acc.iter_mut().enumerate() {
291 *a ^= u8::from(u.get(j, c));
292 }
293 }
294 (0..n).filter(|&c| acc[c] == 1).collect()
295 })
296 .collect();
297 let a = Matrix::from_rows(n, &lu_rows);
298 let inv = a.inverse().unwrap();
299 for c in 0..n {
301 let col: Vec<u8> = (0..n).map(|r| u8::from(inv.get(r, c))).collect();
302 let back = a.mul_vec(&col);
303 assert!(back.iter().enumerate().all(|(r, &b)| b == u8::from(r == c)), "column {c}");
304 }
305 }
306}