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#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
27#[serde(transparent)]
28#[repr(transparent)]
29#[must_use]
30pub struct Gf2(u8);
31
32impl Gf2 {
33 #[inline]
37 const fn new(bit: u8) -> Self {
38 Self(bit & 1)
39 }
40
41 #[inline]
45 pub const fn from_le_bytes(bytes: [u8; 1]) -> Self {
46 Self::new(bytes[0])
47 }
48
49 #[doc(hidden)]
55 #[inline]
56 pub fn reference_mul(self, rhs: Self) -> Self {
57 self * rhs
58 }
59}
60
61impl Packable for Gf2 {}
62
63impl<'de> Deserialize<'de> for Gf2 {
64 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
65 let val = u8::deserialize(d)?;
66 if val < 2 {
69 Ok(Self(val))
70 } else {
71 Err(D::Error::custom("Value is out of range"))
72 }
73 }
74}
75
76impl Display for Gf2 {
77 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
78 Display::fmt(&self.0, f)
79 }
80}
81
82impl Debug for Gf2 {
83 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84 Debug::fmt(&self.0, f)
85 }
86}
87
88impl Distribution<Gf2> for StandardUniform {
89 #[inline]
90 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Gf2 {
91 Gf2::new((rng.next_u32() & 1) as u8)
92 }
93}
94
95impl PrimeCharacteristicRing for Gf2 {
96 type PrimeSubfield = Self;
97
98 const ZERO: Self = Self(0);
99 const ONE: Self = Self(1);
100 const TWO: Self = Self(0);
102 const NEG_ONE: Self = Self(1);
104
105 #[inline]
106 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
107 f
108 }
109
110 #[inline]
111 fn from_bool(b: bool) -> Self {
112 Self(b as u8)
113 }
114
115 #[inline]
116 fn double(&self) -> Self {
117 Self::ZERO
119 }
120
121 #[inline]
124 fn halve(&self) -> Self {
125 panic!("halve is undefined in characteristic 2")
126 }
127
128 #[inline]
129 fn square(&self) -> Self {
130 *self
132 }
133
134 #[inline]
135 fn xor(&self, y: &Self) -> Self {
136 *self + *y
137 }
138
139 #[inline]
140 fn mul_2exp_u64(&self, exp: u64) -> Self {
141 if exp == 0 { *self } else { Self::ZERO }
142 }
143
144 #[inline]
147 fn div_2exp_u64(&self, _exp: u64) -> Self {
148 panic!("div_2exp_u64 is undefined in characteristic 2")
149 }
150}
151
152impl Field for Gf2 {
153 type Packing = Self;
154
155 const GENERATOR: Self = Self::ONE;
157
158 #[inline]
159 fn try_inverse(&self) -> Option<Self> {
160 (self.0 == 1).then_some(Self::ONE)
161 }
162
163 #[inline]
164 fn try_sqrt(&self) -> Option<Self> {
165 Some(*self)
167 }
168
169 #[inline]
170 fn order() -> BigUint {
171 BigUint::from(2u8)
172 }
173
174 #[inline]
179 fn bits() -> usize {
180 1
181 }
182
183 #[inline]
191 fn interpolation_node(i: usize) -> Self {
192 assert!(i < 2, "interpolation node index out of range");
193 Self::new(i as u8)
194 }
195}
196
197impl QuotientMap<u8> for Gf2 {
198 #[inline]
200 fn from_int(int: u8) -> Self {
201 Self::new(int)
202 }
203
204 #[inline]
208 fn from_canonical_checked(int: u8) -> Option<Self> {
209 (int < 2).then(|| Self::new(int))
210 }
211
212 #[inline]
217 unsafe fn from_canonical_unchecked(int: u8) -> Self {
218 debug_assert!(int < 2);
221 Self::new(int)
222 }
223}
224
225impl QuotientMap<i8> for Gf2 {
226 #[inline]
228 fn from_int(int: i8) -> Self {
229 Self::new((int & 1) as u8)
231 }
232
233 #[inline]
237 fn from_canonical_checked(int: i8) -> Option<Self> {
238 (0..2).contains(&int).then(|| Self::new(int as u8))
239 }
240
241 #[inline]
246 unsafe fn from_canonical_unchecked(int: i8) -> Self {
247 debug_assert!((0..2).contains(&int));
250 Self::new(int as u8)
251 }
252}
253
254quotient_map_large_uint!(Gf2, u8, 2u8, "`[0, 1]`", "`[0, 1]`", [u16, u32, u64, u128]);
256
257quotient_map_large_iint!(
259 Gf2,
260 i8,
261 "`[0, 1]`",
262 "`[0, 1]`",
263 [(i16, u16), (i32, u32), (i64, u64), (i128, u128)]
264);
265
266impl PrimeField for Gf2 {
267 #[inline]
268 fn as_canonical_biguint(&self) -> BigUint {
269 BigUint::from(self.0)
270 }
271}
272
273impl PrimeField64 for Gf2 {
274 const ORDER_U64: u64 = 2;
275
276 #[inline]
277 fn as_canonical_u64(&self) -> u64 {
278 self.0 as u64
279 }
280}
281
282impl PrimeField32 for Gf2 {
283 const ORDER_U32: u32 = 2;
284
285 #[inline]
286 fn as_canonical_u32(&self) -> u32 {
287 self.0 as u32
288 }
289}
290
291impl RawDataSerializable for Gf2 {
292 const NUM_BYTES: usize = 1;
293
294 #[inline]
295 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
296 [self.0]
297 }
298}
299
300impl Add for Gf2 {
301 type Output = Self;
302
303 #[inline]
304 #[allow(clippy::suspicious_arithmetic_impl)]
305 fn add(self, rhs: Self) -> Self {
306 Self(self.0 ^ rhs.0)
308 }
309}
310
311impl Sub for Gf2 {
312 type Output = Self;
313
314 #[inline]
315 #[allow(clippy::suspicious_arithmetic_impl)]
316 fn sub(self, rhs: Self) -> Self {
317 self + rhs
319 }
320}
321
322impl Neg for Gf2 {
323 type Output = Self;
324
325 #[inline]
326 fn neg(self) -> Self::Output {
327 self
329 }
330}
331
332impl Mul for Gf2 {
333 type Output = Self;
334
335 #[inline]
336 #[allow(clippy::suspicious_arithmetic_impl)]
337 fn mul(self, rhs: Self) -> Self {
338 Self(self.0 & rhs.0)
340 }
341}
342
343impl_add_assign!(Gf2);
344impl_sub_assign!(Gf2);
345impl_mul_methods!(Gf2);
346impl_div_methods!(Gf2, Gf2);
347ring_sum!(Gf2);
348
349#[cfg(test)]
350mod tests {
351 use p3_field::integers::QuotientMap;
352 use p3_field::{
353 Field, PrimeCharacteristicRing, PrimeField, PrimeField32, PrimeField64, RawDataSerializable,
354 };
355
356 use super::Gf2;
357
358 #[test]
359 fn arithmetic_is_boolean() {
360 assert_eq!(Gf2::ONE + Gf2::ONE, Gf2::ZERO);
361 assert_eq!(Gf2::TWO, Gf2::ZERO);
362 assert_eq!(Gf2::NEG_ONE, Gf2::ONE);
363 assert_eq!(Gf2::ONE * Gf2::ONE, Gf2::ONE);
364 assert_eq!(Gf2::ONE.inverse(), Gf2::ONE);
365 assert_eq!(Gf2::ZERO.try_inverse(), None);
366 assert_eq!(Gf2::from_u64(7), Gf2::ONE);
367 assert_eq!(Gf2::from_u64(6), Gf2::ZERO);
368 }
369
370 #[test]
384 fn quotient_map_covers_every_integer_type() {
385 macro_rules! check_unsigned {
386 ($int:ty) => {
387 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(0), Gf2::ZERO);
388 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(1), Gf2::ONE);
389 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(2), Gf2::ZERO);
390 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(3), Gf2::ONE);
391 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(<$int>::MAX), Gf2::ONE);
392 assert_eq!(
393 <Gf2 as QuotientMap<$int>>::from_canonical_checked(0),
394 Some(Gf2::ZERO)
395 );
396 assert_eq!(
397 <Gf2 as QuotientMap<$int>>::from_canonical_checked(1),
398 Some(Gf2::ONE)
399 );
400 assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(2), None);
401 assert_eq!(
402 <Gf2 as QuotientMap<$int>>::from_canonical_checked(<$int>::MAX),
403 None
404 );
405 unsafe {
406 assert_eq!(
407 <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(0),
408 Gf2::ZERO
409 );
410 assert_eq!(
411 <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(1),
412 Gf2::ONE
413 );
414 }
415 };
416 }
417 macro_rules! check_signed {
418 ($int:ty) => {
419 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(0), Gf2::ZERO);
420 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(1), Gf2::ONE);
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>::MIN), Gf2::ZERO);
425 assert_eq!(<Gf2 as QuotientMap<$int>>::from_int(<$int>::MAX), Gf2::ONE);
426 assert_eq!(
427 <Gf2 as QuotientMap<$int>>::from_canonical_checked(0),
428 Some(Gf2::ZERO)
429 );
430 assert_eq!(
431 <Gf2 as QuotientMap<$int>>::from_canonical_checked(1),
432 Some(Gf2::ONE)
433 );
434 assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(2), None);
435 assert_eq!(<Gf2 as QuotientMap<$int>>::from_canonical_checked(-1), None);
436 assert_eq!(
437 <Gf2 as QuotientMap<$int>>::from_canonical_checked(<$int>::MIN),
438 None
439 );
440 unsafe {
441 assert_eq!(
442 <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(0),
443 Gf2::ZERO
444 );
445 assert_eq!(
446 <Gf2 as QuotientMap<$int>>::from_canonical_unchecked(1),
447 Gf2::ONE
448 );
449 }
450 };
451 }
452
453 check_unsigned!(u8);
454 check_unsigned!(u16);
455 check_unsigned!(u32);
456 check_unsigned!(u64);
457 check_unsigned!(u128);
458 check_unsigned!(usize);
459 check_signed!(i8);
460 check_signed!(i16);
461 check_signed!(i32);
462 check_signed!(i64);
463 check_signed!(i128);
464 check_signed!(isize);
465 }
466
467 #[test]
470 fn quotient_map_large_value_reduction() {
471 assert_eq!(<Gf2 as QuotientMap<u16>>::from_int(60_000), Gf2::ZERO);
472 assert_eq!(<Gf2 as QuotientMap<u16>>::from_int(60_001), Gf2::ONE);
473 assert_eq!(
474 <Gf2 as QuotientMap<u32>>::from_int(4_000_000_000),
475 Gf2::ZERO
476 );
477 assert_eq!(<Gf2 as QuotientMap<u32>>::from_int(4_000_000_001), Gf2::ONE);
478 assert_eq!(
479 <Gf2 as QuotientMap<u64>>::from_int(18_000_000_000_000_000_002),
480 Gf2::ZERO
481 );
482 assert_eq!(
483 <Gf2 as QuotientMap<u128>>::from_int(u128::MAX - 1),
484 Gf2::ZERO
485 );
486 assert_eq!(
487 <Gf2 as QuotientMap<i32>>::from_int(-2_000_000_001),
488 Gf2::ONE
489 );
490 assert_eq!(
491 <Gf2 as QuotientMap<i64>>::from_int(-9_000_000_000_000_000_002),
492 Gf2::ZERO
493 );
494 assert_eq!(
495 <Gf2 as QuotientMap<i128>>::from_int(i128::MIN + 1),
496 Gf2::ONE
497 );
498 }
499
500 #[test]
501 fn as_canonical_and_raw_bytes() {
502 assert_eq!(Gf2::ORDER_U32, 2);
503 assert_eq!(Gf2::ORDER_U64, 2);
504 assert_eq!(Gf2::ZERO.as_canonical_u32(), 0);
505 assert_eq!(Gf2::ONE.as_canonical_u32(), 1);
506 assert_eq!(Gf2::ZERO.as_canonical_u64(), 0);
507 assert_eq!(Gf2::ONE.as_canonical_u64(), 1);
508 assert_eq!(
509 Gf2::ONE.as_canonical_biguint(),
510 num_bigint::BigUint::from(1u8)
511 );
512 assert_eq!(
513 Gf2::ZERO.as_canonical_biguint(),
514 num_bigint::BigUint::from(0u8)
515 );
516
517 assert_eq!(Gf2::NUM_BYTES, 1);
518 let mut ones_bytes = Gf2::ONE.into_bytes().into_iter();
519 assert_eq!(ones_bytes.next(), Some(1u8));
520 assert_eq!(ones_bytes.next(), None);
521 let mut zeros_bytes = Gf2::ZERO.into_bytes().into_iter();
522 assert_eq!(zeros_bytes.next(), Some(0u8));
523 assert_eq!(zeros_bytes.next(), None);
524
525 assert_eq!(Gf2::from_le_bytes([0]), Gf2::ZERO);
526 assert_eq!(Gf2::from_le_bytes([1]), Gf2::ONE);
527 assert_eq!(Gf2::from_le_bytes([2]), Gf2::ZERO);
529 assert_eq!(Gf2::from_le_bytes([255]), Gf2::ONE);
530 }
531
532 #[test]
533 #[should_panic = "halve is undefined in characteristic 2"]
534 fn halve_panics() {
535 let _halved = Gf2::ONE.halve();
536 }
537
538 #[test]
539 #[should_panic = "div_2exp_u64 is undefined in characteristic 2"]
540 fn div_2exp_u64_panics() {
541 let _divided = Gf2::ONE.div_2exp_u64(1);
542 }
543
544 #[test]
545 fn field_order_and_interpolation_nodes() {
546 assert_eq!(Gf2::order(), num_bigint::BigUint::from(2u8));
547 assert_eq!(Gf2::bits(), 1);
548 assert_eq!(Gf2::interpolation_node(0), Gf2::ZERO);
549 assert_eq!(Gf2::interpolation_node(1), Gf2::ONE);
550 }
551
552 #[test]
553 #[should_panic = "interpolation node index out of range"]
554 fn interpolation_node_rejects_an_index_beyond_the_field() {
555 let _node = Gf2::interpolation_node(2);
556 }
557
558 #[test]
563 fn serde_round_trip_rejects_non_canonical_encodings() {
564 assert_eq!(serde_json::to_string(&Gf2::ZERO).unwrap(), "0");
565 assert_eq!(serde_json::to_string(&Gf2::ONE).unwrap(), "1");
566
567 let decoded_zero: Gf2 = serde_json::from_str("0").unwrap();
568 let decoded_one: Gf2 = serde_json::from_str("1").unwrap();
569 assert_eq!(decoded_zero, Gf2::ZERO);
570 assert_eq!(decoded_one, Gf2::ONE);
571
572 assert!(serde_json::from_str::<Gf2>("2").is_err());
574 assert!(serde_json::from_str::<Gf2>("255").is_err());
575 }
576}