use alloc::vec::Vec;
use core::fmt::{self, Debug, Display, Formatter};
use core::iter::{Product, Sum};
use core::mem::ManuallyDrop;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use core::slice;
use num_bigint::BigUint;
use p3_field::op_assign_macros::{
impl_add_assign, impl_add_base_field, impl_div_methods, impl_mul_base_field, impl_mul_methods,
impl_sub_assign, impl_sub_base_field, ring_sum,
};
use p3_field::{Algebra, Field, Packable, PrimeCharacteristicRing, RawDataSerializable};
use p3_maybe_rayon::prelude::*;
use rand::Rng;
use rand::distr::{Distribution, StandardUniform};
use serde::{Deserialize, Serialize};
use crate::cantor::CANTOR_BASIS_128;
use crate::gf2::characteristic_two_methods;
use crate::tower::TowerLevel;
use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, Gf2, clmul};
const TABLE_CHUNK: usize = 1 << 16;
const CANTOR_BASIS: [u64; 64] = {
let mut basis = [0u64; 64];
let mut i = 0;
while i < 64 {
basis[i] = clmul::tower_image_64(CANTOR_BASIS_128[i] as u64);
i += 1;
}
basis
};
const TOWER_GENERATOR: u64 = 0x1_0000_0004;
const ALPHA: u64 = clmul::tower_image_64(1 << 32);
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
#[repr(transparent)]
#[must_use]
pub struct Poly64(u64);
impl Poly64 {
pub(crate) const BITS: usize = 64;
pub fn from_tower_vec(values: Vec<BinaryField64>) -> Vec<Self> {
let mut values = ManuallyDrop::new(values);
let (ptr, len, capacity) = (values.as_mut_ptr(), values.len(), values.capacity());
let words = unsafe { slice::from_raw_parts_mut(ptr.cast::<u64>(), len) };
words.par_chunks_mut(TABLE_CHUNK).for_each(|chunk| {
chunk
.iter_mut()
.for_each(|word| *word = clmul::tower_to_poly_64(*word));
});
unsafe { Vec::from_raw_parts(ptr.cast::<Self>(), len, capacity) }
}
#[inline]
pub const fn new(coordinates: u64) -> Self {
Self(coordinates)
}
#[must_use]
#[inline]
pub const fn to_bits(self) -> u64 {
self.0
}
#[inline]
pub const fn from_le_bytes(bytes: [u8; 8]) -> Self {
Self(u64::from_le_bytes(bytes))
}
#[inline]
pub fn invert_or_zero(self) -> Self {
Self(clmul::poly_inverse_64(self.0))
}
}
impl Packable for Poly64 {}
impl Display for Poly64 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Debug for Poly64 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Distribution<Poly64> for StandardUniform {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Poly64 {
let mut bytes = [0u8; 8];
rng.fill_bytes(&mut bytes);
Poly64::from_le_bytes(bytes)
}
}
impl PrimeCharacteristicRing for Poly64 {
#[inline]
fn zero_vec(len: usize) -> Vec<Self> {
let mut values = ManuallyDrop::new(alloc::vec![0u64; len]);
unsafe { Vec::from_raw_parts(values.as_mut_ptr().cast(), values.len(), values.capacity()) }
}
type PrimeSubfield = Gf2;
const ZERO: Self = Self(0);
const ONE: Self = Self(1);
const TWO: Self = Self(0);
const NEG_ONE: Self = Self(1);
#[inline]
fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
Self::from_bool(f.is_one())
}
#[inline]
fn from_bool(b: bool) -> Self {
Self(u64::from(b))
}
characteristic_two_methods!();
#[inline]
fn square(&self) -> Self {
Self(clmul::poly_square_64(self.0))
}
#[inline]
fn dot_product<const N: usize>(u: &[Self; N], v: &[Self; N]) -> Self {
Self(clmul::poly_dot_64(u.iter().zip(v).map(|(a, b)| (a.0, b.0))))
}
}
impl Field for Poly64 {
type Packing = Self;
const GENERATOR: Self = Self(clmul::tower_image_64(TOWER_GENERATOR));
#[inline]
fn try_inverse(&self) -> Option<Self> {
let inverse = self.invert_or_zero();
(self.0 != 0).then_some(inverse)
}
#[inline]
fn try_sqrt(&self) -> Option<Self> {
Some(Self(clmul::poly_sqrt_64(self.0)))
}
#[inline]
fn order() -> BigUint {
BigUint::from(1u8) << Self::BITS
}
#[inline]
fn bits() -> usize {
Self::BITS
}
#[inline]
fn interpolation_node(i: usize) -> Self {
Self(i as u64)
}
}
impl RawDataSerializable for Poly64 {
const NUM_BYTES: usize = 8;
#[inline]
fn into_bytes(self) -> impl IntoIterator<Item = u8> {
self.0.to_le_bytes()
}
}
impl crate::tower::private::Sealed for Poly64 {}
impl TowerLevel for Poly64 {
type Repr = u64;
const LOG_BITS: usize = 6;
#[inline]
fn from_repr(r: Self::Repr) -> Self {
Self(r)
}
#[inline]
fn to_repr(self) -> Self::Repr {
self.0
}
#[inline]
fn mul_alpha(self) -> Self {
self * Self(ALPHA)
}
#[inline]
fn from_le_byte_iter(mut bytes: impl Iterator<Item = u8>) -> Self {
let mut buffer = [0u8; 8];
for byte in &mut buffer {
*byte = bytes
.next()
.expect("byte stream ended before a whole element was read");
}
Self::from_le_bytes(buffer)
}
#[inline]
fn cantor_basis(i: usize) -> Self {
assert!(i < Self::BITS, "Cantor basis index out of range");
Self(CANTOR_BASIS[i])
}
}
impl From<BinaryField64> for Poly64 {
#[inline]
fn from(x: BinaryField64) -> Self {
Self(clmul::tower_to_poly_64(x.to_repr()))
}
}
impl From<Poly64> for BinaryField64 {
#[inline]
fn from(x: Poly64) -> Self {
Self::from_repr(clmul::poly_to_tower_64(x.0))
}
}
impl Add<BinaryField64> for Poly64 {
type Output = Self;
#[inline]
fn add(self, rhs: BinaryField64) -> Self {
self + Self::from(rhs)
}
}
impl Sub<BinaryField64> for Poly64 {
type Output = Self;
#[inline]
fn sub(self, rhs: BinaryField64) -> Self {
self - Self::from(rhs)
}
}
impl Mul<BinaryField64> for Poly64 {
type Output = Self;
#[inline]
fn mul(self, rhs: BinaryField64) -> Self {
self * Self::from(rhs)
}
}
impl Algebra<BinaryField64> for Poly64 {}
macro_rules! impl_narrow_algebra {
($($field:ty),* $(,)?) => {$(
impl From<$field> for Poly64 {
#[inline]
fn from(x: $field) -> Self {
Self::from(BinaryField64::from(x))
}
}
impl_add_base_field!(Poly64, $field);
impl_sub_base_field!(Poly64, $field);
impl_mul_base_field!(Poly64, $field);
impl Algebra<$field> for Poly64 {}
)*};
}
impl_narrow_algebra!(BinaryField8, BinaryField16, BinaryField32);
impl Add for Poly64 {
type Output = Self;
#[inline]
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self {
Self(self.0 ^ rhs.0)
}
}
impl Sub for Poly64 {
type Output = Self;
#[inline]
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self {
self + rhs
}
}
impl Neg for Poly64 {
type Output = Self;
#[inline]
fn neg(self) -> Self {
self
}
}
impl Mul for Poly64 {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self(clmul::poly_mul_64(self.0, rhs.0))
}
}
impl_add_assign!(Poly64);
impl_sub_assign!(Poly64);
impl_mul_methods!(Poly64);
impl_div_methods!(Poly64, Poly64);
ring_sum!(Poly64);
impl From<Gf2> for Poly64 {
#[inline]
fn from(x: Gf2) -> Self {
Self::from_prime_subfield(x)
}
}
impl_add_base_field!(Poly64, Gf2);
impl_sub_base_field!(Poly64, Gf2);
impl_mul_base_field!(Poly64, Gf2);
impl Algebra<Gf2> for Poly64 {}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use p3_field::{Algebra, Field, PrimeCharacteristicRing};
use proptest::prelude::*;
use super::Poly64;
use crate::tower::TowerLevel;
use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, Gf2};
#[test]
fn tower_table_conversion_and_narrow_algebras_match_the_tower() {
let table: Vec<BinaryField64> = (0..257)
.map(|i| BinaryField64::from_repr(i * 0x9e37_79b9))
.collect();
let expected: Vec<Poly64> = table.iter().copied().map(Poly64::from).collect();
let allocation = table.as_ptr();
let converted = Poly64::from_tower_vec(table);
assert_eq!(converted, expected);
assert_eq!(converted.as_ptr().cast::<BinaryField64>(), allocation);
const fn assert_algebra<F: PrimeCharacteristicRing, A: Algebra<F>>() {}
assert_algebra::<BinaryField8, Poly64>();
assert_algebra::<BinaryField16, Poly64>();
assert_algebra::<BinaryField32, Poly64>();
assert_algebra::<BinaryField64, Poly64>();
let x = BinaryField64::from_repr(0xd6e8_feb8_6659_fd93);
let x_poly = Poly64::from(x);
let scalar = BinaryField32::from_repr(0xa5c3_19e7);
assert_eq!(
BinaryField64::from(x_poly * scalar),
x * BinaryField64::from(scalar)
);
}
#[test]
fn the_change_of_basis_fixes_the_constants() {
assert_eq!(Poly64::from(BinaryField64::ZERO), Poly64::ZERO);
assert_eq!(Poly64::from(BinaryField64::ONE), Poly64::ONE);
}
#[test]
fn the_generator_is_the_image_of_the_tower_generator() {
assert_eq!(Poly64::GENERATOR, Poly64::from(BinaryField64::GENERATOR));
}
#[test]
fn the_generator_has_the_full_order() {
let order = u64::MAX;
for prime in [3u64, 5, 17, 257, 641, 65537, 6_700_417] {
assert_ne!(
Poly64::GENERATOR.exp_u64(order / prime),
Poly64::ONE,
"{prime}"
);
}
assert_eq!(Poly64::GENERATOR.exp_u64(order), Poly64::ONE);
}
#[test]
fn the_cantor_basis_satisfies_its_recurrence() {
assert_eq!(Poly64::cantor_basis(0), Poly64::ONE);
for i in 1..Poly64::BITS {
let v = Poly64::cantor_basis(i);
assert_eq!(v.square() + v, Poly64::cantor_basis(i - 1), "vector {i}");
}
for i in 0..Poly64::BITS {
assert_eq!(
Poly64::cantor_basis(i),
Poly64::from(BinaryField64::cantor_basis(i)),
"vector {i}"
);
}
}
#[test]
#[should_panic = "Cantor basis index out of range"]
fn the_cantor_basis_rejects_an_index_beyond_the_field() {
let _vector = Poly64::cantor_basis(64);
}
#[test]
#[should_panic = "byte stream ended before a whole element was read"]
fn a_truncated_byte_stream_is_rejected() {
let _element = Poly64::from_le_byte_iter([0u8; 7].into_iter());
}
#[test]
fn zero_vectors_preserve_layout_and_support_growth() {
for len in [0, 1, 33, 1024] {
let mut values = Poly64::zero_vec(len);
assert_eq!(values.len(), len);
assert!(values.iter().all(|x| *x == Poly64::ZERO));
values.push(Poly64::ONE);
values.reserve(100);
assert_eq!(values.pop(), Some(Poly64::ONE));
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(2000))]
#[test]
fn the_change_of_basis_round_trips(bits: u64) {
let x = Poly64::from_repr(bits);
prop_assert_eq!(Poly64::from(BinaryField64::from(x)), x);
}
#[test]
fn the_change_of_basis_is_a_ring_isomorphism(a: u64, b: u64) {
let (x, y) = (BinaryField64::from_repr(a), BinaryField64::from_repr(b));
prop_assert_eq!(Poly64::from(x + y), Poly64::from(x) + Poly64::from(y));
prop_assert_eq!(
Poly64::from(x.reference_mul(y)),
Poly64::from(x) * Poly64::from(y)
);
}
#[test]
fn scaling_by_the_generator_agrees_with_the_tower(bits: u64) {
let x = BinaryField64::from_repr(bits);
prop_assert_eq!(Poly64::from(x).mul_alpha(), Poly64::from(x.mul_alpha()));
}
#[test]
fn the_prime_subfield_embeds_as_zero_and_one(bit: bool) {
prop_assert_eq!(Poly64::from(Gf2::from_bool(bit)), Poly64::from_bool(bit));
}
#[test]
fn the_byte_stream_reads_back_what_the_element_wrote(bits: u64) {
use p3_field::RawDataSerializable;
let x = Poly64::from_repr(bits);
prop_assert_eq!(Poly64::from_le_byte_iter(x.into_bytes().into_iter()), x);
}
}
#[test]
fn serde_accepts_every_bit_pattern() {
for bits in [0u64, 1, u64::MAX, 1 << 63] {
let x = Poly64::from_repr(bits);
let encoded = serde_json::to_string(&x).unwrap();
assert_eq!(serde_json::from_str::<Poly64>(&encoded).unwrap(), x);
}
}
}