2021-07-22 13:08:14 -07:00
|
|
|
use num::bigint::BigUint;
|
2021-07-20 15:42:27 -07:00
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
use crate::field::field::Field;
|
2021-07-22 13:08:14 -07:00
|
|
|
use crate::util::ceil_div_usize;
|
2021-04-02 17:49:51 -07:00
|
|
|
|
|
|
|
|
/// 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.
|
2021-07-21 13:05:32 -07:00
|
|
|
pub fn test_inputs(modulus: BigUint, word_bits: usize) -> Vec<BigUint> {
|
|
|
|
|
//assert!(word_bits == 32 || word_bits == 64);
|
2021-07-22 13:08:14 -07:00
|
|
|
let modwords = ceil_div_usize(modulus.bits() as usize, word_bits);
|
2021-04-02 17:49:51 -07:00
|
|
|
// Start with basic set close to zero: 0 .. 10
|
|
|
|
|
const BIGGEST_SMALL: u32 = 10;
|
2021-07-21 13:05:32 -07:00
|
|
|
let smalls: Vec<_> = (0..BIGGEST_SMALL).map(BigUint::from).collect();
|
2021-04-02 17:49:51 -07:00
|
|
|
// ... and close to MAX: MAX - x
|
2021-07-21 13:05:32 -07:00
|
|
|
let word_max = (BigUint::from(1u32) << word_bits) - 1u32;
|
2021-07-21 14:27:30 -07:00
|
|
|
let multiple_words_max = (BigUint::from(1u32) << modwords * word_bits) - 1u32;
|
2021-04-02 17:49:51 -07:00
|
|
|
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))
|
2021-07-21 13:05:32 -07:00
|
|
|
.collect::<Vec<BigUint>>()
|
2021-04-02 17:49:51 -07:00
|
|
|
})
|
|
|
|
|
.collect();
|
2021-07-21 13:05:32 -07:00
|
|
|
let basic_inputs: Vec<BigUint> = [one_words, multiple_words].concat();
|
2021-04-02 17:49:51 -07:00
|
|
|
|
|
|
|
|
// Biggest value that will fit in `modwords` words
|
|
|
|
|
// Inputs 'difference from' maximum value
|
|
|
|
|
let diff_max = basic_inputs
|
|
|
|
|
.iter()
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|x| x.clone())
|
2021-07-21 14:27:30 -07:00
|
|
|
.map(|x| multiple_words_max.clone() - x)
|
2021-07-21 13:23:50 -07:00
|
|
|
.filter(|x| x < &modulus)
|
2021-04-02 17:49:51 -07:00
|
|
|
.collect();
|
|
|
|
|
// Inputs 'difference from' modulus value
|
|
|
|
|
let diff_mod = basic_inputs
|
|
|
|
|
.iter()
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|x| x.clone())
|
2021-07-21 13:28:11 -07:00
|
|
|
.filter(|x| x.clone() < modulus.clone() && x.clone() != BigUint::from(0u32))
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|x| x.clone())
|
2021-07-21 13:28:11 -07:00
|
|
|
.map(|x| modulus.clone() - x)
|
2021-04-02 17:49:51 -07:00
|
|
|
.collect();
|
|
|
|
|
let basics = basic_inputs
|
|
|
|
|
.into_iter()
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|x| x.clone())
|
|
|
|
|
.filter(|x| x < &modulus)
|
2021-07-21 13:05:32 -07:00
|
|
|
.collect::<Vec<BigUint>>();
|
2021-04-02 17:49:51 -07:00
|
|
|
[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>(
|
2021-07-20 15:42:27 -07:00
|
|
|
modulus: BigUint,
|
2021-04-02 17:49:51 -07:00
|
|
|
word_bits: usize,
|
|
|
|
|
op: UnaryOp,
|
|
|
|
|
expected_op: ExpectedOp,
|
|
|
|
|
) where
|
2021-04-02 18:29:33 -07:00
|
|
|
F: Field,
|
|
|
|
|
UnaryOp: Fn(F) -> F,
|
2021-07-21 13:05:32 -07:00
|
|
|
ExpectedOp: Fn(BigUint) -> BigUint,
|
2021-04-02 17:49:51 -07:00
|
|
|
{
|
|
|
|
|
let inputs = test_inputs(modulus, word_bits);
|
2021-07-21 13:28:11 -07:00
|
|
|
let expected: Vec<_> = inputs.iter().map(|x| expected_op(x.clone())).collect();
|
2021-04-02 18:12:44 -07:00
|
|
|
let output: Vec<_> = inputs
|
2021-04-02 17:49:51 -07:00
|
|
|
.iter()
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|x| x.clone())
|
|
|
|
|
.map(|x| op(F::from_canonical_biguint(x)).to_canonical_biguint())
|
2021-04-02 18:12:44 -07:00
|
|
|
.collect();
|
2021-04-02 17:49:51 -07:00
|
|
|
// Compare expected outputs with actual outputs
|
2021-04-02 18:12:44 -07:00
|
|
|
for i in 0..inputs.len() {
|
2021-04-21 22:31:45 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
output[i], expected[i],
|
|
|
|
|
"Expected {}, got {} for input {}",
|
|
|
|
|
expected[i], output[i], inputs[i]
|
|
|
|
|
);
|
2021-04-02 18:12:44 -07:00
|
|
|
}
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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>(
|
2021-07-20 15:42:27 -07:00
|
|
|
modulus: BigUint,
|
2021-04-02 17:49:51 -07:00
|
|
|
word_bits: usize,
|
|
|
|
|
op: BinaryOp,
|
|
|
|
|
expected_op: ExpectedOp,
|
|
|
|
|
) where
|
2021-04-02 18:29:33 -07:00
|
|
|
F: Field,
|
|
|
|
|
BinaryOp: Fn(F, F) -> F,
|
2021-07-21 13:05:32 -07:00
|
|
|
ExpectedOp: Fn(BigUint, BigUint) -> BigUint,
|
2021-04-02 17:49:51 -07:00
|
|
|
{
|
|
|
|
|
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.
|
2021-04-21 22:31:45 +02:00
|
|
|
let shifted_inputs: Vec<_> = inputs
|
|
|
|
|
.iter()
|
2021-04-02 18:29:33 -07:00
|
|
|
.cycle()
|
|
|
|
|
.skip(inputs.len() - i)
|
|
|
|
|
.take(inputs.len())
|
|
|
|
|
.collect();
|
|
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
// Calculate pointwise operations
|
2021-04-02 18:29:33 -07:00
|
|
|
let expected: Vec<_> = inputs
|
2021-04-02 17:49:51 -07:00
|
|
|
.iter()
|
|
|
|
|
.zip(shifted_inputs.clone())
|
2021-04-02 18:29:33 -07:00
|
|
|
.map(|(x, y)| expected_op(x.clone(), y.clone()))
|
|
|
|
|
.collect();
|
|
|
|
|
|
2021-04-21 22:31:45 +02:00
|
|
|
let output: Vec<_> = inputs
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(shifted_inputs.clone())
|
2021-07-21 13:23:50 -07:00
|
|
|
.map(|(x, y)| (x.clone(), y.clone()))
|
|
|
|
|
.map(|(x, y)| {
|
2021-07-21 14:33:47 -07:00
|
|
|
op(F::from_canonical_biguint(x), F::from_canonical_biguint(y))
|
|
|
|
|
.to_canonical_biguint()
|
2021-04-21 22:31:45 +02:00
|
|
|
})
|
|
|
|
|
.collect();
|
2021-04-02 18:29:33 -07:00
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
// Compare expected outputs with actual outputs
|
2021-04-02 18:29:33 -07:00
|
|
|
for i in 0..inputs.len() {
|
2021-04-21 22:31:45 +02:00
|
|
|
assert_eq!(
|
|
|
|
|
output[i], expected[i],
|
|
|
|
|
"On inputs {} . {}, expected {} but got {}",
|
|
|
|
|
inputs[i], shifted_inputs[i], expected[i], output[i]
|
|
|
|
|
);
|
2021-04-02 18:29:33 -07:00
|
|
|
}
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 13:08:14 -07:00
|
|
|
|
|
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! test_field_arithmetic {
|
|
|
|
|
($field:ty) => {
|
|
|
|
|
mod field_arithmetic {
|
|
|
|
|
use num::bigint::BigUint;
|
|
|
|
|
use rand::{thread_rng, Rng};
|
|
|
|
|
|
|
|
|
|
use crate::field::field::Field;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn batch_inversion() {
|
|
|
|
|
let xs = (1..=3)
|
|
|
|
|
.map(|i| <$field>::from_canonical_u64(i))
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
let invs = <$field>::batch_multiplicative_inverse(&xs);
|
|
|
|
|
for (x, inv) in xs.into_iter().zip(invs) {
|
|
|
|
|
assert_eq!(x * inv, <$field>::ONE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn primitive_root_order() {
|
|
|
|
|
for n_power in 0..8 {
|
|
|
|
|
let root = <$field>::primitive_root_of_unity(n_power);
|
|
|
|
|
let order = <$field>::generator_order(root);
|
|
|
|
|
assert_eq!(order, 1 << n_power, "2^{}'th primitive root", n_power);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn negation() {
|
|
|
|
|
let zero = <$field>::ZERO;
|
|
|
|
|
let order = <$field>::order();
|
|
|
|
|
|
|
|
|
|
for i in [
|
|
|
|
|
BigUint::from(0u32),
|
|
|
|
|
BigUint::from(1u32),
|
|
|
|
|
BigUint::from(2u32),
|
|
|
|
|
order.clone() - 2u32,
|
|
|
|
|
order.clone() - 1u32,
|
|
|
|
|
] {
|
|
|
|
|
let i_f = <$field>::from_canonical_biguint(i);
|
|
|
|
|
assert_eq!(i_f + -i_f, zero);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn bits() {
|
|
|
|
|
assert_eq!(<$field>::ZERO.bits(), 0);
|
|
|
|
|
assert_eq!(<$field>::ONE.bits(), 1);
|
|
|
|
|
assert_eq!(<$field>::TWO.bits(), 2);
|
|
|
|
|
assert_eq!(<$field>::from_canonical_u64(3).bits(), 2);
|
|
|
|
|
assert_eq!(<$field>::from_canonical_u64(4).bits(), 3);
|
|
|
|
|
assert_eq!(<$field>::from_canonical_u64(5).bits(), 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exponentiation() {
|
|
|
|
|
type F = $field;
|
|
|
|
|
|
|
|
|
|
assert_eq!(F::ZERO.exp_u32(0), <F>::ONE);
|
|
|
|
|
assert_eq!(F::ONE.exp_u32(0), <F>::ONE);
|
|
|
|
|
assert_eq!(F::TWO.exp_u32(0), <F>::ONE);
|
|
|
|
|
|
|
|
|
|
assert_eq!(F::ZERO.exp_u32(1), <F>::ZERO);
|
|
|
|
|
assert_eq!(F::ONE.exp_u32(1), <F>::ONE);
|
|
|
|
|
assert_eq!(F::TWO.exp_u32(1), <F>::TWO);
|
|
|
|
|
|
|
|
|
|
assert_eq!(F::ZERO.kth_root_u32(1), <F>::ZERO);
|
|
|
|
|
assert_eq!(F::ONE.kth_root_u32(1), <F>::ONE);
|
|
|
|
|
assert_eq!(F::TWO.kth_root_u32(1), <F>::TWO);
|
|
|
|
|
|
|
|
|
|
for power in 1..10 {
|
|
|
|
|
if F::is_monomial_permutation(power) {
|
|
|
|
|
let x = F::rand();
|
|
|
|
|
assert_eq!(x.exp(power).kth_root(power), x);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exponentiation_large() {
|
|
|
|
|
type F = $field;
|
|
|
|
|
|
|
|
|
|
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.clone()), base.exp_biguint(big_pow));
|
|
|
|
|
assert_ne!(base.exp_biguint(pow), base.exp_biguint(big_pow_wrong));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn inverse_2exp() {
|
|
|
|
|
// Just check consistency with try_inverse()
|
|
|
|
|
type F = $field;
|
|
|
|
|
|
|
|
|
|
let v = <F as Field>::PrimeField::TWO_ADICITY;
|
|
|
|
|
|
|
|
|
|
for e in [0, 1, 2, 3, 4, v - 2, v - 1, v, v + 1, v + 2, 123 * v] {
|
|
|
|
|
let x = F::TWO.exp(e as u64).inverse();
|
|
|
|
|
let y = F::inverse_2exp(e);
|
|
|
|
|
assert_eq!(x, y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
#[macro_export]
|
2021-07-22 10:55:11 -07:00
|
|
|
macro_rules! test_prime_field_arithmetic {
|
2021-04-02 17:49:51 -07:00
|
|
|
($field:ty) => {
|
2021-07-22 10:55:11 -07:00
|
|
|
mod prime_field_arithmetic {
|
2021-04-02 19:04:26 -07:00
|
|
|
use std::ops::{Add, Mul, Neg, Sub};
|
2021-07-21 13:05:40 -07:00
|
|
|
|
2021-07-22 13:08:14 -07:00
|
|
|
use num::bigint::BigUint;
|
2021-04-02 17:49:51 -07:00
|
|
|
|
2021-06-10 14:10:35 -07:00
|
|
|
use crate::field::field::Field;
|
|
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
// 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;
|
2021-07-22 10:57:08 -07:00
|
|
|
|
2021-04-02 17:49:51 -07:00
|
|
|
#[test]
|
|
|
|
|
fn arithmetic_addition() {
|
2021-07-21 13:05:32 -07:00
|
|
|
let modulus = <$field>::order();
|
2021-04-21 22:31:45 +02:00
|
|
|
crate::field::field_testing::run_binaryop_test_cases(
|
2021-07-21 14:33:47 -07:00
|
|
|
modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
WORD_BITS,
|
|
|
|
|
<$field>::add,
|
2021-07-21 14:33:47 -07:00
|
|
|
|x, y| (x.clone() + y.clone()) % modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
)
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn arithmetic_subtraction() {
|
2021-07-21 13:05:32 -07:00
|
|
|
let modulus = <$field>::order();
|
2021-04-21 22:31:45 +02:00
|
|
|
crate::field::field_testing::run_binaryop_test_cases(
|
2021-07-21 14:27:30 -07:00
|
|
|
modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
WORD_BITS,
|
|
|
|
|
<$field>::sub,
|
2021-07-21 14:27:30 -07:00
|
|
|
|x, y| {
|
|
|
|
|
if x >= y {
|
|
|
|
|
x.clone() - y.clone()
|
|
|
|
|
} else {
|
|
|
|
|
modulus.clone() - y.clone() + x
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-04-21 22:31:45 +02:00
|
|
|
)
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn arithmetic_negation() {
|
2021-07-21 13:05:32 -07:00
|
|
|
let modulus = <$field>::order();
|
2021-04-21 22:31:45 +02:00
|
|
|
crate::field::field_testing::run_unaryop_test_cases(
|
2021-07-21 14:27:30 -07:00
|
|
|
modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
WORD_BITS,
|
|
|
|
|
<$field>::neg,
|
2021-07-21 14:27:30 -07:00
|
|
|
|x| {
|
|
|
|
|
if x == BigUint::from(0u32) {
|
|
|
|
|
BigUint::from(0u32)
|
|
|
|
|
} else {
|
|
|
|
|
modulus.clone() - x.clone()
|
|
|
|
|
}
|
|
|
|
|
},
|
2021-04-21 22:31:45 +02:00
|
|
|
)
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn arithmetic_multiplication() {
|
2021-07-21 13:05:32 -07:00
|
|
|
let modulus = <$field>::order();
|
2021-04-21 22:31:45 +02:00
|
|
|
crate::field::field_testing::run_binaryop_test_cases(
|
2021-07-21 14:33:47 -07:00
|
|
|
modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
WORD_BITS,
|
|
|
|
|
<$field>::mul,
|
2021-07-21 14:33:47 -07:00
|
|
|
|x, y| x.clone() * y.clone() % modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
)
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn arithmetic_square() {
|
2021-07-21 13:05:32 -07:00
|
|
|
let modulus = <$field>::order();
|
2021-04-02 17:49:51 -07:00
|
|
|
crate::field::field_testing::run_unaryop_test_cases(
|
2021-07-21 14:33:47 -07:00
|
|
|
modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
WORD_BITS,
|
2021-04-02 17:49:51 -07:00
|
|
|
|x: $field| x.square(),
|
2021-07-21 14:33:47 -07:00
|
|
|
|x| (x.clone() * x.clone()) % modulus.clone(),
|
2021-04-21 22:31:45 +02:00
|
|
|
)
|
2021-04-02 17:49:51 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-22 21:12:34 -07:00
|
|
|
#[test]
|
|
|
|
|
fn inversion() {
|
|
|
|
|
let zero = <$field>::ZERO;
|
|
|
|
|
let one = <$field>::ONE;
|
2021-07-21 13:05:32 -07:00
|
|
|
let order = <$field>::order();
|
2021-04-22 21:12:34 -07:00
|
|
|
|
|
|
|
|
assert_eq!(zero.try_inverse(), None);
|
|
|
|
|
|
2021-07-21 13:23:50 -07:00
|
|
|
for x in [
|
2021-07-21 13:05:40 -07:00
|
|
|
BigUint::from(1u32),
|
|
|
|
|
BigUint::from(2u32),
|
|
|
|
|
BigUint::from(3u32),
|
2021-07-21 13:23:50 -07:00
|
|
|
order.clone() - 3u32,
|
|
|
|
|
order.clone() - 2u32,
|
|
|
|
|
order.clone() - 1u32,
|
2021-07-21 13:05:40 -07:00
|
|
|
] {
|
2021-07-21 13:05:32 -07:00
|
|
|
let x = <$field>::from_canonical_biguint(x);
|
2021-04-22 21:12:34 -07:00
|
|
|
let inv = x.inverse();
|
|
|
|
|
assert_eq!(x * inv, one);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-22 10:55:11 -07:00
|
|
|
#[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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|