plonky2/field/src/extension/quadratic.rs
Daniel Lubarov 2fd5fbbe01 Change from_biguint's behavior with extension fields
These appear to be unused for extension fields, so we're free to change the mapping without breaking anything.

As the TODO says, the mapping that's currently implemented doesn't seem natural or useful. It seems more natural to treat the `BigUint` as a base field element, potentially in a non-canonical form.
2022-08-19 09:21:10 -07:00

238 lines
6.2 KiB
Rust

use std::fmt::{Debug, Display, Formatter};
use std::iter::{Product, Sum};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::bigint::BigUint;
use serde::{Deserialize, Serialize};
use crate::extension::{Extendable, FieldExtension, Frobenius, OEF};
use crate::ops::Square;
use crate::types::Field;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(bound = "")]
pub struct QuadraticExtension<F: Extendable<2>>(pub [F; 2]);
impl<F: Extendable<2>> Default for QuadraticExtension<F> {
fn default() -> Self {
Self::ZERO
}
}
impl<F: Extendable<2>> OEF<2> for QuadraticExtension<F> {
const W: F = F::W;
const DTH_ROOT: F = F::DTH_ROOT;
}
impl<F: Extendable<2>> Frobenius<2> for QuadraticExtension<F> {}
impl<F: Extendable<2>> FieldExtension<2> for QuadraticExtension<F> {
type BaseField = F;
fn to_basefield_array(&self) -> [F; 2] {
self.0
}
fn from_basefield_array(arr: [F; 2]) -> Self {
Self(arr)
}
fn from_basefield(x: F) -> Self {
x.into()
}
}
impl<F: Extendable<2>> From<F> for QuadraticExtension<F> {
fn from(x: F) -> Self {
Self([x, F::ZERO])
}
}
impl<F: Extendable<2>> Field for QuadraticExtension<F> {
const ZERO: Self = Self([F::ZERO; 2]);
const ONE: Self = Self([F::ONE, F::ZERO]);
const TWO: Self = Self([F::TWO, F::ZERO]);
const NEG_ONE: Self = Self([F::NEG_ONE, F::ZERO]);
// `p^2 - 1 = (p - 1)(p + 1)`. The `p - 1` term has a two-adicity of `F::TWO_ADICITY`. As
// long as `F::TWO_ADICITY >= 2`, `p` can be written as `4n + 1`, so `p + 1` can be written as
// `2(2n + 1)`, which has a 2-adicity of 1.
const TWO_ADICITY: usize = F::TWO_ADICITY + 1;
const CHARACTERISTIC_TWO_ADICITY: usize = F::CHARACTERISTIC_TWO_ADICITY;
const MULTIPLICATIVE_GROUP_GENERATOR: Self = Self(F::EXT_MULTIPLICATIVE_GROUP_GENERATOR);
const POWER_OF_TWO_GENERATOR: Self = Self(F::EXT_POWER_OF_TWO_GENERATOR);
const BITS: usize = F::BITS * 2;
fn order() -> BigUint {
F::order() * F::order()
}
fn characteristic() -> BigUint {
F::characteristic()
}
// Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
fn try_inverse(&self) -> Option<Self> {
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!(FieldExtension::<2>::is_in_basefield(&a_pow_r));
Some(FieldExtension::<2>::scalar_mul(
&a_pow_r_minus_1,
a_pow_r.0[0].inverse(),
))
}
fn from_noncanonical_biguint(n: BigUint) -> Self {
F::from_noncanonical_biguint(n).into()
}
fn from_canonical_u64(n: u64) -> Self {
F::from_canonical_u64(n).into()
}
fn from_noncanonical_u128(n: u128) -> Self {
F::from_noncanonical_u128(n).into()
}
#[cfg(feature = "rand")]
fn rand_from_rng<R: rand::Rng>(rng: &mut R) -> Self {
Self([F::rand_from_rng(rng), F::rand_from_rng(rng)])
}
}
impl<F: Extendable<2>> Display for QuadraticExtension<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} + {}*a", self.0[0], self.0[1])
}
}
impl<F: Extendable<2>> Debug for QuadraticExtension<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl<F: Extendable<2>> Neg for QuadraticExtension<F> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self([-self.0[0], -self.0[1]])
}
}
impl<F: Extendable<2>> Add for QuadraticExtension<F> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Self([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1]])
}
}
impl<F: Extendable<2>> AddAssign for QuadraticExtension<F> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<F: Extendable<2>> Sum for QuadraticExtension<F> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, x| acc + x)
}
}
impl<F: Extendable<2>> Sub for QuadraticExtension<F> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1]])
}
}
impl<F: Extendable<2>> SubAssign for QuadraticExtension<F> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<F: Extendable<2>> Mul for QuadraticExtension<F> {
type Output = Self;
#[inline]
default fn mul(self, rhs: Self) -> Self {
let Self([a0, a1]) = self;
let Self([b0, b1]) = rhs;
let c0 = a0 * b0 + <Self as OEF<2>>::W * a1 * b1;
let c1 = a0 * b1 + a1 * b0;
Self([c0, c1])
}
}
impl<F: Extendable<2>> MulAssign for QuadraticExtension<F> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<F: Extendable<2>> Square for QuadraticExtension<F> {
#[inline(always)]
fn square(&self) -> Self {
// Specialising mul reduces the computation of c1 from 2 muls
// and one add to one mul and a shift
let Self([a0, a1]) = *self;
let c0 = a0.square() + <Self as OEF<2>>::W * a1.square();
let c1 = a0 * a1.double();
Self([c0, c1])
}
}
impl<F: Extendable<2>> Product for QuadraticExtension<F> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ONE, |acc, x| acc * x)
}
}
impl<F: Extendable<2>> Div for QuadraticExtension<F> {
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
fn div(self, rhs: Self) -> Self::Output {
self * rhs.inverse()
}
}
impl<F: Extendable<2>> DivAssign for QuadraticExtension<F> {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
#[cfg(test)]
mod tests {
mod goldilocks {
use crate::{test_field_arithmetic, test_field_extension};
test_field_extension!(crate::goldilocks_field::GoldilocksField, 2);
test_field_arithmetic!(
crate::extension::quadratic::QuadraticExtension<
crate::goldilocks_field::GoldilocksField,
>
);
}
}