Skip to content

Commit

Permalink
Use generic implementations for prime field operations.
Browse files Browse the repository at this point in the history
This is a refactor of the FieldParameters struct
to use generic datatypes providing implementations for
primes that fit in one word.

Tests script and documentation parameters were updated to
support the new structure.

Performance for FP32 is twice as fast than before, and no
changes for the FP64 and FP128 fields.
  • Loading branch information
armfazh committed Jul 27, 2024
1 parent 10d8495 commit 74ee996
Show file tree
Hide file tree
Showing 7 changed files with 736 additions and 867 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ bitvec = { version = "1.0.1", optional = true }
byteorder = "1.5.0"
ctr = { version = "0.9.2", optional = true }
fiat-crypto = { version = "0.2.9", optional = true }
fixed = { version = "1.27", optional = true }
fixed = "1.27"
getrandom = { version = "0.2.14", features = ["std"] }
hex = { version = "0.4.3", features = ["serde"], optional = true }
hmac = { version = "0.12.1", optional = true }
num-bigint = { version = "0.4.6", optional = true, features = ["rand", "serde"] }
num-integer = { version = "0.1.46", optional = true }
num-iter = { version = "0.1.45", optional = true }
num-rational = { version = "0.4.2", optional = true, features = ["serde"] }
num-traits = { version = "0.2.19", optional = true }
num-traits = "0.2.19"
rand = "0.8"
rand_core = "0.6.4"
rayon = { version = "1.10.0", optional = true }
Expand Down Expand Up @@ -52,7 +52,7 @@ statrs = "0.17.1"

[features]
default = ["crypto-dependencies"]
experimental = ["bitvec", "fiat-crypto", "fixed", "num-bigint", "num-rational", "num-traits", "num-integer", "num-iter"]
experimental = ["bitvec", "fiat-crypto", "num-bigint", "num-rational", "num-integer", "num-iter"]
multithreaded = ["rayon"]
crypto-dependencies = ["aes", "ctr", "hmac", "sha2"]
test-util = ["hex", "serde_json", "zipf"]
Expand Down
28 changes: 18 additions & 10 deletions documentation/field_parameters.sage
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,27 @@ class Field:
for i in range(min(self.num_roots, 20) + 1)
]

def log2_base(self):
"""
Returns log2(r), where r is the base used for multiprecision arithmetic.
"""
return log(self.r, 2)

def log2_radix(self):
"""
Returns log2(R), where R is the machine word-friendly modulus
used in the Montgomery representation.
"""
return log(self.R, 2)


FIELDS = [
Field(
"FieldPrio2, u128",
"FieldPrio2, u32",
2 ^ 20 * 4095 + 1,
3925978153,
2 ^ 64,
2 ^ 128,
),
Field(
"Field64, u128",
2 ^ 32 * 4294967295 + 1,
pow(7, 4294967295, 2 ^ 32 * 4294967295 + 1),
2 ^ 64,
2 ^ 128,
2 ^ 32,
2 ^ 32,
),
Field(
"Field64, u64",
Expand All @@ -140,4 +146,6 @@ for field in FIELDS:
print(f"bit_mask: {field.bit_mask()}")
print("roots:")
pprint.pprint(field.roots())
print(f"log2_base: {field.log2_base()}")
print(f"log2_radix: {field.log2_radix()}")
print()
55 changes: 27 additions & 28 deletions src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

use crate::{
codec::{CodecError, Decode, Encode},
fp::{FP128, FP32},
fp64::FP64,
fp::{FieldOps, FieldParameters, FP128, FP32, FP64},
prng::{Prng, PrngError},
};
use rand::{
Expand Down Expand Up @@ -465,12 +464,12 @@ macro_rules! make_field {

int &= mask;

if int >= $fp.p {
if int >= $fp::PRIME {
return Err(FieldError::ModulusOverflow);
}
// FieldParameters::montgomery() will return a value that has been fully reduced
// mod p, satisfying the invariant on Self.
Ok(Self($fp.montgomery(int)))
Ok(Self($fp::montgomery(int)))
}
}

Expand All @@ -481,8 +480,8 @@ macro_rules! make_field {
// https://doc.rust-lang.org/std/hash/trait.Hash.html#hash-and-eq

// Check the invariant that the integer representation is fully reduced.
debug_assert!(self.0 < $fp.p);
debug_assert!(rhs.0 < $fp.p);
debug_assert!(self.0 < $fp::PRIME);
debug_assert!(rhs.0 < $fp::PRIME);

self.0 == rhs.0
}
Expand All @@ -507,7 +506,7 @@ macro_rules! make_field {
// https://doc.rust-lang.org/std/hash/trait.Hash.html#hash-and-eq

// Check the invariant that the integer representation is fully reduced.
debug_assert!(self.0 < $fp.p);
debug_assert!(self.0 < $fp::PRIME);

self.0.hash(state);
}
Expand All @@ -520,7 +519,7 @@ macro_rules! make_field {
fn add(self, rhs: Self) -> Self {
// FieldParameters::add() returns a value that has been fully reduced
// mod p, satisfying the invariant on Self.
Self($fp.add(self.0, rhs.0))
Self($fp::add(self.0, rhs.0))
}
}

Expand All @@ -542,7 +541,7 @@ macro_rules! make_field {
fn sub(self, rhs: Self) -> Self {
// We know that self.0 and rhs.0 are both less than p, thus FieldParameters::sub()
// returns a value less than p, satisfying the invariant on Self.
Self($fp.sub(self.0, rhs.0))
Self($fp::sub(self.0, rhs.0))
}
}

Expand All @@ -564,7 +563,7 @@ macro_rules! make_field {
fn mul(self, rhs: Self) -> Self {
// FieldParameters::mul() always returns a value less than p, so the invariant on
// Self is satisfied.
Self($fp.mul(self.0, rhs.0))
Self($fp::mul(self.0, rhs.0))
}
}

Expand Down Expand Up @@ -607,7 +606,7 @@ macro_rules! make_field {
fn neg(self) -> Self {
// FieldParameters::neg() will return a value less than p because self.0 is less
// than p, and neg() dispatches to sub().
Self($fp.neg(self.0))
Self($fp::neg(self.0))
}
}

Expand All @@ -622,19 +621,19 @@ macro_rules! make_field {
fn from(x: $int_conversion) -> Self {
// FieldParameters::montgomery() will return a value that has been fully reduced
// mod p, satisfying the invariant on Self.
Self($fp.montgomery($int_internal::try_from(x).unwrap()))
Self($fp::montgomery($int_internal::try_from(x).unwrap()))
}
}

impl From<$elem> for $int_conversion {
fn from(x: $elem) -> Self {
$int_conversion::try_from($fp.residue(x.0)).unwrap()
$int_conversion::try_from($fp::residue(x.0)).unwrap()
}
}

impl PartialEq<$int_conversion> for $elem {
fn eq(&self, rhs: &$int_conversion) -> bool {
$fp.residue(self.0) == $int_internal::try_from(*rhs).unwrap()
$fp::residue(self.0) == $int_internal::try_from(*rhs).unwrap()
}
}

Expand All @@ -648,7 +647,7 @@ macro_rules! make_field {

impl From<$elem> for [u8; $elem::ENCODED_SIZE] {
fn from(elem: $elem) -> Self {
let int = $fp.residue(elem.0);
let int = $fp::residue(elem.0);
let mut slice = [0; $elem::ENCODED_SIZE];
for i in 0..$elem::ENCODED_SIZE {
slice[i] = ((int >> (i << 3)) & 0xff) as u8;
Expand All @@ -665,13 +664,13 @@ macro_rules! make_field {

impl Display for $elem {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", $fp.residue(self.0))
write!(f, "{}", $fp::residue(self.0))
}
}

impl Debug for $elem {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", $fp.residue(self.0))
write!(f, "{}", $fp::residue(self.0))
}
}

Expand Down Expand Up @@ -719,19 +718,19 @@ macro_rules! make_field {
fn inv(&self) -> Self {
// FieldParameters::inv() ultimately relies on mul(), and will always return a
// value less than p.
Self($fp.inv(self.0))
Self($fp::inv(self.0))
}

fn try_from_random(bytes: &[u8]) -> Result<Self, FieldError> {
$elem::try_from_bytes(bytes, $fp.bit_mask)
$elem::try_from_bytes(bytes, $fp::BIT_MASK)
}

fn zero() -> Self {
Self(0)
}

fn one() -> Self {
Self($fp.roots[0])
Self($fp::ROOTS[0])
}
}

Expand All @@ -741,26 +740,26 @@ macro_rules! make_field {
fn pow(&self, exp: Self::Integer) -> Self {
// FieldParameters::pow() relies on mul(), and will always return a value less
// than p.
Self($fp.pow(self.0, $int_internal::try_from(exp).unwrap()))
Self($fp::pow(self.0, $int_internal::try_from(exp).unwrap()))
}

fn modulus() -> Self::Integer {
$fp.p as $int_conversion
$fp::PRIME as $int_conversion
}
}

impl FftFriendlyFieldElement for $elem {
fn generator() -> Self {
Self($fp.g)
Self($fp::G)
}

fn generator_order() -> Self::Integer {
1 << (Self::Integer::try_from($fp.num_roots).unwrap())
1 << (Self::Integer::try_from($fp::NUM_ROOTS).unwrap())
}

fn root(l: usize) -> Option<Self> {
if l < min($fp.roots.len(), $fp.num_roots+1) {
Some(Self($fp.roots[l]))
if l < min($fp::ROOTS.len(), $fp::NUM_ROOTS+1) {
Some(Self($fp::ROOTS[l]))
} else {
None
}
Expand Down Expand Up @@ -815,9 +814,9 @@ impl Integer for u128 {
}

make_field!(
/// Same as Field32, but encoded in little endian for compatibility with Prio v2.
/// `GF(4293918721)`, a 32-bit field.
FieldPrio2,
u128,
u32,
u32,
FP32,
4,
Expand Down
Loading

0 comments on commit 74ee996

Please sign in to comment.