From 5abcd85f840d687c9a77a5759a318bc2c19f0680 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 10 May 2021 18:45:48 +0200 Subject: [PATCH 1/6] Started extension field implementation --- src/field/extension_field.rs | 91 ++++++++++++++++++++++++++++++++++++ src/field/mod.rs | 1 + 2 files changed, 92 insertions(+) create mode 100644 src/field/extension_field.rs diff --git a/src/field/extension_field.rs b/src/field/extension_field.rs new file mode 100644 index 00000000..c88d9eb6 --- /dev/null +++ b/src/field/extension_field.rs @@ -0,0 +1,91 @@ +use crate::field::crandall_field::CrandallField; +use crate::field::field::Field; +use std::fmt::{Debug, Display, Formatter}; + +pub trait QuarticFieldExtension: Field { + type BaseField: Field; + + // Element W of BaseField, such that `X^4 - W` is irreducible over BaseField. + const W: Self::BaseField; + + fn to_canonical_representation(&self) -> [Self::BaseField; 4]; +} + +pub struct QuarticCrandallField([CrandallField; 4]); + +impl QuarticFieldExtension for QuarticCrandallField { + type BaseField = CrandallField; + // Verifiable in Sage with + // ``R. = GF(p)[]; assert (x^4 -3).is_irreducible()`. + const W: Self::BaseField = CrandallField::from_canonical_u64(3); + + fn to_canonical_representation(&self) -> [Self::BaseField; 4] { + self.0 + } +} + +impl Field for QuarticCrandallField { + const ZERO: Self = Self([CrandallField::ZERO; 4]); + const ONE: Self = Self([ + CrandallField::ONE, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const TWO: Self = Self([ + CrandallField::TWO, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const NEG_ONE: Self = Self([ + CrandallField::NEG_ONE, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + + // Does not fit in 64-bits. + const ORDER: u64 = 0; + const TWO_ADICITY: usize = 30; + const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ + CrandallField::from_canonical_u64(3), + CrandallField::ONE, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const POWER_OF_TWO_GENERATOR: Self = Self([ + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::from_canonical_u64(14096607364803438105), + ]); + + fn try_inverse(&self) -> Option { + todo!() + } + + fn to_canonical_u64(&self) -> u64 { + todo!() + } + + fn from_canonical_u64(n: u64) -> Self { + todo!() + } +} + +impl Display for QuarticCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} + {}*a + {}*a^2 + {}*a^3", + self.0[0], self.0[1], self.0[2], self.0[3] + ) + } +} + +impl Debug for QuarticCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} diff --git a/src/field/mod.rs b/src/field/mod.rs index 6c2828a6..179fb10d 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -1,5 +1,6 @@ pub(crate) mod cosets; pub mod crandall_field; +pub mod extension_field; pub mod fft; pub mod field; pub(crate) mod lagrange; From e670ec3ff0d7f5c15a20fcaab7df2fb1806b3070 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 11:46:01 +0200 Subject: [PATCH 2/6] Multiplication + Frobenius + Inverse --- src/field/crandall_field.rs | 2 +- src/field/extension_field.rs | 200 ++++++++++++++++++++++++++++++++++- 2 files changed, 196 insertions(+), 6 deletions(-) diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 99028016..1553fd25 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -415,7 +415,7 @@ impl DivAssign for CrandallField { /// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the /// field order and `2^64`. #[inline] -fn reduce128(x: u128) -> CrandallField { +pub fn reduce128(x: u128) -> CrandallField { // This is Crandall's algorithm. When we have some high-order bits (i.e. with a weight of 2^64), // we convert them to low-order bits by multiplying by EPSILON (the logic is a simple // generalization of Mersenne prime reduction). The first time we do this, the product will take diff --git a/src/field/extension_field.rs b/src/field/extension_field.rs index c88d9eb6..3f569a83 100644 --- a/src/field/extension_field.rs +++ b/src/field/extension_field.rs @@ -1,6 +1,9 @@ -use crate::field::crandall_field::CrandallField; +use crate::field::crandall_field::{reduce128, CrandallField}; use crate::field::field::Field; use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::iter::{Product, Sum}; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; pub trait QuarticFieldExtension: Field { type BaseField: Field; @@ -9,19 +12,68 @@ pub trait QuarticFieldExtension: Field { const W: Self::BaseField; fn to_canonical_representation(&self) -> [Self::BaseField; 4]; + + fn is_in_basefield(&self) -> bool { + self.to_canonical_representation()[1..] + .iter() + .all(|x| x.is_zero()) + } + + /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. + fn frobenius(&self) -> Self; + + fn scalar_mul(&self, c: Self::BaseField) -> Self; } +#[derive(Copy, Clone)] pub struct QuarticCrandallField([CrandallField; 4]); impl QuarticFieldExtension for QuarticCrandallField { type BaseField = CrandallField; // Verifiable in Sage with // ``R. = GF(p)[]; assert (x^4 -3).is_irreducible()`. - const W: Self::BaseField = CrandallField::from_canonical_u64(3); + const W: Self::BaseField = CrandallField(3); fn to_canonical_representation(&self) -> [Self::BaseField; 4] { self.0 } + + fn frobenius(&self) -> Self { + let [a0, a1, a2, a3] = self.to_canonical_representation(); + let k = (Self::BaseField::ORDER - 1) / 4; + let z0 = Self::W.exp_usize(k as usize); + let mut z = Self::BaseField::ONE; + let b0 = a0 * z; + z *= z0; + let b1 = a1 * z; + z *= z0; + let b2 = a2 * z; + z *= z0; + let b3 = a3 * z; + + Self([b0, b1, b2, b3]) + } + + fn scalar_mul(&self, c: Self::BaseField) -> Self { + let [a0, a1, a2, a3] = self.to_canonical_representation(); + Self([a0 * c, a1 * c, a2 * c, a3 * c]) + } +} + +impl PartialEq for QuarticCrandallField { + fn eq(&self, other: &Self) -> bool { + self.to_canonical_representation() == other.to_canonical_representation() + } +} + +impl Eq for QuarticCrandallField {} + +impl Hash for QuarticCrandallField { + fn hash(&self, state: &mut H) { + for l in &self.to_canonical_representation() { + Hash::hash(l, state); + } + } } impl Field for QuarticCrandallField { @@ -49,7 +101,7 @@ impl Field for QuarticCrandallField { const ORDER: u64 = 0; const TWO_ADICITY: usize = 30; const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ - CrandallField::from_canonical_u64(3), + CrandallField(3), CrandallField::ONE, CrandallField::ZERO, CrandallField::ZERO, @@ -58,11 +110,23 @@ impl Field for QuarticCrandallField { CrandallField::ZERO, CrandallField::ZERO, CrandallField::ZERO, - CrandallField::from_canonical_u64(14096607364803438105), + CrandallField(14096607364803438105), ]); + // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. fn try_inverse(&self) -> Option { - todo!() + if self.is_zero() { + return None; + } + + let a_pow_p = self.frobenius(); + let a_pow_p_plus_1 = a_pow_p * *self; + let a_pow_p3_plus_p2 = a_pow_p_plus_1.frobenius().frobenius(); + let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p; + let a_pow_r = a_pow_r_minus_1 * *self; + debug_assert!(a_pow_r.is_in_basefield()); + + Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) } fn to_canonical_u64(&self) -> u64 { @@ -89,3 +153,129 @@ impl Debug for QuarticCrandallField { Display::fmt(self, f) } } + +impl Neg for QuarticCrandallField { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self([-self.0[0], -self.0[1], -self.0[2], -self.0[3]]) + } +} + +impl Add for QuarticCrandallField { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + Self([ + self.0[0] + rhs.0[0], + self.0[1] + rhs.0[1], + self.0[2] + rhs.0[2], + self.0[3] + rhs.0[3], + ]) + } +} + +impl AddAssign for QuarticCrandallField { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sum for QuarticCrandallField { + fn sum>(iter: I) -> Self { + iter.fold(Self::ZERO, |acc, x| acc + x) + } +} + +impl Sub for QuarticCrandallField { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + Self([ + self.0[0] - rhs.0[0], + self.0[1] - rhs.0[1], + self.0[2] - rhs.0[2], + self.0[3] - rhs.0[3], + ]) + } +} + +impl SubAssign for QuarticCrandallField { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for QuarticCrandallField { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let Self([a0, a1, a2, a3]) = self; + let Self([b0, b1, b2, b3]) = rhs; + let a0 = a0.0 as u128; + let a1 = a1.0 as u128; + let a2 = a2.0 as u128; + let a3 = a3.0 as u128; + let b0 = b0.0 as u128; + let b1 = b1.0 as u128; + let b2 = b2.0 as u128; + let b3 = b3.0 as u128; + let w = Self::W.0 as u128; + + let c0 = reduce128(a0 * b0 + w * (a1 * b3 + a2 * b2 + a3 * b1)); + let c1 = reduce128(a0 * b1 + a1 * b0 + w * (a2 * b3 + a3 * b2)); + let c2 = reduce128(a0 * b2 + a1 * b1 + a2 * b0 + w * a3 * b3); + let c3 = reduce128(a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0); + + Self([c0, c1, c2, c3]) + } +} + +impl MulAssign for QuarticCrandallField { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Product for QuarticCrandallField { + fn product>(iter: I) -> Self { + iter.fold(Self::ONE, |acc, x| acc * x) + } +} + +impl Div for QuarticCrandallField { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for QuarticCrandallField { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::{QuarticCrandallField, QuarticFieldExtension}; + use crate::field::field::Field; + use crate::test_arithmetic; + + test_arithmetic!(crate::field::crandall_field::QuarticCrandallField); + + #[test] + fn test_frobenius() { + let x = QuarticCrandallField::rand(); + assert_eq!(x.exp_usize(CrandallField::ORDER as usize), x.frobenius()); + } +} From 5e86e7dc06b36dbd1938d2bb7f0ef193f25851ca Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 14:06:35 +0200 Subject: [PATCH 3/6] Rand + fix mul + tests --- src/field/extension_field.rs | 85 ++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/src/field/extension_field.rs b/src/field/extension_field.rs index 3f569a83..193ec232 100644 --- a/src/field/extension_field.rs +++ b/src/field/extension_field.rs @@ -1,5 +1,6 @@ use crate::field::crandall_field::{reduce128, CrandallField}; use crate::field::field::Field; +use rand::Rng; use std::fmt::{Debug, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::iter::{Product, Sum}; @@ -130,11 +131,25 @@ impl Field for QuarticCrandallField { } fn to_canonical_u64(&self) -> u64 { - todo!() + self.0[0].to_canonical_u64() } fn from_canonical_u64(n: u64) -> Self { - todo!() + Self([ + ::BaseField::from_canonical_u64(n), + ::BaseField::ZERO, + ::BaseField::ZERO, + ::BaseField::ZERO, + ]) + } + + fn rand_from_rng(rng: &mut R) -> Self { + Self([ + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ]) } } @@ -217,20 +232,11 @@ impl Mul for QuarticCrandallField { fn mul(self, rhs: Self) -> Self { let Self([a0, a1, a2, a3]) = self; let Self([b0, b1, b2, b3]) = rhs; - let a0 = a0.0 as u128; - let a1 = a1.0 as u128; - let a2 = a2.0 as u128; - let a3 = a3.0 as u128; - let b0 = b0.0 as u128; - let b1 = b1.0 as u128; - let b2 = b2.0 as u128; - let b3 = b3.0 as u128; - let w = Self::W.0 as u128; - let c0 = reduce128(a0 * b0 + w * (a1 * b3 + a2 * b2 + a3 * b1)); - let c1 = reduce128(a0 * b1 + a1 * b0 + w * (a2 * b3 + a3 * b2)); - let c2 = reduce128(a0 * b2 + a1 * b1 + a2 * b0 + w * a3 * b3); - let c3 = reduce128(a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0); + let c0 = a0 * b0 + Self::W * (a1 * b3 + a2 * b2 + a3 * b1); + let c1 = a0 * b1 + a1 * b0 + Self::W * (a2 * b3 + a3 * b2); + let c2 = a0 * b2 + a1 * b1 + a2 * b0 + Self::W * a3 * b3; + let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; Self([c0, c1, c2, c3]) } @@ -271,11 +277,56 @@ mod tests { use crate::field::field::Field; use crate::test_arithmetic; - test_arithmetic!(crate::field::crandall_field::QuarticCrandallField); + fn exp_naive(x: F, power: u64) -> F { + let mut current = x; + let mut product = F::ONE; + + for j in 0..64 { + if (power >> j & 1) != 0 { + product *= current; + } + current = current.square(); + } + product + } + + #[test] + fn test_add_neg_sub_mul() { + type F = QuarticCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x + (-x), F::ZERO); + assert_eq!(-x, F::ZERO - x); + assert_eq!( + x + x, + x.scalar_mul(::BaseField::TWO) + ); + assert_eq!(x * (-x), -x.square()); + assert_eq!(x + y, y + x); + assert_eq!(x * y, y * x); + assert_eq!(x * (y * z), (x * y) * z); + assert_eq!(x - (y + z), (x - y) - z); + assert_eq!((x + y) - z, x + (y - z)); + } + + #[test] + fn test_inv_div() { + type F = QuarticCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x * x.inverse(), F::ONE); + assert_eq!(x.inverse() * x, F::ONE); + assert_eq!(x.square().inverse(), x.inverse().square()); + assert_eq!((x / y) * y, x); + assert_eq!(x / (y * z), (x / y) / z); + assert_eq!((x * y) / z, x * (y / z)); + } #[test] fn test_frobenius() { let x = QuarticCrandallField::rand(); - assert_eq!(x.exp_usize(CrandallField::ORDER as usize), x.frobenius()); + assert_eq!(exp_naive(x, CrandallField::ORDER), x.frobenius()); } } From 04664a54eea056e2f09a4ebfc5607df69b9cad33 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 14:21:21 +0200 Subject: [PATCH 4/6] Binary extension fields --- src/field/crandall_field.rs | 2 +- src/field/extension_field.rs | 332 ------------------------------- src/field/extension_field/mod.rs | 2 + 3 files changed, 3 insertions(+), 333 deletions(-) delete mode 100644 src/field/extension_field.rs create mode 100644 src/field/extension_field/mod.rs diff --git a/src/field/crandall_field.rs b/src/field/crandall_field.rs index 1553fd25..99028016 100644 --- a/src/field/crandall_field.rs +++ b/src/field/crandall_field.rs @@ -415,7 +415,7 @@ impl DivAssign for CrandallField { /// Reduces to a 64-bit value. The result might not be in canonical form; it could be in between the /// field order and `2^64`. #[inline] -pub fn reduce128(x: u128) -> CrandallField { +fn reduce128(x: u128) -> CrandallField { // This is Crandall's algorithm. When we have some high-order bits (i.e. with a weight of 2^64), // we convert them to low-order bits by multiplying by EPSILON (the logic is a simple // generalization of Mersenne prime reduction). The first time we do this, the product will take diff --git a/src/field/extension_field.rs b/src/field/extension_field.rs deleted file mode 100644 index 193ec232..00000000 --- a/src/field/extension_field.rs +++ /dev/null @@ -1,332 +0,0 @@ -use crate::field::crandall_field::{reduce128, CrandallField}; -use crate::field::field::Field; -use rand::Rng; -use std::fmt::{Debug, Display, Formatter}; -use std::hash::{Hash, Hasher}; -use std::iter::{Product, Sum}; -use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -pub trait QuarticFieldExtension: Field { - type BaseField: Field; - - // Element W of BaseField, such that `X^4 - W` is irreducible over BaseField. - const W: Self::BaseField; - - fn to_canonical_representation(&self) -> [Self::BaseField; 4]; - - fn is_in_basefield(&self) -> bool { - self.to_canonical_representation()[1..] - .iter() - .all(|x| x.is_zero()) - } - - /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. - fn frobenius(&self) -> Self; - - fn scalar_mul(&self, c: Self::BaseField) -> Self; -} - -#[derive(Copy, Clone)] -pub struct QuarticCrandallField([CrandallField; 4]); - -impl QuarticFieldExtension for QuarticCrandallField { - type BaseField = CrandallField; - // Verifiable in Sage with - // ``R. = GF(p)[]; assert (x^4 -3).is_irreducible()`. - const W: Self::BaseField = CrandallField(3); - - fn to_canonical_representation(&self) -> [Self::BaseField; 4] { - self.0 - } - - fn frobenius(&self) -> Self { - let [a0, a1, a2, a3] = self.to_canonical_representation(); - let k = (Self::BaseField::ORDER - 1) / 4; - let z0 = Self::W.exp_usize(k as usize); - let mut z = Self::BaseField::ONE; - let b0 = a0 * z; - z *= z0; - let b1 = a1 * z; - z *= z0; - let b2 = a2 * z; - z *= z0; - let b3 = a3 * z; - - Self([b0, b1, b2, b3]) - } - - fn scalar_mul(&self, c: Self::BaseField) -> Self { - let [a0, a1, a2, a3] = self.to_canonical_representation(); - Self([a0 * c, a1 * c, a2 * c, a3 * c]) - } -} - -impl PartialEq for QuarticCrandallField { - fn eq(&self, other: &Self) -> bool { - self.to_canonical_representation() == other.to_canonical_representation() - } -} - -impl Eq for QuarticCrandallField {} - -impl Hash for QuarticCrandallField { - fn hash(&self, state: &mut H) { - for l in &self.to_canonical_representation() { - Hash::hash(l, state); - } - } -} - -impl Field for QuarticCrandallField { - const ZERO: Self = Self([CrandallField::ZERO; 4]); - const ONE: Self = Self([ - CrandallField::ONE, - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - ]); - const TWO: Self = Self([ - CrandallField::TWO, - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - ]); - const NEG_ONE: Self = Self([ - CrandallField::NEG_ONE, - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - ]); - - // Does not fit in 64-bits. - const ORDER: u64 = 0; - const TWO_ADICITY: usize = 30; - const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ - CrandallField(3), - CrandallField::ONE, - CrandallField::ZERO, - CrandallField::ZERO, - ]); - const POWER_OF_TWO_GENERATOR: Self = Self([ - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField::ZERO, - CrandallField(14096607364803438105), - ]); - - // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. - fn try_inverse(&self) -> Option { - if self.is_zero() { - return None; - } - - let a_pow_p = self.frobenius(); - let a_pow_p_plus_1 = a_pow_p * *self; - let a_pow_p3_plus_p2 = a_pow_p_plus_1.frobenius().frobenius(); - let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p; - let a_pow_r = a_pow_r_minus_1 * *self; - debug_assert!(a_pow_r.is_in_basefield()); - - Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) - } - - fn to_canonical_u64(&self) -> u64 { - self.0[0].to_canonical_u64() - } - - fn from_canonical_u64(n: u64) -> Self { - Self([ - ::BaseField::from_canonical_u64(n), - ::BaseField::ZERO, - ::BaseField::ZERO, - ::BaseField::ZERO, - ]) - } - - fn rand_from_rng(rng: &mut R) -> Self { - Self([ - ::BaseField::rand_from_rng(rng), - ::BaseField::rand_from_rng(rng), - ::BaseField::rand_from_rng(rng), - ::BaseField::rand_from_rng(rng), - ]) - } -} - -impl Display for QuarticCrandallField { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} + {}*a + {}*a^2 + {}*a^3", - self.0[0], self.0[1], self.0[2], self.0[3] - ) - } -} - -impl Debug for QuarticCrandallField { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(self, f) - } -} - -impl Neg for QuarticCrandallField { - type Output = Self; - - #[inline] - fn neg(self) -> Self { - Self([-self.0[0], -self.0[1], -self.0[2], -self.0[3]]) - } -} - -impl Add for QuarticCrandallField { - type Output = Self; - - #[inline] - fn add(self, rhs: Self) -> Self { - Self([ - self.0[0] + rhs.0[0], - self.0[1] + rhs.0[1], - self.0[2] + rhs.0[2], - self.0[3] + rhs.0[3], - ]) - } -} - -impl AddAssign for QuarticCrandallField { - fn add_assign(&mut self, rhs: Self) { - *self = *self + rhs; - } -} - -impl Sum for QuarticCrandallField { - fn sum>(iter: I) -> Self { - iter.fold(Self::ZERO, |acc, x| acc + x) - } -} - -impl Sub for QuarticCrandallField { - type Output = Self; - - #[inline] - fn sub(self, rhs: Self) -> Self { - Self([ - self.0[0] - rhs.0[0], - self.0[1] - rhs.0[1], - self.0[2] - rhs.0[2], - self.0[3] - rhs.0[3], - ]) - } -} - -impl SubAssign for QuarticCrandallField { - #[inline] - fn sub_assign(&mut self, rhs: Self) { - *self = *self - rhs; - } -} - -impl Mul for QuarticCrandallField { - type Output = Self; - - #[inline] - fn mul(self, rhs: Self) -> Self { - let Self([a0, a1, a2, a3]) = self; - let Self([b0, b1, b2, b3]) = rhs; - - let c0 = a0 * b0 + Self::W * (a1 * b3 + a2 * b2 + a3 * b1); - let c1 = a0 * b1 + a1 * b0 + Self::W * (a2 * b3 + a3 * b2); - let c2 = a0 * b2 + a1 * b1 + a2 * b0 + Self::W * a3 * b3; - let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; - - Self([c0, c1, c2, c3]) - } -} - -impl MulAssign for QuarticCrandallField { - #[inline] - fn mul_assign(&mut self, rhs: Self) { - *self = *self * rhs; - } -} - -impl Product for QuarticCrandallField { - fn product>(iter: I) -> Self { - iter.fold(Self::ONE, |acc, x| acc * x) - } -} - -impl Div for QuarticCrandallField { - type Output = Self; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, rhs: Self) -> Self::Output { - self * rhs.inverse() - } -} - -impl DivAssign for QuarticCrandallField { - fn div_assign(&mut self, rhs: Self) { - *self = *self / rhs; - } -} - -#[cfg(test)] -mod tests { - use crate::field::crandall_field::CrandallField; - use crate::field::extension_field::{QuarticCrandallField, QuarticFieldExtension}; - use crate::field::field::Field; - use crate::test_arithmetic; - - fn exp_naive(x: F, power: u64) -> F { - let mut current = x; - let mut product = F::ONE; - - for j in 0..64 { - if (power >> j & 1) != 0 { - product *= current; - } - current = current.square(); - } - product - } - - #[test] - fn test_add_neg_sub_mul() { - type F = QuarticCrandallField; - let x = F::rand(); - let y = F::rand(); - let z = F::rand(); - assert_eq!(x + (-x), F::ZERO); - assert_eq!(-x, F::ZERO - x); - assert_eq!( - x + x, - x.scalar_mul(::BaseField::TWO) - ); - assert_eq!(x * (-x), -x.square()); - assert_eq!(x + y, y + x); - assert_eq!(x * y, y * x); - assert_eq!(x * (y * z), (x * y) * z); - assert_eq!(x - (y + z), (x - y) - z); - assert_eq!((x + y) - z, x + (y - z)); - } - - #[test] - fn test_inv_div() { - type F = QuarticCrandallField; - let x = F::rand(); - let y = F::rand(); - let z = F::rand(); - assert_eq!(x * x.inverse(), F::ONE); - assert_eq!(x.inverse() * x, F::ONE); - assert_eq!(x.square().inverse(), x.inverse().square()); - assert_eq!((x / y) * y, x); - assert_eq!(x / (y * z), (x / y) / z); - assert_eq!((x * y) / z, x * (y / z)); - } - - #[test] - fn test_frobenius() { - let x = QuarticCrandallField::rand(); - assert_eq!(exp_naive(x, CrandallField::ORDER), x.frobenius()); - } -} diff --git a/src/field/extension_field/mod.rs b/src/field/extension_field/mod.rs new file mode 100644 index 00000000..3380e792 --- /dev/null +++ b/src/field/extension_field/mod.rs @@ -0,0 +1,2 @@ +pub mod binary; +pub mod quartic; From f1d812812e270327ae6740724151d72195758051 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 15:26:20 +0200 Subject: [PATCH 5/6] Added field order test --- src/field/extension_field/binary.rs | 293 ++++++++++++++++++++++ src/field/extension_field/quartic.rs | 351 +++++++++++++++++++++++++++ 2 files changed, 644 insertions(+) create mode 100644 src/field/extension_field/binary.rs create mode 100644 src/field/extension_field/quartic.rs diff --git a/src/field/extension_field/binary.rs b/src/field/extension_field/binary.rs new file mode 100644 index 00000000..5458e18d --- /dev/null +++ b/src/field/extension_field/binary.rs @@ -0,0 +1,293 @@ +use crate::field::crandall_field::CrandallField; +use crate::field::field::Field; +use rand::Rng; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::iter::{Product, Sum}; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +pub trait BinaryFieldExtension: Field { + type BaseField: Field; + + // Element W of BaseField, such that `X^2 - W` is irreducible over BaseField. + const W: Self::BaseField; + + fn to_canonical_representation(&self) -> [Self::BaseField; 2]; + + fn is_in_basefield(&self) -> bool { + self.to_canonical_representation()[1..] + .iter() + .all(|x| x.is_zero()) + } + + /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. + fn frobenius(&self) -> Self; + + fn scalar_mul(&self, c: Self::BaseField) -> Self; +} + +#[derive(Copy, Clone)] +pub struct BinaryCrandallField([CrandallField; 2]); + +impl BinaryFieldExtension for BinaryCrandallField { + type BaseField = CrandallField; + // Verifiable in Sage with + // ``R. = GF(p)[]; assert (x^2 -3).is_irreducible()`. + const W: Self::BaseField = CrandallField(3); + + fn to_canonical_representation(&self) -> [Self::BaseField; 2] { + self.0 + } + + fn frobenius(&self) -> Self { + let [a0, a1] = self.to_canonical_representation(); + let k = (Self::BaseField::ORDER - 1) / 2; + let z = Self::W.exp_usize(k as usize); + + Self([a0, a1 * z]) + } + + fn scalar_mul(&self, c: Self::BaseField) -> Self { + let [a0, a1] = self.to_canonical_representation(); + Self([a0 * c, a1 * c]) + } +} + +impl PartialEq for BinaryCrandallField { + fn eq(&self, other: &Self) -> bool { + self.to_canonical_representation() == other.to_canonical_representation() + } +} + +impl Eq for BinaryCrandallField {} + +impl Hash for BinaryCrandallField { + fn hash(&self, state: &mut H) { + for l in &self.to_canonical_representation() { + Hash::hash(l, state); + } + } +} + +impl Field for BinaryCrandallField { + const ZERO: Self = Self([CrandallField::ZERO; 2]); + const ONE: Self = Self([CrandallField::ONE, CrandallField::ZERO]); + const TWO: Self = Self([CrandallField::TWO, CrandallField::ZERO]); + const NEG_ONE: Self = Self([CrandallField::NEG_ONE, CrandallField::ZERO]); + + // 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 POWER_OF_TWO_GENERATOR: Self = + Self([CrandallField::ZERO, CrandallField(7889429148549342301)]); + + // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. + fn try_inverse(&self) -> Option { + if self.is_zero() { + return None; + } + + let a_pow_r_minus_1 = self.frobenius(); + let a_pow_r = a_pow_r_minus_1 * *self; + debug_assert!(a_pow_r.is_in_basefield()); + + Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) + } + + fn to_canonical_u64(&self) -> u64 { + self.0[0].to_canonical_u64() + } + + fn from_canonical_u64(n: u64) -> Self { + Self([ + ::BaseField::from_canonical_u64(n), + ::BaseField::ZERO, + ]) + } + + fn rand_from_rng(rng: &mut R) -> Self { + Self([ + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ]) + } +} + +impl Display for BinaryCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{} + {}*a", self.0[0], self.0[1]) + } +} + +impl Debug for BinaryCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl Neg for BinaryCrandallField { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self([-self.0[0], -self.0[1]]) + } +} + +impl Add for BinaryCrandallField { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]]) + } +} + +impl AddAssign for BinaryCrandallField { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sum for BinaryCrandallField { + fn sum>(iter: I) -> Self { + iter.fold(Self::ZERO, |acc, x| acc + x) + } +} + +impl Sub for BinaryCrandallField { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]]) + } +} + +impl SubAssign for BinaryCrandallField { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for BinaryCrandallField { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let Self([a0, a1]) = self; + let Self([b0, b1]) = rhs; + + let c0 = a0 * b0 + Self::W * a1 * b1; + let c1 = a0 * b1 + a1 * b0; + + Self([c0, c1]) + } +} + +impl MulAssign for BinaryCrandallField { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Product for BinaryCrandallField { + fn product>(iter: I) -> Self { + iter.fold(Self::ONE, |acc, x| acc * x) + } +} + +impl Div for BinaryCrandallField { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for BinaryCrandallField { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::binary::{BinaryCrandallField, BinaryFieldExtension}; + use crate::field::field::Field; + use crate::test_arithmetic; + + fn exp_naive(x: F, power: u64) -> F { + let mut current = x; + let mut product = F::ONE; + + for j in 0..64 { + if (power >> j & 1) != 0 { + product *= current; + } + current = current.square(); + } + product + } + + #[test] + fn test_add_neg_sub_mul() { + type F = BinaryCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x + (-x), F::ZERO); + assert_eq!(-x, F::ZERO - x); + assert_eq!( + x + x, + x.scalar_mul(::BaseField::TWO) + ); + assert_eq!(x * (-x), -x.square()); + assert_eq!(x + y, y + x); + assert_eq!(x * y, y * x); + assert_eq!(x * (y * z), (x * y) * z); + assert_eq!(x - (y + z), (x - y) - z); + assert_eq!((x + y) - z, x + (y - z)); + assert_eq!(x * (y + z), x * y + x * z); + } + + #[test] + fn test_inv_div() { + type F = BinaryCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x * x.inverse(), F::ONE); + assert_eq!(x.inverse() * x, F::ONE); + assert_eq!(x.square().inverse(), x.inverse().square()); + assert_eq!((x / y) * y, x); + assert_eq!(x / (y * z), (x / y) / z); + assert_eq!((x * y) / z, x * (y / z)); + } + + #[test] + fn test_frobenius() { + type F = BinaryCrandallField; + let x = F::rand(); + assert_eq!( + exp_naive(x, ::BaseField::ORDER), + x.frobenius() + ); + } + + #[test] + fn test_field_order() { + // F::ORDER = 340282366831806780677557380898690695169 = 18446744071293632512 *18446744071293632514 + 1 + type F = BinaryCrandallField; + let x = F::rand(); + assert_eq!( + exp_naive(exp_naive(x, 18446744071293632512), 18446744071293632514), + F::ONE + ); + } +} diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs new file mode 100644 index 00000000..17402bd8 --- /dev/null +++ b/src/field/extension_field/quartic.rs @@ -0,0 +1,351 @@ +use crate::field::crandall_field::CrandallField; +use crate::field::field::Field; +use rand::Rng; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; +use std::iter::{Product, Sum}; +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; + +pub trait QuarticFieldExtension: Field { + type BaseField: Field; + + // Element W of BaseField, such that `X^4 - W` is irreducible over BaseField. + const W: Self::BaseField; + + fn to_canonical_representation(&self) -> [Self::BaseField; 4]; + + fn is_in_basefield(&self) -> bool { + self.to_canonical_representation()[1..] + .iter() + .all(|x| x.is_zero()) + } + + /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. + fn frobenius(&self) -> Self; + + fn scalar_mul(&self, c: Self::BaseField) -> Self; +} + +#[derive(Copy, Clone)] +pub struct QuarticCrandallField([CrandallField; 4]); + +impl QuarticFieldExtension for QuarticCrandallField { + type BaseField = CrandallField; + // Verifiable in Sage with + // ``R. = GF(p)[]; assert (x^4 -3).is_irreducible()`. + const W: Self::BaseField = CrandallField(3); + + fn to_canonical_representation(&self) -> [Self::BaseField; 4] { + self.0 + } + + fn frobenius(&self) -> Self { + let [a0, a1, a2, a3] = self.to_canonical_representation(); + let k = (Self::BaseField::ORDER - 1) / 4; + let z0 = Self::W.exp_usize(k as usize); + let mut z = Self::BaseField::ONE; + let b0 = a0 * z; + z *= z0; + let b1 = a1 * z; + z *= z0; + let b2 = a2 * z; + z *= z0; + let b3 = a3 * z; + + Self([b0, b1, b2, b3]) + } + + fn scalar_mul(&self, c: Self::BaseField) -> Self { + let [a0, a1, a2, a3] = self.to_canonical_representation(); + Self([a0 * c, a1 * c, a2 * c, a3 * c]) + } +} + +impl PartialEq for QuarticCrandallField { + fn eq(&self, other: &Self) -> bool { + self.to_canonical_representation() == other.to_canonical_representation() + } +} + +impl Eq for QuarticCrandallField {} + +impl Hash for QuarticCrandallField { + fn hash(&self, state: &mut H) { + for l in &self.to_canonical_representation() { + Hash::hash(l, state); + } + } +} + +impl Field for QuarticCrandallField { + const ZERO: Self = Self([CrandallField::ZERO; 4]); + const ONE: Self = Self([ + CrandallField::ONE, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const TWO: Self = Self([ + CrandallField::TWO, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const NEG_ONE: Self = Self([ + CrandallField::NEG_ONE, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + + // Does not fit in 64-bits. + const ORDER: u64 = 0; + const TWO_ADICITY: usize = 30; + const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self([ + CrandallField(3), + CrandallField::ONE, + CrandallField::ZERO, + CrandallField::ZERO, + ]); + const POWER_OF_TWO_GENERATOR: Self = Self([ + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField::ZERO, + CrandallField(14096607364803438105), + ]); + + // Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography. + fn try_inverse(&self) -> Option { + if self.is_zero() { + return None; + } + + let a_pow_p = self.frobenius(); + let a_pow_p_plus_1 = a_pow_p * *self; + let a_pow_p3_plus_p2 = a_pow_p_plus_1.frobenius().frobenius(); + let a_pow_r_minus_1 = a_pow_p3_plus_p2 * a_pow_p; + let a_pow_r = a_pow_r_minus_1 * *self; + debug_assert!(a_pow_r.is_in_basefield()); + + Some(a_pow_r_minus_1.scalar_mul(a_pow_r.0[0].inverse())) + } + + fn to_canonical_u64(&self) -> u64 { + self.0[0].to_canonical_u64() + } + + fn from_canonical_u64(n: u64) -> Self { + Self([ + ::BaseField::from_canonical_u64(n), + ::BaseField::ZERO, + ::BaseField::ZERO, + ::BaseField::ZERO, + ]) + } + + fn rand_from_rng(rng: &mut R) -> Self { + Self([ + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ::BaseField::rand_from_rng(rng), + ]) + } +} + +impl Display for QuarticCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} + {}*a + {}*a^2 + {}*a^3", + self.0[0], self.0[1], self.0[2], self.0[3] + ) + } +} + +impl Debug for QuarticCrandallField { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl Neg for QuarticCrandallField { + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Self([-self.0[0], -self.0[1], -self.0[2], -self.0[3]]) + } +} + +impl Add for QuarticCrandallField { + type Output = Self; + + #[inline] + fn add(self, rhs: Self) -> Self { + Self([ + self.0[0] + rhs.0[0], + self.0[1] + rhs.0[1], + self.0[2] + rhs.0[2], + self.0[3] + rhs.0[3], + ]) + } +} + +impl AddAssign for QuarticCrandallField { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + +impl Sum for QuarticCrandallField { + fn sum>(iter: I) -> Self { + iter.fold(Self::ZERO, |acc, x| acc + x) + } +} + +impl Sub for QuarticCrandallField { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + Self([ + self.0[0] - rhs.0[0], + self.0[1] - rhs.0[1], + self.0[2] - rhs.0[2], + self.0[3] - rhs.0[3], + ]) + } +} + +impl SubAssign for QuarticCrandallField { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = *self - rhs; + } +} + +impl Mul for QuarticCrandallField { + type Output = Self; + + #[inline] + fn mul(self, rhs: Self) -> Self { + let Self([a0, a1, a2, a3]) = self; + let Self([b0, b1, b2, b3]) = rhs; + + let c0 = a0 * b0 + Self::W * (a1 * b3 + a2 * b2 + a3 * b1); + let c1 = a0 * b1 + a1 * b0 + Self::W * (a2 * b3 + a3 * b2); + let c2 = a0 * b2 + a1 * b1 + a2 * b0 + Self::W * a3 * b3; + let c3 = a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + + Self([c0, c1, c2, c3]) + } +} + +impl MulAssign for QuarticCrandallField { + #[inline] + fn mul_assign(&mut self, rhs: Self) { + *self = *self * rhs; + } +} + +impl Product for QuarticCrandallField { + fn product>(iter: I) -> Self { + iter.fold(Self::ONE, |acc, x| acc * x) + } +} + +impl Div for QuarticCrandallField { + type Output = Self; + + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self::Output { + self * rhs.inverse() + } +} + +impl DivAssign for QuarticCrandallField { + fn div_assign(&mut self, rhs: Self) { + *self = *self / rhs; + } +} + +#[cfg(test)] +mod tests { + use crate::field::crandall_field::CrandallField; + use crate::field::extension_field::quartic::{QuarticCrandallField, QuarticFieldExtension}; + use crate::field::field::Field; + use crate::test_arithmetic; + + fn exp_naive(x: F, power: u128) -> F { + let mut current = x; + let mut product = F::ONE; + + for j in 0..128 { + if (power >> j & 1) != 0 { + product *= current; + } + current = current.square(); + } + product + } + + #[test] + fn test_add_neg_sub_mul() { + type F = QuarticCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x + (-x), F::ZERO); + assert_eq!(-x, F::ZERO - x); + assert_eq!( + x + x, + x.scalar_mul(::BaseField::TWO) + ); + assert_eq!(x * (-x), -x.square()); + assert_eq!(x + y, y + x); + assert_eq!(x * y, y * x); + assert_eq!(x * (y * z), (x * y) * z); + assert_eq!(x - (y + z), (x - y) - z); + assert_eq!((x + y) - z, x + (y - z)); + assert_eq!(x * (y + z), x * y + x * z); + } + + #[test] + fn test_inv_div() { + type F = QuarticCrandallField; + let x = F::rand(); + let y = F::rand(); + let z = F::rand(); + assert_eq!(x * x.inverse(), F::ONE); + assert_eq!(x.inverse() * x, F::ONE); + assert_eq!(x.square().inverse(), x.inverse().square()); + assert_eq!((x / y) * y, x); + assert_eq!(x / (y * z), (x / y) / z); + assert_eq!((x * y) / z, x * (y / z)); + } + + #[test] + fn test_frobenius() { + type F = QuarticCrandallField; + let x = F::rand(); + assert_eq!( + exp_naive(x, ::BaseField::ORDER as u128), + x.frobenius() + ); + } + + #[test] + fn test_field_order() { + // F::ORDER = 340282366831806780677557380898690695168 * 340282366831806780677557380898690695170 + 1 + type F = QuarticCrandallField; + let x = F::rand(); + assert_eq!( + exp_naive( + exp_naive(x, 340282366831806780677557380898690695168), + 340282366831806780677557380898690695170 + ), + F::ONE + ); + } +} From 1e45b0b1c0f0cf0fd017ead478403fd556266d63 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 20:58:04 +0200 Subject: [PATCH 6/6] Move Frobenius to default trait implementation. --- src/field/extension_field/binary.rs | 20 +++++++++------- src/field/extension_field/quartic.rs | 36 +++++++++++++++------------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/field/extension_field/binary.rs b/src/field/extension_field/binary.rs index 5458e18d..024e005d 100644 --- a/src/field/extension_field/binary.rs +++ b/src/field/extension_field/binary.rs @@ -14,6 +14,8 @@ pub trait BinaryFieldExtension: Field { fn to_canonical_representation(&self) -> [Self::BaseField; 2]; + fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self; + fn is_in_basefield(&self) -> bool { self.to_canonical_representation()[1..] .iter() @@ -21,7 +23,13 @@ pub trait BinaryFieldExtension: Field { } /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. - fn frobenius(&self) -> Self; + fn frobenius(&self) -> Self { + let [a0, a1] = self.to_canonical_representation(); + let k = (Self::BaseField::ORDER - 1) / 2; + let z = Self::W.exp_usize(k as usize); + + Self::from_canonical_representation([a0, a1 * z]) + } fn scalar_mul(&self, c: Self::BaseField) -> Self; } @@ -39,12 +47,8 @@ impl BinaryFieldExtension for BinaryCrandallField { self.0 } - fn frobenius(&self) -> Self { - let [a0, a1] = self.to_canonical_representation(); - let k = (Self::BaseField::ORDER - 1) / 2; - let z = Self::W.exp_usize(k as usize); - - Self([a0, a1 * z]) + fn from_canonical_representation(v: [Self::BaseField; 2]) -> Self { + Self(v) } fn scalar_mul(&self, c: Self::BaseField) -> Self { @@ -217,10 +221,8 @@ impl DivAssign for BinaryCrandallField { #[cfg(test)] mod tests { - use crate::field::crandall_field::CrandallField; use crate::field::extension_field::binary::{BinaryCrandallField, BinaryFieldExtension}; use crate::field::field::Field; - use crate::test_arithmetic; fn exp_naive(x: F, power: u64) -> F { let mut current = x; diff --git a/src/field/extension_field/quartic.rs b/src/field/extension_field/quartic.rs index 17402bd8..3137d88c 100644 --- a/src/field/extension_field/quartic.rs +++ b/src/field/extension_field/quartic.rs @@ -14,6 +14,8 @@ pub trait QuarticFieldExtension: Field { fn to_canonical_representation(&self) -> [Self::BaseField; 4]; + fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self; + fn is_in_basefield(&self) -> bool { self.to_canonical_representation()[1..] .iter() @@ -21,7 +23,21 @@ pub trait QuarticFieldExtension: Field { } /// Frobenius automorphisms: x -> x^p, where p is the order of BaseField. - fn frobenius(&self) -> Self; + fn frobenius(&self) -> Self { + let [a0, a1, a2, a3] = self.to_canonical_representation(); + let k = (Self::BaseField::ORDER - 1) / 4; + let z0 = Self::W.exp_usize(k as usize); + let mut z = Self::BaseField::ONE; + let b0 = a0 * z; + z *= z0; + let b1 = a1 * z; + z *= z0; + let b2 = a2 * z; + z *= z0; + let b3 = a3 * z; + + Self::from_canonical_representation([b0, b1, b2, b3]) + } fn scalar_mul(&self, c: Self::BaseField) -> Self; } @@ -39,20 +55,8 @@ impl QuarticFieldExtension for QuarticCrandallField { self.0 } - fn frobenius(&self) -> Self { - let [a0, a1, a2, a3] = self.to_canonical_representation(); - let k = (Self::BaseField::ORDER - 1) / 4; - let z0 = Self::W.exp_usize(k as usize); - let mut z = Self::BaseField::ONE; - let b0 = a0 * z; - z *= z0; - let b1 = a1 * z; - z *= z0; - let b2 = a2 * z; - z *= z0; - let b3 = a3 * z; - - Self([b0, b1, b2, b3]) + fn from_canonical_representation(v: [Self::BaseField; 4]) -> Self { + Self(v) } fn scalar_mul(&self, c: Self::BaseField) -> Self { @@ -272,10 +276,8 @@ impl DivAssign for QuarticCrandallField { #[cfg(test)] mod tests { - use crate::field::crandall_field::CrandallField; use crate::field::extension_field::quartic::{QuarticCrandallField, QuarticFieldExtension}; use crate::field::field::Field; - use crate::test_arithmetic; fn exp_naive(x: F, power: u128) -> F { let mut current = x;