use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use lens::rspcs::field::mul_base;
use nebu::{Fp3, Goldilocks};
use super::num::Num;
#[derive(Clone, Copy, Debug)]
pub enum Mix {
B(Goldilocks),
E(Fp3),
}
impl Mix {
pub fn of(x: Fp3) -> Self {
if x.c1 == Goldilocks::ZERO && x.c2 == Goldilocks::ZERO {
Mix::B(x.c0)
} else {
Mix::E(x)
}
}
pub fn fp3(self) -> Fp3 {
match self {
Mix::B(b) => Fp3::from_base(b),
Mix::E(e) => e,
}
}
pub fn scale(self, m: Fp3) -> Fp3 {
match self {
Mix::B(b) => mul_base(m, b),
Mix::E(e) => m * e,
}
}
}
impl Add for Mix {
type Output = Mix;
#[inline]
fn add(self, o: Mix) -> Mix {
match (self, o) {
(Mix::B(a), Mix::B(b)) => Mix::B(a + b),
(Mix::B(a), Mix::E(e)) | (Mix::E(e), Mix::B(a)) => {
Mix::E(Fp3::new(e.c0 + a, e.c1, e.c2))
}
(Mix::E(a), Mix::E(b)) => Mix::E(a + b),
}
}
}
impl Sub for Mix {
type Output = Mix;
#[inline]
fn sub(self, o: Mix) -> Mix {
self + (-o)
}
}
impl Neg for Mix {
type Output = Mix;
#[inline]
fn neg(self) -> Mix {
match self {
Mix::B(a) => Mix::B(-a),
Mix::E(e) => Mix::E(-e),
}
}
}
impl Mul for Mix {
type Output = Mix;
#[inline]
fn mul(self, o: Mix) -> Mix {
match (self, o) {
(Mix::B(a), Mix::B(b)) => Mix::B(a * b),
(Mix::B(a), Mix::E(e)) | (Mix::E(e), Mix::B(a)) => Mix::E(mul_base(e, a)),
(Mix::E(a), Mix::E(b)) => Mix::E(a * b),
}
}
}
impl AddAssign for Mix {
fn add_assign(&mut self, o: Mix) {
*self = *self + o;
}
}
impl SubAssign for Mix {
fn sub_assign(&mut self, o: Mix) {
*self = *self - o;
}
}
impl MulAssign for Mix {
fn mul_assign(&mut self, o: Mix) {
*self = *self * o;
}
}
impl Num for Mix {
const ZERO: Self = Mix::B(Goldilocks::ZERO);
const ONE: Self = Mix::B(Goldilocks::ONE);
fn from_fp3(c: Fp3) -> Self {
Mix::of(c)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mixed_arithmetic_is_fp3_arithmetic() {
let vals = [
Fp3::from_base(Goldilocks::new(7)),
Fp3::from_base(Goldilocks::new(u64::MAX - 5)),
Fp3::new(Goldilocks::new(3), Goldilocks::new(9), Goldilocks::new(1)),
Fp3::new(Goldilocks::ZERO, Goldilocks::ONE, Goldilocks::ZERO),
];
for &a in &vals {
for &b in &vals {
let (x, y) = (Mix::of(a), Mix::of(b));
assert_eq!((x + y).fp3(), a + b);
assert_eq!((x - y).fp3(), a - b);
assert_eq!((x * y).fp3(), a * b);
assert_eq!((-x).fp3(), -a);
assert_eq!(x.scale(b), b * a);
}
}
}
}