diff --git a/math/field.sage b/math/field.sage index afea94da..33faa97e 100644 --- a/math/field.sage +++ b/math/field.sage @@ -66,15 +66,16 @@ print(omega_n, "^", n, " = ", omega_n ^ n) # extension field of degree 2 Ft. = F[] -# irreducible element: t^2-2 -P = Ft(t ^ 2 - 2) +# irreducible element: t^2 + 2 +P = Ft(t ^ 2 + 2) assert P.is_irreducible() +F = GF(101) +Ft. = F[] +P = Ft(t ^ 2 + 2) F_2 = GF(101 ^ 2, name="t", modulus=P) -print("extension field:", F_2, "of order:", F_2.order()) - -# Primitive element -f_2_primitive_element = F_2([2, 1]) +f_2_primitive_element = F_2.primitive_element() +assert f_2_primitive_element.multiplicative_order() == (101^2) -1 print("Primitive element of F_2:", f_2_primitive_element, f_2_primitive_element.multiplicative_order()) # 100th root of unity @@ -85,4 +86,20 @@ quotient = (F_2_order-1)//root_of_unity_order f_2_omega_n = f_2_primitive_element ^ quotient print("The", root_of_unity_order, "th root of unity of extension field is: ", f_2_omega_n) -###################################################################### \ No newline at end of file +###################################################################### + +# Define the field and elements for computation +F = GF(101) +Ft. = F[] + +# Define the polynomial for the extension field +P = Ft(t^2 + 2) +F_2 = GF(101^2, name="t", modulus=P) + +# Define the elements 50 and 60t in the extension field +numerator = F_2(50) +denominator = F_2(62*t) + +# Compute the division of 50 by 60t in the extension field +result = numerator / denominator +print("The division of 50 by 60t in the extension field is:", result) diff --git a/src/curves/g2_curve.rs b/src/curves/g2_curve.rs index b79f69d0..1cedd6de 100644 --- a/src/curves/g2_curve.rs +++ b/src/curves/g2_curve.rs @@ -4,8 +4,8 @@ use super::*; #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)] pub struct G2Curve {} // The Elliptic curve $y^2=x^3+3$, i.e. -// - a = 0 -// - b = 3 +// a = 0 +// b = 3 impl CurveParams for G2Curve { type FieldElement = Ext2; @@ -54,22 +54,44 @@ impl G2Curve { } } +#[cfg(test)] mod tests { - // use super::*; - // use crate::curves::AffinePoint; + use super::*; + use crate::curves::AffinePoint; - // #[test] - // fn on_curve() { - // let gen = G2Curve::on_curve(G2Curve::GENERATOR.0, G2Curve::GENERATOR.1); - // } + #[test] + fn point_doubling() { + let g = AffinePoint::::generator(); + let two_g = g.point_doubling(); - // #[test] - // fn doubling() { - // let g = AffinePoint::::generator(); - // println!("g: {:?}", g) + let expected_2g = AffinePoint::::new( + Ext2::::new(GF101::new(90), GF101::ZERO), + Ext2::::new(GF101::ZERO, GF101::new(82)), + ); + let expected_g = AffinePoint::::new( + Ext2::::new(GF101::new(36), GF101::ZERO), + Ext2::::new(GF101::ZERO, GF101::new(31)), + ); - // // want to asset that g = (36, 31*X) - // // right now this ^ fails to construct as it doesn't believe that the generator is a valid - // point on the curve // want to asset that 2g = (90 , 82*X) - // } + assert_eq!(two_g, expected_2g); + assert_eq!(g, expected_g); + } + + #[test] + fn scalar_multiplication_rhs() { + let g = AffinePoint::::generator(); + let two_g = g * 2; + let expected_2g = g.point_doubling(); + assert_eq!(two_g, expected_2g); + assert_eq!(-two_g, -expected_2g); + } + + #[test] + fn scalar_multiplication_lhs() { + let g = AffinePoint::::generator(); + let two_g = 2 * g; + let expected_2g = g.point_doubling(); + assert_eq!(two_g, expected_2g); + assert_eq!(-two_g, -expected_2g); + } } diff --git a/src/curves/mod.rs b/src/curves/mod.rs index f131b31f..8f3a56e6 100644 --- a/src/curves/mod.rs +++ b/src/curves/mod.rs @@ -11,7 +11,7 @@ pub struct Curve { /// say 2 is in GF101 pub trait CurveParams: 'static + Copy + Clone + fmt::Debug + Default + Eq + Ord { /// Integer field element type - type FieldElement: FiniteField + Neg; + type FieldElement: FiniteField + Neg + Mul; /// 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. @@ -40,7 +40,6 @@ pub enum AffinePoint { impl AffinePoint { pub fn new(x: C::FieldElement, y: C::FieldElement) -> Self { - println!("X: {:?}, Y: {:?}", x, y); // okay so this is breaking because the curve equation doesn't know how to plug in polynomials. // y = 31x -> y^2 = 52x^2 // x = 36 -> x^3 = 95 + 3 @@ -148,13 +147,14 @@ impl Add for AffinePoint { } } +// NOTE: Apparently there is a faster way to do this with twisted curve methods impl AffinePoint { pub fn point_doubling(self) -> AffinePoint { let (x, y) = match self { AffinePoint::XY(x, y) => (x, y), AffinePoint::Infty => panic!("Cannot double point at infinity"), }; - // m = 3x^2 / 26 + // m = (3x^2) / (2y) let m = (C::THREE * x * x) / (C::TWO * y); // 2P = (m^2 - 2x, m(3x - m^2)- y) @@ -165,7 +165,6 @@ impl AffinePoint { pub fn generator() -> Self { let (x, y) = C::GENERATOR; - println!("X: {:?}, Y: {:?}", x, y); AffinePoint::new(x, y) } } diff --git a/src/field/gf_101.rs b/src/field/gf_101.rs index e8f8fdad..d95fcc76 100644 --- a/src/field/gf_101.rs +++ b/src/field/gf_101.rs @@ -3,12 +3,6 @@ use rand::distributions::{Distribution, Standard}; 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; -// mask used in (mod R) operation for montgomery reduciton -const MONTY_MASK: u32 = (1 << MONTY_BITS) - 1; -// (-P^-1) mod 2^MONTY_BITS -const MONTY_MU: u32 = 19; #[derive(Copy, Clone, Default, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)] pub struct GF101 { @@ -16,13 +10,11 @@ pub struct GF101 { } impl fmt::Display for GF101 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", from_monty(self.value)) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.value) } } impl GF101 { - pub const fn new(value: u32) -> Self { Self { value: to_monty(value) } } + pub const fn new(value: u32) -> Self { Self { value: value % PLUTO_FIELD_PRIME } } } impl FiniteField for GF101 { @@ -31,6 +23,7 @@ impl FiniteField for GF101 { const NEG_ONE: Self = Self::new(Self::ORDER - 1); const ONE: Self = Self::new(1); const ORDER: Self::Storage = PLUTO_FIELD_PRIME; + const THREE: Self = Self::new(3); const TWO: Self = Self::new(2); const ZERO: Self = Self::new(0); @@ -79,7 +72,7 @@ impl Sub for GF101 { 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 }; + let corr = if over { Self::ORDER } else { 0 }; diff = diff.wrapping_add(corr); Self { value: diff } } @@ -92,7 +85,7 @@ impl SubAssign for GF101 { impl Mul for GF101 { type Output = Self; - fn mul(self, rhs: Self) -> Self { Self { value: from_monty(self.value * rhs.value) } } + fn mul(self, rhs: Self) -> Self { Self { value: (self.value * rhs.value) % Self::ORDER } } } impl MulAssign for GF101 { @@ -141,86 +134,16 @@ impl Distribution for Standard { } } -#[must_use] -#[inline] -/// Converts a number to montgomery form: \bar{x} := x * R mod N. -/// R is chosen such that gcd(R, N) = 1, usually nearest 2^k to N. -/// -/// Arithmetic in finite fields involves mod N operation which involves -/// division, a very costly operation as compared to other arithmetic operations. -/// But, division by 2^k only involves shifting right by `k` bits. Aim of montgomery -/// form is to make resulting number divisible by 2^k. -const fn to_monty(val: u32) -> u32 { - (((val as u64) << MONTY_BITS) % PLUTO_FIELD_PRIME as u64) as u32 +impl From for GF101 { + fn from(val: u32) -> Self { Self::new(val) } } -#[must_use] -#[inline] -/// Computes x*R^{-1} mod N. -/// Assumes: `x` is in montgomery form -/// -/// Add such a multiple `m` of `N` such that `x` is divisible by `R` implies (x + mN) % R^{-1} = 0. -/// So, m = x*(-N)^{-1} % R satisfies above relation. Precompute N' = (-N)^{-1} mod R. -/// - Precompute: N' = (-N)^{-1} -/// - m = x*N' mod R (1 mult) -/// - u = m*N (1 mult) -/// - t = x+u mod R -/// - t \in [0, 2P), subtract N if t >= N -/// -/// Montgomery arithmetic allows to perform modular multiplication in 3 mults, 2 mults per -/// reduction, and some bit shifts and masks since R is a power of 2, saving a costly division. -/// -/// # Examples -/// ```ignore -/// let N = 101; -/// let a = to_monty(10); -/// let b = to_monty(20); -/// let c = from_monty(a * b); -/// assert_eq!(from_monty(c), 99); -/// ``` -fn from_monty(x: u32) -> u32 { - let x = x as u64; - let m = x.wrapping_mul(MONTY_MU as u64) & (MONTY_MASK as u64); // x*N' % R - let u = m * (PLUTO_FIELD_PRIME as u64); // m*P - let t = ((x + u) >> MONTY_BITS) as u32; // x+mP / R - let corr = if t >= PLUTO_FIELD_PRIME { PLUTO_FIELD_PRIME } else { 0 }; - t.wrapping_sub(corr) +impl From for GF101 { + fn from(val: u64) -> Self { Self::new(val as u32) } } -// β=2^7 -// I=β^2/N -const _INV_APPROX: u32 = (1 << (2 * MONTY_BITS)) / PLUTO_FIELD_PRIME; - -#[must_use] -#[inline] -/// Adapted from [Algorithm 1](https://hackmd.io/@chaosma/SyAvcYFxh). -/// -/// Computes x mod N using barret reduction method. Assumes x < N^2. -/// Modular reduction involves division by N, barret's method approximates -/// value of 1/N with m/2^k so that costly division operation is substituted with -/// bit shifts. 2^2k is used because x < n*n < 2^k * 2^k. This allows to approximate -/// value of q closer to x/n. -/// -/// x = q*N + r => r = x-qN => q = x/N => approximate q as ⌊m/2^2k⌋ => approximate m as ⌊2^2k/N⌋ -/// -/// - Precompute: I = ⌊2^{2k}/N⌋. floor is used as approximation function, implicitly used in -/// division -/// - q = x*I / 2^2k. divide by 2^{2k} again to approximate a value closer to 1/N -/// - r = x-qN -/// - r \in [0, 2P), subtract N if t >= N -/// -/// # Examples -/// ```ignore -/// let x = 200 * 10; -/// let res = barret_reduction(x); -/// assert_eq!(res, x % PLUTO_FIELD_PRIME); -/// ``` -fn _barret_reduction(x: u32) -> u32 { - assert!(x < (PLUTO_FIELD_PRIME.pow(2))); - let q = (x * _INV_APPROX) >> (2 * MONTY_BITS); // q = ⌊x*I/β^2⌋ - let r = x - (q * PLUTO_FIELD_PRIME); // t = x - q*N - let corr = if r >= PLUTO_FIELD_PRIME { PLUTO_FIELD_PRIME } else { 0 }; - r.wrapping_sub(corr) +impl From for GF101 { + fn from(val: usize) -> Self { Self::new(val as u32) } } #[cfg(test)] @@ -257,13 +180,6 @@ mod tests { assert_eq!(c, GF101::new(99)); } - #[test] - fn test_barret_reduction() { - let x = 200 * 10; - let res = _barret_reduction(x); - assert_eq!(res, x % PLUTO_FIELD_PRIME); - } - #[test] fn zero() { let f = GF101::new(0); @@ -401,9 +317,9 @@ mod tests { let omega = GF101::primitive_root_of_unity(n); println!("omega: {:?}", omega); assert_eq!(omega, GF101::new(95)); - let omega_n = omega.pow(n); + let omega_n = omega.pow(n.into()); for i in 1..n { - let omega_i = omega.pow(i); + let omega_i = omega.pow(i.into()); println!("omega^{}: {:?}", i, omega_i); assert_ne!(omega_i, GF101::new(1)); } @@ -414,11 +330,11 @@ mod tests { println!("omega: {:?}", omega); assert_eq!(omega, GF101::new(16)); for i in 1..n { - let omega_i = omega.pow(i); + let omega_i = omega.pow(i.into()); println!("omega^{}: {:?}", i, omega_i); assert_ne!(omega_i, GF101::new(1)); } - let omega_n = omega.pow(n); + let omega_n = omega.pow(n.into()); assert_eq!(omega_n, GF101::new(1)); } diff --git a/src/field/gf_101_2.rs b/src/field/gf_101_2.rs index f19f0afd..7baab5b5 100644 --- a/src/field/gf_101_2.rs +++ b/src/field/gf_101_2.rs @@ -21,7 +21,7 @@ impl Ext2 { /// irreducible polynomial used to reduce field polynomials to second degree: /// F[X]/(X^2-2) - fn irreducible() -> F { F::from_canonical_u32(2) } + fn irreducible() -> F { -F::from_canonical_u32(2) } } impl ExtensionField for Ext2 { @@ -42,6 +42,7 @@ impl FiniteField for Ext2 { const NEG_ONE: Self = Self::new(F::NEG_ONE, F::ZERO); const ONE: Self = Self::new(F::ONE, F::ZERO); const ORDER: Self::Storage = QUADRATIC_EXTENSION_FIELD_ORDER; + const THREE: Self = Self::new(F::THREE, F::ZERO); const TWO: Self = Self::new(F::TWO, F::ZERO); const ZERO: Self = Self::new(F::ZERO, F::ZERO); @@ -54,7 +55,7 @@ impl FiniteField for Ext2 { // f_2_primitive_element = F_2([2, 1]) // assert f_2_primitive_element.multiplicative_order() == 101^2-1 // ``` - fn generator() -> Self { Self { value: [F::from_canonical_u32(2), F::from_canonical_u32(1)] } } + fn generator() -> Self { Self { value: [F::from_canonical_u32(14), F::from_canonical_u32(9)] } } /// Computes the multiplicative inverse of `a`, i.e. 1 / (a0 + a1 * t). /// Multiply by `a0 - a1 * t` in numerator and denominator. @@ -208,9 +209,18 @@ impl Rem for Ext2 { fn rem(self, rhs: Self) -> Self::Output { self - (self / rhs) * rhs } } +impl From for Ext2 { + fn from(val: u32) -> Self { Self::new(GF101::from(val), GF101::ZERO) } +} + +impl From for Ext2 { + fn from(val: u64) -> Self { Self::new(GF101::from(val), GF101::ZERO) } +} + #[cfg(test)] mod tests { use super::*; + use crate::curves::{g2_curve::G2Curve, AffinePoint}; #[test] fn test_field() { @@ -235,6 +245,10 @@ mod tests { let a = >::new(GF101::new(10), GF101::new(20)); let b = >::new(GF101::new(20), GF101::new(10)); assert_eq!(a + b, >::new(GF101::new(30), GF101::new(30))); + + let c = >::new(GF101::new(70), GF101::new(80)); + let d = >::new(GF101::new(80), GF101::new(70)); + assert_eq!(c + d, >::new(GF101::new(49), GF101::new(49))); } #[test] @@ -248,7 +262,7 @@ mod tests { fn test_mul() { let a = >::new(GF101::new(10), GF101::new(20)); let b = >::new(GF101::new(20), GF101::new(10)); - assert_eq!(a * b, >::new(GF101::new(95), GF101::new(96))); + assert_eq!(a * b, >::new(GF101::new(2), GF101::new(96))); } #[test] @@ -275,9 +289,9 @@ mod tests { let mut rng = rand::thread_rng(); let x = >::from_base(rng.gen::()); - assert_eq!(x, x.pow( as FiniteField>::Storage::from(1_u32))); + assert_eq!(x, x.pow(1)); - let res = x.pow( as FiniteField>::Storage::from(4_u32)); + let res = x.pow(4); assert_eq!(res, x.square().square()); } @@ -339,14 +353,36 @@ mod tests { assert_eq!(mul1 * inv_mul, res); } - // TODO: THIS TEST IS WRONG AND SHOULD BE REWRITTEN #[test] fn test_generator_order() { let generator = >::generator(); - let mut x = >::ONE; - for _ in 1..>::ORDER { - x *= generator; + + let mut val = generator; + for _ in 0..>::ORDER - 1 { + val *= generator; } - assert_eq!(x, >::ONE); + assert_eq!(val, generator); + } + + #[test] + fn test_point_doubling() { + let g = AffinePoint::::generator(); + + let (x, y) = match g { + AffinePoint::XY(x, y) => (x, y), + AffinePoint::Infty => panic!("Cannot double point at infinity"), + }; + + // m = (3x^2) / (2y) + let m = (>::THREE * x.square()) / (>::TWO * y); + + // 2P = (m^2 - 2x, m(3x - m^2) - y) + let x_new = m.square() - >::TWO * x; + let y_new = m * (>::THREE * x - m.square()) - y; + + let point_double: AffinePoint = AffinePoint::new(x_new, y_new); + + // Check if the doubled point satisfies the curve equation + assert_eq!(point_double, g.point_doubling()); } } diff --git a/src/field/mod.rs b/src/field/mod.rs index d6ef08ad..c354e1be 100644 --- a/src/field/mod.rs +++ b/src/field/mod.rs @@ -46,6 +46,7 @@ pub trait FiniteField: const ONE: Self; const TWO: Self; const NEG_ONE: Self; + const THREE: Self; fn inverse(&self) -> Option; fn from_canonical_u32(n: u32) -> Self; @@ -53,17 +54,18 @@ pub trait FiniteField: fn double(&self) -> Self { *self + *self } fn square(&self) -> Self { *self * *self } - fn pow(&self, power: Self::Storage) -> Self { + fn pow(&self, mut power: u64) -> Self { let mut current = *self; - let power: u64 = power.into(); let mut product = Self::ONE; - for j in 0..(64 - power.leading_zeros()) as usize { - if (power >> j & 1) != 0 { + while power > 0 { + if power % 2 == 1 { product *= current; } current = current * current; + power /= 2; } + product } @@ -80,7 +82,7 @@ pub trait FiniteField: 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) + Self::generator().pow(pow.into()) } } diff --git a/src/lib.rs b/src/lib.rs index f849ed65..e53ff4df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ pub mod curves; pub mod field; pub mod kzg; pub mod polynomial; +pub mod setup; use core::{ fmt::{self, Display, Formatter}, diff --git a/src/polynomial/mod.rs b/src/polynomial/mod.rs index f8394d67..bf3d915e 100644 --- a/src/polynomial/mod.rs +++ b/src/polynomial/mod.rs @@ -105,7 +105,7 @@ impl Polynomial { pub fn evaluate(&self, x: F) -> F { let mut result = F::ZERO; for (i, c) in self.coefficients.iter().enumerate() { - result += *c * x.pow(F::Storage::from(i as u32)); + result += *c * x.pow(i as u64); } result } @@ -191,7 +191,7 @@ impl Polynomial { (0..n) .map(|i| { self.coefficients.iter().enumerate().fold(F::ZERO, |acc, (j, &coeff)| { - acc + coeff * primitive_root_of_unity.pow(F::Storage::from(i as u32 * j as u32)) + acc + coeff * primitive_root_of_unity.pow(i as u64 * j as u64) }) }) .collect(), @@ -239,7 +239,7 @@ impl Polynomial, F> { F::Storage::from(0) ); let primitive_root = F::primitive_root_of_unity(F::Storage::from(n as u32)); - let nodes: Vec = (0..n).map(|i| primitive_root.pow(F::Storage::from(i as u32))).collect(); + let nodes: Vec = (0..n).map(|i| primitive_root.pow(i as u64)).collect(); Self { coefficients, basis: Lagrange { nodes } } } @@ -293,40 +293,6 @@ impl Polynomial, F> { }, ) } - - /// TODO: Implement this function if need be. - pub fn to_monomial(&self) -> Polynomial { - // This is the inverse of the conversion from monomial to Lagrange basis - // This uses something called the Vandermonde matrix which is defined as: - // - // / 1 | x_0 | x_0^2 | x_0^3 | ... | x_0^(N-1) \ - // | 1 | x_1 | x_1^2 | x_1^3 | ... | x_1^(N-1) | - // | 1 | x_2 | x_2^2 | x_2^3 | ... | x_2^(N-1) | - // v = | . | . | . | . | ... | . | - // | . | . | . | . | ... | . | - // | . | . | . | . | ... | . | - // \ 1 | x_N | x_N^2 | x_N^3 | ... | x_N^(N-1) / - // - // where x_i are the nodes of the Lagrange basis - // - // Then the monomial basis m is given V^T * l = m, where l is the Lagrange basis - // because we know the monomial basis we need to compute to monomial coefficients a_m = V^{-1} * - // a_l where a_l are the coefficients of the Lagrange basis - - // It also is the case that the the columns of the inverse matrix are the coefficients of the - // Lagrange polynomial basis TODO Finish this. - // let nodes = self.basis.nodes; - // let mut evaluations = [F::ZERO; N]; - - // // Evaluate the polynomial at N distinct points - // for i in 0..N { - // let x = F::primitive_root().exp_u64(i as u64); - // evaluations[i] = self.evaluate(x); - // } - - // Polynomial::::new(evaluations) - todo!("Finish this after we get the roots of unity from other PRs") - } } impl Display for Polynomial, GF101> { diff --git a/src/setup.rs b/src/setup.rs new file mode 100644 index 00000000..a85d41c1 --- /dev/null +++ b/src/setup.rs @@ -0,0 +1,69 @@ +#![allow(unused_imports)] +use super::*; +use crate::curves::{g1_curve::C101, g2_curve::G2Curve, AffinePoint}; + +// hardcoded degree for now +#[allow(dead_code)] +fn setup() -> (Vec>, Vec>) { + // NOTE: For demonstration purposes only. + + // This is just tau from plonk by hand, it is not actually secure + let tau: u32 = 2; + + // NOTE: Just sample the d of both for now. + // - g1 and g2 SRS have variable sizes for diff kzg uses + // - in eth blobs, g1 is 4096 elements, g2 is 16 elements + // - in plonk, we need d+5 g1 elements and one g2 element + let mut srs_g1_points: Vec> = vec![]; + let mut srs_g2_points: Vec> = vec![]; + for i in 0..7 { + // G1 Group + + // degree seven commitment poly + let result = AffinePoint::::generator() * tau.pow(i); + srs_g1_points.push(result); + // G2 Group + + // degree two divisor poly + if i < 2 { + let result = AffinePoint::::generator() * tau.pow(i); + srs_g2_points.push(result); + } + } + + (srs_g1_points, srs_g2_points) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::field::gf_101_2::Ext2; + + #[test] + fn test_setup() { + let (g1srs, g2srs) = setup(); + assert!(g1srs.len() == 7); + assert!(g2srs.len() == 2); + let expected_g1srs = vec![ + AffinePoint::::new(GF101::new(1), GF101::new(2)), + AffinePoint::::new(GF101::new(68), GF101::new(74)), + AffinePoint::::new(GF101::new(65), GF101::new(98)), + AffinePoint::::new(GF101::new(18), GF101::new(49)), + AffinePoint::::new(GF101::new(1), GF101::new(99)), + AffinePoint::::new(GF101::new(68), GF101::new(27)), + AffinePoint::::new(GF101::new(65), GF101::new(3)), + ]; + assert_eq!(g1srs, expected_g1srs); + + println!("g2srs {:?}", g2srs); + let expected_2g = AffinePoint::::new( + Ext2::::new(GF101::new(90), GF101::ZERO), + Ext2::::new(GF101::ZERO, GF101::new(82)), + ); + + let g2_gen = AffinePoint::::generator(); + let expected_g2srs = vec![g2_gen, expected_2g]; + + assert_eq!(g2srs, expected_g2srs); + } +}