Files
powdr/std/math/fp2.asm
onurinanc 14225aa1d1 Implement Fp4 for BabyBear Field in PIL (#1707)
Solves the issue in #1691

You can run the test using `--field bb`
2024-09-03 15:15:44 +00:00

205 lines
7.1 KiB
Rust

use std::array::len;
use std::array::fold;
use std::check::assert;
use std::check::panic;
use std::convert::fe;
use std::convert::int;
use std::convert::expr;
use std::field::known_field;
use std::field::KnownField;
use std::math::ff::inv_field;
use std::prover::eval;
/// Corresponding Sage code to test irreduciblity
/// BabyBear = 2^27 * 15 + 1
/// M31 = 2^31 - 1
/// BN254 = 21888242871839275222246405745257275088548364400416034343698204186575808495617
/// GL = 0xffffffff00000001
/// F = GF(GL)
/// R.<x> = PolynomialRing(F)
/// f = x^2 - 7
/// f.is_irreducible()
/// An element of the extension field over the implied base field (which has to be either
/// the Goldilocks or the BN254 field) relative to the irreducible polynomial X^2 - 7,
/// (This irreducible polynomial also works for Mersenne31)
/// where Fp2(a0, a1) is interpreted as a0 + a1 * X.
/// T is assumed to either be fe, expr or any other object whose algebraic operations
/// are compatible with fe.
enum Fp2<T> {
Fp2(T, T)
}
/// Converts a base field element to an extension field element
let<T: FromLiteral> from_base: T -> Fp2<T> = |x| Fp2::Fp2(x, 0);
/// Extension field addition
let<T: Add> add_ext: Fp2<T>, Fp2<T> -> Fp2<T> = |a, b| match (a, b) {
(Fp2::Fp2(a0, a1), Fp2::Fp2(b0, b1)) => Fp2::Fp2(
a0 + b0,
a1 + b1
)
};
/// Extension field subtraction
let<T: Sub> sub_ext: Fp2<T>, Fp2<T> -> Fp2<T> = |a, b| match (a, b) {
(Fp2::Fp2(a0, a1), Fp2::Fp2(b0, b1)) => Fp2::Fp2(
a0 - b0,
a1 - b1
)
};
/// Extension field multiplication
let<T: Add + FromLiteral + Mul> mul_ext: Fp2<T>, Fp2<T> -> Fp2<T> = |a, b| match (a, b) {
(Fp2::Fp2(a0, a1), Fp2::Fp2(b0, b1)) => Fp2::Fp2(
// Multiplication modulo the polynomial x^2 - 7. We'll use the fact
// that x^2 == 7 (mod x^2 - 7), so:
// (a0 + a1 * x) * (b0 + b1 * x) = a0 * b0 + 7 * a1 * b1 + (a1 * b0 + a0 * b1) * x (mod x^2 - 7)
a0 * b0 + 7 * a1 * b1,
a1 * b0 + a0 * b1
)
};
/// Converts an Fp2<expr> into an Fp2<fe>
let eval_ext: Fp2<expr> -> Fp2<fe> = query |a| match a {
Fp2::Fp2(a0, a1) => Fp2::Fp2(eval(a0), eval(a1))
};
/// Converts an Fp2<fe> into an Fp2<expr>
let expr_ext: Fp2<fe> -> Fp2<expr> = |a| match a {
Fp2::Fp2(a0, a1) => Fp2::Fp2(expr(a0), expr(a1))
};
/// Extension field equality
let eq_ext: Fp2<fe>, Fp2<fe> -> bool = |a, b| match (a, b) {
(Fp2::Fp2(a0, a1), Fp2::Fp2(b0, b1)) => (a0 == b0) && (a1 == b1)
};
/// Returns constraints that two extension field elements are equal
let constrain_eq_ext: Fp2<expr>, Fp2<expr> -> Constr[] = |a, b| match (a, b) {
(Fp2::Fp2(a0, a1), Fp2::Fp2(b0, b1)) => [a0 = b0, a1 = b1]
};
/// Extension field inversion
let inv_ext: Fp2<fe> -> Fp2<fe> = |a| match a {
// The inverse of (a0, a1) is a point (b0, b1) such that:
// (a0 + a1 * x) (b0 + b1 * x) = 1 (mod x^2 - 7)
// Multiplying out and plugging in x^2 = 7 yields the following system of linear equations:
// a0 * b0 + 7 * a1 * b1 = 1
// a1 * b0 + a0 * b1 = 0
// Solving for (b0, b1) yields:
Fp2::Fp2(a0, a1) => {
let factor = inv_field(7 * a1 * a1 - a0 * a0);
Fp2::Fp2(-a0 * factor, a1 * factor)
}
};
/// Applies the next operator to both components of the extension field element
let next_ext: Fp2<expr> -> Fp2<expr> = |a| match a {
Fp2::Fp2(a0, a1) => Fp2::Fp2(a0', a1')
};
/// Returns the two components of the extension field element as a tuple
let<T> unpack_ext: Fp2<T> -> (T, T) = |a| match a {
Fp2::Fp2(a0, a1) => (a0, a1)
};
/// Returns the two components of the extension field element as an array
let<T> unpack_ext_array: Fp2<T> -> T[] = |a| match a {
Fp2::Fp2(a0, a1) => [a0, a1]
};
/// Whether we need to operate on the F_{p^2} extension field (because the current field is too small).
let needs_extension: -> bool = || match known_field() {
Option::Some(KnownField::Goldilocks) => true,
Option::Some(KnownField::BN254) => false,
None => panic("The permutation/lookup argument is not implemented for the current field!")
};
/// Matches whether the length of a given array is correct to operate on the extension field
let is_extension = |arr| match len(arr) {
1 => false,
2 => true,
_ => panic("Expected 1 or 2 accumulator columns!")
};
/// Constructs an extension field element `a0 + a1 * X` from either `[a0, a1]` or `[a0]` (setting `a1`to zero in that case)
let fp2_from_array = |arr| {
if is_extension(arr) {
Fp2::Fp2(arr[0], arr[1])
} else {
let _ = assert(!needs_extension(), || "The field is too small and needs to move to the extension field. Pass two elements instead!");
from_base(arr[0])
}
};
mod test {
use super::Fp2;
use super::from_base;
use super::add_ext;
use super::sub_ext;
use super::mul_ext;
use super::inv_ext;
use super::eq_ext;
use std::check::assert;
use std::array::map;
let add = || {
let test_add = |a, b, c| assert(eq_ext(add_ext(a, b), c), || "Wrong addition result");
// Test adding 0
let _ = test_add(from_base(0), from_base(0), from_base(0));
let _ = test_add(Fp2::Fp2(123, 1234), from_base(0), Fp2::Fp2(123, 1234));
let _ = test_add(from_base(0), Fp2::Fp2(123, 1234), Fp2::Fp2(123, 1234));
// Add arbitrary elements
let _ = test_add(Fp2::Fp2(123, 1234), Fp2::Fp2(567, 5678), Fp2::Fp2(690, 6912));
test_add(Fp2::Fp2(-1, -1), Fp2::Fp2(3, 4), Fp2::Fp2(2, 3))
};
let sub = || {
let test_sub = |a, b, c| assert(eq_ext(sub_ext(a, b), c), || "Wrong subtraction result");
// Test subtracting 0
let _ = test_sub(from_base(0), from_base(0), from_base(0));
let _ = test_sub(Fp2::Fp2(123, 1234), from_base(0), Fp2::Fp2(123, 1234));
// Subtract arbitrary elements
let _ = test_sub(Fp2::Fp2(123, 1234), Fp2::Fp2(567, 5678), Fp2::Fp2(123 - 567, 1234 - 5678));
test_sub(Fp2::Fp2(-1, -1), Fp2::Fp2(0x100000000, 1), Fp2::Fp2(-0x100000000 - 1, -2))
};
let mul = || {
let test_mul = |a, b, c| assert(eq_ext(mul_ext(a, b), c), || "Wrong multiplication result");
// Test multiplication by 1
let _ = test_mul(from_base(1), from_base(1), from_base(1));
let _ = test_mul(Fp2::Fp2(123, 1234), from_base(1), Fp2::Fp2(123, 1234));
let _ = test_mul(from_base(1), Fp2::Fp2(123, 1234), Fp2::Fp2(123, 1234));
// Test multiplication by 0
let _ = test_mul(Fp2::Fp2(123, 1234), from_base(0), from_base(0));
let _ = test_mul(from_base(0), Fp2::Fp2(123, 1234), from_base(0));
// Multiply arbitrary elements
let _ = test_mul(Fp2::Fp2(123, 1234), Fp2::Fp2(567, 5678), Fp2::Fp2(49116305, 1398072));
// Multiplication with field overflow
test_mul(Fp2::Fp2(-1, -2), Fp2::Fp2(-3, 4), Fp2::Fp2(3 - 7 * 8, 6 - 4))
};
let inverse = || {
let test_elements = [
from_base(1),
Fp2::Fp2(123, 1234),
Fp2::Fp2(-1, 500)
];
map(test_elements, |x| {
let mul_with_inverse = mul_ext(x, inv_ext(x));
assert(eq_ext(mul_with_inverse, from_base(1)), || "Should be 1")
})
};
}