Skip to main content

primitives/algebra/field/binary/
gf2.rs

1use core::iter::{Product, Sum};
2use std::{
3    mem::MaybeUninit,
4    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
5};
6
7use ff::Field;
8use hybrid_array::Array;
9use serde::{Deserialize, Serialize};
10use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
11use typenum::U1;
12
13use crate::{
14    algebra::{
15        field::FieldExtension,
16        ops::{AccReduce, DefaultDotProduct, IntoWide, MulAccReduce, ReduceWide},
17        uniform_bytes::FromUniformBytes,
18    },
19    errors::PrimitiveError,
20    random::{CryptoRngCore, Random},
21    utils::codec::InPlaceCodec,
22};
23
24// TODO: see if changing u8 to Choice makes more sense
25#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize, Ord, PartialOrd)]
26#[repr(transparent)]
27pub struct Gf2(pub(super) u8);
28
29impl Default for Gf2 {
30    fn default() -> Self {
31        Gf2::ZERO
32    }
33}
34
35impl IntoWide for Gf2 {
36    #[inline]
37    fn to_wide(&self) -> Gf2 {
38        *self
39    }
40
41    #[inline]
42    fn zero_wide() -> Gf2 {
43        Gf2::ZERO
44    }
45}
46
47impl ReduceWide for Gf2 {
48    fn reduce_mod_order(a: Self) -> Self {
49        a
50    }
51}
52
53// Dot product: Gf2 x Gf2
54impl MulAccReduce for Gf2 {
55    type WideType = Self;
56
57    fn mul_acc(acc: &mut Self::WideType, a: Self, b: Self) {
58        acc.0 ^= a.0 & b.0;
59    }
60}
61
62impl DefaultDotProduct for Gf2 {}
63
64// Dot product: Gf2 x &Gf2
65impl<'a> MulAccReduce<Self, &'a Self> for Gf2 {
66    type WideType = Self;
67
68    fn mul_acc(acc: &mut Self::WideType, a: Self, b: &'a Self) {
69        acc.0 ^= a.0 & b.0;
70    }
71}
72
73impl DefaultDotProduct<Self, &Self> for Gf2 {}
74
75// Dot product: &Gf2 x Gf2
76impl<'a> MulAccReduce<&'a Self, Self> for Gf2 {
77    type WideType = Self;
78
79    fn mul_acc(acc: &mut Self::WideType, a: &'a Self, b: Self) {
80        acc.0 ^= a.0 & b.0;
81    }
82}
83
84impl DefaultDotProduct<&Self, Self> for Gf2 {}
85
86// Dot product: &Gf2 x &Gf2
87impl<'a, 'b> MulAccReduce<&'a Self, &'b Self> for Gf2 {
88    type WideType = Self;
89
90    fn mul_acc(acc: &mut Self::WideType, a: &'a Self, b: &'b Self) {
91        acc.0 ^= a.0 & b.0;
92    }
93}
94
95impl DefaultDotProduct<&Self, &Self> for Gf2 {}
96
97impl AccReduce for Gf2 {
98    type WideType = Gf2;
99
100    #[inline]
101    fn acc(acc: &mut Self::WideType, a: Self) {
102        acc.0 ^= a.0;
103    }
104}
105
106impl AccReduce<&Self> for Gf2 {
107    type WideType = Gf2;
108
109    #[inline]
110    fn acc(acc: &mut Self::WideType, a: &Self) {
111        acc.0 ^= a.0;
112    }
113}
114
115impl ff::Field for Gf2 {
116    const ZERO: Self = Self(0);
117    const ONE: Self = Self(1);
118
119    fn random(mut rng: impl rand::RngCore) -> Self {
120        let mut tmp = [0u8; 1];
121        rng.fill_bytes(&mut tmp);
122        Self(tmp[0] & 1)
123    }
124
125    fn square(&self) -> Self {
126        Self(self.0)
127    }
128
129    fn double(&self) -> Self {
130        Self::ZERO
131    }
132
133    fn invert(&self) -> CtOption<Self> {
134        CtOption::new(*self, self.ct_eq(&Self::ONE))
135    }
136
137    fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) {
138        unimplemented!()
139    }
140}
141
142impl FieldExtension for Gf2 {
143    type Subfield = Self;
144
145    type Degree = U1;
146    type FieldBitSize = U1;
147    type FieldBytesSize = U1;
148
149    fn to_subfield_elements(&self) -> Array<Self::Subfield, Self::Degree> {
150        Array([*self])
151    }
152
153    fn from_subfield_elements(elems: Array<Self::Subfield, Self::Degree>) -> Self {
154        elems[0]
155    }
156
157    fn to_le_bytes(&self) -> Array<u8, Self::FieldBytesSize> {
158        [self.0].into()
159    }
160
161    fn from_le_bytes(bytes: &[u8]) -> Option<Self> {
162        bytes
163            .first()
164            .and_then(|&byte| if byte <= 1 { Some(Self(byte)) } else { None })
165    }
166
167    fn mul_by_subfield(&self, other: &Self::Subfield) -> Self {
168        self * other
169    }
170
171    fn generator() -> Self {
172        Self::ONE
173    }
174}
175
176impl Random for Gf2 {
177    fn random(mut rng: impl CryptoRngCore) -> Gf2 {
178        let mut tmp = [0u8; 1];
179        rng.fill_bytes(&mut tmp);
180        Gf2(tmp[0] & 1)
181    }
182}
183
184// SAFETY: `write_le_bytes`/`read_le_bytes` encode `Gf2` as its single inner byte. The encoding
185// MUST be architecture-independent by construction, and `write_le_bytes` MUST initialize the one
186// output byte. No validation is needed: every `u8` is a valid `Gf2` bit pattern, so the round-trip
187// is unbiased.
188unsafe impl InPlaceCodec for Gf2 {
189    const ENCODED_SIZE: usize = 1;
190
191    fn write_le_bytes(&self, out: &mut [MaybeUninit<u8>]) {
192        out[0].write(self.0);
193    }
194
195    fn read_le_bytes(bytes: &[u8]) -> Result<Self, PrimitiveError> {
196        if bytes[0] > 1 {
197            return Err(PrimitiveError::DeserializationFailed(
198                "Invalid Gf2 value: must be 0 or 1".to_string(),
199            ));
200        }
201        Ok(Self(bytes[0]))
202    }
203
204    // Sub-byte packing: 8 `Gf2` bits pack into one byte (element `i` maps to bit `i`). This is
205    // ~8x smaller/faster than one byte per element. An `M`-element `HeapArray<Gf2, M>` encodes
206    // as `M / 8` packed bytes plus an `M % 8` one-byte-per-element tail.
207    const PACK: usize = 8;
208    const PACK_BYTES: usize = 1;
209
210    fn write_pack(items: &[Self], out: &mut [MaybeUninit<u8>]) {
211        // `items.len() == 8`, `out.len() == 1`.
212        let mut byte = 0u8;
213        for (i, item) in items.iter().enumerate() {
214            byte |= (item.0 & 1) << i;
215        }
216        out[0].write(byte);
217    }
218
219    fn read_pack(bytes: &[u8], out: &mut [MaybeUninit<Self>]) -> Result<(), PrimitiveError> {
220        // `bytes.len() == 1`, `out.len() == 8`. Every byte is valid: 8 bits map to 8 `Gf2` values.
221        let byte = bytes[0];
222        for (i, slot) in out.iter_mut().enumerate() {
223            slot.write(Self((byte >> i) & 1));
224        }
225        Ok(())
226    }
227}
228
229// === Field traits implementations === //
230
231impl ConditionallySelectable for Gf2 {
232    #[inline]
233    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
234        Gf2(u8::conditional_select(&a.0, &b.0, choice))
235    }
236}
237
238impl ConstantTimeEq for Gf2 {
239    #[inline]
240    fn ct_eq(&self, other: &Self) -> Choice {
241        self.0.ct_eq(&other.0)
242    }
243}
244
245#[macros::op_variants(borrowed)]
246impl Neg for Gf2 {
247    type Output = Gf2;
248
249    #[inline]
250    fn neg(self) -> Self::Output {
251        self
252    }
253}
254
255#[macros::op_variants(owned, borrowed, flipped_commutative)]
256impl Add<&Gf2> for Gf2 {
257    type Output = Gf2;
258
259    #[inline]
260    #[allow(clippy::suspicious_arithmetic_impl)]
261    fn add(self, rhs: &Gf2) -> Self::Output {
262        Gf2(self.0 ^ rhs.0)
263    }
264}
265
266#[macros::op_variants(owned)]
267impl AddAssign<&Gf2> for Gf2 {
268    #[inline]
269    fn add_assign(&mut self, rhs: &Gf2) {
270        *self = *self + rhs;
271    }
272}
273
274#[macros::op_variants(owned, borrowed, flipped_commutative)]
275impl Sub<&Gf2> for Gf2 {
276    type Output = Gf2;
277
278    #[inline]
279    #[allow(clippy::suspicious_arithmetic_impl)]
280    fn sub(self, rhs: &Gf2) -> Self::Output {
281        self + rhs
282    }
283}
284
285#[macros::op_variants(owned)]
286impl SubAssign<&Gf2> for Gf2 {
287    #[inline]
288    fn sub_assign(&mut self, rhs: &Gf2) {
289        *self = *self - rhs;
290    }
291}
292
293#[macros::op_variants(owned, borrowed, flipped_commutative)]
294impl Mul<&Gf2> for Gf2 {
295    type Output = Gf2;
296
297    #[inline]
298    #[allow(clippy::suspicious_arithmetic_impl)]
299    fn mul(self, rhs: &Gf2) -> Self::Output {
300        Gf2(self.0 & rhs.0)
301    }
302}
303#[macros::op_variants(owned)]
304impl<'a> MulAssign<&'a Gf2> for Gf2 {
305    #[inline]
306    fn mul_assign(&mut self, rhs: &'a Gf2) {
307        *self = *self * rhs;
308    }
309}
310
311impl Sum for Gf2 {
312    #[inline]
313    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
314        iter.fold(Gf2::ZERO, |a, b| a + b)
315    }
316}
317
318impl<'a> Sum<&'a Gf2> for Gf2 {
319    #[inline]
320    fn sum<I: Iterator<Item = &'a Gf2>>(iter: I) -> Self {
321        iter.fold(Gf2::ZERO, |a, b| a + b)
322    }
323}
324
325impl Product for Gf2 {
326    #[inline]
327    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
328        iter.fold(Gf2::ONE, |a, b| a * b)
329    }
330}
331
332impl<'a> Product<&'a Gf2> for Gf2 {
333    #[inline]
334    fn product<I: Iterator<Item = &'a Gf2>>(iter: I) -> Self {
335        iter.fold(Gf2::ONE, |a, b| a * b)
336    }
337}
338
339impl From<Gf2> for bool {
340    fn from(value: Gf2) -> Self {
341        value.0 == 1
342    }
343}
344
345impl From<&Gf2> for bool {
346    fn from(value: &Gf2) -> Self {
347        value.0 == 1
348    }
349}
350
351impl From<bool> for Gf2 {
352    fn from(value: bool) -> Self {
353        Gf2(value.into())
354    }
355}
356
357impl From<&bool> for Gf2 {
358    fn from(value: &bool) -> Self {
359        (*value).into()
360    }
361}
362
363impl From<u8> for Gf2 {
364    fn from(val: u8) -> Self {
365        Gf2(val & 1)
366    }
367}
368
369impl From<Gf2> for u8 {
370    fn from(value: Gf2) -> Self {
371        value.0
372    }
373}
374
375impl From<&Gf2> for u64 {
376    fn from(value: &Gf2) -> Self {
377        value.0 as u64
378    }
379}
380
381impl From<u64> for Gf2 {
382    fn from(val: u64) -> Self {
383        Gf2((val & 1) as u8)
384    }
385}
386
387impl From<u128> for Gf2 {
388    fn from(val: u128) -> Self {
389        Gf2((val & 1) as u8)
390    }
391}
392
393impl From<Choice> for Gf2 {
394    fn from(value: Choice) -> Self {
395        Gf2(value.unwrap_u8())
396    }
397}
398
399impl From<&Choice> for Gf2 {
400    fn from(value: &Choice) -> Self {
401        (*value).into()
402    }
403}
404
405impl From<Gf2> for Choice {
406    fn from(value: Gf2) -> Self {
407        value.0.into()
408    }
409}
410
411impl From<&Gf2> for Choice {
412    fn from(value: &Gf2) -> Self {
413        value.0.into()
414    }
415}
416
417impl FromUniformBytes for Gf2 {
418    type UniformBytes = U1;
419
420    fn from_uniform_bytes(bytes: &Array<u8, Self::UniformBytes>) -> Self {
421        Gf2(bytes[0] & 1)
422    }
423}
424
425impl AsRef<[u8]> for Gf2 {
426    fn as_ref(&self) -> &[u8] {
427        unsafe {
428            std::slice::from_raw_parts(self as *const Gf2 as *const u8, std::mem::size_of::<Gf2>())
429        }
430    }
431}