From e806d86f867eb397f23750005b1c74601cdde935 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 19 May 2021 09:35:39 +0200 Subject: [PATCH] Remove custom `primitive_root_of_unity` in extension fields by modifying the generators. --- src/field/extension_field/quadratic.rs | 44 +++++++++++++--------- src/field/extension_field/quartic.rs | 51 +++++++++++++++----------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/field/extension_field/quadratic.rs b/src/field/extension_field/quadratic.rs index 90c41f9d..3382a487 100644 --- a/src/field/extension_field/quadratic.rs +++ b/src/field/extension_field/quadratic.rs @@ -83,9 +83,15 @@ impl Field for QuadraticCrandallField { // Does not fit in 64-bits. const ORDER: u64 = 0; const TWO_ADICITY: usize = 29; - const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([CrandallField(3), CrandallField::ONE]); + const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ + CrandallField(6483724566312148654), + CrandallField(12194665049945415126), + ]); + // Chosen so that when raised to the power `1<<(Self::TWO_ADICITY-Self::BaseField::TWO_ADICITY)`, + // we get `Self::BaseField::POWER_OF_TWO_GENERATOR`. This makes `primitive_root_of_unity` coherent + // with the base field which implies that the FFT commutes with field inclusion. const POWER_OF_TWO_GENERATOR: Self = - Self([CrandallField::ZERO, CrandallField(7889429148549342301)]); + Self([CrandallField::ZERO, CrandallField(14420468973723774561)]); // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { @@ -100,22 +106,6 @@ impl Field for QuadraticCrandallField { Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } - // It's important that the primitive roots of unity are the same as the ones in the base field, - // otherwise the FFT doesn't commute with field inclusion. - fn primitive_root_of_unity(n_log: usize) -> Self { - if n_log <= CrandallField::TWO_ADICITY { - CrandallField::primitive_root_of_unity(n_log).into() - } else { - // The root of unity isn't in the base field so we need to compute it manually. - assert!(n_log <= Self::TWO_ADICITY); - let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base - } - } - fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } @@ -311,4 +301,22 @@ mod tests { F::ONE ); } + + #[test] + fn test_power_of_two_gen() { + type F = QuadraticCrandallField; + // F::ORDER = 2^29 * 2762315674048163 * 229454332791453 + 1 + assert_eq!( + F::MULTIPLICATIVE_GROUP_GENERATOR + .exp_usize(2762315674048163) + .exp_usize(229454332791453), + F::POWER_OF_TWO_GENERATOR + ); + assert_eq!( + F::POWER_OF_TWO_GENERATOR.exp_usize( + 1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY) + ), + ::BaseField::POWER_OF_TWO_GENERATOR.into() + ); + } } diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index c317db32..41a49a26 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -110,16 +110,19 @@ impl Field for QuarticCrandallField { const ORDER: u64 = 0; const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ - CrandallField(3), - CrandallField::ONE, - CrandallField::ZERO, - CrandallField::ZERO, + CrandallField(12476589904174392631), + CrandallField(896937834427772243), + CrandallField(7795248119019507390), + CrandallField(9005769437373554825), ]); + // Chosen so that when raised to the power `1<<(Self::TWO_ADICITY-Self::BaseField::TWO_ADICITY)`, + // we get `Self::BaseField::POWER_OF_TWO_GENERATOR`. This makes `primitive_root_of_unity` coherent + // with the base field which implies that the FFT commutes with field inclusion. const POWER_OF_TWO_GENERATOR: Self = Self([ CrandallField::ZERO, CrandallField::ZERO, CrandallField::ZERO, - CrandallField(14096607364803438105), + CrandallField(15170983443234254033), ]); // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. @@ -138,22 +141,6 @@ impl Field for QuarticCrandallField { Some(a_pow_r_minus_1 * a_pow_r.0[0].inverse().into()) } - // It's important that the primitive roots of unity are the same as the ones in the base field, - // otherwise the FFT doesn't commute with field inclusion. - fn primitive_root_of_unity(n_log: usize) -> Self { - if n_log <= CrandallField::TWO_ADICITY { - CrandallField::primitive_root_of_unity(n_log).into() - } else { - // The root of unity isn't in the base field so we need to compute it manually. - assert!(n_log <= Self::TWO_ADICITY); - let mut base = Self::POWER_OF_TWO_GENERATOR; - for _ in n_log..Self::TWO_ADICITY { - base = base.square(); - } - base - } - } - fn to_canonical_u64(&self) -> u64 { self.0[0].to_canonical_u64() } @@ -370,4 +357,26 @@ mod tests { F::ONE ); } + + #[test] + fn test_power_of_two_gen() { + type F = QuarticCrandallField; + // F::ORDER = 2^30 * 1090552343587053358839971118999869 * 98885475095492590491252558464653635 + 1 + assert_eq!( + exp_naive( + exp_naive( + F::MULTIPLICATIVE_GROUP_GENERATOR, + 1090552343587053358839971118999869 + ), + 98885475095492590491252558464653635 + ), + F::POWER_OF_TWO_GENERATOR + ); + assert_eq!( + F::POWER_OF_TWO_GENERATOR.exp_usize( + 1 << (F::TWO_ADICITY - ::BaseField::TWO_ADICITY) + ), + ::BaseField::POWER_OF_TWO_GENERATOR.into() + ); + } }