1use alloc::vec::Vec;
4use core::fmt::{self, Debug, Display, Formatter};
5use core::iter::{Product, Sum};
6use core::mem::ManuallyDrop;
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8use core::slice;
9
10use num_bigint::BigUint;
11use p3_field::op_assign_macros::{
12 impl_add_assign, impl_add_base_field, impl_div_methods, impl_mul_base_field, impl_mul_methods,
13 impl_sub_assign, impl_sub_base_field, ring_sum,
14};
15use p3_field::{Algebra, Field, Packable, PrimeCharacteristicRing, RawDataSerializable};
16use p3_maybe_rayon::prelude::*;
17use rand::Rng;
18use rand::distr::{Distribution, StandardUniform};
19use serde::{Deserialize, Serialize};
20
21use crate::cantor::CANTOR_BASIS_128;
22use crate::gf2::characteristic_two_methods;
23use crate::tower::TowerLevel;
24use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, Gf2, clmul};
25
26const TABLE_CHUNK: usize = 1 << 16;
28
29const CANTOR_BASIS: [u64; 64] = {
33 let mut basis = [0u64; 64];
34 let mut i = 0;
35 while i < 64 {
36 basis[i] = clmul::tower_image_64(CANTOR_BASIS_128[i] as u64);
37 i += 1;
38 }
39 basis
40};
41
42const TOWER_GENERATOR: u64 = 0x1_0000_0004;
46
47const ALPHA: u64 = clmul::tower_image_64(1 << 32);
51
52#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
64#[serde(transparent)]
65#[repr(transparent)]
66#[must_use]
67pub struct Poly64(u64);
68
69impl Poly64 {
70 pub(crate) const BITS: usize = 64;
72
73 pub fn from_tower_vec(values: Vec<BinaryField64>) -> Vec<Self> {
77 let mut values = ManuallyDrop::new(values);
78 let (ptr, len, capacity) = (values.as_mut_ptr(), values.len(), values.capacity());
79
80 let words = unsafe { slice::from_raw_parts_mut(ptr.cast::<u64>(), len) };
83 words.par_chunks_mut(TABLE_CHUNK).for_each(|chunk| {
84 chunk
85 .iter_mut()
86 .for_each(|word| *word = clmul::tower_to_poly_64(*word));
87 });
88
89 unsafe { Vec::from_raw_parts(ptr.cast::<Self>(), len, capacity) }
92 }
93
94 #[inline]
98 pub const fn new(coordinates: u64) -> Self {
99 Self(coordinates)
100 }
101
102 #[must_use]
104 #[inline]
105 pub const fn to_bits(self) -> u64 {
106 self.0
107 }
108
109 #[inline]
113 pub const fn from_le_bytes(bytes: [u8; 8]) -> Self {
114 Self(u64::from_le_bytes(bytes))
115 }
116
117 #[inline]
121 pub fn invert_or_zero(self) -> Self {
122 Self(clmul::poly_inverse_64(self.0))
123 }
124}
125
126impl Packable for Poly64 {}
127
128impl Display for Poly64 {
129 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
130 Display::fmt(&self.0, f)
131 }
132}
133
134impl Debug for Poly64 {
135 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
136 Debug::fmt(&self.0, f)
137 }
138}
139
140impl Distribution<Poly64> for StandardUniform {
141 #[inline]
142 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Poly64 {
143 let mut bytes = [0u8; 8];
144 rng.fill_bytes(&mut bytes);
145 Poly64::from_le_bytes(bytes)
146 }
147}
148
149impl PrimeCharacteristicRing for Poly64 {
150 #[inline]
151 fn zero_vec(len: usize) -> Vec<Self> {
152 let mut values = ManuallyDrop::new(alloc::vec![0u64; len]);
153 unsafe { Vec::from_raw_parts(values.as_mut_ptr().cast(), values.len(), values.capacity()) }
156 }
157
158 type PrimeSubfield = Gf2;
159
160 const ZERO: Self = Self(0);
161 const ONE: Self = Self(1);
162 const TWO: Self = Self(0);
164 const NEG_ONE: Self = Self(1);
166
167 #[inline]
168 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
169 Self::from_bool(f.is_one())
170 }
171
172 #[inline]
173 fn from_bool(b: bool) -> Self {
174 Self(u64::from(b))
175 }
176
177 characteristic_two_methods!();
178
179 #[inline]
180 fn square(&self) -> Self {
181 Self(clmul::poly_square_64(self.0))
182 }
183
184 #[inline]
185 fn dot_product<const N: usize>(u: &[Self; N], v: &[Self; N]) -> Self {
186 Self(clmul::poly_dot_64(u.iter().zip(v).map(|(a, b)| (a.0, b.0))))
188 }
189}
190
191impl Field for Poly64 {
192 type Packing = Self;
193
194 const GENERATOR: Self = Self(clmul::tower_image_64(TOWER_GENERATOR));
195
196 #[inline]
197 fn try_inverse(&self) -> Option<Self> {
198 let inverse = self.invert_or_zero();
202 (self.0 != 0).then_some(inverse)
203 }
204
205 #[inline]
206 fn try_sqrt(&self) -> Option<Self> {
207 Some(Self(clmul::poly_sqrt_64(self.0)))
209 }
210
211 #[inline]
212 fn order() -> BigUint {
213 BigUint::from(1u8) << Self::BITS
214 }
215
216 #[inline]
218 fn bits() -> usize {
219 Self::BITS
220 }
221
222 #[inline]
228 fn interpolation_node(i: usize) -> Self {
229 Self(i as u64)
230 }
231}
232
233impl RawDataSerializable for Poly64 {
234 const NUM_BYTES: usize = 8;
235
236 #[inline]
237 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
238 self.0.to_le_bytes()
239 }
240}
241
242impl crate::tower::private::Sealed for Poly64 {}
243
244impl TowerLevel for Poly64 {
245 type Repr = u64;
246
247 const LOG_BITS: usize = 6;
248
249 #[inline]
250 fn from_repr(r: Self::Repr) -> Self {
251 Self(r)
252 }
253
254 #[inline]
255 fn to_repr(self) -> Self::Repr {
256 self.0
257 }
258
259 #[inline]
263 fn mul_alpha(self) -> Self {
264 self * Self(ALPHA)
265 }
266
267 #[inline]
270 fn from_le_byte_iter(mut bytes: impl Iterator<Item = u8>) -> Self {
271 let mut buffer = [0u8; 8];
272 for byte in &mut buffer {
273 *byte = bytes
274 .next()
275 .expect("byte stream ended before a whole element was read");
276 }
277 Self::from_le_bytes(buffer)
278 }
279
280 #[inline]
283 fn cantor_basis(i: usize) -> Self {
284 assert!(i < Self::BITS, "Cantor basis index out of range");
285 Self(CANTOR_BASIS[i])
286 }
287}
288
289impl From<BinaryField64> for Poly64 {
290 #[inline]
292 fn from(x: BinaryField64) -> Self {
293 Self(clmul::tower_to_poly_64(x.to_repr()))
294 }
295}
296
297impl From<Poly64> for BinaryField64 {
298 #[inline]
300 fn from(x: Poly64) -> Self {
301 Self::from_repr(clmul::poly_to_tower_64(x.0))
302 }
303}
304
305impl Add<BinaryField64> for Poly64 {
306 type Output = Self;
307
308 #[inline]
309 fn add(self, rhs: BinaryField64) -> Self {
310 self + Self::from(rhs)
311 }
312}
313
314impl Sub<BinaryField64> for Poly64 {
315 type Output = Self;
316
317 #[inline]
318 fn sub(self, rhs: BinaryField64) -> Self {
319 self - Self::from(rhs)
320 }
321}
322
323impl Mul<BinaryField64> for Poly64 {
324 type Output = Self;
325
326 #[inline]
327 fn mul(self, rhs: BinaryField64) -> Self {
328 self * Self::from(rhs)
329 }
330}
331
332impl Algebra<BinaryField64> for Poly64 {}
333
334macro_rules! impl_narrow_algebra {
335 ($($field:ty),* $(,)?) => {$(
336 impl From<$field> for Poly64 {
337 #[inline]
338 fn from(x: $field) -> Self {
339 Self::from(BinaryField64::from(x))
340 }
341 }
342
343 impl_add_base_field!(Poly64, $field);
344 impl_sub_base_field!(Poly64, $field);
345 impl_mul_base_field!(Poly64, $field);
346
347 impl Algebra<$field> for Poly64 {}
348 )*};
349}
350
351impl_narrow_algebra!(BinaryField8, BinaryField16, BinaryField32);
352
353impl Add for Poly64 {
354 type Output = Self;
355
356 #[inline]
357 #[allow(clippy::suspicious_arithmetic_impl)]
358 fn add(self, rhs: Self) -> Self {
359 Self(self.0 ^ rhs.0)
361 }
362}
363
364impl Sub for Poly64 {
365 type Output = Self;
366
367 #[inline]
368 #[allow(clippy::suspicious_arithmetic_impl)]
369 fn sub(self, rhs: Self) -> Self {
370 self + rhs
372 }
373}
374
375impl Neg for Poly64 {
376 type Output = Self;
377
378 #[inline]
379 fn neg(self) -> Self {
380 self
382 }
383}
384
385impl Mul for Poly64 {
386 type Output = Self;
387
388 #[inline]
389 fn mul(self, rhs: Self) -> Self {
390 Self(clmul::poly_mul_64(self.0, rhs.0))
391 }
392}
393
394impl_add_assign!(Poly64);
395impl_sub_assign!(Poly64);
396impl_mul_methods!(Poly64);
397impl_div_methods!(Poly64, Poly64);
398ring_sum!(Poly64);
399
400impl From<Gf2> for Poly64 {
401 #[inline]
403 fn from(x: Gf2) -> Self {
404 Self::from_prime_subfield(x)
405 }
406}
407
408impl_add_base_field!(Poly64, Gf2);
409impl_sub_base_field!(Poly64, Gf2);
410impl_mul_base_field!(Poly64, Gf2);
411
412impl Algebra<Gf2> for Poly64 {}
413
414#[cfg(test)]
415mod tests {
416 use alloc::vec::Vec;
417
418 use p3_field::{Algebra, Field, PrimeCharacteristicRing};
419 use proptest::prelude::*;
420
421 use super::Poly64;
422 use crate::tower::TowerLevel;
423 use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, Gf2};
424
425 #[test]
426 fn tower_table_conversion_and_narrow_algebras_match_the_tower() {
427 let table: Vec<BinaryField64> = (0..257)
429 .map(|i| BinaryField64::from_repr(i * 0x9e37_79b9))
430 .collect();
431 let expected: Vec<Poly64> = table.iter().copied().map(Poly64::from).collect();
432 let allocation = table.as_ptr();
433 let converted = Poly64::from_tower_vec(table);
434 assert_eq!(converted, expected);
435 assert_eq!(converted.as_ptr().cast::<BinaryField64>(), allocation);
436
437 const fn assert_algebra<F: PrimeCharacteristicRing, A: Algebra<F>>() {}
439 assert_algebra::<BinaryField8, Poly64>();
440 assert_algebra::<BinaryField16, Poly64>();
441 assert_algebra::<BinaryField32, Poly64>();
442 assert_algebra::<BinaryField64, Poly64>();
443
444 let x = BinaryField64::from_repr(0xd6e8_feb8_6659_fd93);
445 let x_poly = Poly64::from(x);
446 let scalar = BinaryField32::from_repr(0xa5c3_19e7);
447 assert_eq!(
448 BinaryField64::from(x_poly * scalar),
449 x * BinaryField64::from(scalar)
450 );
451 }
452
453 #[test]
454 fn the_change_of_basis_fixes_the_constants() {
455 assert_eq!(Poly64::from(BinaryField64::ZERO), Poly64::ZERO);
457 assert_eq!(Poly64::from(BinaryField64::ONE), Poly64::ONE);
458 }
459
460 #[test]
461 fn the_generator_is_the_image_of_the_tower_generator() {
462 assert_eq!(Poly64::GENERATOR, Poly64::from(BinaryField64::GENERATOR));
466 }
467
468 #[test]
469 fn the_generator_has_the_full_order() {
470 let order = u64::MAX;
474 for prime in [3u64, 5, 17, 257, 641, 65537, 6_700_417] {
475 assert_ne!(
476 Poly64::GENERATOR.exp_u64(order / prime),
477 Poly64::ONE,
478 "{prime}"
479 );
480 }
481 assert_eq!(Poly64::GENERATOR.exp_u64(order), Poly64::ONE);
482 }
483
484 #[test]
485 fn the_cantor_basis_satisfies_its_recurrence() {
486 assert_eq!(Poly64::cantor_basis(0), Poly64::ONE);
488 for i in 1..Poly64::BITS {
489 let v = Poly64::cantor_basis(i);
490 assert_eq!(v.square() + v, Poly64::cantor_basis(i - 1), "vector {i}");
491 }
492
493 for i in 0..Poly64::BITS {
495 assert_eq!(
496 Poly64::cantor_basis(i),
497 Poly64::from(BinaryField64::cantor_basis(i)),
498 "vector {i}"
499 );
500 }
501 }
502
503 #[test]
504 #[should_panic = "Cantor basis index out of range"]
505 fn the_cantor_basis_rejects_an_index_beyond_the_field() {
506 let _vector = Poly64::cantor_basis(64);
507 }
508
509 #[test]
510 #[should_panic = "byte stream ended before a whole element was read"]
511 fn a_truncated_byte_stream_is_rejected() {
512 let _element = Poly64::from_le_byte_iter([0u8; 7].into_iter());
514 }
515
516 #[test]
517 fn zero_vectors_preserve_layout_and_support_growth() {
518 for len in [0, 1, 33, 1024] {
519 let mut values = Poly64::zero_vec(len);
520 assert_eq!(values.len(), len);
521 assert!(values.iter().all(|x| *x == Poly64::ZERO));
522 values.push(Poly64::ONE);
523 values.reserve(100);
524 assert_eq!(values.pop(), Some(Poly64::ONE));
525 }
526 }
527
528 proptest! {
529 #![proptest_config(ProptestConfig::with_cases(2000))]
530
531 #[test]
532 fn the_change_of_basis_round_trips(bits: u64) {
533 let x = Poly64::from_repr(bits);
534 prop_assert_eq!(Poly64::from(BinaryField64::from(x)), x);
535 }
536
537 #[test]
539 fn the_change_of_basis_is_a_ring_isomorphism(a: u64, b: u64) {
540 let (x, y) = (BinaryField64::from_repr(a), BinaryField64::from_repr(b));
541 prop_assert_eq!(Poly64::from(x + y), Poly64::from(x) + Poly64::from(y));
542 prop_assert_eq!(
543 Poly64::from(x.reference_mul(y)),
544 Poly64::from(x) * Poly64::from(y)
545 );
546 }
547
548 #[test]
550 fn scaling_by_the_generator_agrees_with_the_tower(bits: u64) {
551 let x = BinaryField64::from_repr(bits);
552 prop_assert_eq!(Poly64::from(x).mul_alpha(), Poly64::from(x.mul_alpha()));
553 }
554
555 #[test]
556 fn the_prime_subfield_embeds_as_zero_and_one(bit: bool) {
557 prop_assert_eq!(Poly64::from(Gf2::from_bool(bit)), Poly64::from_bool(bit));
558 }
559
560 #[test]
561 fn the_byte_stream_reads_back_what_the_element_wrote(bits: u64) {
562 use p3_field::RawDataSerializable;
563 let x = Poly64::from_repr(bits);
564 prop_assert_eq!(Poly64::from_le_byte_iter(x.into_bytes().into_iter()), x);
565 }
566 }
567
568 #[test]
569 fn serde_accepts_every_bit_pattern() {
570 for bits in [0u64, 1, u64::MAX, 1 << 63] {
572 let x = Poly64::from_repr(bits);
573 let encoded = serde_json::to_string(&x).unwrap();
574 assert_eq!(serde_json::from_str::<Poly64>(&encoded).unwrap(), x);
575 }
576 }
577}