Skip to main content

p3_binary_field/
gf2.rs

1use core::fmt::{self, Debug, Display, Formatter};
2use core::iter::{Product, Sum};
3use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5use num_bigint::BigUint;
6use p3_field::integers::QuotientMap;
7use p3_field::op_assign_macros::{
8    impl_add_assign, impl_div_methods, impl_mul_methods, impl_sub_assign, ring_sum,
9};
10use p3_field::{
11    Field, Packable, PrimeCharacteristicRing, PrimeField, PrimeField32, PrimeField64,
12    RawDataSerializable, quotient_map_large_iint, quotient_map_large_uint,
13};
14use rand::Rng;
15use rand::distr::{Distribution, StandardUniform};
16use serde::de::Error;
17use serde::{Deserialize, Deserializer, Serialize};
18
19/// The [`PrimeCharacteristicRing`] methods whose values characteristic 2 fixes.
20///
21/// Invoked inside an implementation of that trait, it supplies:
22///
23/// - `double`, which is `ZERO`, since `a + a = 0`,
24/// - `halve` and `div_2exp_u64`, which always panic, since `2` is not invertible,
25/// - `xor`, which is addition,
26/// - `mul_2exp_u64`, which is the identity at exponent `0` and `ZERO` above it.
27///
28/// `div_2exp_u64` panics at exponent `0` as well.
29///
30/// The implementing type has to be `Copy` and add to itself.
31///
32/// These override default trait methods. Stable Rust cannot override a default for a class of
33/// types through a blanket implementation, so each implementation invokes this instead.
34macro_rules! characteristic_two_methods {
35    () => {
36        #[inline]
37        fn double(&self) -> Self {
38            // `a + a = 0` in characteristic 2.
39            Self::ZERO
40        }
41
42        /// # Panics
43        /// Always panics: `2` is not invertible in characteristic 2.
44        #[inline]
45        fn halve(&self) -> Self {
46            panic!("halve is undefined in characteristic 2")
47        }
48
49        #[inline]
50        fn xor(&self, y: &Self) -> Self {
51            *self + *y
52        }
53
54        #[inline]
55        fn mul_2exp_u64(&self, exp: u64) -> Self {
56            if exp == 0 { *self } else { Self::ZERO }
57        }
58
59        /// # Panics
60        /// Always panics: `2` is not invertible in characteristic 2.
61        #[inline]
62        fn div_2exp_u64(&self, _exp: u64) -> Self {
63            panic!("div_2exp_u64 is undefined in characteristic 2")
64        }
65    };
66}
67
68pub(crate) use characteristic_two_methods;
69
70/// The prime field `GF(2) = {0, 1}`, with addition given by `XOR` and multiplication by `AND`.
71///
72/// This is the base case of the characteristic-2 tower `GF(2) ⊂ GF(4) ⊂ … ⊂ GF(2^128)`.
73///
74/// The serde encoding is canonical: every field element has exactly one valid byte
75/// representation (see the manual [`Deserialize`] impl below). `#[serde(transparent)]` makes the
76/// derived [`Serialize`] emit the backing integer itself, which is what that impl reads back.
77#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
78#[serde(transparent)]
79#[repr(transparent)]
80#[must_use]
81pub struct Gf2(u8);
82
83impl Gf2 {
84    /// Create a new field element from a bit.
85    ///
86    /// Only the least significant bit of `bit` is used.
87    #[inline]
88    pub(crate) const fn new(bit: u8) -> Self {
89        Self(bit & 1)
90    }
91
92    /// The element's single bit, as `0` or `1`.
93    #[inline]
94    pub(crate) const fn to_bit(self) -> u8 {
95        self.0
96    }
97
98    /// Construct a field element from its little-endian byte representation.
99    ///
100    /// Every byte is a valid input; only its least significant bit is used.
101    #[inline]
102    pub const fn from_le_bytes(bytes: [u8; 1]) -> Self {
103        Self::new(bytes[0])
104    }
105
106    /// The bottom of the recursion that the tower levels above multiply through.
107    ///
108    /// `GF(2)` has no faster multiplication to dispatch to, so this is `Mul` itself; it exists
109    /// so that the tower's reference multiplication never routes through a level whose `Mul`
110    /// has been swapped out for a hardware fast path.
111    #[doc(hidden)]
112    #[inline]
113    pub fn reference_mul(self, rhs: Self) -> Self {
114        self * rhs
115    }
116}
117
118impl Packable for Gf2 {}
119
120impl<'de> Deserialize<'de> for Gf2 {
121    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
122        let val = u8::deserialize(d)?;
123        // Reject non-canonical encodings so a proof cannot be re-encoded without the witness.
124        // Only `0` and `1` are canonical for `GF(2)`.
125        if val < 2 {
126            Ok(Self(val))
127        } else {
128            Err(D::Error::custom("Value is out of range"))
129        }
130    }
131}
132
133impl Display for Gf2 {
134    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
135        Display::fmt(&self.0, f)
136    }
137}
138
139impl Debug for Gf2 {
140    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141        Debug::fmt(&self.0, f)
142    }
143}
144
145impl Distribution<Gf2> for StandardUniform {
146    #[inline]
147    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Gf2 {
148        Gf2::new((rng.next_u32() & 1) as u8)
149    }
150}
151
152impl PrimeCharacteristicRing for Gf2 {
153    type PrimeSubfield = Self;
154
155    const ZERO: Self = Self(0);
156    const ONE: Self = Self(1);
157    // The characteristic is 2, so `TWO = ONE + ONE = ZERO`.
158    const TWO: Self = Self(0);
159    // The characteristic is 2, so `NEG_ONE = ONE`.
160    const NEG_ONE: Self = Self(1);
161
162    #[inline]
163    fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
164        f
165    }
166
167    #[inline]
168    fn from_bool(b: bool) -> Self {
169        Self(b as u8)
170    }
171
172    characteristic_two_methods!();
173
174    #[inline]
175    fn square(&self) -> Self {
176        // `0^2 = 0` and `1^2 = 1`.
177        *self
178    }
179}
180
181impl Field for Gf2 {
182    /// No packing: the packed-value contract needs a cast to an array of `WIDTH` scalars.
183    /// Eight elements are eight bytes as scalars and one byte bit-sliced, so that cast lies.
184    ///
185    /// The bit-sliced packings therefore live beside this type rather than inside it.
186    type Packing = Self;
187
188    // The only nonzero element, `1`, trivially generates the (trivial) multiplicative group.
189    const GENERATOR: Self = Self::ONE;
190
191    #[inline]
192    fn try_inverse(&self) -> Option<Self> {
193        (self.0 == 1).then_some(Self::ONE)
194    }
195
196    #[inline]
197    fn try_sqrt(&self) -> Option<Self> {
198        // `0^2 = 0` and `1^2 = 1`, so every element is its own (unique) square root.
199        Some(*self)
200    }
201
202    #[inline]
203    fn order() -> BigUint {
204        BigUint::from(2u8)
205    }
206
207    /// An element of `GF(2)` is exactly one bit wide.
208    ///
209    /// The default implementation returns `order().bits()`, which over-reports by one whenever
210    /// the order is an exact power of two.
211    #[inline]
212    fn bits() -> usize {
213        1
214    }
215
216    /// The enumeration `interpolation_node(0) = 0, interpolation_node(1) = 1` is the only
217    /// injective enumeration of the two elements of `GF(2)` satisfying `Field::interpolation_node`'s
218    /// contract.
219    ///
220    /// # Panics
221    /// Panics if `i > 1`. `new` keeps only the low bit, so a larger index would otherwise alias
222    /// one of the two nodes and break an interpolation argument silently.
223    #[inline]
224    fn interpolation_node(i: usize) -> Self {
225        assert!(i < 2, "interpolation node index out of range");
226        Self::new(i as u8)
227    }
228}
229
230impl QuotientMap<u8> for Gf2 {
231    /// Convert a given `u8` integer into an element of the `Gf2` field.
232    #[inline]
233    fn from_int(int: u8) -> Self {
234        Self::new(int)
235    }
236
237    /// Convert a given `u8` integer into an element of the `Gf2` field.
238    ///
239    /// Returns `None` if the input does not lie in the range `[0, 1]`.
240    #[inline]
241    fn from_canonical_checked(int: u8) -> Option<Self> {
242        (int < 2).then(|| Self::new(int))
243    }
244
245    /// Convert a given `u8` integer into an element of the `Gf2` field.
246    ///
247    /// # Safety
248    /// The caller must guarantee that `int < 2`.
249    #[inline]
250    unsafe fn from_canonical_unchecked(int: u8) -> Self {
251        // SAFETY: the caller guarantees `int < 2`, so `int` is already the canonical
252        // representative and masking to its least significant bit is a no-op.
253        debug_assert!(int < 2);
254        Self::new(int)
255    }
256}
257
258impl QuotientMap<i8> for Gf2 {
259    /// Convert a given `i8` integer into an element of the `Gf2` field.
260    #[inline]
261    fn from_int(int: i8) -> Self {
262        // Two's-complement `& 1` extracts the parity bit regardless of sign.
263        Self::new((int & 1) as u8)
264    }
265
266    /// Convert a given `i8` integer into an element of the `Gf2` field.
267    ///
268    /// Returns `None` if the input does not lie in the range `[0, 1]`.
269    #[inline]
270    fn from_canonical_checked(int: i8) -> Option<Self> {
271        (0..2).contains(&int).then(|| Self::new(int as u8))
272    }
273
274    /// Convert a given `i8` integer into an element of the `Gf2` field.
275    ///
276    /// # Safety
277    /// The caller must guarantee that `int` lies in `[0, 1]`.
278    #[inline]
279    unsafe fn from_canonical_unchecked(int: i8) -> Self {
280        // SAFETY: the caller guarantees `0 <= int < 2`, so `int` is already the
281        // canonical representative and casting to `u8` is exact.
282        debug_assert!((0..2).contains(&int));
283        Self::new(int as u8)
284    }
285}
286
287// `u8` is the smallest integer type, so `QuotientMap<u8>` is implemented by hand above.
288quotient_map_large_uint!(Gf2, u8, 2u8, "`[0, 1]`", "`[0, 1]`", [u16, u32, u64, u128]);
289
290// `i8` is the smallest signed integer type, so `QuotientMap<i8>` is implemented by hand above.
291quotient_map_large_iint!(
292    Gf2,
293    i8,
294    "`[0, 1]`",
295    "`[0, 1]`",
296    [(i16, u16), (i32, u32), (i64, u64), (i128, u128)]
297);
298
299impl PrimeField for Gf2 {
300    #[inline]
301    fn as_canonical_biguint(&self) -> BigUint {
302        BigUint::from(self.0)
303    }
304}
305
306impl PrimeField64 for Gf2 {
307    const ORDER_U64: u64 = 2;
308
309    #[inline]
310    fn as_canonical_u64(&self) -> u64 {
311        self.0 as u64
312    }
313}
314
315impl PrimeField32 for Gf2 {
316    const ORDER_U32: u32 = 2;
317
318    #[inline]
319    fn as_canonical_u32(&self) -> u32 {
320        self.0 as u32
321    }
322}
323
324impl RawDataSerializable for Gf2 {
325    const NUM_BYTES: usize = 1;
326
327    #[inline]
328    fn into_bytes(self) -> impl IntoIterator<Item = u8> {
329        [self.0]
330    }
331}
332
333impl Add for Gf2 {
334    type Output = Self;
335
336    #[inline]
337    #[allow(clippy::suspicious_arithmetic_impl)]
338    fn add(self, rhs: Self) -> Self {
339        // Addition in `GF(2)` is `XOR`.
340        Self(self.0 ^ rhs.0)
341    }
342}
343
344impl Sub for Gf2 {
345    type Output = Self;
346
347    #[inline]
348    #[allow(clippy::suspicious_arithmetic_impl)]
349    fn sub(self, rhs: Self) -> Self {
350        // Subtraction coincides with addition in characteristic 2.
351        self + rhs
352    }
353}
354
355impl Neg for Gf2 {
356    type Output = Self;
357
358    #[inline]
359    fn neg(self) -> Self::Output {
360        // `-x = x` in characteristic 2.
361        self
362    }
363}
364
365impl Mul for Gf2 {
366    type Output = Self;
367
368    #[inline]
369    #[allow(clippy::suspicious_arithmetic_impl)]
370    fn mul(self, rhs: Self) -> Self {
371        // Multiplication in `GF(2)` is `AND`.
372        Self(self.0 & rhs.0)
373    }
374}
375
376impl_add_assign!(Gf2);
377impl_sub_assign!(Gf2);
378impl_mul_methods!(Gf2);
379impl_div_methods!(Gf2, Gf2);
380ring_sum!(Gf2);
381
382#[cfg(test)]
383mod tests {
384    use p3_field::integers::QuotientMap;
385    use p3_field::{
386        Field, PrimeCharacteristicRing, PrimeField, PrimeField32, PrimeField64, RawDataSerializable,
387    };
388
389    use super::Gf2;
390
391    #[test]
392    fn arithmetic_is_boolean() {
393        assert_eq!(Gf2::ONE + Gf2::ONE, Gf2::ZERO);
394        assert_eq!(Gf2::TWO, Gf2::ZERO);
395        assert_eq!(Gf2::NEG_ONE, Gf2::ONE);
396        assert_eq!(Gf2::ONE * Gf2::ONE, Gf2::ONE);
397        assert_eq!(Gf2::ONE.inverse(), Gf2::ONE);
398        assert_eq!(Gf2::ZERO.try_inverse(), None);
399        assert_eq!(Gf2::from_u64(7), Gf2::ONE);
400        assert_eq!(Gf2::from_u64(6), Gf2::ZERO);
401    }
402
403    // `p3_field_testing`'s `test_prime_field!`/`test_prime_field_64!`/`test_prime_field_32!`
404    // are not usable against `Gf2`: they hard-code assumptions that only hold for fields with
405    // a "reasonably large" order (e.g. `generate_from_small_int_tests!` asserts
406    // `from_canonical_checked` is `Some` for literal test values up to `108`;
407    // `generate_from_large_u_int_tests!`/`generate_from_large_i_int_tests!` unconditionally
408    // call `.halve()`, which is undefined in characteristic 2; and
409    // `test_prime_field_32!`'s raw-data-serializable/JSON-boundary sub-tests assume a 4-byte,
410    // ~31-bit representative). The tests below cover the same ground by hand, adapted to
411    // `GF(2)`'s actual canonical range of `{0, 1}`.
412
413    /// Every integer type required by `PrimeField`'s `QuotientMap<Int>` bound reduces mod 2,
414    /// and `from_canonical_checked`/`from_canonical_unchecked` agree with `from_int` exactly on
415    /// `{0, 1}` and reject (or are only guaranteed correct for) everything else.
416    #[test]
417    fn quotient_map_covers_every_integer_type() {
418        macro_rules! check_unsigned {
419            ($int:ty) => {
420                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(0), Gf2::ZERO);
421                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(1), Gf2::ONE);
422                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(2), Gf2::ZERO);
423                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(3), Gf2::ONE);
424                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(<$int>::MAX), Gf2::ONE);
425                assert_eq!(
426                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(0),
427                    Some(Gf2::ZERO)
428                );
429                assert_eq!(
430                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(1),
431                    Some(Gf2::ONE)
432                );
433                assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(2), None);
434                assert_eq!(
435                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(<$int>::MAX),
436                    None
437                );
438                unsafe {
439                    assert_eq!(
440                        <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(0),
441                        Gf2::ZERO
442                    );
443                    assert_eq!(
444                        <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(1),
445                        Gf2::ONE
446                    );
447                }
448            };
449        }
450        macro_rules! check_signed {
451            ($int:ty) => {
452                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(0), Gf2::ZERO);
453                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(1), Gf2::ONE);
454                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(-1), Gf2::ONE);
455                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(-2), Gf2::ZERO);
456                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(-3), Gf2::ONE);
457                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(<$int>::MIN), Gf2::ZERO);
458                assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(<$int>::MAX), Gf2::ONE);
459                assert_eq!(
460                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(0),
461                    Some(Gf2::ZERO)
462                );
463                assert_eq!(
464                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(1),
465                    Some(Gf2::ONE)
466                );
467                assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(2), None);
468                assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(-1), None);
469                assert_eq!(
470                    <Gf2 as QuotientMap<$int>>::from_canonical_checked(<$int>::MIN),
471                    None
472                );
473                unsafe {
474                    assert_eq!(
475                        <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(0),
476                        Gf2::ZERO
477                    );
478                    assert_eq!(
479                        <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(1),
480                        Gf2::ONE
481                    );
482                }
483            };
484        }
485
486        check_unsigned!(u8);
487        check_unsigned!(u16);
488        check_unsigned!(u32);
489        check_unsigned!(u64);
490        check_unsigned!(u128);
491        check_unsigned!(usize);
492        check_signed!(i8);
493        check_signed!(i16);
494        check_signed!(i32);
495        check_signed!(i64);
496        check_signed!(i128);
497        check_signed!(isize);
498    }
499
500    /// Large values reduce mod 2 rather than saturating or panicking, on both sides of a
501    /// `u8`-internal-representation boundary (256) and near the top of wider integer ranges.
502    #[test]
503    fn quotient_map_large_value_reduction() {
504        assert_eq!(<Gf2 as QuotientMap<u16>>::from_int(60_000), Gf2::ZERO);
505        assert_eq!(<Gf2 as QuotientMap<u16>>::from_int(60_001), Gf2::ONE);
506        assert_eq!(
507            <Gf2 as QuotientMap<u32>>::from_int(4_000_000_000),
508            Gf2::ZERO
509        );
510        assert_eq!(<Gf2 as QuotientMap<u32>>::from_int(4_000_000_001), Gf2::ONE);
511        assert_eq!(
512            <Gf2 as QuotientMap<u64>>::from_int(18_000_000_000_000_000_002),
513            Gf2::ZERO
514        );
515        assert_eq!(
516            <Gf2 as QuotientMap<u128>>::from_int(u128::MAX - 1),
517            Gf2::ZERO
518        );
519        assert_eq!(
520            <Gf2 as QuotientMap<i32>>::from_int(-2_000_000_001),
521            Gf2::ONE
522        );
523        assert_eq!(
524            <Gf2 as QuotientMap<i64>>::from_int(-9_000_000_000_000_000_002),
525            Gf2::ZERO
526        );
527        assert_eq!(
528            <Gf2 as QuotientMap<i128>>::from_int(i128::MIN + 1),
529            Gf2::ONE
530        );
531    }
532
533    #[test]
534    fn as_canonical_and_raw_bytes() {
535        assert_eq!(Gf2::ORDER_U32, 2);
536        assert_eq!(Gf2::ORDER_U64, 2);
537        assert_eq!(Gf2::ZERO.as_canonical_u32(), 0);
538        assert_eq!(Gf2::ONE.as_canonical_u32(), 1);
539        assert_eq!(Gf2::ZERO.as_canonical_u64(), 0);
540        assert_eq!(Gf2::ONE.as_canonical_u64(), 1);
541        assert_eq!(
542            Gf2::ONE.as_canonical_biguint(),
543            num_bigint::BigUint::from(1u8)
544        );
545        assert_eq!(
546            Gf2::ZERO.as_canonical_biguint(),
547            num_bigint::BigUint::from(0u8)
548        );
549
550        assert_eq!(Gf2::NUM_BYTES, 1);
551        let mut ones_bytes = Gf2::ONE.into_bytes().into_iter();
552        assert_eq!(ones_bytes.next(), Some(1u8));
553        assert_eq!(ones_bytes.next(), None);
554        let mut zeros_bytes = Gf2::ZERO.into_bytes().into_iter();
555        assert_eq!(zeros_bytes.next(), Some(0u8));
556        assert_eq!(zeros_bytes.next(), None);
557
558        assert_eq!(Gf2::from_le_bytes([0]), Gf2::ZERO);
559        assert_eq!(Gf2::from_le_bytes([1]), Gf2::ONE);
560        // Every byte is a valid input: only its least significant bit is used.
561        assert_eq!(Gf2::from_le_bytes([2]), Gf2::ZERO);
562        assert_eq!(Gf2::from_le_bytes([255]), Gf2::ONE);
563    }
564
565    #[test]
566    #[should_panic = "halve is undefined in characteristic 2"]
567    fn halve_panics() {
568        let _halved = Gf2::ONE.halve();
569    }
570
571    #[test]
572    #[should_panic = "div_2exp_u64 is undefined in characteristic 2"]
573    fn div_2exp_u64_panics() {
574        let _divided = Gf2::ONE.div_2exp_u64(1);
575    }
576
577    #[test]
578    #[should_panic = "div_2exp_u64 is undefined in characteristic 2"]
579    fn div_2exp_u64_panics_at_exponent_zero() {
580        let _divided = Gf2::ONE.div_2exp_u64(0);
581    }
582
583    #[test]
584    fn field_order_and_interpolation_nodes() {
585        assert_eq!(Gf2::order(), num_bigint::BigUint::from(2u8));
586        assert_eq!(Gf2::bits(), 1);
587        assert_eq!(Gf2::interpolation_node(0), Gf2::ZERO);
588        assert_eq!(Gf2::interpolation_node(1), Gf2::ONE);
589    }
590
591    #[test]
592    #[should_panic = "interpolation node index out of range"]
593    fn interpolation_node_rejects_an_index_beyond_the_field() {
594        let _node = Gf2::interpolation_node(2);
595    }
596
597    /// Serialization always emits the canonical byte, and deserialization rejects any byte
598    /// other than `0`/`1`. Without that rejection, deserializing corrupted or adversarial
599    /// bytes could construct a `Gf2` whose invariant (`.0 in {0, 1}`) is broken, silently
600    /// propagating an invalid field element through arithmetic.
601    #[test]
602    fn serde_round_trip_rejects_non_canonical_encodings() {
603        assert_eq!(serde_json::to_string(&Gf2::ZERO).unwrap(), "0");
604        assert_eq!(serde_json::to_string(&Gf2::ONE).unwrap(), "1");
605
606        let decoded_zero: Gf2 = serde_json::from_str("0").unwrap();
607        let decoded_one: Gf2 = serde_json::from_str("1").unwrap();
608        assert_eq!(decoded_zero, Gf2::ZERO);
609        assert_eq!(decoded_one, Gf2::ONE);
610
611        // Every byte other than 0/1 must be rejected, not silently truncated into range.
612        assert!(serde_json::from_str::<Gf2>("2").is_err());
613        assert!(serde_json::from_str::<Gf2>("255").is_err());
614    }
615}