diff --git a/Cargo.toml b/Cargo.toml index 0f0bf57..e2dda9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "primitives", "polynomial", "univariate-polynomial-iop-zerotest", "halo2-trials", diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml new file mode 100644 index 0000000..cc9a745 --- /dev/null +++ b/primitives/Cargo.toml @@ -0,0 +1,6 @@ +[package] +edition = "2021" +name = "primitives" +version = "0.1.0" + +[dependencies] diff --git a/primitives/src/finitefield.rs b/primitives/src/finitefield.rs new file mode 100644 index 0000000..b999eac --- /dev/null +++ b/primitives/src/finitefield.rs @@ -0,0 +1,90 @@ +//! This module containt the definition of the trait `FiniteField` + +use std::{ + fmt::Debug, hash::Hash, iter::{Product, Sum}, ops::{ + Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign + } +}; + +/// A field is a set of elements on which the following operations are defined: +/// - Addition +/// - Subtraction +/// - Multiplication +/// - Division +/// +/// A "finite field" is a field with "finite" number of elements + +pub trait FiniteField: + Debug + + From // assumed that all fields in use will be build-able from + // `usize` + + Default + + Sized + + Copy + + Clone + // One should be able to compare two elements for equality + + PartialEq + + Eq + // Addition operations + + AddAssign + + Add + + Sum + // Subtraction operations + + SubAssign + + Sub + // Multiplication operations + + MulAssign + + Mul + + Product + // Division operationss + + DivAssign + + Div + + Rem // if you can divide, you can rem + // Unary operations + + Neg // required for additive identity + // Hashability is required + + Hash + + 'static + + { + /// The order of the field is the number of elements in the field + const ORDER: usize; + + /// The additive identity element + const ZERO: Self; + + /// The multiplicative identity element + const ONE: Self; + + /// Multiplicative generator of the group + /// also called the primitive element + const MULTIPLICATIVE_GENERATOR: Self; + + // REQUIRED IMPLEMENTATIONS + + /// Gets the multiplicative inverse of a field element if it exists. + fn multiplicative_inverse(&self) -> Option; + + /// Computes the power of a field element + fn pow(self, pow: usize) -> Self; + + /// Returns the primitive n-th root of unity in the field. + /// All fields of prime order have + /// - An Additive group + /// - A multiplicative subgroup generated by generator `g` + /// + /// According to Sylow's theorem, for existence of a non-trivial + /// multiplicative subgroup of prime order `n`, `p-1` should be divisible + /// by `n`. (-1) because `0` cannot be part of the multiplicative + /// subgroup. + fn primitive_root_of_unity(n: usize) -> Self { + // Check for divisibility of (p-1) + let remainder = (Self::ORDER - 1) % n; + assert!(remainder == 0, "n must divide (field_order - 1)"); + + // Multiply the generator by how small the subgroup is + // with respect to (p-1) + Self::MULTIPLICATIVE_GENERATOR.pow((Self::ORDER - 1) / n) + } + +} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs new file mode 100644 index 0000000..415b205 --- /dev/null +++ b/primitives/src/lib.rs @@ -0,0 +1,2 @@ +pub mod finitefield; +pub mod primefield; diff --git a/primitives/src/primefield.rs b/primitives/src/primefield.rs new file mode 100644 index 0000000..4a32745 --- /dev/null +++ b/primitives/src/primefield.rs @@ -0,0 +1,148 @@ +//! This module contains the definition of struct `PrimeField` + +use std::fmt::{Display, Formatter}; + +use crate::finitefield::FiniteField; + +/// The `PrimeField` struct represents elements of a field of prime order. +/// This field is defined by a prime `P` and elements are integers mod `P`. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, PartialOrd)] +pub struct PrimeField { + pub(crate) value: usize, +} + +// ------ USEFUL COMPILE TIME FUNCTIONS ------ + +/// Useful utility function to check results at compile time. Checks whether +/// a number given is prime. `n` should be known at compile time. +const fn check_prime(n: usize) { + // Run-of-the-mill prime checking logic + let mut test_num = 2; + while test_num * test_num <= n { + if n % test_num == 0 { + panic!("not a prime"); + } + test_num += 1; + } +} + +/// Checks prime and only returns value if check passes +const fn ensure_prime(n: usize) -> usize { + check_prime(n); + n +} + +/// This function finds multiplicative generator given `P`. Weirdly, `P` is +/// needed as a const generic because we need to return `PrimeField

`. +/// +/// A multiplicative generator should be able to generate all the elements +/// in the multiplicative subgroup in the field. +const fn find_multiplicative_generator() -> PrimeField

{ + const fn gcd(a: usize, b: usize) -> usize { + let mut a = a; + let mut b = b; + while b != 0 { + let temp = b; + b = a % b; + a = temp; + } + a + } + // This is wrong, through the run of the code below it would be + // hold values which are non-prime. But that's okay... it will only + // ever possibly pass for primes + let mut num = 2; + while num * num <= P { + if gcd(num, P) == 1 { + return PrimeField::

::new(num); + } + i += 1; + } + panic!("cannot find generator"); +} + +impl Display for PrimeField

{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.value) + } +} + +impl FiniteField for PrimeField

{ + const MULTIPLICATIVE_GENERATOR: Self = if P == 2 { + Self::ONE + } else { + find_multiplicative_generator::

() + }; + const ONE: Self = Self { value: 1 }; + const ORDER: usize = ensure_prime(P); + const ZERO: Self = Self { value: 0 }; + + fn multiplicative_inverse(&self) -> Option { + if self.value == 0 { + return None; + } + + // We know that within a prime field, due to fermat's little theorem + // any element `e` will have + // e^(p-1) = 1 mod P + // Hence, + // e^(p-2) = p^(-1) mod P + + Some(self.clone().pow(Self::ORDER - 2)) + } + + fn pow(self, pow: usize) -> Self { + let mut pow = pow; + let result = Self::ONE; + + while pow > 0 { + if pow & 1 == 1 { + result *= self; + } + self *= self; + pow >>= 1; + } + + result + } +} + +// Implement aspects of `PrimeField` +impl PrimeField

{ + // TODO: Check whether this should a const fn + pub const fn new(value: usize) -> Self { + is_prime(P); + Self { value: value % P } + } + + /// Checks whether the field element is a quadratic residue in field mod P + /// Returns `true` if it is a quadratic residue + /// + /// ## NOTES + /// We make use of something called the "euler's criterion". + /// By fermat's little theorem, (assume `is_congruent_to` is =) + /// x^(p-1) - 1 = 0 mod P + /// + /// All primes > 2 are odd, a.k.a P is odd, hence (p-1) is even. + /// So, we can split as follows: + /// (x^(p-1)/2 - 1)(x^(p-1)/2 + 1) = 0 mod P + /// or L * R = 0 mod P + /// + /// All quadratic residues are of the form (g^(2k)) where `g` is the + /// multiplicative generator and k is some natural number. All non-residues + /// on the other hand are of the form (g^(2k+1)). + /// + /// In case of QR, substitute x = g^2k + /// g^(2k)((p-1)/2) = 1 mod P + /// g^(p-1) = 1 mod P + /// which is true by fermat's little theorem + /// + /// In the other case, the same doesn't hold. + /// Hence, the case `L` should hold for all quadratic residues and is the + /// test for quadratic residuosity. + /// + /// More info here: https://www.youtube.com/watch?v=2IBPOI43jek + pub fn is_quadratic_residue(&self) -> bool { + self.pow((P - 1) / 2).value == Self::ONE + } +}