Merge pull request #124 from mir-protocol/order_bigint

Field order as BigUint
This commit is contained in:
Nicholas Ward 2021-07-22 14:50:14 -07:00 committed by GitHub
commit 907f1e9147
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 332 additions and 173 deletions

View File

@ -275,7 +275,7 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.constant(F::TWO)
}
/// Returns a routable target with a value of `ORDER - 1`.
/// Returns a routable target with a value of `order() - 1`.
pub fn neg_one(&mut self) -> Target {
self.constant(F::NEG_ONE)
}

View File

@ -1,12 +1,14 @@
use num::bigint::BigUint;
use crate::field::field::Field;
/// Finds a set of shifts that result in unique cosets for the multiplicative subgroup of size
/// `2^subgroup_bits`.
pub(crate) fn get_unique_coset_shifts<F: Field>(subgroup_size: usize, num_shifts: usize) -> Vec<F> {
// From Lagrange's theorem.
let num_cosets = (F::ORDER - 1) / (subgroup_size as u64);
let num_cosets = (F::order() - 1u32) / (subgroup_size as u32);
assert!(
num_shifts as u64 <= num_cosets,
BigUint::from(num_shifts) <= num_cosets,
"The subgroup does not have enough distinct cosets"
);

View File

@ -4,7 +4,10 @@ use std::hash::{Hash, Hasher};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use itertools::Itertools;
use num::bigint::BigUint;
use num::Integer;
use rand::Rng;
use serde::{Deserialize, Serialize};
use crate::field::extension_field::quadratic::QuadraticCrandallField;
@ -12,6 +15,8 @@ use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{Extendable, Frobenius};
use crate::field::field::Field;
const FIELD_ORDER: u64 = 18446744071293632513;
/// EPSILON = 9 * 2**28 - 1
const EPSILON: u64 = 2415919103;
@ -142,15 +147,18 @@ impl Field for CrandallField {
const ZERO: Self = Self(0);
const ONE: Self = Self(1);
const TWO: Self = Self(2);
const NEG_ONE: Self = Self(Self::ORDER - 1);
const NEG_ONE: Self = Self(FIELD_ORDER - 1);
const ORDER: u64 = 18446744071293632513;
const TWO_ADICITY: usize = 28;
const CHARACTERISTIC: u64 = Self::ORDER;
const CHARACTERISTIC: u64 = FIELD_ORDER;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(5);
const POWER_OF_TWO_GENERATOR: Self = Self(10281950781551402419);
fn order() -> BigUint {
BigUint::from(FIELD_ORDER)
}
#[inline]
fn square(&self) -> Self {
*self * *self
@ -170,7 +178,7 @@ impl Field for CrandallField {
// Based on Algorithm 16 of "Efficient Software-Implementation of Finite Fields with
// Applications to Cryptography".
let p = Self::ORDER;
let p = FIELD_ORDER;
let mut u = self.to_canonical_u64();
let mut v = p;
let mut b = 1u64;
@ -228,8 +236,8 @@ impl Field for CrandallField {
fn to_canonical_u64(&self) -> u64 {
let mut c = self.0;
// We only need one condition subtraction, since 2 * ORDER would not fit in a u64.
if c >= Self::ORDER {
c -= Self::ORDER;
if c >= FIELD_ORDER {
c -= FIELD_ORDER;
}
c
}
@ -239,6 +247,14 @@ impl Field for CrandallField {
Self(n)
}
fn to_canonical_biguint(&self) -> BigUint {
BigUint::from(self.to_canonical_u64())
}
fn from_canonical_biguint(n: BigUint) -> Self {
Self(n.iter_u64_digits().next().unwrap_or(0))
}
fn cube_root(&self) -> Self {
let x0 = *self;
let x1 = x0.square();
@ -326,6 +342,10 @@ impl Field for CrandallField {
}
result
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self::from_canonical_u64(rng.gen_range(0, FIELD_ORDER))
}
}
impl Neg for CrandallField {
@ -336,7 +356,7 @@ impl Neg for CrandallField {
if self.is_zero() {
Self::ZERO
} else {
Self(Self::ORDER - self.to_canonical_u64())
Self(FIELD_ORDER - self.to_canonical_u64())
}
}
}
@ -348,7 +368,7 @@ impl Add for CrandallField {
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: Self) -> Self {
let (sum, over) = self.0.overflowing_add(rhs.0);
Self(sum.overflowing_sub((over as u64) * Self::ORDER).0)
Self(sum.overflowing_sub((over as u64) * FIELD_ORDER).0)
}
}
@ -371,7 +391,7 @@ impl Sub for CrandallField {
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: Self) -> Self {
let (diff, under) = self.0.overflowing_sub(rhs.to_canonical_u64());
Self(diff.overflowing_add((under as u64) * Self::ORDER).0)
Self(diff.overflowing_add((under as u64) * FIELD_ORDER).0)
}
}
@ -452,7 +472,8 @@ impl Frobenius<1> for CrandallField {}
#[cfg(test)]
mod tests {
use crate::test_arithmetic;
use crate::{test_field_arithmetic, test_prime_field_arithmetic};
test_arithmetic!(crate::field::crandall_field::CrandallField);
test_prime_field_arithmetic!(crate::field::crandall_field::CrandallField);
test_field_arithmetic!(crate::field::crandall_field::CrandallField);
}

View File

@ -34,8 +34,8 @@ pub trait Frobenius<const D: usize>: OEF<D> {
return self.repeated_frobenius(count % D);
}
let arr = self.to_basefield_array();
let k = (Self::BaseField::ORDER - 1) / (D as u64);
let z0 = Self::W.exp(k * count as u64);
let k = (Self::BaseField::order() - 1u32) / (D as u64);
let z0 = Self::W.exp_biguint(&(k * count as u64));
let mut res = [Self::BaseField::ZERO; D];
for (i, z) in z0.powers().take(D).enumerate() {
res[i] = arr[i] * z;

View File

@ -3,6 +3,8 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use itertools::Itertools;
use num::bigint::BigUint;
use rand::Rng;
use serde::{Deserialize, Serialize};
@ -51,9 +53,7 @@ impl Field for QuadraticCrandallField {
const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]);
const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]);
const CHARACTERISTIC: u64 = CrandallField::ORDER;
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC;
const TWO_ADICITY: usize = 29;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([
CrandallField(6483724566312148654),
@ -65,6 +65,10 @@ impl Field for QuadraticCrandallField {
const POWER_OF_TWO_GENERATOR: Self =
Self([CrandallField::ZERO, CrandallField(14420468973723774561)]);
fn order() -> BigUint {
CrandallField::order() * CrandallField::order()
}
// Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
fn try_inverse(&self) -> Option<Self> {
if self.is_zero() {
@ -86,6 +90,24 @@ impl Field for QuadraticCrandallField {
<Self as FieldExtension<2>>::BaseField::from_canonical_u64(n).into()
}
fn to_canonical_biguint(&self) -> BigUint {
let first = self.0[0].to_canonical_biguint();
let second = self.0[1].to_canonical_biguint();
let combined = second * Self::CHARACTERISTIC + first;
combined
}
fn from_canonical_biguint(n: BigUint) -> Self {
let smaller = n.clone() % Self::CHARACTERISTIC;
let larger = n.clone() / Self::CHARACTERISTIC;
Self([
<Self as FieldExtension<2>>::BaseField::from_canonical_biguint(smaller),
<Self as FieldExtension<2>>::BaseField::from_canonical_biguint(larger),
])
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as FieldExtension<2>>::BaseField::rand_from_rng(rng),
@ -200,6 +222,7 @@ mod tests {
use crate::field::extension_field::quadratic::QuadraticCrandallField;
use crate::field::extension_field::{FieldExtension, Frobenius};
use crate::field::field::Field;
use crate::test_field_arithmetic;
#[test]
fn test_add_neg_sub_mul() {
@ -238,14 +261,14 @@ mod tests {
type F = QuadraticCrandallField;
let x = F::rand();
assert_eq!(
x.exp(<F as FieldExtension<2>>::BaseField::ORDER),
x.exp_biguint(&<F as FieldExtension<2>>::BaseField::order()),
x.frobenius()
);
}
#[test]
fn test_field_order() {
// F::ORDER = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1
// F::order() = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1
type F = QuadraticCrandallField;
let x = F::rand();
assert_eq!(
@ -257,7 +280,7 @@ mod tests {
#[test]
fn test_power_of_two_gen() {
type F = QuadraticCrandallField;
// F::ORDER = 2^29 * 2762315674048163 * 229454332791453 + 1
// F::order() = 2^29 * 2762315674048163 * 229454332791453 + 1
assert_eq!(
F::MULTIPLICATIVE_GROUP_GENERATOR
.exp(2762315674048163)
@ -270,4 +293,6 @@ mod tests {
<F as FieldExtension<2>>::BaseField::POWER_OF_TWO_GENERATOR.into()
);
}
test_field_arithmetic!(crate::field::extension_field::quadratic::QuadraticCrandallField);
}

View File

@ -3,6 +3,9 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use itertools::Itertools;
use num::bigint::BigUint;
use num::traits::Pow;
use rand::Rng;
use serde::{Deserialize, Serialize};
@ -73,9 +76,8 @@ impl Field for QuarticCrandallField {
CrandallField::ZERO,
]);
const CHARACTERISTIC: u64 = CrandallField::ORDER;
const CHARACTERISTIC: u64 = CrandallField::CHARACTERISTIC;
// Does not fit in 64-bits.
const ORDER: u64 = 0;
const TWO_ADICITY: usize = 30;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([
CrandallField(12476589904174392631),
@ -93,6 +95,10 @@ impl Field for QuarticCrandallField {
CrandallField(15170983443234254033),
]);
fn order() -> BigUint {
CrandallField::order().pow(4u32)
}
// Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
fn try_inverse(&self) -> Option<Self> {
if self.is_zero() {
@ -117,6 +123,40 @@ impl Field for QuarticCrandallField {
<Self as FieldExtension<4>>::BaseField::from_canonical_u64(n).into()
}
fn to_canonical_biguint(&self) -> BigUint {
let first = self.0[0].to_canonical_biguint();
let second = self.0[1].to_canonical_biguint();
let third = self.0[2].to_canonical_biguint();
let fourth = self.0[3].to_canonical_biguint();
let mut combined = fourth;
combined *= Self::CHARACTERISTIC;
combined += third;
combined *= Self::CHARACTERISTIC;
combined += second;
combined *= Self::CHARACTERISTIC;
combined += first;
combined
}
fn from_canonical_biguint(n: BigUint) -> Self {
let first = &n % Self::CHARACTERISTIC;
let mut remaining = &n / Self::CHARACTERISTIC;
let second = &remaining % Self::CHARACTERISTIC;
remaining = remaining / Self::CHARACTERISTIC;
let third = &remaining % Self::CHARACTERISTIC;
remaining = remaining / Self::CHARACTERISTIC;
let fourth = &remaining % Self::CHARACTERISTIC;
Self([
<Self as FieldExtension<4>>::BaseField::from_canonical_biguint(first),
<Self as FieldExtension<4>>::BaseField::from_canonical_biguint(second),
<Self as FieldExtension<4>>::BaseField::from_canonical_biguint(third),
<Self as FieldExtension<4>>::BaseField::from_canonical_biguint(fourth),
])
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self([
<Self as FieldExtension<4>>::BaseField::rand_from_rng(rng),
@ -249,6 +289,7 @@ mod tests {
use crate::field::extension_field::quartic::QuarticCrandallField;
use crate::field::extension_field::{FieldExtension, Frobenius};
use crate::field::field::Field;
use crate::test_field_arithmetic;
fn exp_naive<F: Field>(x: F, power: u128) -> F {
let mut current = x;
@ -301,7 +342,7 @@ mod tests {
const D: usize = 4;
let x = F::rand();
assert_eq!(
exp_naive(x, <F as FieldExtension<D>>::BaseField::ORDER as u128),
x.exp_biguint(&<F as FieldExtension<D>>::BaseField::order()),
x.frobenius()
);
for count in 2..D {
@ -314,7 +355,7 @@ mod tests {
#[test]
fn test_field_order() {
// F::ORDER = 340282366831806780677557380898690695168 * 340282366831806780677557380898690695170 + 1
// F::order() = 340282366831806780677557380898690695168 * 340282366831806780677557380898690695170 + 1
type F = QuarticCrandallField;
let x = F::rand();
assert_eq!(
@ -329,7 +370,7 @@ mod tests {
#[test]
fn test_power_of_two_gen() {
type F = QuarticCrandallField;
// F::ORDER = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1
// F::order() = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1
assert_eq!(
exp_naive(
exp_naive(
@ -346,4 +387,6 @@ mod tests {
<F as FieldExtension<4>>::BaseField::POWER_OF_TWO_GENERATOR.into()
);
}
test_field_arithmetic!(crate::field::extension_field::quartic::QuarticCrandallField);
}

View File

@ -31,8 +31,8 @@ impl<const D: usize> ExtensionTarget<D> {
return self.repeated_frobenius(count % D, builder);
}
let arr = self.to_target_array();
let k = (F::ORDER - 1) / (D as u64);
let z0 = F::Extension::W.exp(k * count as u64);
let k = (F::order() - 1u32) / (D as u64);
let z0 = F::Extension::W.exp_biguint(&(k * count as u64));
let zs = z0
.powers()
.take(D)

View File

@ -4,7 +4,8 @@ use std::hash::Hash;
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::Integer;
use num::bigint::BigUint;
use num::{Integer, One, Zero};
use rand::Rng;
use serde::de::DeserializeOwned;
use serde::Serialize;
@ -44,7 +45,6 @@ pub trait Field:
const NEG_ONE: Self;
const CHARACTERISTIC: u64;
const ORDER: u64;
const TWO_ADICITY: usize;
/// Generator of the entire multiplicative group, i.e. all non-zero elements.
@ -52,6 +52,8 @@ pub trait Field:
/// Generator of a multiplicative subgroup of order `2^TWO_ADICITY`.
const POWER_OF_TWO_GENERATOR: Self;
fn order() -> BigUint;
fn is_zero(&self) -> bool {
*self == Self::ZERO
}
@ -183,6 +185,12 @@ pub trait Field:
Self::from_canonical_u64(n as u64)
}
fn to_canonical_biguint(&self) -> BigUint;
fn from_canonical_biguint(n: BigUint) -> Self;
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self;
fn bits(&self) -> usize {
bits_u64(self.to_canonical_u64())
}
@ -212,18 +220,33 @@ pub trait Field:
self.exp(power as u64)
}
fn exp_biguint(&self, power: &BigUint) -> Self {
let digits = power.to_u32_digits();
let radix = 1u64 << 32;
let mut result = Self::ONE;
for (radix_power, &digit) in digits.iter().enumerate() {
let mut current = self.exp_u32(digit);
for _ in 0..radix_power {
current = current.exp(radix);
}
result *= current;
}
result
}
/// Returns whether `x^power` is a permutation of this field.
fn is_monomial_permutation(power: u64) -> bool {
match power {
0 => false,
1 => true,
_ => (Self::ORDER - 1).gcd(&power) == 1,
_ => (Self::order() - 1u32).gcd(&BigUint::from(power)).is_one(),
}
}
fn kth_root(&self, k: u64) -> Self {
let p = Self::ORDER;
let p_minus_1 = p - 1;
let p = Self::order().clone();
let p_minus_1 = &p - 1u32;
debug_assert!(
Self::is_monomial_permutation(k),
"Not a permutation of this field"
@ -236,10 +259,10 @@ pub trait Field:
// x^((p + n(p - 1))/k)^k = x,
// implying that x^((p + n(p - 1))/k) is a k'th root of x.
for n in 0..k {
let numerator = p as u128 + n as u128 * p_minus_1 as u128;
if numerator % k as u128 == 0 {
let power = (numerator / k as u128) as u64 % p_minus_1;
return self.exp(power);
let numerator = &p + &p_minus_1 * n;
if (&numerator % k).is_zero() {
let power = (numerator / k) % p_minus_1;
return self.exp_biguint(&power);
}
}
panic!(
@ -292,10 +315,6 @@ pub trait Field:
Self::mds(vec.to_vec()).try_into().unwrap()
}
fn rand_from_rng<R: Rng>(rng: &mut R) -> Self {
Self::from_canonical_u64(rng.gen_range(0, Self::ORDER))
}
fn rand() -> Self {
Self::rand_from_rng(&mut rand::thread_rng())
}

View File

@ -1,18 +1,21 @@
use num::{bigint::BigUint, Zero};
use crate::field::field::Field;
use crate::util::{bits_u64, ceil_div_usize};
use crate::util::ceil_div_usize;
/// Generates a series of non-negative integers less than
/// `modulus` which cover a range of values and which will
/// generate lots of carries, especially at `word_bits` word
/// boundaries.
pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
assert!(word_bits == 32 || word_bits == 64);
let modwords = ceil_div_usize(bits_u64(modulus), word_bits);
pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec<BigUint> {
//assert!(word_bits == 32 || word_bits == 64);
let modwords = ceil_div_usize(modulus.bits() as usize, word_bits);
// Start with basic set close to zero: 0 .. 10
const BIGGEST_SMALL: u32 = 10;
let smalls: Vec<_> = (0..BIGGEST_SMALL).map(u64::from).collect();
let smalls: Vec<_> = (0..BIGGEST_SMALL).map(BigUint::from).collect();
// ... and close to MAX: MAX - x
let word_max = (1u64 << word_bits) - 1;
let word_max = (BigUint::from(1u32) << word_bits) - 1u32;
let multiple_words_max = (BigUint::from(1u32) << modwords * word_bits) - 1u32;
let bigs = smalls.iter().map(|x| &word_max - x).collect();
let one_words = [smalls, bigs].concat();
// For each of the one word inputs above, create a new one at word i.
@ -22,28 +25,28 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
one_words
.iter()
.map(|x| x << (word_bits * i))
.collect::<Vec<u64>>()
.collect::<Vec<BigUint>>()
})
.collect();
let basic_inputs: Vec<u64> = [one_words, multiple_words].concat();
let basic_inputs: Vec<BigUint> = [one_words, multiple_words].concat();
// Biggest value that will fit in `modwords` words
// Inputs 'difference from' maximum value
let diff_max = basic_inputs
.iter()
.map(|&x| u64::MAX - x)
.filter(|&x| x < modulus)
.map(|x| &multiple_words_max - x)
.filter(|x| x < &modulus)
.collect();
// Inputs 'difference from' modulus value
let diff_mod = basic_inputs
.iter()
.filter(|&&x| x < modulus && x != 0)
.map(|&x| modulus - x)
.filter(|&x| x < &modulus && !x.is_zero())
.map(|x| &modulus - x)
.collect();
let basics = basic_inputs
.into_iter()
.filter(|&x| x < modulus)
.collect::<Vec<u64>>();
.filter(|x| x < &modulus)
.collect::<Vec<BigUint>>();
[basics, diff_max, diff_mod].concat()
// // There should be a nicer way to express the code above; something
@ -59,20 +62,21 @@ pub fn test_inputs(modulus: u64, word_bits: usize) -> Vec<u64> {
/// coordinate-wise to the inputs from `test_inputs(modulus,
/// word_bits)` and panic if the two resulting vectors differ.
pub fn run_unaryop_test_cases<F, UnaryOp, ExpectedOp>(
modulus: u64,
modulus: BigUint,
word_bits: usize,
op: UnaryOp,
expected_op: ExpectedOp,
) where
F: Field,
UnaryOp: Fn(F) -> F,
ExpectedOp: Fn(u64) -> u64,
ExpectedOp: Fn(BigUint) -> BigUint,
{
let inputs = test_inputs(modulus, word_bits);
let expected: Vec<_> = inputs.iter().map(|&x| expected_op(x)).collect();
let expected: Vec<_> = inputs.iter().map(|x| expected_op(x.clone())).collect();
let output: Vec<_> = inputs
.iter()
.map(|&x| op(F::from_canonical_u64(x)).to_canonical_u64())
.cloned()
.map(|x| op(F::from_canonical_biguint(x)).to_canonical_biguint())
.collect();
// Compare expected outputs with actual outputs
for i in 0..inputs.len() {
@ -90,14 +94,14 @@ pub fn run_unaryop_test_cases<F, UnaryOp, ExpectedOp>(
/// `inputs.len()`. Panic if the two functions ever give
/// different answers.
pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(
modulus: u64,
modulus: BigUint,
word_bits: usize,
op: BinaryOp,
expected_op: ExpectedOp,
) where
F: Field,
BinaryOp: Fn(F, F) -> F,
ExpectedOp: Fn(u64, u64) -> u64,
ExpectedOp: Fn(BigUint, BigUint) -> BigUint,
{
let inputs = test_inputs(modulus, word_bits);
@ -122,8 +126,12 @@ pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(
let output: Vec<_> = inputs
.iter()
.zip(shifted_inputs.clone())
.map(|(&x, &y)| {
op(F::from_canonical_u64(x), F::from_canonical_u64(y)).to_canonical_u64()
.map(|(x, y)| {
op(
F::from_canonical_biguint(x.clone()),
F::from_canonical_biguint(y.clone()),
)
.to_canonical_biguint()
})
.collect();
@ -139,108 +147,14 @@ pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(
}
#[macro_export]
macro_rules! test_arithmetic {
macro_rules! test_field_arithmetic {
($field:ty) => {
mod arithmetic {
use std::ops::{Add, Mul, Neg, Sub};
mod field_arithmetic {
use num::{bigint::BigUint, One, Zero};
use rand::{thread_rng, Rng};
use crate::field::field::Field;
// Can be 32 or 64; doesn't have to be computer's actual word
// bits. Choosing 32 gives more tests...
const WORD_BITS: usize = 32;
#[test]
fn arithmetic_addition() {
let modulus = <$field>::ORDER;
crate::field::field_testing::run_binaryop_test_cases(
modulus,
WORD_BITS,
<$field>::add,
|x, y| {
let (z, over) = x.overflowing_add(y);
if over {
z.overflowing_sub(modulus).0
} else if z >= modulus {
z - modulus
} else {
z
}
},
)
}
#[test]
fn arithmetic_subtraction() {
let modulus = <$field>::ORDER;
crate::field::field_testing::run_binaryop_test_cases(
modulus,
WORD_BITS,
<$field>::sub,
|x, y| {
if x >= y {
x - y
} else {
&modulus - y + x
}
},
)
}
#[test]
fn arithmetic_negation() {
let modulus = <$field>::ORDER;
crate::field::field_testing::run_unaryop_test_cases(
modulus,
WORD_BITS,
<$field>::neg,
|x| {
if x == 0 {
0
} else {
modulus - x
}
},
)
}
#[test]
fn arithmetic_multiplication() {
let modulus = <$field>::ORDER;
crate::field::field_testing::run_binaryop_test_cases(
modulus,
WORD_BITS,
<$field>::mul,
|x, y| ((x as u128) * (y as u128) % (modulus as u128)) as u64,
)
}
#[test]
fn arithmetic_square() {
let modulus = <$field>::ORDER;
crate::field::field_testing::run_unaryop_test_cases(
modulus,
WORD_BITS,
|x: $field| x.square(),
|x| ((x as u128) * (x as u128) % (modulus as u128)) as u64,
)
}
#[test]
fn inversion() {
let zero = <$field>::ZERO;
let one = <$field>::ONE;
let order = <$field>::ORDER;
assert_eq!(zero.try_inverse(), None);
for &x in &[1, 2, 3, order - 3, order - 2, order - 1] {
let x = <$field>::from_canonical_u64(x);
let inv = x.inverse();
assert_eq!(x * inv, one);
}
}
#[test]
fn batch_inversion() {
let xs = (1..=3)
@ -264,10 +178,16 @@ macro_rules! test_arithmetic {
#[test]
fn negation() {
let zero = <$field>::ZERO;
let order = <$field>::ORDER;
let order = <$field>::order();
for &i in &[0, 1, 2, order - 2, order - 1] {
let i_f = <$field>::from_canonical_u64(i);
for i in [
BigUint::zero(),
BigUint::one(),
BigUint::from(2u32),
&order - 1u32,
&order - 2u32,
] {
let i_f = <$field>::from_canonical_biguint(i);
assert_eq!(i_f + -i_f, zero);
}
}
@ -307,13 +227,20 @@ macro_rules! test_arithmetic {
}
#[test]
fn subtraction() {
fn exponentiation_large() {
type F = $field;
let (a, b) = (F::from_canonical_u64((F::ORDER + 1) / 2), F::TWO);
let x = a * b;
assert_eq!(x, F::ONE);
assert_eq!(F::ZERO - x, F::NEG_ONE);
let mut rng = rand::thread_rng();
let base = F::rand();
let pow = BigUint::from(rng.gen::<u64>());
let cycles = rng.gen::<u32>();
let mul_group_order = F::order() - 1u32;
let big_pow = &pow + &mul_group_order * cycles;
let big_pow_wrong = &pow + &mul_group_order * cycles + 1u32;
assert_eq!(base.exp_biguint(&pow), base.exp_biguint(&big_pow));
assert_ne!(base.exp_biguint(&pow), base.exp_biguint(&big_pow_wrong));
}
#[test]
@ -332,3 +259,122 @@ macro_rules! test_arithmetic {
}
};
}
#[macro_export]
macro_rules! test_prime_field_arithmetic {
($field:ty) => {
mod prime_field_arithmetic {
use std::ops::{Add, Mul, Neg, Sub};
use num::{bigint::BigUint, One, Zero};
use crate::field::field::Field;
// Can be 32 or 64; doesn't have to be computer's actual word
// bits. Choosing 32 gives more tests...
const WORD_BITS: usize = 32;
#[test]
fn arithmetic_addition() {
let modulus = <$field>::order();
crate::field::field_testing::run_binaryop_test_cases(
modulus.clone(),
WORD_BITS,
<$field>::add,
|x, y| (&x + &y) % &modulus,
)
}
#[test]
fn arithmetic_subtraction() {
let modulus = <$field>::order();
crate::field::field_testing::run_binaryop_test_cases(
modulus.clone(),
WORD_BITS,
<$field>::sub,
|x, y| {
if x >= y {
&x - &y
} else {
&modulus - &y + &x
}
},
)
}
#[test]
fn arithmetic_negation() {
let modulus = <$field>::order();
crate::field::field_testing::run_unaryop_test_cases(
modulus.clone(),
WORD_BITS,
<$field>::neg,
|x| {
if x.is_zero() {
BigUint::zero()
} else {
&modulus - &x
}
},
)
}
#[test]
fn arithmetic_multiplication() {
let modulus = <$field>::order();
crate::field::field_testing::run_binaryop_test_cases(
modulus.clone(),
WORD_BITS,
<$field>::mul,
|x, y| &x * &y % &modulus,
)
}
#[test]
fn arithmetic_square() {
let modulus = <$field>::order();
crate::field::field_testing::run_unaryop_test_cases(
modulus.clone(),
WORD_BITS,
|x: $field| x.square(),
|x| (&x * &x) % &modulus,
)
}
#[test]
fn inversion() {
let zero = <$field>::ZERO;
let one = <$field>::ONE;
let order = <$field>::order();
assert_eq!(zero.try_inverse(), None);
for x in [
BigUint::one(),
BigUint::from(2u32),
BigUint::from(3u32),
&order - 3u32,
&order - 2u32,
&order - 1u32,
] {
let x = <$field>::from_canonical_biguint(x);
let inv = x.inverse();
assert_eq!(x * inv, one);
}
}
#[test]
fn subtraction() {
type F = $field;
let (a, b) = (
F::from_canonical_biguint((F::order() + 1u32) / 2u32),
F::TWO,
);
let x = a * b;
assert_eq!(x, F::ONE);
assert_eq!(F::ZERO - x, F::NEG_ONE);
}
}
};
}

View File

@ -111,7 +111,7 @@ fn fri_proof_of_work<F: Field>(current_hash: Hash<F>, config: &FriConfig) -> F {
)
.to_canonical_u64()
.leading_zeros()
>= config.proof_of_work_bits + F::ORDER.leading_zeros()
>= config.proof_of_work_bits + (64 - F::order().bits()) as u32
})
.map(F::from_canonical_u64)
.expect("Proof of work failed. This is highly unlikely!")

View File

@ -65,7 +65,10 @@ impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
inputs.push(proof.pow_witness);
let hash = self.hash_n_to_m(inputs, 1, false)[0];
self.assert_leading_zeros(hash, config.proof_of_work_bits + F::ORDER.leading_zeros());
self.assert_leading_zeros(
hash,
config.proof_of_work_bits + (64 - F::order().bits()) as u32,
);
}
pub fn verify_fri_proof(

View File

@ -59,7 +59,7 @@ fn fri_verify_proof_of_work<F: Field + Extendable<D>, const D: usize>(
);
ensure!(
hash.to_canonical_u64().leading_zeros()
>= config.proof_of_work_bits + F::ORDER.leading_zeros(),
>= config.proof_of_work_bits + (64 - F::order().bits()) as u32,
"Invalid proof of work witness."
);