Skip to content

Commit

Permalink
Add 130 tests for fp::field (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeyBF authored Aug 14, 2024
1 parent b0a826b commit 49fcd3f
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 33 deletions.
8 changes: 8 additions & 0 deletions ext/crates/fp/src/field/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ impl<F: FieldInternal> FieldElement<F> {
pub(crate) fn val(self) -> F::ElementContainer {
self.value
}

pub fn inv(self) -> Option<Self> {
self.field.inv(self)
}

pub fn frobenius(self) -> Self {
self.field.frobenius(self)
}
}

// Allows us to access methods on `F::Element` directly
Expand Down
74 changes: 73 additions & 1 deletion ext/crates/fp/src/field/fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ pub struct Fp<P> {
p: P,
}

impl<P> Fp<P> {
impl<P: Prime> Fp<P> {
pub const fn new(p: P) -> Self {
Self { p }
}

pub fn element(&self, value: u32) -> FieldElement<Self> {
self.el(value)
}
}

impl<P: Prime> Field for Fp<P> {
Expand Down Expand Up @@ -152,3 +156,71 @@ impl<P: Prime> From<FieldElement<Fp<P>>> for u32 {
element.value
}
}

#[cfg(test)]
mod tests {
use proptest::prelude::*;

use super::Fp;
use crate::{
field::{element::FieldElement, Field},
prime::Prime,
};

fn arb_element<P: Prime>(f: Fp<P>) -> impl Strategy<Value = FieldElement<Fp<P>>> {
(0..f.characteristic().as_u32()).prop_map(move |v| f.element(v))
}

fn arb_elements<P: Prime, const N: usize>(
p: P,
) -> impl Strategy<Value = (Fp<P>, [FieldElement<Fp<P>>; N])> {
let f = Fp::new(p);

let elements: [_; N] = (0..N)
.map(|_| arb_element(f))
.collect::<Vec<_>>()
.try_into()
.unwrap();
(Just(f), elements)
}

mod validprime {
use super::*;
use crate::{field_tests, prime::ValidPrime};

fn arb_elements<const N: usize>(
) -> impl Strategy<Value = (Fp<ValidPrime>, [FieldElement<Fp<ValidPrime>>; N])> {
crate::prime::tests::arb_prime().prop_flat_map(super::arb_elements)
}

field_tests!();
}

macro_rules! static_fp_tests {
($p:tt) => {
paste::paste! {
static_fp_tests!(@ [<$p:lower>], $p, $p, $p);
}
};
(@ $mod_name:ident, $p_expr:expr, $p_ident:ident, $p_ty:ty) => {
mod $mod_name {
use super::*;
use crate::{field_tests, prime::$p_ident};

fn arb_elements<const N: usize>(
) -> impl Strategy<Value = (Fp<$p_ty>, [FieldElement<Fp<$p_ty>>; N])> {
super::arb_elements($p_expr)
}

field_tests!();
}
};
}

static_fp_tests!(P2);
cfg_if::cfg_if! { if #[cfg(feature = "odd-primes")] {
static_fp_tests!(P3);
static_fp_tests!(P5);
static_fp_tests!(P7);
}}
}
87 changes: 85 additions & 2 deletions ext/crates/fp/src/field/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ pub trait Field: FieldInternal + Sized {
fn one(self) -> FieldElement<Self>;
}

// TODO: Figure out better tests
#[cfg(test)]
mod test {
mod tests {
use super::{Field, SmallFq};
use crate::prime::P2;

Expand Down Expand Up @@ -138,4 +137,88 @@ mod test {

assert_eq!(elements, expansions);
}

/// Given a function that generates elements of a field, test the field axioms and good behavior
/// of the Frobenius endomorphism. The function should have signature
///
/// ```ignore
/// fn arb_elements<const N: usize>() -> impl Strategy<Value = (F, [FieldElement<F>; N])>
/// ```
#[macro_export]
macro_rules! field_tests {
() => {
proptest! {
#[test]
fn test_addition_associative((_, [a, b, c]) in arb_elements()) {
prop_assert_eq!((a + b) + c, a + (b + c));
}

#[test]
fn test_addition_identity((f, [a]) in arb_elements()) {
prop_assert_eq!(a + f.zero(), a);
}

#[test]
fn test_addition_inverse((f, [a]) in arb_elements()) {
prop_assert_eq!(a + (-a), f.zero());
}

#[test]
fn test_addition_commutative((_, [a, b]) in arb_elements()) {
prop_assert_eq!(a + b, b + a);
}

#[test]
fn test_multiplication_associative((_, [a, b, c]) in arb_elements()) {
prop_assert_eq!((a * b) * c, a * (b * c));
}

#[test]
fn test_multiplication_identity((f, [a]) in arb_elements()) {
prop_assert_eq!(a * f.one(), a);
}

#[test]
fn test_multiplication_inverse((f, [a]) in arb_elements()) {
if a != f.zero() {
prop_assert_eq!(a * a.inv().unwrap(), f.one());
}
}

#[test]
fn test_multiplication_commutative((_, [a, b]) in arb_elements()) {
prop_assert_eq!(a * b, b * a);
}

#[test]
fn test_division_is_multiplication_by_inverse((f, [a, b]) in arb_elements()) {
if b != f.zero() {
prop_assert_eq!((a / b).unwrap(), a * b.inv().unwrap());
}
}

#[test]
fn test_distributive((_, [a, b, c]) in arb_elements()) {
prop_assert_eq!(a * (b + c), a * b + a * c);
}

#[test]
fn test_frobenius_is_pth_power((f, [a]) in arb_elements()) {
let a_frob = a.frobenius();
let a_pth_power = (0..f.characteristic().as_u32()).fold(f.one(), |acc, _| acc * a);
prop_assert_eq!(a_frob, a_pth_power);
}

#[test]
fn test_frobenius_additive((_, [a, b]) in arb_elements()) {
prop_assert_eq!((a + b).frobenius(), a.frobenius() + b.frobenius());
}

#[test]
fn test_frobenius_multiplicative((_, [a, b]) in arb_elements()) {
prop_assert_eq!((a * b).frobenius(), a.frobenius() * b.frobenius());
}
}
}
}
}
93 changes: 93 additions & 0 deletions ext/crates/fp/src/field/smallfq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,96 @@ impl std::fmt::Display for SmallFqElement {
}
}
}

#[cfg(test)]
mod tests {
use proptest::prelude::*;

use super::{SmallFq, SmallFqElement};
use crate::{
field::{element::FieldElement, field_internal::FieldInternal, Field},
prime::Prime,
};

fn largest_degree(p: impl Prime) -> u32 {
let mut d = 2;
while p.pow(d) < 1 << 16 {
d += 1;
}
d - 1
}

fn arb_field<P: Prime>(p: P) -> impl Strategy<Value = SmallFq<P>> {
(2..=largest_degree(p)).prop_map(move |d| SmallFq::new(p, d))
}

/// Return the `i`th element of the field, where the 0th element is zero and the others are the
/// corresponding powers of `a`. Note that this includes 1 if `i` is q - 1, so that this
/// function called with all i in 0..q gives all elements of the field.
fn ith_element<P: Prime>(f: SmallFq<P>, i: u32) -> FieldElement<SmallFq<P>> {
f.el(SmallFqElement(if i == 0 { None } else { Some(i) }))
}

fn arb_element<P: Prime>(f: SmallFq<P>) -> impl Strategy<Value = FieldElement<SmallFq<P>>> {
(0..f.q()).prop_map(move |i| ith_element(f, i))
}

fn arb_elements<P: Prime, const N: usize>(
p: P,
) -> impl Strategy<Value = (SmallFq<P>, [FieldElement<SmallFq<P>>; N])> {
arb_field(p).prop_flat_map(|f| {
let elements: [_; N] = (0..N)
.map(|_| arb_element(f))
.collect::<Vec<_>>()
.try_into()
.unwrap();
(Just(f), elements)
})
}

mod validprime {
use super::*;
use crate::{field_tests, prime::ValidPrime, PRIMES};

fn arb_smallfq_prime() -> impl Strategy<Value = ValidPrime> {
(0..PRIMES.len()).prop_map(|i| ValidPrime::new(PRIMES[i]))
}

fn arb_elements<const N: usize>(
) -> impl Strategy<Value = (SmallFq<ValidPrime>, [FieldElement<SmallFq<ValidPrime>>; N])>
{
arb_smallfq_prime().prop_flat_map(super::arb_elements)
}

field_tests!();
}

macro_rules! static_smallfq_tests {
($p:tt) => {
paste::paste! {
static_smallfq_tests!(@ [<$p:lower>], $p, $p, $p);
}
};
(@ $mod_name:ident, $p_expr:expr, $p_ident:ident, $p_ty:ty) => {
mod $mod_name {
use super::*;
use crate::{field_tests, prime::$p_ident};

fn arb_elements<const N: usize>(
) -> impl Strategy<Value = (SmallFq<$p_ty>, [FieldElement<SmallFq<$p_ty>>; N])>
{
super::arb_elements($p_expr)
}

field_tests!();
}
};
}

static_smallfq_tests!(P2);
cfg_if::cfg_if! { if #[cfg(feature = "odd-primes")] {
static_smallfq_tests!(P3);
static_smallfq_tests!(P5);
static_smallfq_tests!(P7);
}}
}
32 changes: 31 additions & 1 deletion ext/crates/fp/src/prime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,43 @@ pub fn minus_one_to_the_n<P: Prime>(p: P, i: i32) -> u32 {
}

#[cfg(test)]
mod tests {
pub(crate) mod tests {
use std::sync::OnceLock;

use proptest::prelude::*;

use super::{binomial::Binomial, inverse, iter::BinomialIterator, Prime, ValidPrime};
use crate::{
constants::PRIMES,
prime::{is_prime, PrimeError},
};

/// An arbitrary `ValidPrime` in the range `2..(1 << 24)`, plus the largest prime that we support.
pub(crate) fn arb_prime() -> impl Strategy<Value = ValidPrime> {
static TEST_PRIMES: OnceLock<Vec<ValidPrime>> = OnceLock::new();
let test_primes = TEST_PRIMES.get_or_init(|| {
// Sieve of erathosthenes
const MAX: usize = 1 << 24;
let mut is_prime = Vec::new();
is_prime.resize_with(MAX, || true);
is_prime[0] = false;
is_prime[1] = false;
for i in 2..MAX {
if is_prime[i] {
for j in ((2 * i)..MAX).step_by(i) {
is_prime[j] = false;
}
}
}
(0..MAX)
.filter(|&i| is_prime[i])
.map(|p| ValidPrime::new_unchecked(p as u32))
.chain(std::iter::once(ValidPrime::new_unchecked(2147483647)))
.collect()
});
(0..test_primes.len()).prop_map(|i| test_primes[i])
}

#[test]
fn validprime_test() {
for p in (0..(1 << 16)).filter(|&p| is_prime(p)) {
Expand Down
30 changes: 1 addition & 29 deletions ext/crates/fp/src/vector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ pub use inner::*;

#[cfg(test)]
mod tests {
use std::sync::OnceLock;

use itertools::Itertools;
use proptest::prelude::*;
use rstest::rstest;
Expand All @@ -21,7 +19,7 @@ mod tests {
use crate::{
field::{field_internal::FieldInternal, fp::F2, Fp},
limb,
prime::{Prime, ValidPrime},
prime::{tests::arb_prime, Prime, ValidPrime},
};

pub struct VectorDiffEntry {
Expand Down Expand Up @@ -99,32 +97,6 @@ mod tests {
(0..dimension).map(|_| rng.gen_range(0..p)).collect()
}

/// An arbitrary `ValidPrime` in the range `2..(1 << 24)`, plus the largest prime that we support.
fn arb_prime() -> impl Strategy<Value = ValidPrime> {
static TEST_PRIMES: OnceLock<Vec<ValidPrime>> = OnceLock::new();
let test_primes = TEST_PRIMES.get_or_init(|| {
// Sieve of erathosthenes
const MAX: usize = 1 << 24;
let mut is_prime = Vec::new();
is_prime.resize_with(MAX, || true);
is_prime[0] = false;
is_prime[1] = false;
for i in 2..MAX {
if is_prime[i] {
for j in ((2 * i)..MAX).step_by(i) {
is_prime[j] = false;
}
}
}
(0..MAX)
.filter(|&i| is_prime[i])
.map(|p| ValidPrime::new_unchecked(p as u32))
.chain(std::iter::once(ValidPrime::new_unchecked(2147483647)))
.collect()
});
(0..test_primes.len()).prop_map(|i| test_primes[i])
}

/// An arbitrary (prime, dimension) pair
fn arb_prime_dim() -> impl Strategy<Value = (ValidPrime, usize)> {
arb_prime().prop_flat_map(|p| (Just(p), 0usize..=10_000))
Expand Down

0 comments on commit 49fcd3f

Please sign in to comment.