mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-02-17 12:23:07 +00:00
Rework the field test code a bit (#225)
- Split it into two files, one for general `Field` tests and one for `PrimeField` tests. - Replace most uses of `BigUint` in tests with `u64`. These uses were only applicable for `PrimeField`s, which are 64-bit fields anyway. This lets us delete the `BigUInt` conversion methods. - Simplify `test_inputs`, which was originally written for large prime fields. Now that it's only used for 64-bit fields, I think interesting inputs are just the smallest and largest elements, and those close to 2^32 etc.
This commit is contained in:
parent
50274883c7
commit
a2eaaceb34
@ -242,10 +242,6 @@ impl Field for CrandallField {
|
|||||||
Self(n)
|
Self(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_canonical_biguint(n: BigUint) -> Self {
|
|
||||||
Self(n.iter_u64_digits().next().unwrap_or(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_noncanonical_u128(n: u128) -> Self {
|
fn from_noncanonical_u128(n: u128) -> Self {
|
||||||
reduce128(n)
|
reduce128(n)
|
||||||
@ -361,10 +357,6 @@ impl PrimeField for CrandallField {
|
|||||||
fn to_noncanonical_u64(&self) -> u64 {
|
fn to_noncanonical_u64(&self) -> u64 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_canonical_biguint(&self) -> BigUint {
|
|
||||||
BigUint::from(self.to_canonical_u64())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Neg for CrandallField {
|
impl Neg for CrandallField {
|
||||||
|
|||||||
@ -93,16 +93,6 @@ impl Field for QuadraticCrandallField {
|
|||||||
<Self as FieldExtension<2>>::BaseField::from_canonical_u64(n).into()
|
<Self as FieldExtension<2>>::BaseField::from_canonical_u64(n).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 from_noncanonical_u128(n: u128) -> Self {
|
fn from_noncanonical_u128(n: u128) -> Self {
|
||||||
<Self as FieldExtension<2>>::BaseField::from_noncanonical_u128(n).into()
|
<Self as FieldExtension<2>>::BaseField::from_noncanonical_u128(n).into()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,23 +126,6 @@ impl Field for QuarticCrandallField {
|
|||||||
<Self as FieldExtension<4>>::BaseField::from_canonical_u64(n).into()
|
<Self as FieldExtension<4>>::BaseField::from_canonical_u64(n).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 from_noncanonical_u128(n: u128) -> Self {
|
fn from_noncanonical_u128(n: u128) -> Self {
|
||||||
<Self as FieldExtension<4>>::BaseField::from_noncanonical_u128(n).into()
|
<Self as FieldExtension<4>>::BaseField::from_noncanonical_u128(n).into()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,156 +1,10 @@
|
|||||||
use num::{bigint::BigUint, Zero};
|
|
||||||
|
|
||||||
use crate::field::field_types::PrimeField;
|
use crate::field::field_types::PrimeField;
|
||||||
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: 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(BigUint::from).collect();
|
|
||||||
// ... and close to MAX: MAX - x
|
|
||||||
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.
|
|
||||||
// TODO: Create all possible `modwords` combinations of those
|
|
||||||
let multiple_words = (1..modwords)
|
|
||||||
.flat_map(|i| {
|
|
||||||
one_words
|
|
||||||
.iter()
|
|
||||||
.map(|x| x << (word_bits * i))
|
|
||||||
.collect::<Vec<BigUint>>()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
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| &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.is_zero())
|
|
||||||
.map(|x| &modulus - x)
|
|
||||||
.collect();
|
|
||||||
let basics = basic_inputs
|
|
||||||
.into_iter()
|
|
||||||
.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
|
|
||||||
// // like this (and removing collect() calls from diff_max and diff_mod):
|
|
||||||
// basic_inputs.into_iter()
|
|
||||||
// .chain(diff_max)
|
|
||||||
// .chain(diff_mod)
|
|
||||||
// .filter(|x| x < &modulus)
|
|
||||||
// .collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply the unary functions `op` and `expected_op`
|
|
||||||
/// 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: BigUint,
|
|
||||||
word_bits: usize,
|
|
||||||
op: UnaryOp,
|
|
||||||
expected_op: ExpectedOp,
|
|
||||||
) where
|
|
||||||
F: PrimeField,
|
|
||||||
UnaryOp: Fn(F) -> F,
|
|
||||||
ExpectedOp: Fn(BigUint) -> BigUint,
|
|
||||||
{
|
|
||||||
let inputs = test_inputs(modulus, word_bits);
|
|
||||||
let expected: Vec<_> = inputs.iter().map(|x| expected_op(x.clone())).collect();
|
|
||||||
let output: Vec<_> = inputs
|
|
||||||
.iter()
|
|
||||||
.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() {
|
|
||||||
assert_eq!(
|
|
||||||
output[i], expected[i],
|
|
||||||
"Expected {}, got {} for input {}",
|
|
||||||
expected[i], output[i], inputs[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply the binary functions `op` and `expected_op` to each pair
|
|
||||||
/// in `zip(inputs, rotate_right(inputs, i))` where `inputs` is
|
|
||||||
/// `test_inputs(modulus, word_bits)` and `i` ranges from 0 to
|
|
||||||
/// `inputs.len()`. Panic if the two functions ever give
|
|
||||||
/// different answers.
|
|
||||||
pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(
|
|
||||||
modulus: BigUint,
|
|
||||||
word_bits: usize,
|
|
||||||
op: BinaryOp,
|
|
||||||
expected_op: ExpectedOp,
|
|
||||||
) where
|
|
||||||
F: PrimeField,
|
|
||||||
BinaryOp: Fn(F, F) -> F,
|
|
||||||
ExpectedOp: Fn(BigUint, BigUint) -> BigUint,
|
|
||||||
{
|
|
||||||
let inputs = test_inputs(modulus, word_bits);
|
|
||||||
|
|
||||||
for i in 0..inputs.len() {
|
|
||||||
// Iterator over inputs rotated right by i places. Since
|
|
||||||
// cycle().skip(i) rotates left by i, we need to rotate by
|
|
||||||
// n_input_elts - i.
|
|
||||||
let shifted_inputs: Vec<_> = inputs
|
|
||||||
.iter()
|
|
||||||
.cycle()
|
|
||||||
.skip(inputs.len() - i)
|
|
||||||
.take(inputs.len())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Calculate pointwise operations
|
|
||||||
let expected: Vec<_> = inputs
|
|
||||||
.iter()
|
|
||||||
.zip(shifted_inputs.clone())
|
|
||||||
.map(|(x, y)| expected_op(x.clone(), y.clone()))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let output: Vec<_> = inputs
|
|
||||||
.iter()
|
|
||||||
.zip(shifted_inputs.clone())
|
|
||||||
.map(|(x, y)| {
|
|
||||||
op(
|
|
||||||
F::from_canonical_biguint(x.clone()),
|
|
||||||
F::from_canonical_biguint(y.clone()),
|
|
||||||
)
|
|
||||||
.to_canonical_biguint()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Compare expected outputs with actual outputs
|
|
||||||
for i in 0..inputs.len() {
|
|
||||||
assert_eq!(
|
|
||||||
output[i], expected[i],
|
|
||||||
"On inputs {} . {}, expected {} but got {}",
|
|
||||||
inputs[i], shifted_inputs[i], expected[i], output[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! test_field_arithmetic {
|
macro_rules! test_field_arithmetic {
|
||||||
($field:ty) => {
|
($field:ty) => {
|
||||||
mod field_arithmetic {
|
mod field_arithmetic {
|
||||||
use num::{bigint::BigUint, One, Zero};
|
use num::bigint::BigUint;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::field::field_types::Field;
|
use crate::field::field_types::Field;
|
||||||
@ -177,18 +31,10 @@ macro_rules! test_field_arithmetic {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn negation() {
|
fn negation() {
|
||||||
let zero = <$field>::ZERO;
|
type F = $field;
|
||||||
let order = <$field>::order();
|
|
||||||
|
|
||||||
for i in [
|
for x in [F::ZERO, F::ONE, F::TWO, F::NEG_ONE] {
|
||||||
BigUint::zero(),
|
assert_eq!(x + -x, F::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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,135 +95,3 @@ macro_rules! test_field_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_types::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_double_wraparound() {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn addition_double_wraparound() {
|
|
||||||
type F = $field;
|
|
||||||
|
|
||||||
let a = F::from_canonical_biguint(u64::MAX - F::order());
|
|
||||||
let b = F::NEG_ONE;
|
|
||||||
|
|
||||||
let c = (a + a) + (b + b);
|
|
||||||
let d = (a + b) + (a + b);
|
|
||||||
|
|
||||||
assert_eq!(c, d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@ -194,8 +194,6 @@ pub trait Field:
|
|||||||
Self::from_canonical_u64(b as u64)
|
Self::from_canonical_u64(b as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_canonical_biguint(n: BigUint) -> Self;
|
|
||||||
|
|
||||||
/// Returns `n % Self::CHARACTERISTIC`.
|
/// Returns `n % Self::CHARACTERISTIC`.
|
||||||
fn from_noncanonical_u128(n: u128) -> Self;
|
fn from_noncanonical_u128(n: u128) -> Self;
|
||||||
|
|
||||||
@ -328,8 +326,6 @@ pub trait PrimeField: Field {
|
|||||||
fn to_canonical_u64(&self) -> u64;
|
fn to_canonical_u64(&self) -> u64;
|
||||||
|
|
||||||
fn to_noncanonical_u64(&self) -> u64;
|
fn to_noncanonical_u64(&self) -> u64;
|
||||||
|
|
||||||
fn to_canonical_biguint(&self) -> BigUint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
|
/// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`.
|
||||||
|
|||||||
@ -9,3 +9,5 @@ pub(crate) mod packed_field;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod field_testing;
|
mod field_testing;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod prime_field_testing;
|
||||||
|
|||||||
163
src/field/prime_field_testing.rs
Normal file
163
src/field/prime_field_testing.rs
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
use crate::field::field_types::PrimeField;
|
||||||
|
|
||||||
|
/// Generates a series of non-negative integers less than `modulus` which cover a range of
|
||||||
|
/// interesting test values.
|
||||||
|
pub fn test_inputs(modulus: u64) -> Vec<u64> {
|
||||||
|
const CHUNK_SIZE: u64 = 10;
|
||||||
|
|
||||||
|
(0..CHUNK_SIZE)
|
||||||
|
.chain((1 << 31) - CHUNK_SIZE..(1 << 31) + CHUNK_SIZE)
|
||||||
|
.chain((1 << 32) - CHUNK_SIZE..(1 << 32) + CHUNK_SIZE)
|
||||||
|
.chain((1 << 63) - CHUNK_SIZE..(1 << 63) + CHUNK_SIZE)
|
||||||
|
.chain(modulus - CHUNK_SIZE..modulus)
|
||||||
|
.filter(|&x| x < modulus)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply the unary functions `op` and `expected_op`
|
||||||
|
/// 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>(op: UnaryOp, expected_op: ExpectedOp)
|
||||||
|
where
|
||||||
|
F: PrimeField,
|
||||||
|
UnaryOp: Fn(F) -> F,
|
||||||
|
ExpectedOp: Fn(u64) -> u64,
|
||||||
|
{
|
||||||
|
let inputs = test_inputs(F::ORDER);
|
||||||
|
let expected: Vec<_> = inputs.iter().map(|x| expected_op(x.clone())).collect();
|
||||||
|
let output: Vec<_> = inputs
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|x| op(F::from_canonical_u64(x)).to_canonical_u64())
|
||||||
|
.collect();
|
||||||
|
// Compare expected outputs with actual outputs
|
||||||
|
for i in 0..inputs.len() {
|
||||||
|
assert_eq!(
|
||||||
|
output[i], expected[i],
|
||||||
|
"Expected {}, got {} for input {}",
|
||||||
|
expected[i], output[i], inputs[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply the binary functions `op` and `expected_op` to each pair of inputs.
|
||||||
|
pub fn run_binaryop_test_cases<F, BinaryOp, ExpectedOp>(op: BinaryOp, expected_op: ExpectedOp)
|
||||||
|
where
|
||||||
|
F: PrimeField,
|
||||||
|
BinaryOp: Fn(F, F) -> F,
|
||||||
|
ExpectedOp: Fn(u64, u64) -> u64,
|
||||||
|
{
|
||||||
|
let inputs = test_inputs(F::ORDER);
|
||||||
|
|
||||||
|
for &lhs in &inputs {
|
||||||
|
for &rhs in &inputs {
|
||||||
|
let lhs_f = F::from_canonical_u64(lhs);
|
||||||
|
let rhs_f = F::from_canonical_u64(rhs);
|
||||||
|
let actual = op(lhs_f, rhs_f).to_canonical_u64();
|
||||||
|
let expected = expected_op(lhs, rhs);
|
||||||
|
assert_eq!(
|
||||||
|
actual, expected,
|
||||||
|
"Expected {}, got {} for inputs ({}, {})",
|
||||||
|
expected, actual, lhs, rhs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! test_prime_field_arithmetic {
|
||||||
|
($field:ty) => {
|
||||||
|
mod prime_field_arithmetic {
|
||||||
|
use std::ops::{Add, Mul, Neg, Sub};
|
||||||
|
|
||||||
|
use crate::field::field_types::{Field, PrimeField};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arithmetic_addition() {
|
||||||
|
let modulus = <$field>::ORDER;
|
||||||
|
crate::field::prime_field_testing::run_binaryop_test_cases(<$field>::add, |x, y| {
|
||||||
|
((x as u128 + y as u128) % (modulus as u128)) as u64
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arithmetic_subtraction() {
|
||||||
|
let modulus = <$field>::ORDER;
|
||||||
|
crate::field::prime_field_testing::run_binaryop_test_cases(<$field>::sub, |x, y| {
|
||||||
|
if x >= y {
|
||||||
|
x - y
|
||||||
|
} else {
|
||||||
|
modulus - y + x
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arithmetic_negation() {
|
||||||
|
let modulus = <$field>::ORDER;
|
||||||
|
crate::field::prime_field_testing::run_unaryop_test_cases(<$field>::neg, |x| {
|
||||||
|
if x == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
modulus - x
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arithmetic_multiplication() {
|
||||||
|
let modulus = <$field>::ORDER;
|
||||||
|
crate::field::prime_field_testing::run_binaryop_test_cases(<$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::prime_field_testing::run_unaryop_test_cases(
|
||||||
|
|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 subtraction_double_wraparound() {
|
||||||
|
type F = $field;
|
||||||
|
|
||||||
|
let (a, b) = (F::from_canonical_u64((F::ORDER + 1u64) / 2u64), F::TWO);
|
||||||
|
let x = a * b;
|
||||||
|
assert_eq!(x, F::ONE);
|
||||||
|
assert_eq!(F::ZERO - x, F::NEG_ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn addition_double_wraparound() {
|
||||||
|
type F = $field;
|
||||||
|
|
||||||
|
let a = F::from_canonical_u64(u64::MAX - F::ORDER);
|
||||||
|
let b = F::NEG_ONE;
|
||||||
|
|
||||||
|
let c = (a + a) + (b + b);
|
||||||
|
let d = (a + b) + (a + b);
|
||||||
|
|
||||||
|
assert_eq!(c, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user