diff --git a/field/Cargo.toml b/field/Cargo.toml index 13ae20000..645f20c05 100644 --- a/field/Cargo.toml +++ b/field/Cargo.toml @@ -16,6 +16,9 @@ seq-macro = "0.3" proc-macro2 = {version = "1", optional = true } quote = {version = "1", optional = true } +[dev-dependencies] +proptest = "1" + [features] use_division = [] proc_macro_ops = ["proc-macro2", "quote"] diff --git a/field/src/baby_bear/base.rs b/field/src/baby_bear/base.rs index c1fdf5e7d..d94c6f20e 100644 --- a/field/src/baby_bear/base.rs +++ b/field/src/baby_bear/base.rs @@ -292,6 +292,8 @@ impl Field for BabyBearField { const TWO: Self = Self::new(2); const MINUS_ONE: Self = Self::new(Self::ORDER - 1); + type CharField = Self; + #[cfg_attr(not(feature = "no_inline"), inline(always))] fn is_zero(&self) -> bool { self.is_zero_impl() @@ -522,25 +524,191 @@ impl crate::TwoAdicField for BabyBearField { #[cfg(test)] mod test { use super::*; + use crate::field::Field; + use proptest::prelude::*; - #[test] - fn calculator() { - let one = BabyBearField::ONE; - // one = 268435454 - dbg!(one); - dbg!(one.0); - - dbg!(BabyBearField::TWO_ADICITY_GENERATORS); - dbg!(BabyBearField::TWO_ADICITY_GENERATORS_INVERSED); + fn arb_babybear() -> impl Strategy { + 0..BabyBearField::ORDER } #[test] fn test_inversion_chain() { let el = BabyBearField::new(42); let pow = BabyBearField::CHARACTERISTICS - 2; - dbg!(pow); let naive_inverse = el.pow(pow); let faster_inverse = el.inverse_impl().unwrap(); assert_eq!(naive_inverse, faster_inverse); } + + // --- Field axiom tests --- + + proptest! { + #[test] + fn add_commutative(a in arb_babybear(), b in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let mut ab = fa; ab.add_assign(&fb); + let mut ba = fb; ba.add_assign(&fa); + prop_assert_eq!(ab, ba); + } + + #[test] + fn add_associative(a in arb_babybear(), b in arb_babybear(), c in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let fc = BabyBearField::new(c); + let mut ab = fa; ab.add_assign(&fb); + let mut abc_left = ab; abc_left.add_assign(&fc); + let mut bc = fb; bc.add_assign(&fc); + let mut abc_right = fa; abc_right.add_assign(&bc); + prop_assert_eq!(abc_left, abc_right); + } + + #[test] + fn add_identity(a in arb_babybear()) { + let fa = BabyBearField::new(a); + let mut r = fa; + r.add_assign(&BabyBearField::ZERO); + prop_assert_eq!(r, fa); + } + + #[test] + fn add_inverse(a in arb_babybear()) { + let fa = BabyBearField::new(a); + let mut neg = fa; neg.negate(); + let mut sum = fa; sum.add_assign(&neg); + prop_assert_eq!(sum, BabyBearField::ZERO); + } + + #[test] + fn mul_commutative(a in arb_babybear(), b in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let mut ab = fa; ab.mul_assign(&fb); + let mut ba = fb; ba.mul_assign(&fa); + prop_assert_eq!(ab, ba); + } + + #[test] + fn mul_associative(a in arb_babybear(), b in arb_babybear(), c in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let fc = BabyBearField::new(c); + let mut ab = fa; ab.mul_assign(&fb); + let mut abc_left = ab; abc_left.mul_assign(&fc); + let mut bc = fb; bc.mul_assign(&fc); + let mut abc_right = fa; abc_right.mul_assign(&bc); + prop_assert_eq!(abc_left, abc_right); + } + + #[test] + fn mul_identity(a in arb_babybear()) { + let fa = BabyBearField::new(a); + let mut r = fa; + r.mul_assign(&BabyBearField::ONE); + prop_assert_eq!(r, fa); + } + + #[test] + fn mul_inverse(a in 1..BabyBearField::ORDER) { + let fa = BabyBearField::new(a); + let inv = fa.inverse().unwrap(); + let mut product = fa; + product.mul_assign(&inv); + prop_assert_eq!(product, BabyBearField::ONE); + } + + #[test] + fn distributive(a in arb_babybear(), b in arb_babybear(), c in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let fc = BabyBearField::new(c); + let mut bc = fb; bc.add_assign(&fc); + let mut left = fa; left.mul_assign(&bc); + let mut ab = fa; ab.mul_assign(&fb); + let mut ac = fa; ac.mul_assign(&fc); + let mut right = ab; right.add_assign(&ac); + prop_assert_eq!(left, right); + } + + #[test] + fn sub_is_add_neg(a in arb_babybear(), b in arb_babybear()) { + let fa = BabyBearField::new(a); + let fb = BabyBearField::new(b); + let mut via_sub = fa; via_sub.sub_assign(&fb); + let mut neg_b = fb; neg_b.negate(); + let mut via_add = fa; via_add.add_assign(&neg_b); + prop_assert_eq!(via_sub, via_add); + } + + #[test] + fn double_is_add_self(a in arb_babybear()) { + let fa = BabyBearField::new(a); + let mut doubled = fa; doubled.double(); + let mut added = fa; added.add_assign(&fa); + prop_assert_eq!(doubled, added); + } + + #[test] + fn square_is_mul_self(a in arb_babybear()) { + let fa = BabyBearField::new(a); + let mut squared = fa; squared.square(); + let mut mulled = fa; mulled.mul_assign(&fa); + prop_assert_eq!(squared, mulled); + } + } + + // --- Const value and generator tests --- + + #[test] + fn two_adicity_generators_are_valid() { + for k in 1..=27 { + let g = BabyBearField::TWO_ADICITY_GENERATORS[k]; + let mut powered = g; + for _ in 0..k { + powered.square(); + } + assert_eq!(powered, BabyBearField::ONE, "generator[{k}]^(2^{k}) != 1"); + + let mut half_powered = g; + for _ in 0..k - 1 { + half_powered.square(); + } + assert_ne!( + half_powered, + BabyBearField::ONE, + "generator[{k}] has order < 2^{k}" + ); + } + } + + #[test] + fn two_adicity_generators_inversed_are_correct() { + for k in 0..=27 { + let g = BabyBearField::TWO_ADICITY_GENERATORS[k]; + let g_inv = BabyBearField::TWO_ADICITY_GENERATORS_INVERSED[k]; + let mut product = g; + product.mul_assign(&g_inv); + assert_eq!( + product, + BabyBearField::ONE, + "generator[{k}] * inverse[{k}] != 1" + ); + } + } + + #[test] + fn const_values_are_correct() { + assert_eq!(BabyBearField::NON_RES.to_u32(), 11); + + let mut two_halves = BabyBearField::HALF; + two_halves.double(); + assert_eq!(two_halves, BabyBearField::ONE); + + assert_eq!(BabyBearField::TWO.to_u32(), 2); + + let mut should_be_zero = BabyBearField::MINUS_ONE; + should_be_zero.add_assign(&BabyBearField::ONE); + assert_eq!(should_be_zero, BabyBearField::ZERO); + } } diff --git a/field/src/base.rs b/field/src/base.rs index 1a8b391b0..8bd241d39 100644 --- a/field/src/base.rs +++ b/field/src/base.rs @@ -338,6 +338,8 @@ impl Field for Mersenne31Field { const MINUS_ONE: Self = Self(Self::ORDER - 1); const TWO: Self = Self(2); + type CharField = Self; + #[cfg_attr(not(feature = "no_inline"), inline(always))] fn is_zero(&self) -> bool { self.is_zero_impl() @@ -514,3 +516,120 @@ impl BaseField<2> for Mersenne31Field { Self::mul_by_non_residue_impl(elem); } } + +#[cfg(test)] +mod test { + use super::*; + use crate::field::Field; + use proptest::prelude::*; + + fn arb_mersenne31() -> impl Strategy { + 0..Mersenne31Field::ORDER + } + + proptest! { + #[test] + fn add_commutative(a in arb_mersenne31(), b in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let fb = Mersenne31Field::new(b); + let mut ab = fa; ab.add_assign(&fb); + let mut ba = fb; ba.add_assign(&fa); + prop_assert_eq!(ab, ba); + } + + #[test] + fn add_associative(a in arb_mersenne31(), b in arb_mersenne31(), c in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let fb = Mersenne31Field::new(b); + let fc = Mersenne31Field::new(c); + let mut ab = fa; ab.add_assign(&fb); + let mut abc_left = ab; abc_left.add_assign(&fc); + let mut bc = fb; bc.add_assign(&fc); + let mut abc_right = fa; abc_right.add_assign(&bc); + prop_assert_eq!(abc_left, abc_right); + } + + #[test] + fn add_identity(a in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let mut r = fa; + r.add_assign(&Mersenne31Field::ZERO); + prop_assert_eq!(r, fa); + } + + #[test] + fn add_inverse(a in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let mut neg = fa; neg.negate(); + let mut sum = fa; sum.add_assign(&neg); + prop_assert_eq!(sum, Mersenne31Field::ZERO); + } + + #[test] + fn mul_commutative(a in arb_mersenne31(), b in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let fb = Mersenne31Field::new(b); + let mut ab = fa; ab.mul_assign(&fb); + let mut ba = fb; ba.mul_assign(&fa); + prop_assert_eq!(ab, ba); + } + + #[test] + fn mul_associative(a in arb_mersenne31(), b in arb_mersenne31(), c in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let fb = Mersenne31Field::new(b); + let fc = Mersenne31Field::new(c); + let mut ab = fa; ab.mul_assign(&fb); + let mut abc_left = ab; abc_left.mul_assign(&fc); + let mut bc = fb; bc.mul_assign(&fc); + let mut abc_right = fa; abc_right.mul_assign(&bc); + prop_assert_eq!(abc_left, abc_right); + } + + #[test] + fn mul_identity(a in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let mut r = fa; + r.mul_assign(&Mersenne31Field::ONE); + prop_assert_eq!(r, fa); + } + + #[test] + fn mul_inverse(a in 1..Mersenne31Field::ORDER) { + let fa = Mersenne31Field::new(a); + let inv = fa.inverse().unwrap(); + let mut product = fa; + product.mul_assign(&inv); + prop_assert_eq!(product, Mersenne31Field::ONE); + } + + #[test] + fn distributive(a in arb_mersenne31(), b in arb_mersenne31(), c in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let fb = Mersenne31Field::new(b); + let fc = Mersenne31Field::new(c); + let mut bc = fb; bc.add_assign(&fc); + let mut left = fa; left.mul_assign(&bc); + let mut ab = fa; ab.mul_assign(&fb); + let mut ac = fa; ac.mul_assign(&fc); + let mut right = ab; right.add_assign(&ac); + prop_assert_eq!(left, right); + } + + #[test] + fn double_is_add_self(a in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let mut doubled = fa; doubled.double(); + let mut added = fa; added.add_assign(&fa); + prop_assert_eq!(doubled, added); + } + + #[test] + fn square_is_mul_self(a in arb_mersenne31()) { + let fa = Mersenne31Field::new(a); + let mut squared = fa; squared.square(); + let mut mulled = fa; mulled.mul_assign(&fa); + prop_assert_eq!(squared, mulled); + } + } +} diff --git a/field/src/complex.rs b/field/src/complex.rs index 2f54d3dc2..a0d191e90 100644 --- a/field/src/complex.rs +++ b/field/src/complex.rs @@ -523,3 +523,108 @@ impl FieldExtension for Mersenne31Complex { } } } + +#[cfg(test)] +mod test { + use super::*; + use crate::field::Field; + use proptest::prelude::*; + + fn arb_mersenne31_complex() -> impl Strategy { + (0..Mersenne31Field::ORDER, 0..Mersenne31Field::ORDER).prop_map(|(a, b)| { + Mersenne31Complex::new(Mersenne31Field::new(a), Mersenne31Field::new(b)) + }) + } + + proptest! { + #[test] + fn mul_commutative( + a in arb_mersenne31_complex(), + b in arb_mersenne31_complex(), + ) { + let mut ab = a; ab.mul_assign(&b); + let mut ba = b; ba.mul_assign(&a); + prop_assert_eq!(ab, ba); + } + + #[test] + fn mul_associative( + a in arb_mersenne31_complex(), + b in arb_mersenne31_complex(), + c in arb_mersenne31_complex(), + ) { + let mut ab = a; ab.mul_assign(&b); + let mut abc_left = ab; abc_left.mul_assign(&c); + let mut bc = b; bc.mul_assign(&c); + let mut abc_right = a; abc_right.mul_assign(&bc); + prop_assert_eq!(abc_left, abc_right); + } + + #[test] + fn mul_inverse(a in arb_mersenne31_complex()) { + if let Some(inv) = a.inverse() { + let mut product = a; + product.mul_assign(&inv); + prop_assert_eq!(product, Mersenne31Complex::ONE); + } + } + + #[test] + fn distributive( + a in arb_mersenne31_complex(), + b in arb_mersenne31_complex(), + c in arb_mersenne31_complex(), + ) { + let mut bc = b; bc.add_assign(&c); + let mut left = a; left.mul_assign(&bc); + let mut ab = a; ab.mul_assign(&b); + let mut ac = a; ac.mul_assign(&c); + let mut right = ab; right.add_assign(&ac); + prop_assert_eq!(left, right); + } + + #[test] + fn square_is_mul_self(a in arb_mersenne31_complex()) { + let mut squared = a; squared.square(); + let mut mulled = a; mulled.mul_assign(&a); + prop_assert_eq!(squared, mulled); + } + } + + #[test] + fn two_adicity_generators_are_valid() { + for k in 1..=31 { + let g = Mersenne31Complex::TWO_ADICITY_GENERATORS[k]; + let mut powered = g; + for _ in 0..k { + powered.square(); + } + assert_eq!(powered, Mersenne31Complex::ONE, "generator[{k}]^(2^{k}) != 1"); + + let mut half_powered = g; + for _ in 0..k - 1 { + half_powered.square(); + } + assert_ne!( + half_powered, + Mersenne31Complex::ONE, + "generator[{k}] has order < 2^{k}" + ); + } + } + + #[test] + fn two_adicity_generators_inversed_are_correct() { + for k in 0..=31 { + let g = Mersenne31Complex::TWO_ADICITY_GENERATORS[k]; + let g_inv = Mersenne31Complex::TWO_ADICITY_GENERATORS_INVERSED[k]; + let mut product = g; + product.mul_assign(&g_inv); + assert_eq!( + product, + Mersenne31Complex::ONE, + "generator[{k}] * inverse[{k}] != 1" + ); + } + } +} diff --git a/field/src/field.rs b/field/src/field.rs index c32822b03..ad2e69a0b 100644 --- a/field/src/field.rs +++ b/field/src/field.rs @@ -24,7 +24,7 @@ pub trait Field: const TWO: Self; const MINUS_ONE: Self; - type CharField = Self; + type CharField; // zero check fn is_zero(&self) -> bool; diff --git a/field/src/lib.rs b/field/src/lib.rs index f4bfefe69..bff7e5881 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -1,8 +1,7 @@ -#![allow(internal_features)] +#![cfg_attr(target_arch = "riscv32", allow(internal_features))] #![cfg_attr(not(test), no_std)] -#![feature(associated_type_defaults)] -#![feature(core_intrinsics)] -#![feature(const_eval_select)] +#![cfg_attr(target_arch = "riscv32", feature(core_intrinsics))] +#![cfg_attr(target_arch = "riscv32", feature(const_eval_select))] use core::fmt::Debug; use core::fmt::Display;