use core::{ops::{Add, Sub, Mul, Rem, Neg}, fmt::Debug};
use cryptix_bigint::{
ops::{BigIntOps, BigIntOpsExt, slice::{slice_mul_dig, slice_add_inplace}},
property::IsBigInt,
digit::Digit, BigUInt
};
use crate::{PrimeModular, Element, field::montgomery::MontgomeryOps};
use crate::group::*;
use crate::ring::*;
use crate::field::*;
use crate::field::montgomery::Montgomery;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct FpElement<I, M: PrimeModular<I>>(pub Element<I, M>);
impl<I: Debug, M: PrimeModular<I>> Debug for FpElement<I, M> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.0.fmt(f)
}
}
impl<I: Copy, M: PrimeModular<I>> Clone for FpElement<I, M> {
fn clone(&self) -> Self {
*self
}
}
impl<I: Copy, M: PrimeModular<I>> Copy for FpElement<I, M> { }
impl<I, M: PrimeModular<I>> From<FpElement<I, M>> for Element<I, M> {
#[inline(always)]
fn from(value: FpElement<I, M>) -> Self {
value.0
}
}
impl<I, M: PrimeModular<I>> FpElement<I, M> {
#[inline(always)]
pub fn new_unchecked(value: I) -> Self {
Self(Element::new_unchecked(value))
}
}
impl<I: Copy, M: PrimeModular<I>> FpElement<I, M> {
pub fn repr(self) -> I {
self.0.0
}
}
impl<I, M: PrimeModular<I>> From<I> for FpElement<I, M>
where
I: Rem<Output = I>
{
fn from(value: I) -> Self {
FpElement(Element::new(value))
}
}
impl<I, M> AbelianGroup for FpElement<I, M>
where
M: PrimeModular<I>,
Self: Group + CommunicativeAdd
{ }
impl<I, M> Group for FpElement<I, M>
where
M: PrimeModular<I>,
I: BigIntOps
{ }
impl<I, M> Add for FpElement<I, M>
where
M: PrimeModular<I>,
I: BigIntOps
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl<I, M> Sub for FpElement<I, M>
where
M: PrimeModular<I>,
I: BigIntOps
{
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl<I, M> AddIdentity for FpElement<I, M>
where
M: PrimeModular<I>,
I: BigIntOps + IsBigInt
{
const ADD_IDENTITY: Self = Self(Element::ZERO);
}
impl<I, M> Neg for FpElement<I, M>
where
M: PrimeModular<I>,
I: BigIntOps
{
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl<I, M> AssociativeAdd for FpElement<I, M>
where
M: PrimeModular<I>,
Self: Add<Output = Self>
{ }
impl<I, M> CommunicativeAdd for FpElement<I, M>
where
M: PrimeModular<I>,
Self: Add<Output = Self>
{ }
impl<I, M> Ring for FpElement<I, M>
where
M: PrimeModular<I>,
Self: Mul<Output = Self> + AssociativeMul + DistributiveMul + AbelianGroup
{ }
impl<I, M> Mul for FpElement<I, M>
where
M: Montgomery<I>,
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
{
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self.mont_mul(rhs).mont_form()
}
}
impl<I, M> AssociativeMul for FpElement<I, M>
where
M: Montgomery<I>,
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
{ }
impl<I, M> DistributiveMul for FpElement<I, M>
where
M: Montgomery<I>,
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
{ }
impl<I, M> MulIdentity for FpElement<I, M>
where
I: BigIntOpsExt + IsBigInt,
M: Montgomery<I>,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
{
const MUL_IDENTITY: Self = Self(Element(I::ONE, core::marker::PhantomData));
}
impl<I, M> CommunicativeMul for FpElement<I, M>
where
M: Montgomery<I>,
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
{ }
impl<I, M> MulInverse for FpElement<I, M>
where
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
M: Montgomery<I>
{
fn mul_inv(self) -> Self {
let ar = self.mont_mul(M::RR_P);
let am1r = ar.mont_inv();
am1r.mont_rdc()
}
}
impl<I, M> Field for FpElement<I, M>
where
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
M: Montgomery<I>
{
fn hlv(self) -> Self {
if !self.repr().is_odd() {
return Self::new_unchecked(self.repr() >> 1)
}
let (tmp, c) = self.repr() + M::P;
if c.is_zero() {
Self::new_unchecked(tmp >> 1)
} else {
Self::new_unchecked((tmp >> 1).set_bit(I::BIT_LEN - 1, true))
}
}
fn is_zero(&self) -> bool {
self.0.repr().is_zero()
}
}
impl<I, M> MontgomeryOps<I, M> for FpElement<I, M>
where
I: BigIntOpsExt,
[(); I::DIG_LEN + 1]: ,
[(); I::DIG_LEN * 2 + 1]: ,
M: Montgomery<I>,
{
fn mont_mul(self, rhs: Self) -> Self {
let y0 = rhs.repr().as_slice()[0];
let mut buf = BigUInt([I::Dig::ZERO; I::DIG_LEN * 2 + 1]);
self.repr().as_slice().iter().enumerate().for_each(
|(idx, &xi)| {
let a0 = buf[idx];
let ui = M::NEG_P_INV_B
.overflow_mul(xi.overflow_mul(y0).0.overflow_add(a0).0)
.0;
let mut tmp = [I::Dig::ZERO; I::DIG_LEN + 1];
slice_mul_dig(&mut tmp, rhs.repr().as_slice(), xi);
slice_add_inplace(&mut buf[idx..I::DIG_LEN + idx + 1], &tmp);
slice_mul_dig(&mut tmp, M::P.as_slice(), ui);
slice_add_inplace(&mut buf[idx..I::DIG_LEN + idx + 1], &tmp);
},
);
let output = I::from_array(
buf[I::DIG_LEN..I::DIG_LEN * 2].try_into().unwrap()
);
if !buf[I::DIG_LEN * 2].is_zero() || output >= M::P {
FpElement(Element::new_unchecked((output - M::P).0))
} else {
FpElement(Element::new_unchecked(output))
}
}
fn mont_rdc(self) -> Self {
let mut a = BigUInt([I::Dig::ZERO; I::DIG_LEN * 2]);
a[..I::DIG_LEN].copy_from_slice(self.repr().as_slice());
let a = (0..I::DIG_LEN).fold(a, |a, idx| {
let ui = M::NEG_P_INV_B.overflow_mul(a[idx]).0;
let mut tmp = BigUInt([I::Dig::ZERO; I::DIG_LEN * 2]);
slice_mul_dig(&mut tmp[idx..idx + I::DIG_LEN + 1], M::P.as_slice(), ui);
(a + tmp).0
});
let output = I::from_array(a.0[I::DIG_LEN..].try_into().unwrap());
if output >= M::P {
FpElement(Element::new_unchecked((output - M::P).0))
} else {
FpElement(Element::new_unchecked(output))
}
}
fn mont_inv(self) -> Self {
let mut r = Self::ZERO;
let mut u = M::P;
let mut s = M::RR_P;
let mut v = self.repr();
while !v.is_zero() {
if !u.is_odd() {
u = u >> 1;
r = r.hlv();
} else if !v.is_odd() {
v = v >> 1;
s = s.hlv();
} else if u > v {
u = (u - v).0 >> 1;
r = r - s;
r = r.hlv();
} else {
v = (v - u).0 >> 1;
s = s - r;
s = s.hlv();
}
}
r
}
fn mont_mul_fp(self, rhs: FpElement<I, M>) -> Self {
self.mont_mul(rhs)
}
}
impl<I: IsBigInt, M: PrimeModular<I>> From<FpElement<I, M>> for [u8; I::BYTE_LEN]
where
[(); I::DIG_LEN]:
{
fn from(val: FpElement<I, M>) -> Self {
unsafe {
core::intrinsics::transmute_unchecked(val.0.0.to_array())
}
}
}
#[cfg(feature = "rand")]
impl<I, M> FpElement<I, M>
where
I: IsBigInt + BigIntOpsExt,
M: PrimeModular<I>,
[(); I::DIG_LEN]:
{
pub fn rand(rng: &mut impl rand_core::CryptoRngCore) -> Self {
loop {
let i = I::rand(rng);
if i < M::P {
return FpElement(Element::new_unchecked(i))
}
}
}
}