diff --git a/src/field/field.rs b/src/field/field.rs index cfadfbe6..32ee89e7 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -110,6 +110,21 @@ pub trait Field: subgroup } + fn cyclic_subgroup_unknown_order(generator: Self) -> Vec { + let mut subgroup = Vec::new(); + for power in generator.powers() { + if power.is_one() && !subgroup.is_empty() { + break; + } + subgroup.push(power); + } + subgroup + } + + fn generator_order(generator: Self) -> usize { + Self::cyclic_subgroup_unknown_order(generator).len() + } + /// Computes a coset of a multiplicative subgroup whose order is known in advance. fn cyclic_subgroup_coset_known_order(generator: Self, shift: Self, order: usize) -> Vec { let subgroup = Self::cyclic_subgroup_known_order(generator, order); diff --git a/src/field/field_testing.rs b/src/field/field_testing.rs index d070cb8d..59eee681 100644 --- a/src/field/field_testing.rs +++ b/src/field/field_testing.rs @@ -225,35 +225,61 @@ macro_rules! test_arithmetic { ) } - // #[test] - // #[ignore] - // fn arithmetic_division() { - // // This test takes ages to finish so is #[ignore]d by default. - // // TODO: Re-enable and reimplement when - // // https://github.com/rust-num/num-bigint/issues/60 is finally resolved. - // let modulus = <$field>::ORDER; - // crate::field::field_testing::run_binaryop_test_cases( - // modulus, - // WORD_BITS, - // // Need to help the compiler infer the type of y here - // |x: $field, y: $field| { - // // TODO: Work out how to check that div() panics - // // appropriately when given a zero divisor. - // if !y.is_zero() { - // <$field>::div(x, y) - // } else { - // <$field>::ZERO - // } - // }, - // |x, y| { - // // yinv = y^-1 (mod modulus) - // let exp = modulus - 2u64; - // let yinv = y.modpow(exp, modulus); - // // returns 0 if y was 0 - // x * yinv % 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 &[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) + .map(|i| <$field>::from_canonical_u64(i)) + .collect::>(); + 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 &[0, 1, 2, order - 2, order - 1] { + let i_f = <$field>::from_canonical_u64(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); + } } }; } diff --git a/src/polynomial/division.rs b/src/polynomial/division.rs index 41d872d0..31f746f4 100644 --- a/src/polynomial/division.rs +++ b/src/polynomial/division.rs @@ -60,8 +60,38 @@ pub(crate) fn divide_by_z_h(mut a: PolynomialCoeffs, n: usize) -> P #[cfg(test)] mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::field::Field; + use crate::polynomial::division::divide_by_z_h; + use crate::polynomial::polynomial::PolynomialCoeffs; + + #[test] + fn zero_div_z_h() { + type F = CrandallField; + let zero = PolynomialCoeffs::::zero(16); + let quotient = divide_by_z_h(zero.clone(), 4); + assert_eq!(quotient, zero); + } + #[test] fn division_by_z_h() { - // TODO + type F = CrandallField; + let zero = F::ZERO; + let one = F::ONE; + let two = F::TWO; + let three = F::from_canonical_u64(3); + let four = F::from_canonical_u64(4); + let five = F::from_canonical_u64(5); + let six = F::from_canonical_u64(6); + + // a(x) = Z_4(x) q(x), where + // a(x) = 3 x^7 + 4 x^6 + 5 x^5 + 6 x^4 - 3 x^3 - 4 x^2 - 5 x - 6 + // Z_4(x) = x^4 - 1 + // q(x) = 3 x^3 + 4 x^2 + 5 x + 6 + let a = PolynomialCoeffs::new(vec![-six, -five, -four, -three, six, five, four, three]); + let q = PolynomialCoeffs::new(vec![six, five, four, three, zero, zero, zero, zero]); + + let computed_q = divide_by_z_h(a, 4); + assert_eq!(computed_q, q); } } diff --git a/src/rescue.rs b/src/rescue.rs index 88785a1e..c28f315a 100644 --- a/src/rescue.rs +++ b/src/rescue.rs @@ -502,6 +502,7 @@ fn sbox_layer_b(x: [F; W]) -> [F; W] { #[unroll_for_loops] fn sbox_a(x: F) -> F { // x^{-5}, via Fermat's little theorem + // TODO: This only works for our current field. const EXP: u64 = 7378697628517453005; let mut product = F::ONE;