use crate::field::extension::Descriptor;
use crate::field::extension::descriptor::PrimePresentation;
use crate::field::{
Elem, Field, FieldBuffer, PrimeCharacteristic, PrimeIdentity, RangeError, Validate,
};
use super::{FiniteField, FiniteFieldRepr, checked_order};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct Residue;
impl super::private::Sealed for Residue {}
impl<const P: u64> FiniteFieldRepr<P, 1> for Residue {
const NAME: &'static str = "residue";
const VALID: () = {
checked_order::<P, 1>();
};
}
impl<const P: u64> super::private::Sealed for FiniteField<P, 1, Residue> {}
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
const fn add_body<const P: u64>(left: u64, right: u64) -> u64 {
let sum = left as u128 + right as u128;
let modulus = P as u128;
if sum >= modulus {
(sum - modulus) as u64
} else {
sum as u64
}
}
const fn sub_body<const P: u64>(left: u64, right: u64) -> u64 {
if left >= right {
left - right
} else {
P - (right - left)
}
}
const fn neg_body<const P: u64>(value: u64) -> u64 {
if value == 0 { 0 } else { P - value }
}
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
const fn mul_body<const P: u64>(left: u64, right: u64) -> u64 {
(left as u128 * right as u128 % P as u128) as u64
}
const fn pow_body<const P: u64>(mut base: u64, mut exponent: u128) -> u64 {
let mut result = 1;
while exponent != 0 {
if exponent & 1 != 0 {
result = mul_body::<P>(result, base);
}
exponent >>= 1;
if exponent != 0 {
base = mul_body::<P>(base, base);
}
}
result
}
#[allow(clippy::cast_lossless)]
const fn inv_body<const P: u64>(value: u64) -> u64 {
if value == 0 {
0
} else {
pow_body::<P>(value, P as u128 - 2)
}
}
const fn reduce_body<const P: u64>(value: u64) -> u64 {
value % P
}
const fn lane_bytes(characteristic: u64) -> usize {
if characteristic == 2 {
8
} else if characteristic <= (1 << 8) {
1
} else if characteristic <= (1 << 16) {
2
} else if characteristic <= (1 << 32) {
4
} else {
8
}
}
#[allow(clippy::cast_lossless)]
const fn read_lane(lane: &[u8]) -> u64 {
let mut word = 0;
let mut index = lane.len();
while index > 0 {
index -= 1;
word = (word << 8) | (lane[index] as u64);
}
word
}
impl<const P: u64> Field for FiniteField<P, 1, Residue> {
type Raw = u64;
type Characteristic = PrimeCharacteristic<P>;
const NAME: &'static str = "GF(p)";
const DEGREE: u32 = {
checked_order::<P, 1>();
1
};
const ORDER: u128 = checked_order::<P, 1>();
const ZERO_RAW: u64 = {
let () = <Self as Field>::VALID;
0
};
const ONE_RAW: u64 = {
let () = <Self as Field>::VALID;
1
};
const VALID: () = {
checked_order::<P, 1>();
let () = <Residue as FiniteFieldRepr<P, 1>>::VALID;
};
#[inline]
fn canonical_raw(raw: u64) -> u64 {
let () = Validate::<Self>::OK;
reduce_body::<P>(raw)
}
#[inline]
fn add_raw(left: u64, right: u64) -> u64 {
let () = Validate::<Self>::OK;
add_body::<P>(left, right)
}
#[inline]
fn sub_raw(left: u64, right: u64) -> u64 {
let () = Validate::<Self>::OK;
sub_body::<P>(left, right)
}
#[inline]
fn neg_raw(value: u64) -> u64 {
let () = Validate::<Self>::OK;
neg_body::<P>(value)
}
#[inline]
fn mul_raw(left: u64, right: u64) -> u64 {
let () = Validate::<Self>::OK;
mul_body::<P>(left, right)
}
#[inline]
fn inv_raw(value: u64) -> u64 {
let () = Validate::<Self>::OK;
inv_body::<P>(value)
}
}
impl<const P: u64> super::super::private::RowAccess for FiniteField<P, 1, Residue> {
const DESCRIPTOR: &'static Descriptor = &Descriptor::prime(P, PrimePresentation::Residue);
const BINARY_DESCRIPTION: Option<
&'static super::super::binary::description::BinaryDescription,
> = None;
fn prime_coordinates_into(dst: &mut [u64], raw: <Self as Field>::Raw) {
let () = Validate::<Self>::OK;
assert_eq!(
dst.len(),
1,
"prime coordinate count differs from the field degree"
);
dst[0] = reduce_body::<P>(raw);
}
fn from_prime_coordinates(src: &[u64]) -> <Self as Field>::Raw {
let () = Validate::<Self>::OK;
assert_eq!(
src.len(),
1,
"prime coordinate count differs from the field degree"
);
assert!(
src[0] < P,
"prime coordinate is not below the characteristic"
);
src[0]
}
fn from_prime(value: u64) -> <Self as Field>::Raw {
Elem::<Self>::from_raw(value).to_raw()
}
fn to_prime(raw: <Self as Field>::Raw) -> Option<u64> {
Some(reduce_body::<P>(raw))
}
fn coordinate_word(raw: <Self as Field>::Raw) -> u64 {
let () = Validate::<Self>::OK;
assert!(P == 2, "coordinate word on a row of odd characteristic");
reduce_body::<P>(raw)
}
fn from_coordinate_word(word: u64) -> <Self as Field>::Raw {
let () = Validate::<Self>::OK;
assert!(P == 2, "coordinate word on a row of odd characteristic");
assert!(
word < 2,
"coordinate word holds bits at or above the field degree"
);
word
}
}
impl<const P: u64> FieldBuffer for FiniteField<P, 1, Residue> {
const BYTES: usize = lane_bytes(P);
#[allow(clippy::cast_possible_truncation)]
const STORAGE_BITS: u32 = (8 * lane_bytes(P)) as u32;
#[inline]
fn decode(bytes: &[u8]) -> Elem<Self> {
assert_eq!(
bytes.len(),
Self::BYTES,
"GF(p) element has the wrong byte width"
);
Elem::<Self>::from_raw(read_lane(bytes))
}
#[inline]
fn encode(bytes: &mut [u8], value: Elem<Self>) {
assert_eq!(
bytes.len(),
Self::BYTES,
"GF(p) element has the wrong byte width"
);
let raw = value.to_raw().to_le_bytes();
bytes.copy_from_slice(&raw[..Self::BYTES]);
}
}
impl<const P: u64> Elem<FiniteField<P, 1, Residue>> {
#[inline]
#[must_use]
pub const fn from_raw(value: u64) -> Self {
let () = Validate::<FiniteField<P, 1, Residue>>::OK;
Elem {
raw: reduce_body::<P>(value),
}
}
#[inline]
pub const fn from_residue(value: u64) -> Result<Self, RangeError> {
match super::super::check_residue(
value,
<PrimeCharacteristic<P> as PrimeIdentity>::CHARACTERISTIC,
) {
Ok(checked) => Ok(Self::from_raw(checked)),
Err(error) => Err(error),
}
}
#[inline]
#[must_use]
pub const fn to_residue(self) -> Option<u64> {
Some(self.raw)
}
#[inline]
#[must_use]
pub const fn add(self, rhs: Self) -> Self {
Elem {
raw: add_body::<P>(self.raw, rhs.raw),
}
}
#[inline]
#[must_use]
pub const fn sub(self, rhs: Self) -> Self {
Elem {
raw: sub_body::<P>(self.raw, rhs.raw),
}
}
#[inline]
#[must_use]
pub const fn neg(self) -> Self {
Elem {
raw: neg_body::<P>(self.raw),
}
}
#[inline]
#[must_use]
pub const fn mul(self, rhs: Self) -> Self {
Elem {
raw: mul_body::<P>(self.raw, rhs.raw),
}
}
#[inline]
#[must_use]
pub const fn square(self) -> Self {
self.mul(self)
}
#[inline]
#[must_use]
pub const fn inv(self) -> Self {
Elem {
raw: inv_body::<P>(self.raw),
}
}
#[inline]
#[must_use]
pub const fn div(self, rhs: Self) -> Self {
if rhs.raw == 0 {
return Self::ZERO;
}
self.mul(rhs.inv())
}
#[inline]
#[must_use]
pub const fn pow(self, exponent: u128) -> Self {
Elem {
raw: pow_body::<P>(self.raw, exponent),
}
}
#[inline]
#[must_use]
pub const fn canonical(self) -> Self {
Self::from_raw(self.raw)
}
#[inline]
#[must_use]
pub const fn is_zero(self) -> bool {
self.raw == 0
}
#[inline]
#[must_use]
pub const fn is_one(self) -> bool {
self.raw == 1
}
}
impl<const P: u64> core::fmt::Display for Elem<FiniteField<P, 1, Residue>> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "{}", self.raw)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lane_bytes_switches_at_each_capacity() {
let cases: [(u64, usize); 12] = [
(0, 1),
(1, 1),
(2, 8),
(3, 1),
(1 << 8, 1),
((1 << 8) + 1, 2),
(1 << 16, 2),
((1 << 16) + 1, 4),
(1 << 32, 4),
((1 << 32) + 1, 8),
(u64::MAX - 58, 8),
(u64::MAX, 8),
];
for (characteristic, bytes) in cases {
assert_eq!(lane_bytes(characteristic), bytes, "{characteristic}");
}
}
#[test]
fn read_lane_is_little_endian() {
assert_eq!(read_lane(&[]), 0);
assert_eq!(read_lane(&[0xAB]), 0xAB);
assert_eq!(read_lane(&[0x34, 0x12]), 0x1234);
assert_eq!(read_lane(&[0xFF; 8]), u64::MAX);
}
#[test]
fn identity_predicates() {
type Seven = FiniteField<7, 1, Residue>;
assert!(Elem::<Seven>::ONE.is_one());
assert!(!Elem::<Seven>::ZERO.is_one());
assert!(Elem::<Seven>::ZERO.is_zero());
assert!(!Elem::<Seven>::from_raw(8).is_zero());
assert!(Elem::<Seven>::from_raw(8).is_one());
}
}