Abstract away field operations into `Field` trait

This commit is contained in:
Sean Bowe 2016-07-01 13:50:55 -06:00
parent 391fa61173
commit 4b32ed6585
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
7 changed files with 495 additions and 376 deletions

187
src/fields/fp.rs Normal file
View File

@ -0,0 +1,187 @@
use rand::Rng;
use num::{BigUint, Num};
use std::ops::{Mul,Add,Sub,Neg};
use std::cmp::{PartialEq, Eq};
use std::convert::From;
use std::fmt;
use std::marker::PhantomData;
use super::Field;
pub trait PrimeFieldParams {
fn modulus() -> BigUint;
fn bits() -> usize;
fn name() -> &'static str;
}
pub struct Fp<P: PrimeFieldParams> {
value: BigUint,
_marker: PhantomData<P>
}
impl<P: PrimeFieldParams> fmt::Debug for Fp<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({})", P::name(), self.value)
}
}
impl<P: PrimeFieldParams> Field for Fp<P> {
fn zero() -> Self {
use num::Zero;
Fp {
value: BigUint::zero(),
_marker: PhantomData
}
}
fn one() -> Self {
use num::One;
Fp {
value: BigUint::one(),
_marker: PhantomData
}
}
fn random<R: Rng>(rng: &mut R) -> Self {
use num::num_bigint::RandBigInt;
use num::Zero;
Fp {
value: rng.gen_biguint_range(&BigUint::zero(), &P::modulus()),
_marker: PhantomData
}
}
fn is_zero(&self) -> bool {
use num::Zero;
self.value == BigUint::zero()
}
fn inverse(&self) -> Self {
if self.is_zero() {
// TODO: this should likely bleed through the abstraction layers
panic!("cannot get the multiplicative inverse of zero")
} else {
let mut res = Self::one();
let mut found_one = false;
let exp = Self::zero() - Self::one() - Self::one();
for i in (0..P::bits()).rev() {
if found_one {
res = res.squared();
}
if exp.test_bit(i) {
found_one = true;
res = self * &res;
}
}
res
}
}
fn pow<P2: PrimeFieldParams>(&self, exp: &Fp<P2>) -> Self {
let mut res = Self::one();
let mut found_one = false;
for i in (0..P2::bits()).rev() {
if found_one {
res = res.squared();
}
if exp.test_bit(i) {
found_one = true;
res = res * self;
}
}
res
}
fn neg(&self) -> Self {
use num::Zero;
Fp {
value: if self.value.is_zero() {
self.value.clone()
} else {
P::modulus() - &self.value
},
_marker: PhantomData
}
}
fn mul(&self, other: &Self) -> Self {
Fp {
value: (&self.value * &other.value) % &P::modulus(),
_marker: PhantomData
}
}
fn sub(&self, other: &Self) -> Self {
if other.value > self.value {
Fp {
value: (&self.value + P::modulus()) - &other.value,
_marker: PhantomData
}
} else {
Fp {
value: &self.value - &other.value,
_marker: PhantomData
}
}
}
fn add(&self, other: &Self) -> Self {
let tmp = &self.value + &other.value;
if tmp >= P::modulus() {
Fp {
value: tmp - P::modulus(),
_marker: PhantomData
}
} else {
Fp {
value: tmp,
_marker: PhantomData
}
}
}
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<P: PrimeFieldParams> Fp<P> {
pub fn test_bit(&self, bit: usize) -> bool {
// TODO: This is a naive approach.
use num::{One, Zero};
let mut b = BigUint::one();
let two = &b + &b;
for _ in 0..bit {
b = &b + &b;
}
(&self.value / b) % two != BigUint::zero()
}
}
impl<'a, P: PrimeFieldParams> From<&'a str> for Fp<P> {
fn from(s: &'a str) -> Self {
Fp {
value: BigUint::from_str_radix(s, 10).unwrap() % P::modulus(),
_marker: PhantomData
}
}
}
impl<P: PrimeFieldParams> Clone for Fp<P> {
fn clone(&self) -> Self { unimplemented!() }
}
forward_ops_to_field_ops!(impl(P: PrimeFieldParams) Fp<P>);

28
src/fields/mod.rs Normal file
View File

@ -0,0 +1,28 @@
pub mod fp;
#[cfg(test)]
pub mod tests;
use rand::Rng;
use self::fp::{Fp, PrimeFieldParams};
pub trait Field: Sized + Clone {
fn zero() -> Self;
fn one() -> Self;
fn random<R: Rng>(rng: &mut R) -> Self;
fn is_zero(&self) -> bool;
fn inverse(&self) -> Self;
fn squared(&self) -> Self {
self.mul(self)
}
fn pow<P: PrimeFieldParams>(&self, exp: &Fp<P>) -> Self;
fn eq(&self, other: &Self) -> bool;
fn ne(&self, other: &Self) -> bool {
!self.eq(other)
}
fn neg(&self) -> Self;
fn mul(&self, other: &Self) -> Self;
fn sub(&self, other: &Self) -> Self;
fn add(&self, other: &Self) -> Self;
}

203
src/fields/tests.rs Normal file
View File

@ -0,0 +1,203 @@
use rand::{Rng,SeedableRng,StdRng};
use fields::Field;
mod large_field {
use fields::fp::*;
use num::{BigUint, Num};
struct Large;
impl PrimeFieldParams for Large {
fn modulus() -> BigUint {
BigUint::from_str_radix("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10).unwrap()
}
fn bits() -> usize { 254 }
fn name() -> &'static str { "Large" }
}
type Ft = Fp<Large>;
#[test]
fn bit_testing() {
let a = Ft::from("13");
assert!(a.test_bit(0) == true);
assert!(a.test_bit(1) == false);
assert!(a.test_bit(2) == true);
assert!(a.test_bit(3) == true);
let expected: Vec<bool> = [1,1,0,1,1,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1]
.iter().map(|a| *a == 1).rev().collect();
let a = Ft::from("453624211");
for (i, b) in expected.into_iter().enumerate() {
assert!(a.test_bit(i) == b);
}
let expected: Vec<bool> = [1,1,1,1,0,1,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,1,1,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,0,0,1,0,1,1,0,0]
.iter().map(|a| *a == 1).rev().collect();
let a = Ft::from("13888242871869275222244405745257275088696211157297823662689037894645226208556");
for (i, b) in expected.into_iter().enumerate() {
assert!(a.test_bit(i) == b);
}
}
}
mod small_field {
use fields::fp::*;
use fields::Field;
use num::{BigUint, Num};
struct Small;
impl PrimeFieldParams for Small {
fn modulus() -> BigUint {
BigUint::from_str_radix("13", 10).unwrap()
}
fn bits() -> usize { 6 }
fn name() -> &'static str { "Small" }
}
type Ft = Fp<Small>;
#[test]
fn field_ops() {
fn test_field_operation<C: Fn(&Ft, &Ft) -> Ft>(a: u64, b: u64, f: C, expected: u64) {
let af = Ft::from(format!("{}", a).as_ref());
let bf = Ft::from(format!("{}", b).as_ref());
let expectedf = Ft::from(format!("{}", expected).as_ref());
let res = f(&af, &bf);
if res != expectedf {
panic!("res={:?} != expectedf={:?} (a={}, b={}, expected={})", res, expectedf, a, b, expected);
}
}
const MODULO: u64 = 13;
for a in 0..13u64 {
for b in 0..13u64 {
test_field_operation(a, b, |a,b| {a * b}, (a*b)%MODULO);
test_field_operation(a, b, |a,b| {a + b}, (a+b)%MODULO);
test_field_operation(a, b, |a,b| {a - b}, {
let mut tmp = (a as i64) - (b as i64);
if tmp < 0 {
tmp += MODULO as i64;
}
tmp as u64
});
test_field_operation(a, b, |a,b| {a.pow(b)}, (a.pow(b as u32))%MODULO);
}
test_field_operation(a, 0, |a,_| {-a}, if a == 0 { 0 } else { MODULO - a });
if a > 0 {
test_field_operation(a, 0, |a,_| {&a.inverse() * a}, 1);
}
}
}
}
fn can_invert<F: Field>() {
let mut a = F::one();
for _ in 0..1000 {
assert!(a.ne(&F::zero()));
let inv = a.inverse();
assert!(a.mul(&inv).eq(&F::one()));
a = a.add(&F::one());
}
}
fn rand_element_squaring<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..100 {
let a = F::random(rng);
let mul = a.mul(&a);
let sq = a.squared();
assert!(sq.eq(&mul));
}
let mut cur = F::zero();
for _ in 0..100 {
let mul = cur.mul(&cur);
let sq = cur.squared();
assert!(sq.eq(&mul));
cur = cur.add(&F::one());
}
}
fn rand_element_addition_and_negation<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..10 {
let mut a = F::random(rng);
let r = F::random(rng);
let mut b = a.add(&r);
for _ in 0..10 {
let r = F::random(rng);
a = a.add(&r);
b = b.add(&r);
let r = F::random(rng);
a = a.sub(&r);
b = b.sub(&r);
let r = F::random(rng);
a = a.add(&r);
b = b.add(&r);
}
b = b.sub(&r);
assert!(a.eq(&b));
}
}
fn rand_element_inverse<F: Field, R: Rng>(rng: &mut R) {
for _ in 0..100 {
let mut n = F::random(rng);
n = n.inverse().mul(&n);
assert!(n.eq(&F::one()));
}
for _ in 0..100 {
let a = F::random(rng);
let b = F::random(rng);
assert!(a.mul(&b).mul(&a.inverse()).eq(&b));
}
}
fn rand_element_multiplication<F: Field, R: Rng>(rng: &mut R) {
// If field is not associative under multiplication, 1/8 of all triplets a, b, c
// will fail the test (a*b)*c = a*(b*c).
for _ in 0..250 {
let a = F::random(rng);
let b = F::random(rng);
let c = F::random(rng);
assert!(a.mul(&b).mul(&c).eq(&b.mul(&c).mul(&a)));
}
}
pub fn field_trials<F: Field>() {
can_invert::<F>();
let seed: [usize; 4] = [103245, 191922, 1293, 192103];
let mut rng = StdRng::from_seed(&seed);
rand_element_squaring::<F, StdRng>(&mut rng);
rand_element_addition_and_negation::<F, StdRng>(&mut rng);
rand_element_multiplication::<F, StdRng>(&mut rng);
rand_element_inverse::<F, StdRng>(&mut rng);
}

374
src/fp.rs
View File

@ -1,374 +0,0 @@
use rand::Rng;
use num::{BigUint, Num};
use std::ops::{Mul,Add,Sub,Neg};
use std::cmp::{PartialEq, Eq};
use std::convert::From;
use std::fmt;
use std::marker::PhantomData;
pub trait PrimeFieldParams {
fn modulus() -> BigUint;
fn bits() -> usize;
fn name() -> &'static str;
}
pub struct Fp<P: PrimeFieldParams> {
value: BigUint,
_marker: PhantomData<P>
}
impl<P: PrimeFieldParams> fmt::Debug for Fp<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}({})", P::name(), self.value)
}
}
impl<P: PrimeFieldParams> Fp<P> {
pub fn zero() -> Self {
use num::Zero;
Fp {
value: BigUint::zero(),
_marker: PhantomData
}
}
pub fn one() -> Self {
use num::One;
Fp {
value: BigUint::one(),
_marker: PhantomData
}
}
pub fn random<R: Rng>(rng: &mut R) -> Self {
use num::num_bigint::RandBigInt;
use num::Zero;
Fp {
value: rng.gen_biguint_range(&BigUint::zero(), &P::modulus()),
_marker: PhantomData
}
}
pub fn is_zero(&self) -> bool {
use num::Zero;
self.value == BigUint::zero()
}
pub fn inverse(&self) -> Self {
if self.is_zero() {
// TODO: this should likely bleed through the abstraction layers
panic!("cannot get the multiplicative inverse of zero")
} else {
let mut res = Self::one();
let mut found_one = false;
let exp = Self::zero() - Self::one() - Self::one();
for i in (0..P::bits()).rev() {
if found_one {
res = res.squared();
}
if exp.test_bit(i) {
found_one = true;
res = self * &res;
}
}
res
}
}
pub fn squared(&self) -> Self {
self * self
}
pub fn pow<P2: PrimeFieldParams>(&self, exp: &Fp<P2>) -> Self {
let mut res = Self::one();
let mut found_one = false;
for i in (0..P2::bits()).rev() {
if found_one {
res = res.squared();
}
if exp.test_bit(i) {
found_one = true;
res = res * self;
}
}
res
}
pub fn test_bit(&self, bit: usize) -> bool {
// TODO: This is a naive approach.
use num::{One, Zero};
let mut b = BigUint::one();
let two = &b + &b;
for _ in 0..bit {
b = &b + &b;
}
(&self.value / b) % two != BigUint::zero()
}
}
impl<'a, P: PrimeFieldParams> From<&'a str> for Fp<P> {
fn from(s: &'a str) -> Self {
Fp {
value: BigUint::from_str_radix(s, 10).unwrap() % P::modulus(),
_marker: PhantomData
}
}
}
impl<P: PrimeFieldParams> Clone for Fp<P> {
fn clone(&self) -> Self { unimplemented!() }
}
impl<'a, 'b, P: PrimeFieldParams> Add<&'b Fp<P>> for &'a Fp<P> {
type Output = Fp<P>;
fn add(self, other: &Fp<P>) -> Fp<P> {
let tmp = &self.value + &other.value;
if tmp >= P::modulus() {
Fp {
value: tmp - P::modulus(),
_marker: PhantomData
}
} else {
Fp {
value: tmp,
_marker: PhantomData
}
}
}
}
impl<'a, 'b, P: PrimeFieldParams> Sub<&'b Fp<P>> for &'a Fp<P> {
type Output = Fp<P>;
fn sub(self, other: &Fp<P>) -> Fp<P> {
if other.value > self.value {
Fp {
value: (&self.value + P::modulus()) - &other.value,
_marker: PhantomData
}
} else {
Fp {
value: &self.value - &other.value,
_marker: PhantomData
}
}
}
}
impl<'a, 'b, P: PrimeFieldParams> Mul<&'b Fp<P>> for &'a Fp<P> {
type Output = Fp<P>;
fn mul(self, other: &Fp<P>) -> Fp<P> {
Fp {
value: (&self.value * &other.value) % &P::modulus(),
_marker: PhantomData
}
}
}
impl<'a, P: PrimeFieldParams> Neg for &'a Fp<P> {
type Output = Fp<P>;
fn neg(self) -> Fp<P> {
use num::Zero;
Fp {
value: if self.value.is_zero() {
self.value.clone()
} else {
P::modulus() - &self.value
},
_marker: PhantomData
}
}
}
impl<P: PrimeFieldParams> Neg for Fp<P> {
type Output = Fp<P>;
fn neg(self) -> Fp<P> {
-(&self)
}
}
impl<P: PrimeFieldParams> PartialEq for Fp<P> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<P: PrimeFieldParams> Eq for Fp<P> {}
forward_all_binop_to_ref_ref!(impl(P: PrimeFieldParams) Mul for Fp<P>, mul);
#[cfg(test)]
mod large_field_tests {
use super::*;
use rand::{Rng,SeedableRng,StdRng};
use num::{BigUint, Num};
struct Small;
impl PrimeFieldParams for Small {
fn modulus() -> BigUint {
BigUint::from_str_radix("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10).unwrap()
}
fn bits() -> usize { 254 }
fn name() -> &'static str { "Small" }
}
type Ft = Fp<Small>;
#[test]
fn rand_element_squaring() {
let seed: [usize; 4] = [0, 0, 0, 0];
let rng = &mut StdRng::from_seed(&seed);
for _ in 0..100 {
let a = Ft::random(rng);
let mul = &a * &a;
let sq = a.squared();
assert!(sq == mul);
}
let mut cur = Ft::zero();
for _ in 0..100 {
let mul = &cur * &cur;
let sq = cur.squared();
assert!(sq == mul);
cur = &cur + &Ft::one();
}
}
#[test]
fn rand_element_multiplication() {
// If field is not associative under multiplication, 1/8 of all triplets a, b, c
// will fail the test (a*b)*c = a*(b*c).
let seed: [usize; 4] = [0, 0, 0, 0];
let rng = &mut StdRng::from_seed(&seed);
for _ in 0..250 {
let a = &Ft::random(rng);
let b = &Ft::random(rng);
let c = &Ft::random(rng);
assert!((a * b) * c == (b * c) * a);
}
}
#[test]
fn rand_element_inverse() {
let seed: [usize; 4] = [0, 0, 0, 0];
let rng = &mut StdRng::from_seed(&seed);
for _ in 0..100 {
let mut n = Ft::random(rng);
n = n.inverse() * n;
assert_eq!(n, Ft::one());
}
for _ in 0..100 {
let a = Ft::random(rng);
let b = Ft::random(rng);
assert_eq!(&a * &b * (a.inverse()), b);
}
}
#[test]
fn bit_testing() {
let a = Ft::from("13");
assert!(a.test_bit(0) == true);
assert!(a.test_bit(1) == false);
assert!(a.test_bit(2) == true);
assert!(a.test_bit(3) == true);
let expected: Vec<bool> = [1,1,0,1,1,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1]
.iter().map(|a| *a == 1).rev().collect();
let a = Ft::from("453624211");
for (i, b) in expected.into_iter().enumerate() {
assert!(a.test_bit(i) == b);
}
let expected: Vec<bool> = [1,1,1,1,0,1,0,1,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,0,0,1,1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,1,0,1,1,0,1,0,1,1,0,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,0,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,0,0,1,0,1,1,0,0]
.iter().map(|a| *a == 1).rev().collect();
let a = Ft::from("13888242871869275222244405745257275088696211157297823662689037894645226208556");
for (i, b) in expected.into_iter().enumerate() {
assert!(a.test_bit(i) == b);
}
}
}
#[cfg(test)]
mod small_field_tests {
use super::*;
use num::{BigUint, Num};
struct Small;
impl PrimeFieldParams for Small {
fn modulus() -> BigUint {
BigUint::from_str_radix("13", 10).unwrap()
}
fn bits() -> usize { 6 }
fn name() -> &'static str { "Small" }
}
type Ft = Fp<Small>;
#[test]
fn field_ops() {
fn test_field_operation<C: Fn(&Ft, &Ft) -> Ft>(a: u64, b: u64, f: C, expected: u64) {
let af = Ft::from(format!("{}", a).as_ref());
let bf = Ft::from(format!("{}", b).as_ref());
let expectedf = Ft::from(format!("{}", expected).as_ref());
let res = f(&af, &bf);
if res != expectedf {
panic!("res={:?} != expectedf={:?} (a={}, b={}, expected={})", res, expectedf, a, b, expected);
}
}
const MODULO: u64 = 13;
for a in 0..13u64 {
for b in 0..13u64 {
test_field_operation(a, b, |a,b| {a * b}, (a*b)%MODULO);
test_field_operation(a, b, |a,b| {a + b}, (a+b)%MODULO);
test_field_operation(a, b, |a,b| {a - b}, {
let mut tmp = (a as i64) - (b as i64);
if tmp < 0 {
tmp += MODULO as i64;
}
tmp as u64
});
test_field_operation(a, b, |a,b| {a.pow(b)}, (a.pow(b as u32))%MODULO);
}
test_field_operation(a, 0, |a,_| {-a}, if a == 0 { 0 } else { MODULO - a });
if a > 0 {
test_field_operation(a, 0, |a,_| {&a.inverse() * a}, 1);
}
}
}
}

View File

@ -3,5 +3,5 @@ extern crate rand;
#[macro_use]
mod macros;
mod fp;
mod fields;
mod params;

View File

@ -44,3 +44,64 @@ macro_rules! forward_all_binop_to_ref_ref {
forward_val_ref_binop!(impl($($t: $p),*) $imp for $res, $method);
};
}
macro_rules! forward_ops_to_field_ops {
(impl($($t:ident: $p:ident),*) $res:ty) => {
impl<'a, 'b, $($t: $p),*> Add<&'a $res> for &'b $res {
type Output = $res;
#[inline]
fn add(self, other: &'a $res) -> $res {
Field::add(self, other)
}
}
impl<'a, 'b, $($t: $p),*> Sub<&'a $res> for &'b $res {
type Output = $res;
#[inline]
fn sub(self, other: &'a $res) -> $res {
Field::sub(self, other)
}
}
impl<'a, 'b, $($t: $p),*> Mul<&'a $res> for &'b $res {
type Output = $res;
#[inline]
fn mul(self, other: &'a $res) -> $res {
Field::mul(self, other)
}
}
impl<'a, $($t: $p),*> Neg for &'a $res {
type Output = $res;
#[inline]
fn neg(self) -> $res {
Field::neg(self)
}
}
impl<$($t: $p),*> Neg for $res {
type Output = $res;
#[inline]
fn neg(self) -> $res {
Field::neg(&self)
}
}
impl<$($t: $p),*> PartialEq for $res {
fn eq(&self, other: &Self) -> bool {
Field::eq(self, other)
}
}
impl<$($t: $p),*> Eq for $res {}
forward_all_binop_to_ref_ref!(impl($($t: $p),*) Add for $res, add);
forward_all_binop_to_ref_ref!(impl($($t: $p),*) Sub for $res, sub);
forward_all_binop_to_ref_ref!(impl($($t: $p),*) Mul for $res, mul);
}
}

View File

@ -1,5 +1,5 @@
use num::{Num,BigUint};
use fp::PrimeFieldParams;
use fields::fp::PrimeFieldParams;
pub struct FrParams;
@ -20,3 +20,17 @@ impl PrimeFieldParams for FqParams {
fn bits() -> usize { 254 }
fn name() -> &'static str { "Fq" }
}
#[test]
fn test_fr() {
use fields;
fields::tests::field_trials::<fields::fp::Fp<FrParams>>();
}
#[test]
fn test_fq() {
use fields;
fields::tests::field_trials::<fields::fp::Fp<FqParams>>();
}