1use super::scalar::GF2;
4use alloc::vec;
5use alloc::vec::Vec;
6use core::fmt;
7use core::ops::{Add, BitAnd, BitXor};
8
9const WORD_BITS: usize = 64;
10
11#[inline]
12const fn word_index(i: usize) -> usize {
13 i / WORD_BITS
14}
15
16#[inline]
17const fn bit_index(i: usize) -> usize {
18 i % WORD_BITS
19}
20
21#[inline]
22fn num_words(dim: usize) -> usize {
23 dim.div_ceil(WORD_BITS)
24}
25
26#[inline]
28fn trailing_mask(dim: usize) -> u64 {
29 let rem = dim % WORD_BITS;
30 if rem == 0 && dim > 0 {
31 u64::MAX
32 } else if dim == 0 {
33 0
34 } else {
35 (1u64 << rem) - 1
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub struct GF2Vector {
45 words: Vec<u64>,
46 dim: usize,
47}
48
49impl GF2Vector {
50 #[must_use]
52 pub fn zero(dim: usize) -> Self {
53 Self {
54 words: vec![0u64; num_words(dim)],
55 dim,
56 }
57 }
58
59 #[must_use]
61 pub fn from_bits(bits: &[u8]) -> Self {
62 let dim = bits.len();
63 let mut v = Self::zero(dim);
64 for (i, &b) in bits.iter().enumerate() {
65 if b & 1 != 0 {
66 v.words[word_index(i)] |= 1u64 << bit_index(i);
67 }
68 }
69 v
70 }
71
72 #[must_use]
74 pub fn from_u64(dim: usize, value: u64) -> Self {
75 assert!(dim <= WORD_BITS, "from_u64 requires dim <= 64");
76 let masked = value & trailing_mask(dim);
77 Self {
78 words: vec![masked],
79 dim,
80 }
81 }
82
83 #[inline]
85 #[must_use]
86 pub fn dim(&self) -> usize {
87 self.dim
88 }
89
90 #[inline]
92 #[must_use]
93 pub fn get(&self, i: usize) -> GF2 {
94 assert!(
95 i < self.dim,
96 "index {} out of bounds for dim {}",
97 i,
98 self.dim
99 );
100 GF2::new(((self.words[word_index(i)] >> bit_index(i)) & 1) as u8)
101 }
102
103 #[inline]
105 pub fn set(&mut self, i: usize, value: GF2) {
106 assert!(
107 i < self.dim,
108 "index {} out of bounds for dim {}",
109 i,
110 self.dim
111 );
112 let w = word_index(i);
113 let b = bit_index(i);
114 self.words[w] = (self.words[w] & !(1u64 << b)) | ((value.value() as u64) << b);
115 }
116
117 #[must_use]
119 pub fn weight(&self) -> usize {
120 self.words.iter().map(|w| w.count_ones() as usize).sum()
121 }
122
123 #[must_use]
125 pub fn hamming_distance(&self, other: &Self) -> usize {
126 assert_eq!(self.dim, other.dim, "dimension mismatch");
127 self.words
128 .iter()
129 .zip(other.words.iter())
130 .map(|(a, b)| (a ^ b).count_ones() as usize)
131 .sum()
132 }
133
134 #[must_use]
136 pub fn dot(&self, other: &Self) -> GF2 {
137 assert_eq!(self.dim, other.dim, "dimension mismatch");
138 let parity: u32 = self
139 .words
140 .iter()
141 .zip(other.words.iter())
142 .map(|(a, b)| (a & b).count_ones())
143 .sum();
144 GF2::new((parity & 1) as u8)
145 }
146
147 #[must_use]
149 pub fn add(&self, other: &Self) -> Self {
150 assert_eq!(self.dim, other.dim, "dimension mismatch");
151 let words: Vec<u64> = self
152 .words
153 .iter()
154 .zip(other.words.iter())
155 .map(|(a, b)| a ^ b)
156 .collect();
157 Self {
158 words,
159 dim: self.dim,
160 }
161 }
162
163 #[must_use]
165 pub fn bitwise_and(&self, other: &Self) -> Self {
166 assert_eq!(self.dim, other.dim, "dimension mismatch");
167 let words: Vec<u64> = self
168 .words
169 .iter()
170 .zip(other.words.iter())
171 .map(|(a, b)| a & b)
172 .collect();
173 Self {
174 words,
175 dim: self.dim,
176 }
177 }
178
179 #[must_use]
181 pub fn is_zero(&self) -> bool {
182 self.words.iter().all(|&w| w == 0)
183 }
184
185 #[must_use]
187 pub fn as_words(&self) -> &[u64] {
188 &self.words
189 }
190}
191
192impl Add for &GF2Vector {
195 type Output = GF2Vector;
196 fn add(self, rhs: Self) -> GF2Vector {
197 self.add(rhs)
198 }
199}
200
201impl BitXor for &GF2Vector {
202 type Output = GF2Vector;
203 fn bitxor(self, rhs: Self) -> GF2Vector {
204 self.add(rhs)
205 }
206}
207
208impl BitAnd for &GF2Vector {
209 type Output = GF2Vector;
210 fn bitand(self, rhs: Self) -> GF2Vector {
211 self.bitwise_and(rhs)
212 }
213}
214
215impl fmt::Display for GF2Vector {
216 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217 write!(f, "[")?;
218 for i in 0..self.dim {
219 if i > 0 {
220 write!(f, ", ")?;
221 }
222 write!(f, "{}", self.get(i))?;
223 }
224 write!(f, "]")
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 #[test]
233 fn test_zero_vector() {
234 let v = GF2Vector::zero(8);
235 assert_eq!(v.dim(), 8);
236 assert!(v.is_zero());
237 assert_eq!(v.weight(), 0);
238 }
239
240 #[test]
241 fn test_get_set_roundtrip() {
242 let mut v = GF2Vector::zero(10);
243 v.set(0, GF2::ONE);
244 v.set(5, GF2::ONE);
245 v.set(9, GF2::ONE);
246 assert_eq!(v.get(0), GF2::ONE);
247 assert_eq!(v.get(1), GF2::ZERO);
248 assert_eq!(v.get(5), GF2::ONE);
249 assert_eq!(v.get(9), GF2::ONE);
250 assert_eq!(v.weight(), 3);
251 }
252
253 #[test]
254 fn test_from_bits() {
255 let v = GF2Vector::from_bits(&[1, 0, 1, 1, 0]);
256 assert_eq!(v.dim(), 5);
257 assert_eq!(v.get(0), GF2::ONE);
258 assert_eq!(v.get(1), GF2::ZERO);
259 assert_eq!(v.get(2), GF2::ONE);
260 assert_eq!(v.get(3), GF2::ONE);
261 assert_eq!(v.get(4), GF2::ZERO);
262 }
263
264 #[test]
265 fn test_xor_addition() {
266 let a = GF2Vector::from_bits(&[1, 0, 1, 0]);
267 let b = GF2Vector::from_bits(&[1, 1, 0, 0]);
268 let c = a.add(&b);
269 assert_eq!(c, GF2Vector::from_bits(&[0, 1, 1, 0]));
270 }
271
272 #[test]
273 fn test_dot_product() {
274 let a = GF2Vector::from_bits(&[1, 1, 0, 1]);
275 let b = GF2Vector::from_bits(&[1, 0, 1, 1]);
276 assert_eq!(a.dot(&b), GF2::ZERO);
278
279 let c = GF2Vector::from_bits(&[1, 1, 1]);
280 let d = GF2Vector::from_bits(&[1, 0, 1]);
281 assert_eq!(c.dot(&d), GF2::ZERO);
283
284 let e = GF2Vector::from_bits(&[1, 1, 0]);
285 let f = GF2Vector::from_bits(&[1, 0, 0]);
286 assert_eq!(e.dot(&f), GF2::ONE);
288 }
289
290 #[test]
291 fn test_hamming_weight_and_distance() {
292 let a = GF2Vector::from_bits(&[1, 0, 1, 1, 0, 1]);
293 assert_eq!(a.weight(), 4);
294
295 let b = GF2Vector::from_bits(&[0, 1, 1, 0, 0, 1]);
296 assert_eq!(a.hamming_distance(&b), 3);
297 }
298
299 #[test]
300 fn test_from_u64_roundtrip() {
301 let v = GF2Vector::from_u64(8, 0b10110011);
302 assert_eq!(v.get(0), GF2::ONE);
303 assert_eq!(v.get(1), GF2::ONE);
304 assert_eq!(v.get(2), GF2::ZERO);
305 assert_eq!(v.get(3), GF2::ZERO);
306 assert_eq!(v.get(4), GF2::ONE);
307 assert_eq!(v.get(5), GF2::ONE);
308 assert_eq!(v.get(6), GF2::ZERO);
309 assert_eq!(v.get(7), GF2::ONE);
310 }
311
312 #[test]
313 fn test_large_vector() {
314 let mut v = GF2Vector::zero(128);
315 v.set(0, GF2::ONE);
316 v.set(63, GF2::ONE);
317 v.set(64, GF2::ONE);
318 v.set(127, GF2::ONE);
319 assert_eq!(v.weight(), 4);
320 assert_eq!(v.get(63), GF2::ONE);
321 assert_eq!(v.get(64), GF2::ONE);
322 assert_eq!(v.get(65), GF2::ZERO);
323 }
324}