#![allow(clippy::needless_borrow)]
use core::cmp::Ordering;
use core::fmt;
use core::ops::{Add, Mul, Neg, Sub};
use ff::{Field, PrimeField, WithSmallOrderMulGroup};
use rand_core::RngCore;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use crate::arithmetic::{adc, mac, sbb};
use crate::{
impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output,
impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Fp(pub(crate) [u64; 6]);
impl fmt::Debug for Fp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let tmp = self.to_bytes();
write!(f, "0x")?;
for &b in tmp.iter().rev() {
write!(f, "{b:02x}")?;
}
Ok(())
}
}
impl Default for Fp {
fn default() -> Self {
Fp::zero()
}
}
#[cfg(feature = "zeroize")]
impl zeroize::DefaultIsZeroes for Fp {}
impl ConstantTimeEq for Fp {
fn ct_eq(&self, other: &Self) -> Choice {
self.0[0].ct_eq(&other.0[0])
& self.0[1].ct_eq(&other.0[1])
& self.0[2].ct_eq(&other.0[2])
& self.0[3].ct_eq(&other.0[3])
& self.0[4].ct_eq(&other.0[4])
& self.0[5].ct_eq(&other.0[5])
}
}
impl Ord for Fp {
fn cmp(&self, other: &Self) -> Ordering {
let left = self.to_repr().0;
let right = other.to_repr().0;
left.iter()
.zip(right.iter())
.rev()
.find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) {
Ordering::Equal => None,
res => Some(res),
})
.unwrap_or(Ordering::Equal)
}
}
impl PartialOrd for Fp {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl ConditionallySelectable for Fp {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Fp([
u64::conditional_select(&a.0[0], &b.0[0], choice),
u64::conditional_select(&a.0[1], &b.0[1], choice),
u64::conditional_select(&a.0[2], &b.0[2], choice),
u64::conditional_select(&a.0[3], &b.0[3], choice),
u64::conditional_select(&a.0[4], &b.0[4], choice),
u64::conditional_select(&a.0[5], &b.0[5], choice),
])
}
}
impl From<u64> for Fp {
fn from(value: u64) -> Self {
Self([value, 0, 0, 0, 0, 0]) * R2
}
}
const MODULUS: [u64; 6] = [
0xb9fe_ffff_ffff_aaab,
0x1eab_fffe_b153_ffff,
0x6730_d2a0_f6b0_f624,
0x6477_4b84_f385_12bf,
0x4b1b_a7b6_434b_acd7,
0x1a01_11ea_397f_e69a,
];
const INV: u64 = 0x89f3_fffc_fffc_fffd;
const R: Fp = Fp([
0x7609_0000_0002_fffd,
0xebf4_000b_c40c_0002,
0x5f48_9857_53c7_58ba,
0x77ce_5853_7052_5745,
0x5c07_1a97_a256_ec6d,
0x15f6_5ec3_fa80_e493,
]);
const R2: Fp = Fp([
0xf4df_1f34_1c34_1746,
0x0a76_e6a6_09d1_04f1,
0x8de5_476c_4c95_b6d5,
0x67eb_88a9_939d_83c0,
0x9a79_3e85_b519_952d,
0x1198_8fe5_92ca_e3aa,
]);
const R3: Fp = Fp([
0xed48_ac6b_d94c_a1e0,
0x315f_831e_03a7_adf8,
0x9a53_352a_615e_29dd,
0x34c0_4e5e_921e_1761,
0x2512_d435_6572_4728,
0x0aa6_3460_9175_5d4d,
]);
const TWO_INV: Fp = Fp([
0x1804000000015554,
0x855000053ab00001,
0x633cb57c253c276f,
0x6e22d1ec31ebb502,
0xd3916126f2d14ca2,
0x17fbb8571a006596,
]);
const GENERATOR: Fp = Fp([
0x321300000006554f,
0xb93c0018d6c40005,
0x57605e0db0ddbb51,
0x8b256521ed1f9bcb,
0x6cf28d7901622c03,
0x11ebab9dbb81e28c,
]);
const ROOT_OF_UNITY: Fp = Fp([
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x40ab3263eff0206,
]);
const ROOT_OF_UNITY_INV: Fp = Fp([
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x40ab3263eff0206,
]);
const DELTA: Fp = Fp([
0xaa270000000cfff3,
0x53cc0032fc34000a,
0x478fe97a6b0a807f,
0xb1d37ebee6ba24d7,
0x8ec9733bbf78ab2f,
0x9d645513d83de7e,
]);
const ZETA: Fp = Fp([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
]);
impl<'a> Neg for &'a Fp {
type Output = Fp;
#[inline]
fn neg(self) -> Fp {
self.neg()
}
}
impl Neg for Fp {
type Output = Fp;
#[inline]
fn neg(self) -> Fp {
-&self
}
}
impl<'a, 'b> Sub<&'b Fp> for &'a Fp {
type Output = Fp;
#[inline]
fn sub(self, rhs: &'b Fp) -> Fp {
self.sub(rhs)
}
}
impl<'a, 'b> Add<&'b Fp> for &'a Fp {
type Output = Fp;
#[inline]
fn add(self, rhs: &'b Fp) -> Fp {
self.add(rhs)
}
}
impl<'a, 'b> Mul<&'b Fp> for &'a Fp {
type Output = Fp;
#[inline]
fn mul(self, rhs: &'b Fp) -> Fp {
self.mul(rhs)
}
}
impl_binops_additive!(Fp, Fp);
impl_binops_multiplicative!(Fp, Fp);
impl<T> core::iter::Sum<T> for Fp
where
T: core::borrow::Borrow<Fp>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::zero(), |acc, item| acc + item.borrow())
}
}
impl<T> core::iter::Product<T> for Fp
where
T: core::borrow::Borrow<Fp>,
{
fn product<I>(iter: I) -> Self
where
I: Iterator<Item = T>,
{
iter.fold(Self::one(), |acc, item| acc * item.borrow())
}
}
impl Field for Fp {
const ZERO: Self = Fp::zero();
const ONE: Self = Fp::one();
fn random(rng: impl RngCore) -> Self {
Self::random(rng)
}
fn square(&self) -> Self {
self.square()
}
fn double(&self) -> Self {
self.add(self)
}
fn invert(&self) -> CtOption<Self> {
self.invert()
}
fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
ff::helpers::sqrt_ratio_generic(num, div)
}
fn sqrt(&self) -> CtOption<Self> {
self.sqrt()
}
}
#[derive(Debug, Clone, Copy)]
pub struct ReprFp(pub(crate) [u8; 48]);
impl AsMut<[u8]> for ReprFp {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}
impl AsRef<[u8]> for ReprFp {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl AsRef<[u8; 48]> for ReprFp {
fn as_ref(&self) -> &[u8; 48] {
&self.0
}
}
impl From<[u8; 48]> for ReprFp {
fn from(value: [u8; 48]) -> Self {
Self(value)
}
}
impl Default for ReprFp {
fn default() -> Self {
Self([0u8; 48])
}
}
impl PrimeField for Fp {
type Repr = ReprFp;
fn from_repr(r: Self::Repr) -> CtOption<Self> {
Self::from_bytes(&r.0)
}
fn to_repr(&self) -> Self::Repr {
ReprFp(self.to_bytes())
}
fn is_odd(&self) -> Choice {
Choice::from(self.to_bytes()[0] & 1)
}
const MODULUS: &'static str = "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab";
const NUM_BITS: u32 = 381;
const CAPACITY: u32 = Self::NUM_BITS - 1;
const TWO_INV: Self = TWO_INV;
const MULTIPLICATIVE_GENERATOR: Self = GENERATOR;
const S: u32 = 1;
const ROOT_OF_UNITY: Self = ROOT_OF_UNITY;
const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV;
const DELTA: Self = DELTA;
}
impl WithSmallOrderMulGroup<3> for Fp {
const ZETA: Self = ZETA;
}
impl Fp {
#[inline]
pub const fn zero() -> Fp {
Fp([0, 0, 0, 0, 0, 0])
}
#[inline]
pub const fn one() -> Fp {
R
}
pub fn from_bytes(bytes: &[u8; 48]) -> CtOption<Fp> {
let mut tmp = Fp([0, 0, 0, 0, 0, 0]);
tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap());
tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
tmp.0[4] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap());
tmp.0[5] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap());
let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow);
let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow);
let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow);
let is_some = (borrow as u8) & 1;
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
pub fn from_bytes_be(bytes: &[u8; 48]) -> CtOption<Fp> {
let mut tmp = Fp([0, 0, 0, 0, 0, 0]);
tmp.0[5] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap());
tmp.0[4] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap());
tmp.0[3] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap());
tmp.0[2] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap());
tmp.0[1] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap());
tmp.0[0] = u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap());
let (_, borrow) = sbb(tmp.0[0], MODULUS[0], 0);
let (_, borrow) = sbb(tmp.0[1], MODULUS[1], borrow);
let (_, borrow) = sbb(tmp.0[2], MODULUS[2], borrow);
let (_, borrow) = sbb(tmp.0[3], MODULUS[3], borrow);
let (_, borrow) = sbb(tmp.0[4], MODULUS[4], borrow);
let (_, borrow) = sbb(tmp.0[5], MODULUS[5], borrow);
let is_some = (borrow as u8) & 1;
tmp *= &R2;
CtOption::new(tmp, Choice::from(is_some))
}
pub fn to_bytes_be(self) -> [u8; 48] {
let tmp = Fp::montgomery_reduce(
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
);
let mut res = [0; 48];
res[0..8].copy_from_slice(&tmp.0[5].to_be_bytes());
res[8..16].copy_from_slice(&tmp.0[4].to_be_bytes());
res[16..24].copy_from_slice(&tmp.0[3].to_be_bytes());
res[24..32].copy_from_slice(&tmp.0[2].to_be_bytes());
res[32..40].copy_from_slice(&tmp.0[1].to_be_bytes());
res[40..48].copy_from_slice(&tmp.0[0].to_be_bytes());
res
}
pub fn to_bytes(self) -> [u8; 48] {
let tmp = Fp::montgomery_reduce(
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
);
let mut res = [0; 48];
res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes());
res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes());
res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes());
res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes());
res[32..40].copy_from_slice(&tmp.0[4].to_le_bytes());
res[40..48].copy_from_slice(&tmp.0[5].to_le_bytes());
res
}
pub fn random(mut rng: impl RngCore) -> Fp {
let mut bytes = [0u8; 96];
rng.fill_bytes(&mut bytes);
Fp::from_u768([
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[64..72]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[72..80]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[80..88]).unwrap()),
u64::from_be_bytes(<[u8; 8]>::try_from(&bytes[88..96]).unwrap()),
])
}
fn from_u768(limbs: [u64; 12]) -> Fp {
let d1 = Fp([limbs[11], limbs[10], limbs[9], limbs[8], limbs[7], limbs[6]]);
let d0 = Fp([limbs[5], limbs[4], limbs[3], limbs[2], limbs[1], limbs[0]]);
d0 * R2 + d1 * R3
}
pub fn lexicographically_largest(&self) -> Choice {
let tmp = Fp::montgomery_reduce(
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], 0, 0, 0, 0, 0, 0,
);
let (_, borrow) = sbb(tmp.0[0], 0xdcff_7fff_ffff_d556, 0);
let (_, borrow) = sbb(tmp.0[1], 0x0f55_ffff_58a9_ffff, borrow);
let (_, borrow) = sbb(tmp.0[2], 0xb398_6950_7b58_7b12, borrow);
let (_, borrow) = sbb(tmp.0[3], 0xb23b_a5c2_79c2_895f, borrow);
let (_, borrow) = sbb(tmp.0[4], 0x258d_d3db_21a5_d66b, borrow);
let (_, borrow) = sbb(tmp.0[5], 0x0d00_88f5_1cbf_f34d, borrow);
!Choice::from((borrow as u8) & 1)
}
pub const fn from_raw_unchecked(v: [u64; 6]) -> Fp {
Fp(v)
}
pub fn pow_vartime(&self, by: &[u64; 6]) -> Self {
let mut res = Self::one();
for e in by.iter().rev() {
for i in (0..64).rev() {
res = res.square();
if ((*e >> i) & 1) == 1 {
res *= self;
}
}
}
res
}
#[inline]
pub fn sqrt(&self) -> CtOption<Self> {
let sqrt = self.pow_vartime(&[
0xee7f_bfff_ffff_eaab,
0x07aa_ffff_ac54_ffff,
0xd9cc_34a8_3dac_3d89,
0xd91d_d2e1_3ce1_44af,
0x92c6_e9ed_90d2_eb35,
0x0680_447a_8e5f_f9a6,
]);
CtOption::new(sqrt, sqrt.square().ct_eq(self))
}
#[inline]
pub fn invert(&self) -> CtOption<Self> {
let t = self.pow_vartime(&[
0xb9fe_ffff_ffff_aaa9,
0x1eab_fffe_b153_ffff,
0x6730_d2a0_f6b0_f624,
0x6477_4b84_f385_12bf,
0x4b1b_a7b6_434b_acd7,
0x1a01_11ea_397f_e69a,
]);
CtOption::new(t, !self.is_zero())
}
#[inline]
const fn subtract_p(&self) -> Fp {
let (r0, borrow) = sbb(self.0[0], MODULUS[0], 0);
let (r1, borrow) = sbb(self.0[1], MODULUS[1], borrow);
let (r2, borrow) = sbb(self.0[2], MODULUS[2], borrow);
let (r3, borrow) = sbb(self.0[3], MODULUS[3], borrow);
let (r4, borrow) = sbb(self.0[4], MODULUS[4], borrow);
let (r5, borrow) = sbb(self.0[5], MODULUS[5], borrow);
let r0 = (self.0[0] & borrow) | (r0 & !borrow);
let r1 = (self.0[1] & borrow) | (r1 & !borrow);
let r2 = (self.0[2] & borrow) | (r2 & !borrow);
let r3 = (self.0[3] & borrow) | (r3 & !borrow);
let r4 = (self.0[4] & borrow) | (r4 & !borrow);
let r5 = (self.0[5] & borrow) | (r5 & !borrow);
Fp([r0, r1, r2, r3, r4, r5])
}
#[inline]
pub const fn add(&self, rhs: &Fp) -> Fp {
let (d0, carry) = adc(self.0[0], rhs.0[0], 0);
let (d1, carry) = adc(self.0[1], rhs.0[1], carry);
let (d2, carry) = adc(self.0[2], rhs.0[2], carry);
let (d3, carry) = adc(self.0[3], rhs.0[3], carry);
let (d4, carry) = adc(self.0[4], rhs.0[4], carry);
let (d5, _) = adc(self.0[5], rhs.0[5], carry);
(Fp([d0, d1, d2, d3, d4, d5])).subtract_p()
}
#[inline]
pub const fn neg(&self) -> Fp {
let (d0, borrow) = sbb(MODULUS[0], self.0[0], 0);
let (d1, borrow) = sbb(MODULUS[1], self.0[1], borrow);
let (d2, borrow) = sbb(MODULUS[2], self.0[2], borrow);
let (d3, borrow) = sbb(MODULUS[3], self.0[3], borrow);
let (d4, borrow) = sbb(MODULUS[4], self.0[4], borrow);
let (d5, _) = sbb(MODULUS[5], self.0[5], borrow);
let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4] | self.0[5]) == 0)
as u64)
.wrapping_sub(1);
Fp([
d0 & mask,
d1 & mask,
d2 & mask,
d3 & mask,
d4 & mask,
d5 & mask,
])
}
#[inline]
pub const fn sub(&self, rhs: &Fp) -> Fp {
(&rhs.neg()).add(self)
}
#[inline]
pub(crate) fn sum_of_products<const T: usize>(a: [Fp; T], b: [Fp; T]) -> Fp {
let (u0, u1, u2, u3, u4, u5) =
(0..6).fold((0, 0, 0, 0, 0, 0), |(u0, u1, u2, u3, u4, u5), j| {
let (t0, t1, t2, t3, t4, t5, t6) = (0..T).fold(
(u0, u1, u2, u3, u4, u5, 0),
|(t0, t1, t2, t3, t4, t5, t6), i| {
let (t0, carry) = mac(t0, a[i].0[j], b[i].0[0], 0);
let (t1, carry) = mac(t1, a[i].0[j], b[i].0[1], carry);
let (t2, carry) = mac(t2, a[i].0[j], b[i].0[2], carry);
let (t3, carry) = mac(t3, a[i].0[j], b[i].0[3], carry);
let (t4, carry) = mac(t4, a[i].0[j], b[i].0[4], carry);
let (t5, carry) = mac(t5, a[i].0[j], b[i].0[5], carry);
let (t6, _) = adc(t6, 0, carry);
(t0, t1, t2, t3, t4, t5, t6)
},
);
let k = t0.wrapping_mul(INV);
let (_, carry) = mac(t0, k, MODULUS[0], 0);
let (r1, carry) = mac(t1, k, MODULUS[1], carry);
let (r2, carry) = mac(t2, k, MODULUS[2], carry);
let (r3, carry) = mac(t3, k, MODULUS[3], carry);
let (r4, carry) = mac(t4, k, MODULUS[4], carry);
let (r5, carry) = mac(t5, k, MODULUS[5], carry);
let (r6, _) = adc(t6, 0, carry);
(r1, r2, r3, r4, r5, r6)
});
(&Fp([u0, u1, u2, u3, u4, u5])).subtract_p()
}
#[inline(always)]
pub(crate) const fn montgomery_reduce(
t0: u64,
t1: u64,
t2: u64,
t3: u64,
t4: u64,
t5: u64,
t6: u64,
t7: u64,
t8: u64,
t9: u64,
t10: u64,
t11: u64,
) -> Self {
let k = t0.wrapping_mul(INV);
let (_, carry) = mac(t0, k, MODULUS[0], 0);
let (r1, carry) = mac(t1, k, MODULUS[1], carry);
let (r2, carry) = mac(t2, k, MODULUS[2], carry);
let (r3, carry) = mac(t3, k, MODULUS[3], carry);
let (r4, carry) = mac(t4, k, MODULUS[4], carry);
let (r5, carry) = mac(t5, k, MODULUS[5], carry);
let (r6, r7) = adc(t6, 0, carry);
let k = r1.wrapping_mul(INV);
let (_, carry) = mac(r1, k, MODULUS[0], 0);
let (r2, carry) = mac(r2, k, MODULUS[1], carry);
let (r3, carry) = mac(r3, k, MODULUS[2], carry);
let (r4, carry) = mac(r4, k, MODULUS[3], carry);
let (r5, carry) = mac(r5, k, MODULUS[4], carry);
let (r6, carry) = mac(r6, k, MODULUS[5], carry);
let (r7, r8) = adc(t7, r7, carry);
let k = r2.wrapping_mul(INV);
let (_, carry) = mac(r2, k, MODULUS[0], 0);
let (r3, carry) = mac(r3, k, MODULUS[1], carry);
let (r4, carry) = mac(r4, k, MODULUS[2], carry);
let (r5, carry) = mac(r5, k, MODULUS[3], carry);
let (r6, carry) = mac(r6, k, MODULUS[4], carry);
let (r7, carry) = mac(r7, k, MODULUS[5], carry);
let (r8, r9) = adc(t8, r8, carry);
let k = r3.wrapping_mul(INV);
let (_, carry) = mac(r3, k, MODULUS[0], 0);
let (r4, carry) = mac(r4, k, MODULUS[1], carry);
let (r5, carry) = mac(r5, k, MODULUS[2], carry);
let (r6, carry) = mac(r6, k, MODULUS[3], carry);
let (r7, carry) = mac(r7, k, MODULUS[4], carry);
let (r8, carry) = mac(r8, k, MODULUS[5], carry);
let (r9, r10) = adc(t9, r9, carry);
let k = r4.wrapping_mul(INV);
let (_, carry) = mac(r4, k, MODULUS[0], 0);
let (r5, carry) = mac(r5, k, MODULUS[1], carry);
let (r6, carry) = mac(r6, k, MODULUS[2], carry);
let (r7, carry) = mac(r7, k, MODULUS[3], carry);
let (r8, carry) = mac(r8, k, MODULUS[4], carry);
let (r9, carry) = mac(r9, k, MODULUS[5], carry);
let (r10, r11) = adc(t10, r10, carry);
let k = r5.wrapping_mul(INV);
let (_, carry) = mac(r5, k, MODULUS[0], 0);
let (r6, carry) = mac(r6, k, MODULUS[1], carry);
let (r7, carry) = mac(r7, k, MODULUS[2], carry);
let (r8, carry) = mac(r8, k, MODULUS[3], carry);
let (r9, carry) = mac(r9, k, MODULUS[4], carry);
let (r10, carry) = mac(r10, k, MODULUS[5], carry);
let (r11, _) = adc(t11, r11, carry);
(&Fp([r6, r7, r8, r9, r10, r11])).subtract_p()
}
#[inline]
pub const fn mul(&self, rhs: &Fp) -> Fp {
let (t0, carry) = mac(0, self.0[0], rhs.0[0], 0);
let (t1, carry) = mac(0, self.0[0], rhs.0[1], carry);
let (t2, carry) = mac(0, self.0[0], rhs.0[2], carry);
let (t3, carry) = mac(0, self.0[0], rhs.0[3], carry);
let (t4, carry) = mac(0, self.0[0], rhs.0[4], carry);
let (t5, t6) = mac(0, self.0[0], rhs.0[5], carry);
let (t1, carry) = mac(t1, self.0[1], rhs.0[0], 0);
let (t2, carry) = mac(t2, self.0[1], rhs.0[1], carry);
let (t3, carry) = mac(t3, self.0[1], rhs.0[2], carry);
let (t4, carry) = mac(t4, self.0[1], rhs.0[3], carry);
let (t5, carry) = mac(t5, self.0[1], rhs.0[4], carry);
let (t6, t7) = mac(t6, self.0[1], rhs.0[5], carry);
let (t2, carry) = mac(t2, self.0[2], rhs.0[0], 0);
let (t3, carry) = mac(t3, self.0[2], rhs.0[1], carry);
let (t4, carry) = mac(t4, self.0[2], rhs.0[2], carry);
let (t5, carry) = mac(t5, self.0[2], rhs.0[3], carry);
let (t6, carry) = mac(t6, self.0[2], rhs.0[4], carry);
let (t7, t8) = mac(t7, self.0[2], rhs.0[5], carry);
let (t3, carry) = mac(t3, self.0[3], rhs.0[0], 0);
let (t4, carry) = mac(t4, self.0[3], rhs.0[1], carry);
let (t5, carry) = mac(t5, self.0[3], rhs.0[2], carry);
let (t6, carry) = mac(t6, self.0[3], rhs.0[3], carry);
let (t7, carry) = mac(t7, self.0[3], rhs.0[4], carry);
let (t8, t9) = mac(t8, self.0[3], rhs.0[5], carry);
let (t4, carry) = mac(t4, self.0[4], rhs.0[0], 0);
let (t5, carry) = mac(t5, self.0[4], rhs.0[1], carry);
let (t6, carry) = mac(t6, self.0[4], rhs.0[2], carry);
let (t7, carry) = mac(t7, self.0[4], rhs.0[3], carry);
let (t8, carry) = mac(t8, self.0[4], rhs.0[4], carry);
let (t9, t10) = mac(t9, self.0[4], rhs.0[5], carry);
let (t5, carry) = mac(t5, self.0[5], rhs.0[0], 0);
let (t6, carry) = mac(t6, self.0[5], rhs.0[1], carry);
let (t7, carry) = mac(t7, self.0[5], rhs.0[2], carry);
let (t8, carry) = mac(t8, self.0[5], rhs.0[3], carry);
let (t9, carry) = mac(t9, self.0[5], rhs.0[4], carry);
let (t10, t11) = mac(t10, self.0[5], rhs.0[5], carry);
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
}
#[inline]
pub const fn square(&self) -> Self {
let (t1, carry) = mac(0, self.0[0], self.0[1], 0);
let (t2, carry) = mac(0, self.0[0], self.0[2], carry);
let (t3, carry) = mac(0, self.0[0], self.0[3], carry);
let (t4, carry) = mac(0, self.0[0], self.0[4], carry);
let (t5, t6) = mac(0, self.0[0], self.0[5], carry);
let (t3, carry) = mac(t3, self.0[1], self.0[2], 0);
let (t4, carry) = mac(t4, self.0[1], self.0[3], carry);
let (t5, carry) = mac(t5, self.0[1], self.0[4], carry);
let (t6, t7) = mac(t6, self.0[1], self.0[5], carry);
let (t5, carry) = mac(t5, self.0[2], self.0[3], 0);
let (t6, carry) = mac(t6, self.0[2], self.0[4], carry);
let (t7, t8) = mac(t7, self.0[2], self.0[5], carry);
let (t7, carry) = mac(t7, self.0[3], self.0[4], 0);
let (t8, t9) = mac(t8, self.0[3], self.0[5], carry);
let (t9, t10) = mac(t9, self.0[4], self.0[5], 0);
let t11 = t10 >> 63;
let t10 = (t10 << 1) | (t9 >> 63);
let t9 = (t9 << 1) | (t8 >> 63);
let t8 = (t8 << 1) | (t7 >> 63);
let t7 = (t7 << 1) | (t6 >> 63);
let t6 = (t6 << 1) | (t5 >> 63);
let t5 = (t5 << 1) | (t4 >> 63);
let t4 = (t4 << 1) | (t3 >> 63);
let t3 = (t3 << 1) | (t2 >> 63);
let t2 = (t2 << 1) | (t1 >> 63);
let t1 = t1 << 1;
let (t0, carry) = mac(0, self.0[0], self.0[0], 0);
let (t1, carry) = adc(t1, 0, carry);
let (t2, carry) = mac(t2, self.0[1], self.0[1], carry);
let (t3, carry) = adc(t3, 0, carry);
let (t4, carry) = mac(t4, self.0[2], self.0[2], carry);
let (t5, carry) = adc(t5, 0, carry);
let (t6, carry) = mac(t6, self.0[3], self.0[3], carry);
let (t7, carry) = adc(t7, 0, carry);
let (t8, carry) = mac(t8, self.0[4], self.0[4], carry);
let (t9, carry) = adc(t9, 0, carry);
let (t10, carry) = mac(t10, self.0[5], self.0[5], carry);
let (t11, _) = adc(t11, 0, carry);
Self::montgomery_reduce(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11)
}
}
impl ff::FromUniformBytes<64> for Fp {
fn from_uniform_bytes(bytes: &[u8; 64]) -> Self {
Self::from_u512([
u64::from_le_bytes(bytes[0..8].try_into().unwrap()),
u64::from_le_bytes(bytes[8..16].try_into().unwrap()),
u64::from_le_bytes(bytes[16..24].try_into().unwrap()),
u64::from_le_bytes(bytes[24..32].try_into().unwrap()),
u64::from_le_bytes(bytes[32..40].try_into().unwrap()),
u64::from_le_bytes(bytes[40..48].try_into().unwrap()),
u64::from_le_bytes(bytes[48..56].try_into().unwrap()),
u64::from_le_bytes(bytes[56..64].try_into().unwrap()),
])
}
}
impl Fp {
fn from_u512(limbs: [u64; 8]) -> Fp {
let d0 = Fp([limbs[0], limbs[1], limbs[2], limbs[3], limbs[4], limbs[5]]);
let d1 = Fp([limbs[6], limbs[7], 0, 0, 0, 0]);
d0 * R2 + d1 * R3
}
}
impl From<bool> for Fp {
fn from(bit: bool) -> Fp {
if bit {
Fp::one()
} else {
Fp::zero()
}
}
}
impl From<[u64; 6]> for Fp {
fn from(digits: [u64; 6]) -> Self {
Self::from_raw_unchecked(digits)
}
}
#[test]
fn test_constants() {
assert_eq!(Fp::ONE.double(), GENERATOR);
assert_eq!(Fp::ONE, Fp::ONE.double() * TWO_INV);
assert_eq!(Fp::ONE, ROOT_OF_UNITY.pow([1 << Fp::S]));
assert_eq!(Fp::ONE, ROOT_OF_UNITY * ROOT_OF_UNITY_INV);
let t = [
0xdcff7fffffffd555,
0x0f55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0xd0088f51cbff34d,
];
assert_eq!(Fp::ONE, DELTA.pow(t));
assert_eq!(Fp::ONE, ZETA.pow([3]));
}
#[test]
fn test_conditional_selection() {
let a = Fp([1, 2, 3, 4, 5, 6]);
let b = Fp([7, 8, 9, 10, 11, 12]);
assert_eq!(
ConditionallySelectable::conditional_select(&a, &b, Choice::from(0u8)),
a
);
assert_eq!(
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1u8)),
b
);
}
#[test]
fn test_equality() {
fn is_equal(a: &Fp, b: &Fp) -> bool {
let eq = a == b;
let ct_eq = a.ct_eq(b);
assert_eq!(eq, bool::from(ct_eq));
eq
}
assert!(is_equal(&Fp([1, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([7, 2, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([1, 7, 3, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([1, 2, 7, 4, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([1, 2, 3, 7, 5, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([1, 2, 3, 4, 7, 6]), &Fp([1, 2, 3, 4, 5, 6])));
assert!(!is_equal(&Fp([1, 2, 3, 4, 5, 7]), &Fp([1, 2, 3, 4, 5, 6])));
}
#[test]
fn test_squaring() {
let a = Fp([
0xd215_d276_8e83_191b,
0x5085_d80f_8fb2_8261,
0xce9a_032d_df39_3a56,
0x3e9c_4fff_2ca0_c4bb,
0x6436_b6f7_f4d9_5dfb,
0x1060_6628_ad4a_4d90,
]);
let b = Fp([
0x33d9_c42a_3cb3_e235,
0xdad1_1a09_4c4c_d455,
0xa2f1_44bd_729a_aeba,
0xd415_0932_be9f_feac,
0xe27b_c7c4_7d44_ee50,
0x14b6_a78d_3ec7_a560,
]);
assert_eq!(a.square(), b);
}
#[test]
fn test_multiplication() {
let a = Fp([
0x0397_a383_2017_0cd4,
0x734c_1b2c_9e76_1d30,
0x5ed2_55ad_9a48_beb5,
0x095a_3c6b_22a7_fcfc,
0x2294_ce75_d4e2_6a27,
0x1333_8bd8_7001_1ebb,
]);
let b = Fp([
0xb9c3_c7c5_b119_6af7,
0x2580_e208_6ce3_35c1,
0xf49a_ed3d_8a57_ef42,
0x41f2_81e4_9846_e878,
0xe076_2346_c384_52ce,
0x0652_e893_26e5_7dc0,
]);
let c = Fp([
0xf96e_f3d7_11ab_5355,
0xe8d4_59ea_00f1_48dd,
0x53f7_354a_5f00_fa78,
0x9e34_a4f3_125c_5f83,
0x3fbe_0c47_ca74_c19e,
0x01b0_6a8b_bd4a_dfe4,
]);
assert_eq!(a * b, c);
}
#[test]
fn test_addition() {
let a = Fp([
0x5360_bb59_7867_8032,
0x7dd2_75ae_799e_128e,
0x5c5b_5071_ce4f_4dcf,
0xcdb2_1f93_078d_bb3e,
0xc323_65c5_e73f_474a,
0x115a_2a54_89ba_be5b,
]);
let b = Fp([
0x9fd2_8773_3d23_dda0,
0xb16b_f2af_738b_3554,
0x3e57_a75b_d3cc_6d1d,
0x900b_c0bd_627f_d6d6,
0xd319_a080_efb2_45fe,
0x15fd_caa4_e4bb_2091,
]);
let c = Fp([
0x3934_42cc_b58b_b327,
0x1092_685f_3bd5_47e3,
0x3382_252c_ab6a_c4c9,
0xf946_94cb_7688_7f55,
0x4b21_5e90_93a5_e071,
0x0d56_e30f_34f5_f853,
]);
assert_eq!(a + b, c);
}
#[test]
fn test_subtraction() {
let a = Fp([
0x5360_bb59_7867_8032,
0x7dd2_75ae_799e_128e,
0x5c5b_5071_ce4f_4dcf,
0xcdb2_1f93_078d_bb3e,
0xc323_65c5_e73f_474a,
0x115a_2a54_89ba_be5b,
]);
let b = Fp([
0x9fd2_8773_3d23_dda0,
0xb16b_f2af_738b_3554,
0x3e57_a75b_d3cc_6d1d,
0x900b_c0bd_627f_d6d6,
0xd319_a080_efb2_45fe,
0x15fd_caa4_e4bb_2091,
]);
let c = Fp([
0x6d8d_33e6_3b43_4d3d,
0xeb12_82fd_b766_dd39,
0x8534_7bb6_f133_d6d5,
0xa21d_aa5a_9892_f727,
0x3b25_6cfb_3ad8_ae23,
0x155d_7199_de7f_8464,
]);
assert_eq!(a - b, c);
}
#[test]
fn test_negation() {
let a = Fp([
0x5360_bb59_7867_8032,
0x7dd2_75ae_799e_128e,
0x5c5b_5071_ce4f_4dcf,
0xcdb2_1f93_078d_bb3e,
0xc323_65c5_e73f_474a,
0x115a_2a54_89ba_be5b,
]);
let b = Fp([
0x669e_44a6_8798_2a79,
0xa0d9_8a50_37b5_ed71,
0x0ad5_822f_2861_a854,
0x96c5_2bf1_ebf7_5781,
0x87f8_41f0_5c0c_658c,
0x08a6_e795_afc5_283e,
]);
assert_eq!(-a, b);
}
#[test]
fn test_from_bytes() {
let mut a = Fp([
0xdc90_6d9b_e3f9_5dc8,
0x8755_caf7_4596_91a1,
0xcff1_a7f4_e958_3ab3,
0x9b43_821f_849e_2284,
0xf575_54f3_a297_4f3f,
0x085d_bea8_4ed4_7f79,
]);
for _ in 0..100 {
a = a.square();
let tmp = a.to_bytes_be();
let b = Fp::from_bytes_be(&tmp).unwrap();
assert_eq!(a, b);
}
assert_eq!(
-Fp::one(),
Fp::from_bytes_be(&[
26, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
])
.unwrap()
);
assert!(bool::from(
Fp::from_bytes_be(&[
27, 1, 17, 234, 57, 127, 230, 154, 75, 27, 167, 182, 67, 75, 172, 215, 100, 119, 75,
132, 243, 133, 18, 191, 103, 48, 210, 160, 246, 176, 246, 36, 30, 171, 255, 254, 177,
83, 255, 255, 185, 254, 255, 255, 255, 255, 170, 170
])
.is_none()
));
assert!(bool::from(Fp::from_bytes_be(&[0xff; 48]).is_none()));
}
#[test]
fn test_sqrt() {
let a = Fp::from_raw_unchecked([
0xaa27_0000_000c_fff3,
0x53cc_0032_fc34_000a,
0x478f_e97a_6b0a_807f,
0xb1d3_7ebe_e6ba_24d7,
0x8ec9_733b_bf78_ab2f,
0x09d6_4551_3d83_de7e,
]);
assert_eq!(
-a.sqrt().unwrap(),
Fp::from_raw_unchecked([
0x3213_0000_0006_554f,
0xb93c_0018_d6c4_0005,
0x5760_5e0d_b0dd_bb51,
0x8b25_6521_ed1f_9bcb,
0x6cf2_8d79_0162_2c03,
0x11eb_ab9d_bb81_e28c,
])
);
}
#[test]
fn test_inversion() {
let a = Fp([
0x43b4_3a50_78ac_2076,
0x1ce0_7630_46f8_962b,
0x724a_5276_486d_735c,
0x6f05_c2a6_282d_48fd,
0x2095_bd5b_b4ca_9331,
0x03b3_5b38_94b0_f7da,
]);
let b = Fp([
0x69ec_d704_0952_148f,
0x985c_cc20_2219_0f55,
0xe19b_ba36_a9ad_2f41,
0x19bb_16c9_5219_dbd8,
0x14dc_acfd_fb47_8693,
0x115f_f58a_fff9_a8e1,
]);
assert_eq!(a.invert().unwrap(), b);
assert!(bool::from(Fp::zero().invert().is_none()));
}
#[test]
fn test_lexicographic_largest() {
assert!(!bool::from(Fp::zero().lexicographically_largest()));
assert!(!bool::from(Fp::one().lexicographically_largest()));
assert!(!bool::from(
Fp::from_raw_unchecked([
0xa1fa_ffff_fffe_5557,
0x995b_fff9_76a3_fffe,
0x03f4_1d24_d174_ceb4,
0xf654_7998_c199_5dbd,
0x778a_468f_507a_6034,
0x0205_5993_1f7f_8103
])
.lexicographically_largest()
));
assert!(bool::from(
Fp::from_raw_unchecked([
0x1804_0000_0001_5554,
0x8550_0005_3ab0_0001,
0x633c_b57c_253c_276f,
0x6e22_d1ec_31eb_b502,
0xd391_6126_f2d1_4ca2,
0x17fb_b857_1a00_6596,
])
.lexicographically_largest()
));
assert!(bool::from(
Fp::from_raw_unchecked([
0x43f5_ffff_fffc_aaae,
0x32b7_fff2_ed47_fffd,
0x07e8_3a49_a2e9_9d69,
0xeca8_f331_8332_bb7a,
0xef14_8d1e_a0f4_c069,
0x040a_b326_3eff_0206,
])
.lexicographically_largest()
));
}
#[cfg(feature = "zeroize")]
#[test]
fn test_zeroize() {
use zeroize::Zeroize;
let mut a = Fp::one();
a.zeroize();
assert!(bool::from(a.is_zero()));
}
#[test]
fn test_from_bytes_debug() {
assert!(bool::from(
Fp::from_bytes(&[
220u8, 214, 26, 93, 113, 152, 62, 165, 185, 230, 195, 24, 61, 41, 116, 148, 248, 59,
10, 81, 185, 242, 151, 83, 182, 242, 42, 201, 239, 33, 70, 221, 221, 179, 240, 87, 84,
220, 123, 92, 72, 180, 248, 99, 100, 200, 6, 9
])
.is_some()
));
}