Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support operations over small field elements #225

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ff/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ark-ff-asm = { path = "../ff-asm" }
ark-ff-macros = { path = "../ff-macros" }
ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false }
ark-serialize = { path = "../serialize", default-features = false }
either = { version = "1", default-features = false }
derivative = { version = "2", features = ["use_core"] }
num-traits = { version = "0.2", default-features = false }
rayon = { version = "1", optional = true }
Expand Down
37 changes: 30 additions & 7 deletions ff/src/fields/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ macro_rules! impl_Fp {

impl<P: $FpParameters> Field for $Fp<P> {
type BasePrimeField = Self;
type SmallValue = SmallFp<Self>;

fn extension_degree() -> u64 {
1
Expand Down Expand Up @@ -347,8 +348,8 @@ macro_rules! impl_Fp {
*b &= m;
}
Self::deserialize(&result_bytes[..($limbs * 8)])
.ok()
.and_then(|f| F::from_u8(flags).map(|flag| (f, flag)))
.ok()
.and_then(|f| F::from_u8(flags).map(|flag| (f, flag)))
}
}

Expand Down Expand Up @@ -522,6 +523,31 @@ macro_rules! impl_Fp {
impl_prime_field_from_int!($Fp, u8, $FpParameters, $limbs);
impl_prime_field_from_int!($Fp, bool, $FpParameters, $limbs);

impl<P: $FpParameters> From<SmallFp<Self>> for $Fp<P> {
fn from(other: SmallFp<Self>) -> Self {
match other {
SmallFp::Full(f) => f,
SmallFp::Small(f) => {
// TODO: replace with `unsigned_abs` once it is stable.
let f_u8 = f.wrapping_abs() as u8;
if f.is_positive() {
Self::from(f_u8)
} else {
-Self::from(f_u8)
}
}
}
}
}

impl<P: $FpParameters> Mul<SmallFp<Self>> for $Fp<P> {
type Output = Self;
fn mul(self, other: SmallFp<Self>) -> Self {
// forwards to the `Mul` impl on `SmallFp`.
other * self
}
}

impl_prime_field_standard_sample!($Fp, $FpParameters);

impl_prime_field_serializer!($Fp, $FpParameters, $limbs * 8);
Expand All @@ -536,11 +562,8 @@ macro_rules! impl_Fp {
impl<P: $FpParameters> FromBytes for $Fp<P> {
#[inline]
fn read<R: Read>(reader: R) -> IoResult<Self> {
$BigInteger::read(reader).and_then(|b|
match $Fp::from_repr(b) {
Some(f) => Ok(f),
None => Err(crate::error("FromBytes::read failed")),
})
let b = $BigInteger::read(reader)?;
$Fp::from_repr(b).ok_or(crate::error("FromBytes::read failed"))
}
}

Expand Down
34 changes: 34 additions & 0 deletions ff/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub trait Field:
+ CanonicalDeserializeWithFlags
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<<Self as Field>::SmallValue, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ AddAssign<Self>
Expand All @@ -112,6 +113,7 @@ pub trait Field:
+ for<'a> core::iter::Sum<&'a Self>
+ core::iter::Product<Self>
+ for<'a> core::iter::Product<&'a Self>
+ From<<Self as Field>::SmallValue>
+ From<u128>
+ From<u64>
+ From<u32>
Expand All @@ -120,6 +122,7 @@ pub trait Field:
+ From<bool>
{
type BasePrimeField: PrimeField;
type SmallValue: SmallFieldValue<Self>;

/// Returns the characteristic of the field,
/// in little-endian representation.
Expand Down Expand Up @@ -190,6 +193,35 @@ pub trait Field:
}
}

pub trait SmallFieldValue<F: Field>: Copy
+ Neg<Output = Self>
+ Mul<F, Output = F>
+ From<i8>
+ From<F>
+ PartialEq
+ Eq
+ PartialEq<i8>
{
/// Returns the additive identity of the field.
// Note: we add explicit methods instead of using the `Zero` trait
// to avoid an unnecessary `Add` impl that's required by the `Zero` trait.
fn zero() -> Self;

/// Is `self` the additive identity of the field?
fn is_zero(&self) -> bool;

/// Is `self` less than or equal to the additive identity of the field?
///
/// * When `F: PrimeField`, for an element x ∈ [p-1] of `F`, this method returns
/// `(x - (p-1)/2) <= 0`.
/// * Otherwise, `F` is an extension field F_q (q is a prime power), for an element
/// `x` of `F`, this method returns true only if each coefficient of the underlying
/// representation is less than or equal to zero.
///
/// Note that this ordering is *different* from the ordering on `F`; there every element
/// lies in the range `0..(p-1)`, and is hence never negative.
fn is_leq_zero(&self) -> bool;
}
/// A trait that defines parameters for a field that can be used for FFTs.
pub trait FftParameters: 'static + Send + Sync + Sized {
type BigInt: BigInteger;
Expand Down Expand Up @@ -601,6 +633,8 @@ fn serial_batch_inversion_and_mul<F: Field>(v: &mut [F], coeff: &F) {
}
}



#[cfg(all(test, feature = "std"))]
mod std_tests {
use super::BitIteratorLE;
Expand Down
115 changes: 112 additions & 3 deletions ff/src/fields/models/cubic_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use ark_std::rand::{

use crate::{
bytes::{FromBytes, ToBytes},
fields::{Field, PrimeField},
fields::{Field, PrimeField, SmallFieldValue},
ToConstraintField, UniformRand,
};

Expand All @@ -38,7 +38,7 @@ pub trait CubicExtParameters: 'static + Send + Sync {
const DEGREE_OVER_BASE_PRIME_FIELD: usize;

/// The cubic non-residue used to construct the extension.
const NONRESIDUE: Self::BaseField;
const NONRESIDUE: <Self::BaseField as Field>::SmallValue;

/// Coefficients for the Frobenius automorphism.
const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff];
Expand All @@ -48,7 +48,7 @@ pub trait CubicExtParameters: 'static + Send + Sync {
/// the quadratic non-residue. This is used in multiplication and squaring.
#[inline(always)]
fn mul_base_field_by_nonresidue(fe: &Self::BaseField) -> Self::BaseField {
Self::NONRESIDUE * fe
Self::NONRESIDUE * *fe
}

/// A specializable method for multiplying an element of the base field by
Expand Down Expand Up @@ -139,6 +139,7 @@ impl<P: CubicExtParameters> One for CubicExtField<P> {

impl<P: CubicExtParameters> Field for CubicExtField<P> {
type BasePrimeField = P::BasePrimeField;
type SmallValue = SmallCubicExtField<P::BaseField>;

fn extension_degree() -> u64 {
3 * P::BaseField::extension_degree()
Expand Down Expand Up @@ -567,6 +568,114 @@ where
}
}

/// Represents an element of the cubic extension field where the elements
/// might have small absolute magnitude.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct SmallCubicExtField<F: Field> {
c0: F::SmallValue,
c1: F::SmallValue,
c2: F::SmallValue,
}

impl<F: Field> SmallFieldValue<F> for SmallCubicExtField<F> {
fn zero() -> Self {
Self {
c0: F::SmallValue::zero(),
c1: F::SmallValue::zero(),
c2: F::SmallValue::zero(),
}
}

fn is_zero(&self) -> bool {
self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
}

fn is_leq_zero(&self) -> bool {
self.c0.is_leq_zero() && self.c1.is_leq_zero() && self.c2.is_leq_zero()
}
}

impl<F: Field> Neg for SmallCubicExtField<F> {
type Output = Self;
fn neg(mut self) -> Self {
self.c0 = -self.c0;
self.c1 = -self.c1;
self.c2 = -self.c2;
self
}
}

impl<P: CubicExtParameters> Mul<CubicExtField<P>> for SmallCubicExtField<P::BaseField> {
type Output = CubicExtField<P>;
fn mul(self, mut other: CubicExtField<P>) -> Self::Output {
// Devegili OhEig Scott Dahab --- Multiplication and Squaring on
// AbstractPairing-Friendly
// Fields.pdf; Section 4 (Karatsuba)

let a = other.c0;
let b = other.c1;
let c = other.c2;

let d = self.c0;
let e = self.c1;
let f = self.c2;

let ad = d * a;
let be = e * b;
let cf = f * c;

let x = (e + f) * (b + &c) - &be - &cf;
let y = (d + e) * (a + &b) - &ad - &be;
let z = (d + f) * (a + &c) - &ad + &be - &cf;

other.c0 = ad + &P::mul_base_field_by_nonresidue(&x);
other.c1 = y + &P::mul_base_field_by_nonresidue(&cf);
other.c2 = z;
other
}
}

impl<P: CubicExtParameters> From<SmallCubicExtField<P::BaseField>> for CubicExtField<P> {
fn from(other: SmallCubicExtField<P::BaseField>) -> Self {
Self::new(other.c0.into(), other.c1.into(), other.c2.into())
}
}


impl<P: CubicExtParameters> Mul<SmallCubicExtField<P::BaseField>> for CubicExtField<P> {
type Output = Self;
fn mul(self, other: SmallCubicExtField<P::BaseField>) -> Self {
// this just forwards to the `Mul` impl for `SmallCubicExtField`.
other * self
}
}

impl<F: Field> PartialEq<i8> for SmallCubicExtField<F> {
fn eq(&self, other: &i8) -> bool {
self == &Self::from(*other)
}
}

impl<F: Field> From<i8> for SmallCubicExtField<F> {
fn from(other: i8) -> Self {
Self {
c0: F::SmallValue::from(other),
c1: F::SmallValue::zero(),
c2: F::SmallValue::zero(),
}
}
}

impl<P: CubicExtParameters> From<CubicExtField<P>> for SmallCubicExtField<P::BaseField> {
fn from(other: CubicExtField<P>) -> Self {
Self {
c0: other.c0.into(),
c1: other.c1.into(),
c2: other.c2.into(),
}
}
}

#[cfg(test)]
mod cube_ext_tests {
use super::*;
Expand Down
4 changes: 2 additions & 2 deletions ff/src/fields/models/fp12_2over3over2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait Fp12Parameters: 'static + Send + Sync + Copy {

/// This *must* equal (0, 1, 0);
/// see [[DESD06, Section 6.1]](https://eprint.iacr.org/2006/471.pdf).
const NONRESIDUE: Fp6<Self::Fp6Params>;
const NONRESIDUE: <Fp6<Self::Fp6Params> as Field>::SmallValue;

/// Coefficients for the Frobenius automorphism.
const FROBENIUS_COEFF_FP12_C1: &'static [Fp2<Fp2Params<Self>>];
Expand All @@ -38,7 +38,7 @@ impl<P: Fp12Parameters> QuadExtParameters for Fp12ParamsWrapper<P> {

const DEGREE_OVER_BASE_PRIME_FIELD: usize = 12;

const NONRESIDUE: Self::BaseField = P::NONRESIDUE;
const NONRESIDUE: <Fp6<P::Fp6Params> as Field>::SmallValue = P::NONRESIDUE;

const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP12_C1;

Expand Down
8 changes: 4 additions & 4 deletions ff/src/fields/models/fp2.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use super::quadratic_extension::*;
use crate::fields::PrimeField;
use crate::fields::{Field, PrimeField};
use core::marker::PhantomData;

pub trait Fp2Parameters: 'static + Send + Sync {
type Fp: PrimeField;

const NONRESIDUE: Self::Fp;
const NONRESIDUE: <Self::Fp as Field>::SmallValue;

const QUADRATIC_NONRESIDUE: (Self::Fp, Self::Fp);

Expand All @@ -15,7 +15,7 @@ pub trait Fp2Parameters: 'static + Send + Sync {
/// Return `fe * Self::NONRESIDUE`.
#[inline(always)]
fn mul_fp_by_nonresidue(fe: &Self::Fp) -> Self::Fp {
Self::NONRESIDUE * fe
Self::NONRESIDUE * *fe
}

/// A specializable method for computing `x + mul_base_field_by_nonresidue(y)`
Expand Down Expand Up @@ -53,7 +53,7 @@ impl<P: Fp2Parameters> QuadExtParameters for Fp2ParamsWrapper<P> {

const DEGREE_OVER_BASE_PRIME_FIELD: usize = 2;

const NONRESIDUE: Self::BaseField = P::NONRESIDUE;
const NONRESIDUE: <Self::BaseField as Field>::SmallValue = P::NONRESIDUE;

const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP2_C1;

Expand Down
6 changes: 3 additions & 3 deletions ff/src/fields/models/fp3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::marker::PhantomData;
pub trait Fp3Parameters: 'static + Send + Sync {
type Fp: PrimeField + SquareRootField;

const NONRESIDUE: Self::Fp;
const NONRESIDUE: <Self::Fp as Field>::SmallValue;

const FROBENIUS_COEFF_FP3_C1: &'static [Self::Fp];
const FROBENIUS_COEFF_FP3_C2: &'static [Self::Fp];
Expand All @@ -18,7 +18,7 @@ pub trait Fp3Parameters: 'static + Send + Sync {

#[inline(always)]
fn mul_fp_by_nonresidue(fe: &Self::Fp) -> Self::Fp {
Self::NONRESIDUE * fe
Self::NONRESIDUE * *fe
}
}

Expand All @@ -31,7 +31,7 @@ impl<P: Fp3Parameters> CubicExtParameters for Fp3ParamsWrapper<P> {

const DEGREE_OVER_BASE_PRIME_FIELD: usize = 3;

const NONRESIDUE: Self::BaseField = P::NONRESIDUE;
const NONRESIDUE: <P::Fp as Field>::SmallValue = P::NONRESIDUE;

const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP3_C1;
const FROBENIUS_COEFF_C2: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP3_C2;
Expand Down
Loading