From d1c84ebdc16c2aabe0ff9ff0328df62275eb8307 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Mon, 6 May 2024 16:39:25 -0700 Subject: [PATCH] feat: home-baked `FiniteField` trait (#38) * feat: new `FiniteField` trait Now everything compiles again. Will work to clean this all up and get all the tests to pass. * fix: `GF101` tests pass * fix: reimplement monty optimizations * clean: udeps --------- Co-authored-by: Waylon Jepsen <57912727+0xJepsen@users.noreply.github.com> --- Cargo.lock | 38 +--- Cargo.toml | 19 +- src/curve.rs | 20 +-- src/{field.rs => field/gf_101.rs} | 290 +++++++++++++----------------- src/field/mod.rs | 65 +++++++ src/main.rs | 4 +- src/polynomial.rs | 76 ++++---- 7 files changed, 246 insertions(+), 266 deletions(-) rename src/{field.rs => field/gf_101.rs} (62%) create mode 100644 src/field/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 63f6a38..292062d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,6 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" -[[package]] name = "ark-bn254" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -43,7 +42,7 @@ dependencies = [ "ark-std", "derivative", "hashbrown", - "itertools 0.10.5", + "itertools", "num-traits", "zeroize", ] @@ -60,7 +59,7 @@ dependencies = [ "ark-std", "derivative", "digest", - "itertools 0.10.5", + "itertools", "num-bigint", "num-traits", "paste", @@ -224,15 +223,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "libc" version = "0.2.154" @@ -274,27 +264,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "p3-field" -version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git#2e274173200ece6254883e7e6eb8bc028574332e" -dependencies = [ - "itertools 0.12.1", - "num-bigint", - "num-traits", - "p3-util", - "rand", - "serde", -] - -[[package]] -name = "p3-util" -version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git#2e274173200ece6254883e7e6eb8bc028574332e" -dependencies = [ - "serde", -] - [[package]] name = "paste" version = "1.0.14" @@ -359,15 +328,12 @@ dependencies = [ name = "ronkathon" version = "0.1.0" dependencies = [ - "anyhow", "ark-bn254", "ark-ec", "ark-ff", "ark-poly", "ark-std", - "itertools 0.12.1", "num-bigint", - "p3-field", "rand", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 1e77d63..9217e41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,14 +8,11 @@ repository ="https://github.com/thor314/ronkathon" version ="0.1.0" [dependencies] -anyhow = "1.0" -p3-field = { version="0.1.0", git="https://github.com/Plonky3/Plonky3.git" } -itertools = "0.12.0" -rand = "0.8" -serde = { version="1.0", default-features=false, features=["derive"] } -num-bigint= { version="0.4.3", default-features=false } -ark-std = { version = "0.4.0", default-features = false } -ark-bn254 = "0.4.0" -ark-poly = "0.4.0" -ark-ff = "0.4.0" -ark-ec = "0.4.0" \ No newline at end of file +rand ="0.8" +serde ={ version="1.0", default-features=false, features=["derive"] } +num-bigint={ version="0.4.3", default-features=false } +ark-std ={ version="0.4.0", default-features=false } +ark-bn254 ="0.4.0" +ark-poly ="0.4.0" +ark-ff ="0.4.0" +ark-ec ="0.4.0" diff --git a/src/curve.rs b/src/curve.rs index 62372f2..eef220f 100644 --- a/src/curve.rs +++ b/src/curve.rs @@ -3,12 +3,10 @@ use std::{ ops::{Add, Neg}, }; -use p3_field::{AbstractField, Field}; - -use crate::field::PlutoField; +use crate::field::{gf_101::GF101, FiniteField}; /// Elliptic curve in Weierstrass form: y^2 = x^3 + ax + b -pub struct Curve { +pub struct Curve { pub a: F, pub b: F, three: F, @@ -17,7 +15,7 @@ pub struct Curve { pub trait CurveParams: 'static + Copy + Clone + fmt::Debug + Default + Eq + Ord { /// Integer field element type - type FieldElement: Field + Neg; + type FieldElement: FiniteField; /// Order of this elliptic curve, i.e. number of elements in the scalar field. const ORDER: u32; /// Coefficient `a` in the Weierstrass equation of this elliptic curve. @@ -47,14 +45,14 @@ pub trait CurveParams: 'static + Copy + Clone + fmt::Debug + Default + Eq + Ord pub struct C101; impl CurveParams for C101 { - type FieldElement = crate::field::PlutoField; + type FieldElement = GF101; - const EQUATION_A: Self::FieldElement = PlutoField::const_new(0); - const EQUATION_B: Self::FieldElement = PlutoField::const_new(3); + const EQUATION_A: Self::FieldElement = GF101::new(0); + const EQUATION_B: Self::FieldElement = GF101::new(3); const GENERATOR: (Self::FieldElement, Self::FieldElement) = todo!(); - const ORDER: u32 = PlutoField::ORDER_U32; - const THREE: Self::FieldElement = PlutoField::const_new(3); - const TWO: Self::FieldElement = PlutoField::const_new(2); + const ORDER: u32 = GF101::ORDER; + const THREE: Self::FieldElement = GF101::new(3); + const TWO: Self::FieldElement = GF101::new(2); } /// An Affine Coordinate Point on a Weierstrass elliptic curve diff --git a/src/field.rs b/src/field/gf_101.rs similarity index 62% rename from src/field.rs rename to src/field/gf_101.rs index 84c3796..e8caf42 100644 --- a/src/field.rs +++ b/src/field/gf_101.rs @@ -6,13 +6,14 @@ use core::{ use std::fmt; use num_bigint::BigUint; -use p3_field::{exp_u64_by_squaring, halve_u32, AbstractField, Field, Packable}; use rand::{ distributions::{Distribution, Standard}, Rng, }; use serde::{Deserialize, Serialize}; +use super::*; + const PLUTO_FIELD_PRIME: u32 = 101; // value chosen such that 2^k is closest power of two from modulus const MONTY_BITS: u32 = 7; @@ -22,62 +23,43 @@ const MONTY_MASK: u32 = (1 << MONTY_BITS) - 1; const MONTY_MU: u32 = 19; #[derive(Copy, Clone, Default, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] -pub struct PlutoField { +pub struct GF101 { value: u32, } -impl PlutoField { - pub const ORDER_U32: u32 = PLUTO_FIELD_PRIME; - - pub fn new(value: u32) -> Self { Self { value: to_monty(value) } } - - pub const fn const_new(value: u32) -> Self { Self { value: to_monty(value) } } - - // In any field of prime order F_p: - // - There exists an additive group. - // - There exists a multiplicative subgroup generated by a primitive element 'a'. - // - // According to the Sylow theorems (https://en.wikipedia.org/wiki/Sylow_theorems): - // A non-trivial multiplicative subgroup of prime order 'q' exists if and only if - // 'p - 1' is divisible by 'q'. - // The primitive q-th root of unity 'w' is defined as: w = a^((p - 1) / q), - // and the roots of unity are generated by 'w', such that {w^i | i in [0, q - 1]}. - pub fn primitive_root_of_unity(n: u32) -> Self { - let p_minus_one = PLUTO_FIELD_PRIME - 1; - assert!(p_minus_one % n == 0, "n must divide p - 1"); - let pow = p_minus_one / n; - Self::generator().exp_u64(pow as u64) - } -} - -impl fmt::Display for PlutoField { +impl fmt::Display for GF101 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) } } -impl Packable for PlutoField {} - -impl Div for PlutoField { - type Output = Self; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn div(self, rhs: Self) -> Self { self * rhs.inverse() } +impl GF101 { + pub const fn new(value: u32) -> Self { Self { value: to_monty(value) } } } -impl Field for PlutoField { - // TODO: Add cfg-guarded Packing for AVX2, NEON, etc. - type Packing = Self; - fn is_zero(&self) -> bool { self.value == 0 || self.value == Self::ORDER_U32 } +impl FiniteField for GF101 { + type Storage = u32; + + const ORDER: Self::Storage = PLUTO_FIELD_PRIME; #[inline] - fn exp_u64_generic>(val: AF, power: u64) -> AF { - exp_u64_by_squaring(val, power) + fn pow(&self, power: Self::Storage) -> Self { + let mut current = *self; + let power = power as u64; + let mut product = Self::one(); + + for j in 0..(64 - power.leading_zeros()) as usize { + if (power >> j & 1) != 0 { + product *= current; + } + current = current * current; + } + product } - fn try_inverse(&self) -> Option { - if self.is_zero() { + fn inverse(&self) -> Option { + if self.value == 0 { return None; } - let exponent = PLUTO_FIELD_PRIME - 2; // p - 2 + let exponent = Self::ORDER - 2; let mut result = Self::one(); let mut base = *self; let mut power = exponent; @@ -86,135 +68,103 @@ impl Field for PlutoField { if power & 1 == 1 { result *= base; } - base = base.square(); + base = base * base; power >>= 1; } Some(result) } - #[inline] - fn halve(&self) -> Self { Self { value: halve_u32::(self.value) } } - - #[inline] - fn order() -> BigUint { PLUTO_FIELD_PRIME.into() } -} - -impl AbstractField for PlutoField { - type F = Self; - fn zero() -> Self { Self::new(0) } fn one() -> Self { Self::new(1) } fn two() -> Self { Self::new(2) } - fn neg_one() -> Self { Self::new(Self::ORDER_U32 - 1) } + fn neg_one() -> Self { Self::new(Self::ORDER - 1) } - #[inline] - fn from_f(f: Self::F) -> Self { f } - - fn from_bool(b: bool) -> Self { Self::new(u32::from(b)) } - - fn from_canonical_u8(n: u8) -> Self { Self::new(u32::from(n)) } + fn generator() -> Self { Self::new(2) } +} - fn from_canonical_u16(n: u16) -> Self { Self::new(u32::from(n)) } +impl Add for GF101 { + type Output = Self; - fn from_canonical_u32(n: u32) -> Self { Self::new(n) } + fn add(self, rhs: Self) -> Self { Self { value: (self.value + rhs.value) % Self::ORDER } } +} - #[inline] - fn from_canonical_u64(n: u64) -> Self { - debug_assert!(n < PLUTO_FIELD_PRIME as u64); - Self::from_canonical_u32(n as u32) - } +impl AddAssign for GF101 { + fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } +} - #[inline] - fn from_canonical_usize(n: usize) -> Self { - debug_assert!(n < PLUTO_FIELD_PRIME as usize); - Self::from_canonical_u32(n as u32) +impl Sum for GF101 { + fn sum>(iter: I) -> Self { + iter.reduce(|x, y| x + y).unwrap_or(Self::zero()) } +} - #[inline] - fn from_wrapped_u32(n: u32) -> Self { Self::new(n % PLUTO_FIELD_PRIME) } +impl Sub for GF101 { + type Output = Self; - #[inline] - fn from_wrapped_u64(n: u64) -> Self { - Self { value: to_monty((n % PLUTO_FIELD_PRIME as u64) as u32) } + fn sub(self, rhs: Self) -> Self { + let (mut diff, over) = self.value.overflowing_sub(rhs.value); + let corr = if over { PLUTO_FIELD_PRIME } else { 0 }; + diff = diff.wrapping_add(corr); + Self { value: diff } } +} - // generator for multiplicative subgroup of the field - fn generator() -> Self { Self::new(2) } +impl SubAssign for GF101 { + fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } -impl Mul for PlutoField { +impl Mul for GF101 { type Output = Self; fn mul(self, rhs: Self) -> Self { Self { value: from_monty(self.value * rhs.value) } } } -impl Distribution for Standard { - #[inline] - fn sample(&self, rng: &mut R) -> PlutoField { - loop { - let next_u31 = rng.next_u32() >> 4; - let is_canonical = next_u31 < PLUTO_FIELD_PRIME; - if is_canonical { - return PlutoField { value: next_u31 }; - } - } - } +impl MulAssign for GF101 { + fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; } } -impl Product for PlutoField { +impl Product for GF101 { fn product>(iter: I) -> Self { iter.reduce(|x, y| x * y).unwrap_or(Self::one()) } } -impl SubAssign for PlutoField { - fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } -} +impl Div for GF101 { + type Output = Self; -impl AddAssign for PlutoField { - fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } + #[allow(clippy::suspicious_arithmetic_impl)] + fn div(self, rhs: Self) -> Self { self * rhs.inverse().unwrap() } } -impl MulAssign for PlutoField { - fn mul_assign(&mut self, rhs: Self) { *self = *self * rhs; } +impl DivAssign for GF101 { + fn div_assign(&mut self, rhs: Self) { *self = *self / rhs; } } -impl Neg for PlutoField { +impl Neg for GF101 { type Output = Self; fn neg(self) -> Self::Output { Self::zero() - self } } -impl Add for PlutoField { +impl Rem for GF101 { type Output = Self; - fn add(self, rhs: Self) -> Self { - let mut sum = self.value + rhs.value; - let (corr_sum, over) = sum.overflowing_sub(PLUTO_FIELD_PRIME); - if !over { - sum = corr_sum; - } - Self { value: sum } - } -} - -impl Sum for PlutoField { - fn sum>(iter: I) -> Self { - iter.reduce(|x, y| x + y).unwrap_or(Self::zero()) - } + fn rem(self, rhs: Self) -> Self { self - (self / rhs) * rhs } } -impl Sub for PlutoField { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - let (mut diff, over) = self.value.overflowing_sub(rhs.value); - let corr = if over { PLUTO_FIELD_PRIME } else { 0 }; - diff = diff.wrapping_add(corr); - Self { value: diff } +impl Distribution for Standard { + #[inline] + fn sample(&self, rng: &mut R) -> GF101 { + loop { + let next_u31 = rng.next_u32() >> 4; + let is_canonical = next_u31 < PLUTO_FIELD_PRIME; + if is_canonical { + return GF101 { value: next_u31 }; + } + } } } @@ -305,7 +255,7 @@ mod tests { use super::*; - type F = PlutoField; + type F = GF101; #[test] fn test_overflowing_add() { @@ -323,9 +273,10 @@ mod tests { assert_eq!(c, F::new(91)); } + #[test] fn halve() { let a = F::new(10); - assert_eq!(a, a.halve() * F::two()); + assert_eq!(a, (a / F::two()) * F::two()); } #[test] @@ -345,24 +296,24 @@ mod tests { #[test] fn zero() { - let f = F::from_canonical_u32(0); - assert!(f.is_zero()); + let f = F::new(0); + assert_eq!(f.value, 0); - let f = F::from_wrapped_u32(F::ORDER_U32); - assert!(f.is_zero()); + let f = F::new(F::ORDER); + assert_eq!(f.value, 0); } #[test] - fn exp_u64_generic() { - let f = F::from_canonical_u32(2); - let f = F::exp_u64_generic(f, 3); - assert_eq!(f, F::new(8)); + fn exp_generic() { + let f = F::new(2); + let exp = f.pow(3); + assert_eq!(exp, F::new(8)); } #[test] fn addition_subtraction() { - let a = PlutoField::new(50); - let b = PlutoField::new(60); + let a = GF101::new(50); + let b = GF101::new(60); let c = a + b; assert_eq!(c, F::new(9)); // (50 + 60) % 101 = 9 @@ -380,8 +331,8 @@ mod tests { assert_eq!(x + (-x), F::zero()); assert_eq!(-x, F::zero() - x); assert_eq!(x + x, x * F::two()); - assert_eq!(x, x.halve() * F::two()); - assert_eq!(x * (-x), -x.square()); + assert_eq!(x, x.div(F::new(2)) * F::two()); + assert_eq!(x * (-x), -(x * x)); assert_eq!(x + y, y + x); assert_eq!(x * y, y * x); assert_eq!(x * (y * z), (x * y) * z); @@ -389,76 +340,81 @@ mod tests { assert_eq!((x + y) - z, x + (y - z)); assert_eq!(x * (y + z), x * y + x * z); assert_eq!(x + y + z + x + y + z, [x, x, y, y, z, z].iter().cloned().sum()); - - // } } #[test] fn multiplicative_inverse() { - let a = PlutoField::new(10); - let a_inv = a.inverse(); + let a = GF101::new(10); + let a_inv = a.inverse().unwrap(); let should_be_one = a * a_inv; assert_eq!(should_be_one, F::new(1)); } + #[should_panic] + #[test] + fn no_zero_inverse() { + let zero = GF101::new(0); + let _inv = zero.inverse().unwrap(); + } + #[test] fn identity_elements() { - let a = PlutoField::new(10); - let zero = PlutoField::new(0); - let one = PlutoField::new(1); + let a = GF101::new(10); + let zero = GF101::new(0); + let one = GF101::new(1); assert_eq!((a + zero).value, a.value); assert_eq!((a * one).value, a.value); } #[test] fn zero_multiplication() { - let a = PlutoField::new(10); - let zero = PlutoField::new(0); + let a = GF101::new(10); + let zero = GF101::new(0); assert_eq!((a * zero).value, 0); } #[test] fn negation() { - let a = PlutoField::new(10); + let a = GF101::new(10); let neg_a = -a; assert_eq!((a + neg_a).value, 0); } #[test] fn inverse_of_inverse() { - let a = PlutoField::new(10); - let a_inv = a.inverse(); - let a_inv_inv = a_inv.inverse(); + let a = GF101::new(10); + let a_inv = a.inverse().unwrap(); + let a_inv_inv = a_inv.inverse().unwrap(); assert_eq!(a_inv_inv, a); } #[test] fn distributivity() { - let a = PlutoField::new(5); - let b = PlutoField::new(6); - let c = PlutoField::new(7); + let a = GF101::new(5); + let b = GF101::new(6); + let c = GF101::new(7); assert_eq!((a * (b + c)).value, (a * b + a * c).value); } #[test] fn associativity() { - let a = PlutoField::new(5); - let b = PlutoField::new(6); - let c = PlutoField::new(7); + let a = GF101::new(5); + let b = GF101::new(6); + let c = GF101::new(7); assert_eq!(((a + b) + c).value, (a + (b + c)).value); assert_eq!(((a * b) * c).value, (a * (b * c)).value); } #[test] fn non_zero_element() { - let a = PlutoField::new(10); - assert!(!a.is_zero()); + let a = GF101::new(10); + assert!(!(a == F::zero())); } #[test] fn power_of_zero() { - let a = PlutoField::new(0); - let b = PlutoField::exp_u64_generic(a, 3); + let a = GF101::new(0); + let b = a.pow(3); assert_eq!(b.value, 0); } @@ -466,33 +422,33 @@ mod tests { #[test] fn not_primitive_root_of_unity() { let n = 3; - let omega = PlutoField::primitive_root_of_unity(n); + let omega = GF101::primitive_root_of_unity(n); } #[test] fn primitive_root_of_unity() { let n = 5; - let omega = PlutoField::primitive_root_of_unity(n); + let omega = GF101::primitive_root_of_unity(n); println!("omega: {:?}", omega); assert_eq!(omega, F::new(95)); - let omega_n = omega.exp_u64(n as u64); + let omega_n = omega.pow(n); for i in 1..n { - let omega_i = omega.exp_u64(i as u64); + let omega_i = omega.pow(i); println!("omega^{}: {:?}", i, omega_i); assert_ne!(omega_i, F::new(1)); } assert_eq!(omega_n, F::new(1)); let n = 25; - let omega = PlutoField::primitive_root_of_unity(n); + let omega = GF101::primitive_root_of_unity(n); println!("omega: {:?}", omega); assert_eq!(omega, F::new(16)); for i in 1..n { - let omega_i = omega.exp_u64(i as u64); + let omega_i = omega.pow(i); println!("omega^{}: {:?}", i, omega_i); assert_ne!(omega_i, F::new(1)); } - let omega_n = omega.exp_u64(n as u64); + let omega_n = omega.pow(n); assert_eq!(omega_n, F::new(1)); } } diff --git a/src/field/mod.rs b/src/field/mod.rs new file mode 100644 index 0000000..3d6bfa1 --- /dev/null +++ b/src/field/mod.rs @@ -0,0 +1,65 @@ +use std::{ + hash::Hash, + iter::{Product, Sum}, + ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign}, +}; + +pub mod gf_101; + +/// A field is a set of elements on which addition, subtraction, multiplication, and division are +/// defined. +/// +/// We restrict to finite fields, which are fields with a finite number of elements. +pub trait FiniteField: + std::fmt::Debug + + Sized + + Copy + + Clone + + PartialEq + + Eq + + Add + + AddAssign + + Sum + + Sub + + SubAssign + + Mul + + MulAssign + + Product + + Div + + DivAssign + + Neg + + Rem + + Hash + + 'static { + type Storage: From + + Copy + + std::fmt::Debug + + Sub + + Div + + Rem + + Eq; + const ORDER: Self::Storage; + fn zero() -> Self; + fn one() -> Self; + fn two() -> Self; + fn neg_one() -> Self; + fn inverse(&self) -> Option; + fn pow(&self, power: Self::Storage) -> Self; + fn generator() -> Self; + + // In any field of prime order F_p: + // - There exists an additive group. + // - There exists a multiplicative subgroup generated by a primitive element 'a'. + // + // According to the Sylow theorems (https://en.wikipedia.org/wiki/Sylow_theorems): + // A non-trivial multiplicative subgroup of prime order 'q' exists if and only if + // 'p - 1' is divisible by 'q'. + // The primitive q-th root of unity 'w' is defined as: w = a^((p - 1) / q), + // and the roots of unity are generated by 'w', such that {w^i | i in [0, q - 1]}. + fn primitive_root_of_unity(n: Self::Storage) -> Self { + let p_minus_one = Self::ORDER - Self::Storage::from(1); + assert!(p_minus_one % n == 0.into(), "n must divide p - 1"); + let pow = p_minus_one / n; + Self::generator().pow(pow) + } +} diff --git a/src/main.rs b/src/main.rs index 7fda564..82fc5cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ -use ronkathon::field::PlutoField; +use ronkathon::field::gf_101::GF101; fn main() { - let f = PlutoField::new(1); + let f = GF101::new(1); println!("hello field={:?}", f); let g = generator(101); diff --git a/src/polynomial.rs b/src/polynomial.rs index 9f6b216..a78f325 100644 --- a/src/polynomial.rs +++ b/src/polynomial.rs @@ -1,12 +1,12 @@ use std::{collections::HashSet, ops::Div}; -use p3_field::Field; +use crate::field::FiniteField; // https://people.inf.ethz.ch/gander/papers/changing.pdf /// A polynomial of degree N-1 #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Polynomial { +pub struct Polynomial { pub coefficients: [F; N], pub basis: B, } @@ -22,21 +22,21 @@ impl Basis for Monomial { } // https://en.wikipedia.org/wiki/Lagrange_polynomial -pub struct Lagrange { +pub struct Lagrange { pub nodes: [F; N], pub weights: [F; N], pub L: Box F>, } -impl Basis for Lagrange { +impl Basis for Lagrange { type Data = Self; } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Nodes([F; N]); +pub struct Nodes([F; N]); /// A polynomial in monomial basis -impl Polynomial { +impl Polynomial { // TODO check that the polynomial degree divides the field order pub fn new(coefficients: [F; N]) -> Self { Self { coefficients, basis: Monomial } } @@ -60,14 +60,14 @@ impl Polynomial { pub fn evaluate(&self, x: F) -> F { let mut result = F::zero(); for (i, c) in self.coefficients.into_iter().enumerate() { - result += c * x.exp_u64(i as u64); + result += c * x.pow(F::Storage::from(i as u32)); } result } } /// A polynomial in Lagrange basis -impl Polynomial, F> { +impl Polynomial, F> { // TODO check that the polynomial degree divides the field order pub fn new(coefficients: [F; N], nodes: [F; N]) -> Self { let mut weights = [F::one(); N]; @@ -145,73 +145,71 @@ impl Polynomial, F> { } mod test { use super::*; - use crate::field::PlutoField; + use crate::field::gf_101::GF101; #[test] fn evaluation() { // Coefficients of the polynomial 1 + 2x + 3x^2 - let a = PlutoField::new(1); - let b = PlutoField::new(2); - let c = PlutoField::new(3); - let polynomial = Polynomial::<3, Monomial, PlutoField>::new([a, b, c]); - let y = polynomial.evaluate(PlutoField::new(2)); - let r = PlutoField::new(17); + let a = GF101::new(1); + let b = GF101::new(2); + let c = GF101::new(3); + let polynomial = Polynomial::<3, Monomial, GF101>::new([a, b, c]); + let y = polynomial.evaluate(GF101::new(2)); + let r = GF101::new(17); assert_eq!(y, r); // 1 + 2*(2) + 3(2)^2 = 17 } #[test] fn evaluation_with_zero() { // Coefficients of the polynomial 1 + 3x^2 - let a = PlutoField::new(1); - let b = PlutoField::new(0); - let c = PlutoField::new(3); - let polynomial = Polynomial::<3, Monomial, PlutoField>::new([a, b, c]); - let y = polynomial.evaluate(PlutoField::new(0)); - let r = PlutoField::new(1); + let a = GF101::new(1); + let b = GF101::new(0); + let c = GF101::new(3); + let polynomial = Polynomial::<3, Monomial, GF101>::new([a, b, c]); + let y = polynomial.evaluate(GF101::new(0)); + let r = GF101::new(1); assert_eq!(y, r); // 1 + 3(0)^2 = 1 } #[test] fn lagrange_evaluation() { // Coefficients of the polynomial 1 + 2x + 3x^2 - let a = PlutoField::new(1); - let b = PlutoField::new(2); - let c = PlutoField::new(3); - let polynomial = Polynomial::<3, Monomial, PlutoField>::new([a, b, c]); + let a = GF101::new(1); + let b = GF101::new(2); + let c = GF101::new(3); + let polynomial = Polynomial::<3, Monomial, GF101>::new([a, b, c]); // Nodes for the Lagrange basis - let nodes = - Nodes::<3, PlutoField>([PlutoField::new(1), PlutoField::new(2), PlutoField::new(3)]); + let nodes = Nodes::<3, GF101>([GF101::new(1), GF101::new(2), GF101::new(3)]); let lagrange = polynomial.to_lagrange(nodes); - let r = lagrange.evaluate(PlutoField::new(2)); - assert_eq!(r, PlutoField::new(17)); + let r = lagrange.evaluate(GF101::new(2)); + assert_eq!(r, GF101::new(17)); } #[test] #[should_panic] fn non_unique_nodes() { // Coefficients of the polynomial 1 + 2x - let a = PlutoField::new(1); - let b = PlutoField::new(2); + let a = GF101::new(1); + let b = GF101::new(2); - let polynomial = Polynomial::<2, Monomial, PlutoField>::new([a, b]); + let polynomial = Polynomial::<2, Monomial, GF101>::new([a, b]); // This should panic because the nodes are not distinct - let nodes = Nodes::<2, PlutoField>([PlutoField::new(1), PlutoField::new(1)]); + let nodes = Nodes::<2, GF101>([GF101::new(1), GF101::new(1)]); let lagrange = polynomial.to_lagrange(nodes); } #[test] fn test_by_hand() { // Coefficients of the polynomial 1 + x + 2x^2 - let a = PlutoField::new(1); - let b = PlutoField::new(1); - let c = PlutoField::new(2); - let polynomial = Polynomial::<3, Monomial, PlutoField>::new([a, b, c]); + let a = GF101::new(1); + let b = GF101::new(1); + let c = GF101::new(2); + let polynomial = Polynomial::<3, Monomial, GF101>::new([a, b, c]); println!("monomial coefficients {:?}", polynomial.coefficients); // Nodes for the Lagrange basis - let nodes = - Nodes::<3, PlutoField>([PlutoField::new(1), PlutoField::new(3), PlutoField::new(2)]); + let nodes = Nodes::<3, GF101>([GF101::new(1), GF101::new(3), GF101::new(2)]); let lagrange = polynomial.to_lagrange(nodes); println!("lagrange coefficients {:?}", lagrange.coefficients);