use std::array;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use bytemuck::{Pod, Zeroable};
use num_traits::{One, Zero};
use rand::distributions::{Distribution, Standard};
use super::cm31::PackedCM31;
use super::m31::{PackedM31, N_LANES};
use super::PACKED_QM31_BATCH_INVERSE_CHUNK_SIZE;
use crate::core::fields::m31::M31;
use crate::core::fields::qm31::QM31;
use crate::core::fields::{batch_inverse_chunked, FieldExpOps};
use crate::core::utils;
pub type PackedSecureField = PackedQM31;
#[derive(Copy, Clone, Debug)]
pub struct PackedQM31(pub [PackedCM31; 2]);
unsafe impl Send for PackedQM31 {}
unsafe impl Sync for PackedQM31 {}
impl PackedQM31 {
pub const fn broadcast(value: QM31) -> Self {
Self([
PackedCM31::broadcast(value.0),
PackedCM31::broadcast(value.1),
])
}
pub const fn a(&self) -> PackedCM31 {
self.0[0]
}
pub const fn b(&self) -> PackedCM31 {
self.0[1]
}
pub fn to_array(&self) -> [QM31; N_LANES] {
let a = self.a().to_array();
let b = self.b().to_array();
array::from_fn(|i| QM31(a[i], b[i]))
}
pub fn from_array(values: [QM31; N_LANES]) -> Self {
let a = values.map(|v| v.0);
let b = values.map(|v| v.1);
Self([PackedCM31::from_array(a), PackedCM31::from_array(b)])
}
pub fn interleave(self, other: Self) -> (Self, Self) {
let Self([a_evens, b_evens]) = self;
let Self([a_odds, b_odds]) = other;
let (a_lhs, a_rhs) = a_evens.interleave(a_odds);
let (b_lhs, b_rhs) = b_evens.interleave(b_odds);
(Self([a_lhs, b_lhs]), Self([a_rhs, b_rhs]))
}
pub fn deinterleave(self, other: Self) -> (Self, Self) {
let Self([a_lhs, b_lhs]) = self;
let Self([a_rhs, b_rhs]) = other;
let (a_evens, a_odds) = a_lhs.deinterleave(a_rhs);
let (b_evens, b_odds) = b_lhs.deinterleave(b_rhs);
(Self([a_evens, b_evens]), Self([a_odds, b_odds]))
}
pub fn pointwise_sum(self) -> QM31 {
self.to_array().into_iter().sum()
}
pub fn double(self) -> Self {
let Self([a, b]) = self;
Self([a.double(), b.double()])
}
pub const fn into_packed_m31s(self) -> [PackedM31; 4] {
let Self([PackedCM31([a, b]), PackedCM31([c, d])]) = self;
[a, b, c, d]
}
pub const fn from_packed_m31s([a, b, c, d]: [PackedM31; 4]) -> Self {
Self([PackedCM31([a, b]), PackedCM31([c, d])])
}
}
impl Add for PackedQM31 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self([self.a() + rhs.a(), self.b() + rhs.b()])
}
}
impl Sub for PackedQM31 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self([self.a() - rhs.a(), self.b() - rhs.b()])
}
}
impl Mul for PackedQM31 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let ac = self.a() * rhs.a();
let bd = self.b() * rhs.b();
let bd_times_1_plus_i = PackedCM31([bd.a() - bd.b(), bd.a() + bd.b()]);
let ac_p_bd = ac + bd;
let ad_p_bc = (self.a() + self.b()) * (rhs.a() + rhs.b()) - ac_p_bd;
let l = PackedCM31([
ac_p_bd.a() + bd_times_1_plus_i.a(),
ac_p_bd.b() + bd_times_1_plus_i.b(),
]);
Self([l, ad_p_bc])
}
}
impl Zero for PackedQM31 {
fn zero() -> Self {
Self([PackedCM31::zero(), PackedCM31::zero()])
}
fn is_zero(&self) -> bool {
self.a().is_zero() && self.b().is_zero()
}
}
impl One for PackedQM31 {
fn one() -> Self {
Self([PackedCM31::one(), PackedCM31::zero()])
}
}
impl AddAssign for PackedQM31 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl MulAssign for PackedQM31 {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl FieldExpOps for PackedQM31 {
fn inverse(&self) -> Self {
assert!(!self.is_zero(), "0 has no inverse");
let b2 = self.b().square();
let ib2 = PackedCM31([-b2.b(), b2.a()]);
let denom = self.a().square() - (b2 + b2 + ib2);
let denom_inverse = denom.inverse();
Self([self.a() * denom_inverse, -self.b() * denom_inverse])
}
fn batch_inverse(column: &[Self]) -> Vec<Self> {
let mut result = unsafe { utils::uninit_vec(column.len()) };
batch_inverse_chunked(column, &mut result, PACKED_QM31_BATCH_INVERSE_CHUNK_SIZE);
result
}
}
pub fn batch_inverse_packed_qm31(column: &[PackedQM31], dst: &mut [PackedQM31]) {
assert!(column.len() <= dst.len());
batch_inverse_chunked(column, dst, PACKED_QM31_BATCH_INVERSE_CHUNK_SIZE);
}
impl Add<PackedM31> for PackedQM31 {
type Output = Self;
fn add(self, rhs: PackedM31) -> Self::Output {
Self([self.a() + rhs, self.b()])
}
}
impl Mul<PackedM31> for PackedQM31 {
type Output = Self;
fn mul(self, rhs: PackedM31) -> Self::Output {
let Self([a, b]) = self;
Self([a * rhs, b * rhs])
}
}
impl Mul<PackedCM31> for PackedQM31 {
type Output = Self;
fn mul(self, rhs: PackedCM31) -> Self::Output {
let Self([a, b]) = self;
Self([a * rhs, b * rhs])
}
}
impl Sub<PackedM31> for PackedQM31 {
type Output = Self;
fn sub(self, rhs: PackedM31) -> Self::Output {
let Self([a, b]) = self;
Self([a - rhs, b])
}
}
impl Add<QM31> for PackedQM31 {
type Output = Self;
fn add(self, rhs: QM31) -> Self::Output {
self + PackedQM31::broadcast(rhs)
}
}
impl Sub<QM31> for PackedQM31 {
type Output = Self;
fn sub(self, rhs: QM31) -> Self::Output {
self - PackedQM31::broadcast(rhs)
}
}
impl Mul<QM31> for PackedQM31 {
type Output = Self;
fn mul(self, rhs: QM31) -> Self::Output {
self * PackedQM31::broadcast(rhs)
}
}
impl Mul<M31> for PackedQM31 {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: M31) -> Self::Output {
self * PackedM31::broadcast(rhs)
}
}
impl Add<M31> for PackedQM31 {
type Output = Self;
#[inline(always)]
fn add(self, rhs: M31) -> Self::Output {
self + PackedM31::broadcast(rhs)
}
}
impl SubAssign for PackedQM31 {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
unsafe impl Pod for PackedQM31 {}
unsafe impl Zeroable for PackedQM31 {
fn zeroed() -> Self {
unsafe { core::mem::zeroed() }
}
}
impl Sum for PackedQM31 {
fn sum<I>(mut iter: I) -> Self
where
I: Iterator<Item = Self>,
{
let first = iter.next().unwrap_or_else(Self::zero);
iter.fold(first, |a, b| a + b)
}
}
impl<'a> Sum<&'a Self> for PackedQM31 {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
iter.copied().sum()
}
}
impl Neg for PackedQM31 {
type Output = Self;
fn neg(self) -> Self::Output {
let Self([a, b]) = self;
Self([-a, -b])
}
}
impl Distribution<PackedQM31> for Standard {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PackedQM31 {
PackedQM31::from_array(rng.gen())
}
}
impl From<PackedM31> for PackedQM31 {
fn from(value: PackedM31) -> Self {
PackedQM31::from_packed_m31s([
value,
PackedM31::zero(),
PackedM31::zero(),
PackedM31::zero(),
])
}
}
impl From<QM31> for PackedQM31 {
fn from(value: QM31) -> Self {
PackedQM31::broadcast(value)
}
}
#[cfg(test)]
mod tests {
use std::array;
use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
use crate::prover::backend::simd::qm31::PackedQM31;
#[test]
fn addition_works() {
let mut rng = SmallRng::seed_from_u64(0);
let lhs = rng.gen();
let rhs = rng.gen();
let packed_lhs = PackedQM31::from_array(lhs);
let packed_rhs = PackedQM31::from_array(rhs);
let res = packed_lhs + packed_rhs;
assert_eq!(res.to_array(), array::from_fn(|i| lhs[i] + rhs[i]));
}
#[test]
fn subtraction_works() {
let mut rng = SmallRng::seed_from_u64(0);
let lhs = rng.gen();
let rhs = rng.gen();
let packed_lhs = PackedQM31::from_array(lhs);
let packed_rhs = PackedQM31::from_array(rhs);
let res = packed_lhs - packed_rhs;
assert_eq!(res.to_array(), array::from_fn(|i| lhs[i] - rhs[i]));
}
#[test]
fn multiplication_works() {
let mut rng = SmallRng::seed_from_u64(0);
let lhs = rng.gen();
let rhs = rng.gen();
let packed_lhs = PackedQM31::from_array(lhs);
let packed_rhs = PackedQM31::from_array(rhs);
let res = packed_lhs * packed_rhs;
assert_eq!(res.to_array(), array::from_fn(|i| lhs[i] * rhs[i]));
}
#[test]
fn negation_works() {
let mut rng = SmallRng::seed_from_u64(0);
let values = rng.gen();
let packed_values = PackedQM31::from_array(values);
let res = -packed_values;
assert_eq!(res.to_array(), values.map(|v| -v));
}
}