use alloc::vec::Vec;
use core::ops::{Add, Mul, Sub};
use core::{ptr, slice};
use p3_field::extension::HasFrobenius;
use p3_field::op_assign_macros::{impl_add_base_field, impl_sub_base_field};
use p3_field::{
Algebra, BasedVectorSpace, ExtensionField, Field, PackedFieldExtension, Powers,
PrimeCharacteristicRing,
};
use crate::tower::TowerLevel;
use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128};
macro_rules! binary_tower_extension {
($upper:ty, $upper_repr:ty, $lower:ty, $lower_repr:ty) => {
impl From<$lower> for $upper {
#[inline]
fn from(x: $lower) -> Self {
Self::from_repr(<$upper_repr>::from(x.to_repr()))
}
}
impl_add_base_field!($upper, $lower);
impl_sub_base_field!($upper, $lower);
impl Mul<$lower> for $upper {
type Output = Self;
#[inline]
fn mul(self, rhs: $lower) -> Self {
if crate::clmul::HAS_HARDWARE_CLMUL
&& matches!(<$upper>::BITS, 64 | 128)
&& matches!(<$lower>::BITS, 16 | 32)
{
return self * Self::from(rhs);
}
<Self as BasedVectorSpace<$lower>>::from_basis_coefficients_fn(|i| {
<$lower>::from_repr((self.to_repr() >> (i * <$lower>::BITS)) as $lower_repr) * rhs
})
}
}
impl Mul<$upper> for $lower {
type Output = $upper;
#[inline]
fn mul(self, rhs: $upper) -> $upper {
rhs * self
}
}
impl Algebra<$lower> for $upper {}
impl p3_field::AlgebraIdentity<$lower> for $upper {
fn algebra_id() -> Vec<u8> {
alloc::format!("p3-Wiedemann-tower-v1:chunk-basis:{}:{}", <$lower>::BITS, <$upper>::BITS).into_bytes()
}
}
impl BasedVectorSpace<$lower> for $upper {
const DIMENSION: usize = <$upper>::BITS / <$lower>::BITS;
#[inline]
fn as_basis_coefficients_slice(&self) -> &[$lower] {
const {
assert!(
cfg!(target_endian = "little"),
"the tower basis coincides with the memory layout only on little-endian targets"
);
assert!(<$lower>::BITS == <$lower_repr>::BITS as usize);
assert!(
size_of::<$upper>()
== <$upper as BasedVectorSpace<$lower>>::DIMENSION
* size_of::<$lower>()
);
assert!(align_of::<$lower>() <= align_of::<$upper>());
}
unsafe {
slice::from_raw_parts(
ptr::from_ref(self).cast::<$lower>(),
<$upper as BasedVectorSpace<$lower>>::DIMENSION,
)
}
}
#[inline]
fn from_basis_coefficients_fn<Fn: FnMut(usize) -> $lower>(mut f: Fn) -> Self {
let mut repr: $upper_repr = 0;
for i in 0..<$upper as BasedVectorSpace<$lower>>::DIMENSION {
repr |= <$upper_repr>::from(f(i).to_repr()) << (i * <$lower>::BITS);
}
Self::from_repr(repr)
}
#[inline]
fn flatten_to_base(vec: Vec<Self>) -> Vec<$lower> {
const {
assert!(
cfg!(target_endian = "little"),
"the tower basis coincides with the memory layout only on little-endian targets"
);
assert!(<$lower>::BITS == <$lower_repr>::BITS as usize);
assert!(
size_of::<$upper>()
== <$upper as BasedVectorSpace<$lower>>::DIMENSION
* size_of::<$lower>()
);
assert!(align_of::<$lower>() <= align_of::<$upper>());
}
let coefficients: &[$lower] = unsafe {
slice::from_raw_parts(
vec.as_ptr().cast::<$lower>(),
vec.len() * <$upper as BasedVectorSpace<$lower>>::DIMENSION,
)
};
coefficients.to_vec()
}
#[inline]
fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = $lower>>(
iter: I,
) -> Option<Self> {
let dimension = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
(iter.len() == dimension).then(|| {
let mut repr: $upper_repr = 0;
for (i, c) in (0..dimension).zip(iter) {
repr |= <$upper_repr>::from(c.to_repr()) << (i * <$lower>::BITS);
}
Self::from_repr(repr)
})
}
#[cfg(target_endian = "little")]
#[inline]
fn reconstitute_from_base(vec: Vec<$lower>) -> Vec<Self> {
let dimension = <Self as BasedVectorSpace<$lower>>::DIMENSION;
assert_eq!(vec.len() % dimension, 0);
let len = vec.len() / dimension;
let mut result = Vec::<Self>::with_capacity(len);
unsafe {
ptr::copy_nonoverlapping(
vec.as_ptr().cast::<u8>(),
result.as_mut_ptr().cast::<u8>(),
vec.len() * size_of::<$lower>(),
);
result.set_len(len);
}
result
}
}
impl ExtensionField<$lower> for $upper {
type ExtensionPacking = Self;
#[inline]
fn is_in_basefield(&self) -> bool {
(self.to_repr() >> <$lower>::BITS) == 0
}
#[inline]
fn as_base(&self) -> Option<$lower> {
<Self as ExtensionField<$lower>>::is_in_basefield(self)
.then(|| <$lower>::from_repr(self.to_repr() as $lower_repr))
}
}
impl PackedFieldExtension<$lower, $upper> for $upper {
#[inline]
fn from_ext_fn(f: impl Fn(usize) -> Self) -> Self {
f(0)
}
#[inline]
fn from_ext_slice(slice: &[Self]) -> Self {
assert_eq!(slice.len(), 1);
slice[0]
}
#[inline]
fn extract(&self, lane: usize) -> Self {
assert_eq!(lane, 0, "lane index out of range");
*self
}
#[inline]
fn add_assign_lane(&mut self, lane: usize, value: Self) {
assert_eq!(lane, 0, "lane index out of range");
*self += value;
}
#[inline]
fn packed_ext_powers(base: Self) -> Powers<Self> {
base.powers()
}
}
impl HasFrobenius<$lower> for $upper {
#[inline]
fn frobenius(&self) -> Self {
if <$upper as BasedVectorSpace<$lower>>::DIMENSION == 2 {
let (a0, a1) = self.split();
Self::join(a0 + a1.mul_alpha(), a1)
} else {
self.exp_power_of_2(<$lower>::BITS)
}
}
#[inline]
fn repeated_frobenius(&self, count: usize) -> Self {
let count = count % <$upper as BasedVectorSpace<$lower>>::DIMENSION;
if <$upper as BasedVectorSpace<$lower>>::DIMENSION == 2 {
if count == 0 {
*self
} else {
<Self as HasFrobenius<$lower>>::frobenius(self)
}
} else {
self.exp_power_of_2(<$lower>::BITS * count)
}
}
#[inline]
fn pseudo_inv(&self) -> Self {
self.try_inverse().unwrap_or(Self::ZERO)
}
}
};
}
binary_tower_extension!(BinaryField16, u16, BinaryField8, u8);
binary_tower_extension!(BinaryField32, u32, BinaryField8, u8);
binary_tower_extension!(BinaryField64, u64, BinaryField8, u8);
binary_tower_extension!(BinaryField128, u128, BinaryField8, u8);
binary_tower_extension!(BinaryField32, u32, BinaryField16, u16);
binary_tower_extension!(BinaryField64, u64, BinaryField16, u16);
binary_tower_extension!(BinaryField128, u128, BinaryField16, u16);
binary_tower_extension!(BinaryField64, u64, BinaryField32, u32);
binary_tower_extension!(BinaryField128, u128, BinaryField32, u32);
binary_tower_extension!(BinaryField128, u128, BinaryField64, u64);
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use p3_field::extension::HasFrobenius;
use p3_field::{BasedVectorSpace, ExtensionField, PrimeCharacteristicRing};
use proptest::prelude::*;
use crate::tower::TowerLevel;
use crate::{BinaryField8, BinaryField16, BinaryField32, BinaryField64, BinaryField128};
const A128: u128 = 0x1234_5678_9abc_def0_0fed_cba9_8765_4321;
macro_rules! assert_basis_round_trip {
($upper:ty, $lower:ty, $val:expr) => {{
let a = <$upper>::from_repr($val);
let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
let coeffs = BasedVectorSpace::<$lower>::as_basis_coefficients_slice(&a);
assert_eq!(coeffs.len(), dim);
for len in [0, 1, 19] {
let values = alloc::vec![a; len];
let flat = <$upper as BasedVectorSpace<$lower>>::flatten_to_base(values.clone());
assert_eq!(<$upper as BasedVectorSpace<$lower>>::reconstitute_from_base(flat), values);
}
let recomposed = (0..dim)
.map(|i| {
<$upper as BasedVectorSpace<$lower>>::ith_basis_element(i).unwrap() * coeffs[i]
})
.sum::<$upper>();
assert_eq!(recomposed, a);
assert_eq!(
<$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_slice(coeffs),
Some(a)
);
assert_eq!(
<$upper as BasedVectorSpace<$lower>>::from_basis_coefficients_fn(|i| coeffs[i]),
a
);
}};
}
macro_rules! assert_mixed_ops_match_embedding {
($upper:ty, $lower:ty, $val:expr, $scalar:expr) => {{
let a = <$upper>::from_repr($val);
let s = <$lower>::from_repr($scalar);
let s_up = <$upper>::from(s);
assert_eq!(a + s, a + s_up);
assert_eq!(a - s, a - s_up);
assert_eq!(a * s, a * s_up);
assert_eq!(s * a, a * s_up);
let mut acc = a;
acc += s;
assert_eq!(acc, a + s_up);
let mut acc = a;
acc -= s;
assert_eq!(acc, a - s_up);
let mut acc = a;
acc *= s;
assert_eq!(acc, a * s_up);
}};
}
macro_rules! assert_frobenius_over {
($upper:ty, $lower:ty, $val:expr) => {{
let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
for i in 0..=u8::MAX {
let embedded = <$upper>::from(<$lower>::from_repr(i.into()));
assert_eq!(
HasFrobenius::<$lower>::frobenius(&embedded),
embedded,
"Frobenius must fix the embedded base field"
);
}
let x = <$upper as BasedVectorSpace<$lower>>::ith_basis_element(1).unwrap();
assert_ne!(
HasFrobenius::<$lower>::frobenius(&x),
x,
"Frobenius must move elements outside the base field"
);
let a = <$upper>::from_repr($val);
let mut acc = a;
for count in 0..(2 * dim + 3) {
assert_eq!(HasFrobenius::<$lower>::repeated_frobenius(&a, count), acc);
acc = HasFrobenius::<$lower>::frobenius(&acc);
}
assert_eq!(
HasFrobenius::<$lower>::pseudo_inv(&<$upper>::ZERO),
<$upper>::ZERO
);
assert_eq!(a * HasFrobenius::<$lower>::pseudo_inv(&a), <$upper>::ONE);
}};
}
#[test]
fn gf2_128_over_gf2_8_basis_coefficients_are_bytes() {
let a = BinaryField128::from_repr(0x0f0e_0d0c_0b0a_0908_0706_0504_0302_0100);
let coeffs = BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(&a);
assert_eq!(coeffs.len(), 16);
for (i, c) in coeffs.iter().enumerate() {
assert_eq!(*c, BinaryField8::from_repr(i as u8));
}
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField8>>::from_basis_coefficients_slice(
coeffs
),
Some(a)
);
}
#[test]
fn mixed_mul_matches_embedding() {
let a = BinaryField128::from_repr(A128);
let s = BinaryField8::from_repr(0xa7);
assert_eq!(a * s, a * BinaryField128::from(s));
}
#[test]
fn dimension_is_the_ratio_of_bit_widths() {
assert_eq!(
<BinaryField16 as BasedVectorSpace<BinaryField8>>::DIMENSION,
2
);
assert_eq!(
<BinaryField32 as BasedVectorSpace<BinaryField8>>::DIMENSION,
4
);
assert_eq!(
<BinaryField64 as BasedVectorSpace<BinaryField8>>::DIMENSION,
8
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField8>>::DIMENSION,
16
);
assert_eq!(
<BinaryField32 as BasedVectorSpace<BinaryField16>>::DIMENSION,
2
);
assert_eq!(
<BinaryField64 as BasedVectorSpace<BinaryField16>>::DIMENSION,
4
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField16>>::DIMENSION,
8
);
assert_eq!(
<BinaryField64 as BasedVectorSpace<BinaryField32>>::DIMENSION,
2
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField32>>::DIMENSION,
4
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField64>>::DIMENSION,
2
);
}
#[test]
fn basis_coefficients_round_trip_on_every_pair() {
assert_basis_round_trip!(BinaryField16, BinaryField8, 0xfedc);
assert_basis_round_trip!(BinaryField32, BinaryField8, 0xfedc_ba98);
assert_basis_round_trip!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
assert_basis_round_trip!(BinaryField128, BinaryField8, A128);
assert_basis_round_trip!(BinaryField32, BinaryField16, 0xfedc_ba98);
assert_basis_round_trip!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
assert_basis_round_trip!(BinaryField128, BinaryField16, A128);
assert_basis_round_trip!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
assert_basis_round_trip!(BinaryField128, BinaryField32, A128);
assert_basis_round_trip!(BinaryField128, BinaryField64, A128);
}
#[test]
fn mixed_ops_match_embedding_on_every_pair() {
assert_mixed_ops_match_embedding!(BinaryField16, BinaryField8, 0xfedc, 0xa7);
assert_mixed_ops_match_embedding!(BinaryField32, BinaryField8, 0xfedc_ba98, 0xa7);
assert_mixed_ops_match_embedding!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210, 0xa7);
assert_mixed_ops_match_embedding!(BinaryField128, BinaryField8, A128, 0xa7);
assert_mixed_ops_match_embedding!(BinaryField32, BinaryField16, 0xfedc_ba98, 0xa73b);
assert_mixed_ops_match_embedding!(
BinaryField64,
BinaryField16,
0xfedc_ba98_7654_3210,
0xa73b
);
assert_mixed_ops_match_embedding!(BinaryField128, BinaryField16, A128, 0xa73b);
assert_mixed_ops_match_embedding!(
BinaryField64,
BinaryField32,
0xfedc_ba98_7654_3210,
0xa73b_c91d
);
assert_mixed_ops_match_embedding!(BinaryField128, BinaryField32, A128, 0xa73b_c91d);
assert_mixed_ops_match_embedding!(
BinaryField128,
BinaryField64,
A128,
0xa73b_c91d_5566_7788
);
}
#[test]
fn frobenius_fixes_exactly_the_lower_level_on_every_pair() {
assert_frobenius_over!(BinaryField16, BinaryField8, 0xfedc);
assert_frobenius_over!(BinaryField32, BinaryField8, 0xfedc_ba98);
assert_frobenius_over!(BinaryField64, BinaryField8, 0xfedc_ba98_7654_3210);
assert_frobenius_over!(BinaryField128, BinaryField8, A128);
assert_frobenius_over!(BinaryField32, BinaryField16, 0xfedc_ba98);
assert_frobenius_over!(BinaryField64, BinaryField16, 0xfedc_ba98_7654_3210);
assert_frobenius_over!(BinaryField128, BinaryField16, A128);
assert_frobenius_over!(BinaryField64, BinaryField32, 0xfedc_ba98_7654_3210);
assert_frobenius_over!(BinaryField128, BinaryField32, A128);
assert_frobenius_over!(BinaryField128, BinaryField64, A128);
}
#[test]
fn frobenius_exponent_is_the_order_of_the_base_field() {
let a = BinaryField128::from_repr(A128);
assert_eq!(
HasFrobenius::<BinaryField8>::frobenius(&a),
a.exp_power_of_2(8)
);
assert_eq!(
HasFrobenius::<BinaryField64>::frobenius(&a),
a.exp_power_of_2(64)
);
}
#[test]
fn is_in_basefield_detects_the_embedded_subfield() {
let low = BinaryField128::from(BinaryField8::from_repr(0xa7));
assert!(ExtensionField::<BinaryField8>::is_in_basefield(&low));
assert_eq!(
ExtensionField::<BinaryField8>::as_base(&low),
Some(BinaryField8::from_repr(0xa7))
);
let mid = BinaryField128::from_repr(0xa73b);
assert!(!ExtensionField::<BinaryField8>::is_in_basefield(&mid));
assert_eq!(ExtensionField::<BinaryField8>::as_base(&mid), None);
assert!(ExtensionField::<BinaryField16>::is_in_basefield(&mid));
assert_eq!(
ExtensionField::<BinaryField16>::as_base(&mid),
Some(BinaryField16::from_repr(0xa73b))
);
}
#[test]
fn from_basis_coefficients_iter_checks_the_length() {
let coeffs = [BinaryField64::ONE, BinaryField64::ZERO];
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
coeffs.into_iter()
),
Some(BinaryField128::ONE)
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_iter(
coeffs[..1].iter().copied()
),
None
);
assert_eq!(
<BinaryField128 as BasedVectorSpace<BinaryField64>>::from_basis_coefficients_slice(&[]),
None
);
}
#[test]
fn flatten_and_reconstitute_round_trip() {
let elems: Vec<BinaryField128> = (0..64u128)
.map(|i| {
BinaryField128::from_repr(i.wrapping_mul(0x9e37_79b9_7f4a_7c15_f39c_c060_5ced_c834))
})
.collect();
for pos in 0..16 {
assert!(
elems.iter().any(|e| {
BasedVectorSpace::<BinaryField8>::as_basis_coefficients_slice(e)[pos]
!= BinaryField8::ZERO
}),
"coefficient position {pos} is never nonzero"
);
}
let flat =
<BinaryField128 as BasedVectorSpace<BinaryField8>>::flatten_to_base(elems.clone());
assert_eq!(flat.len(), elems.len() * 16);
for (i, e) in elems.iter().enumerate() {
assert_eq!(&flat[i * 16..(i + 1) * 16], e.as_basis_coefficients_slice());
}
let back = <BinaryField128 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(flat);
assert_eq!(back, elems);
}
#[test]
#[should_panic]
fn reconstitute_rejects_an_incomplete_element() {
let _ = <BinaryField128 as BasedVectorSpace<BinaryField64>>::reconstitute_from_base(
alloc::vec![BinaryField64::ONE],
);
}
#[test]
fn reconstitute_is_exhaustive_over_the_byte_level() {
let all: Vec<BinaryField8> = (0..=u8::MAX).map(BinaryField8::from_repr).collect();
let packed =
<BinaryField16 as BasedVectorSpace<BinaryField8>>::reconstitute_from_base(all.clone());
assert_eq!(packed.len(), 128);
let flat = <BinaryField16 as BasedVectorSpace<BinaryField8>>::flatten_to_base(packed);
assert_eq!(flat, all);
}
macro_rules! assert_frobenius_is_repeated_squaring {
($upper:ty, $lower:ty, $val:expr) => {{
let a = <$upper>::from_repr($val);
let dim = <$upper as BasedVectorSpace<$lower>>::DIMENSION;
assert_eq!(dim, 2, "the specialisation only covers adjacent levels");
prop_assert_eq!(
HasFrobenius::<$lower>::frobenius(&a),
a.exp_power_of_2(<$lower>::BITS)
);
for count in 0..(2 * dim + 3) {
prop_assert_eq!(
HasFrobenius::<$lower>::repeated_frobenius(&a, count),
a.exp_power_of_2(<$lower>::BITS * (count % dim))
);
}
}};
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(2000))]
#[test]
fn frobenius_over_the_adjacent_level_is_repeated_squaring(
a in any::<u128>(),
) {
assert_frobenius_is_repeated_squaring!(BinaryField16, BinaryField8, a as u16);
assert_frobenius_is_repeated_squaring!(BinaryField32, BinaryField16, a as u32);
assert_frobenius_is_repeated_squaring!(BinaryField64, BinaryField32, a as u64);
assert_frobenius_is_repeated_squaring!(BinaryField128, BinaryField64, a);
}
#[test]
fn frobenius_over_a_deeper_level_is_repeated_squaring(a in any::<u128>()) {
macro_rules! assert_unspecialised {
($upper:ty, $lower:ty, $val:expr) => {{
let x = <$upper>::from_repr($val);
prop_assert_eq!(
HasFrobenius::<$lower>::frobenius(&x),
x.exp_power_of_2(<$lower>::BITS)
);
}};
}
assert_unspecialised!(BinaryField32, BinaryField8, a as u32);
assert_unspecialised!(BinaryField64, BinaryField8, a as u64);
assert_unspecialised!(BinaryField128, BinaryField8, a);
assert_unspecialised!(BinaryField64, BinaryField16, a as u64);
assert_unspecialised!(BinaryField128, BinaryField16, a);
assert_unspecialised!(BinaryField128, BinaryField32, a);
}
}
macro_rules! assert_flatten_matches_concatenation {
($upper:ty, $lower:ty, $vals:expr) => {{
let elements: Vec<$upper> = $vals.iter().map(|&v| <$upper>::from_repr(v)).collect();
let expected: Vec<$lower> = elements
.iter()
.flat_map(|x| {
<$upper as BasedVectorSpace<$lower>>::as_basis_coefficients_slice(x).to_vec()
})
.collect();
let flat = <$upper as BasedVectorSpace<$lower>>::flatten_to_base(elements.clone());
assert_eq!(flat, expected);
assert_eq!(
flat.len(),
elements.len() * <$upper as BasedVectorSpace<$lower>>::DIMENSION
);
assert_eq!(
<$upper as BasedVectorSpace<$lower>>::reconstitute_from_base(flat),
elements
);
}};
}
#[test]
fn flatten_to_base_concatenates_the_coefficients_on_every_pair() {
assert_flatten_matches_concatenation!(BinaryField128, BinaryField8, [0u128; 0]);
let vals16 = [0u16, 1, 0xfedc, 0xffff];
let vals32 = [0u32, 1, 0xfedc_ba98, 0xffff_ffff];
let vals64 = [0u64, 1, 0xfedc_ba98_7654_3210, u64::MAX];
let vals128 = [0u128, 1, A128, u128::MAX];
assert_flatten_matches_concatenation!(BinaryField16, BinaryField8, vals16);
assert_flatten_matches_concatenation!(BinaryField32, BinaryField8, vals32);
assert_flatten_matches_concatenation!(BinaryField64, BinaryField8, vals64);
assert_flatten_matches_concatenation!(BinaryField128, BinaryField8, vals128);
assert_flatten_matches_concatenation!(BinaryField32, BinaryField16, vals32);
assert_flatten_matches_concatenation!(BinaryField64, BinaryField16, vals64);
assert_flatten_matches_concatenation!(BinaryField128, BinaryField16, vals128);
assert_flatten_matches_concatenation!(BinaryField64, BinaryField32, vals64);
assert_flatten_matches_concatenation!(BinaryField128, BinaryField32, vals128);
assert_flatten_matches_concatenation!(BinaryField128, BinaryField64, vals128);
}
#[test]
fn embedding_is_a_ring_homomorphism() {
for a in 0..=u8::MAX {
for b in 0..=u8::MAX {
let (x, y) = (BinaryField8::from_repr(a), BinaryField8::from_repr(b));
assert_eq!(
BinaryField128::from(x * y),
BinaryField128::from(x) * BinaryField128::from(y)
);
assert_eq!(
BinaryField128::from(x + y),
BinaryField128::from(x) + BinaryField128::from(y)
);
}
}
assert_eq!(BinaryField128::from(BinaryField8::ONE), BinaryField128::ONE);
}
}