use crate::packable::PackedFlat;
use crate::{PackableField, TowerField};
use core::fmt::{self, Debug, Formatter};
use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
use zeroize::Zeroize;
pub trait HardwareField: TowerField + PackableField {
fn to_hardware(self) -> Flat<Self>;
fn from_hardware(value: Flat<Self>) -> Self;
fn add_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self>;
fn add_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self>;
fn mul_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self>;
fn mul_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self>;
fn mul_hardware_scalar_packed(lhs: PackedFlat<Self>, rhs: Flat<Self>) -> PackedFlat<Self>;
fn tower_bit_from_hardware(value: Flat<Self>, bit_idx: usize) -> u8;
}
#[derive(Copy, Clone, Default, PartialEq, Eq, Zeroize)]
#[repr(transparent)]
pub struct Flat<F>(F);
impl<F> Flat<F> {
#[inline(always)]
pub fn from_raw(raw: F) -> Self {
Self(raw)
}
#[inline(always)]
pub fn into_raw(self) -> F {
self.0
}
#[inline(always)]
pub fn as_raw(&self) -> &F {
&self.0
}
}
impl<F: Debug> Debug for Flat<F> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("Flat").field(&self.0).finish()
}
}
impl<F: HardwareField> Flat<F> {
#[inline(always)]
pub fn to_tower(self) -> F {
F::from_hardware(self)
}
#[inline(always)]
pub fn tower_bit(self, bit_idx: usize) -> u8 {
F::tower_bit_from_hardware(self, bit_idx)
}
}
impl<F: HardwareField> Add for Flat<F> {
type Output = Self;
#[inline(always)]
fn add(self, rhs: Self) -> Self::Output {
F::add_hardware(self, rhs)
}
}
impl<F: HardwareField> AddAssign for Flat<F> {
#[inline(always)]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<F: HardwareField> Sub for Flat<F> {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
F::add_hardware(self, rhs)
}
}
impl<F: HardwareField> SubAssign for Flat<F> {
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<F: HardwareField> Mul for Flat<F> {
type Output = Self;
#[inline(always)]
fn mul(self, rhs: Self) -> Self::Output {
F::mul_hardware(self, rhs)
}
}
impl<F: HardwareField> MulAssign for Flat<F> {
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
pub trait FlatPromote<FromF>: HardwareField
where
FromF: HardwareField,
{
fn promote_flat(val: Flat<FromF>) -> Flat<Self>;
fn promote_flat_batch(input: &[Flat<FromF>], output: &mut [Flat<Self>]) {
for (o, v) in output.iter_mut().zip(input.iter()) {
*o = Self::promote_flat(*v);
}
}
}
impl<F: HardwareField> FlatPromote<F> for F {
#[inline(always)]
fn promote_flat(val: Flat<F>) -> Flat<Self> {
val
}
}