use crate::{
core::{
circuits::boolean::utils::shift_right,
expressions::{circuit::ArithmeticCircuitId, field_expr::FieldExpr},
global_value::{field_array::FieldArray, value::FieldValue},
},
traits::*,
utils::{field::BaseField, number::Number, used_field::UsedField},
};
use ff::Field;
use std::ops::{Add, Div, Mul, Sub};
pub type ArcisField = BaseField;
pub type ArcisValue = FieldValue<ArcisField>;
pub type ArcisArray<const N: usize> = FieldArray<N, ArcisField>;
#[derive(Debug, Clone, Copy)]
pub struct ArcisFloatValue(pub ArcisValue);
impl ArcisFloatValue {
pub const MINUS_PLSB: usize = DOUBLE_PRECISION_MANTISSA;
pub fn max_bounds() -> (ArcisField, ArcisField) {
(
-ArcisField::power_of_two(127),
ArcisField::power_of_two(127) - ArcisField::ONE,
)
}
pub fn number_to_f64(a: Number) -> f64 {
f64::from(a) / 2.0f64.powi(52)
}
pub fn number_with_precision_to_f64(a: Number, precision: usize) -> f64 {
f64::from(a) / 2.0f64.powi(precision as i32)
}
pub fn i128_to_f64(a: i128) -> f64 {
Self::number_to_f64(a.into())
}
pub fn i128_to_f32(a: i128) -> f32 {
Self::i128_to_f64(a) as f32
}
pub fn f64_to_i128(a: f64) -> i128 {
let value = a.clamp(-(2f64.powi(75)), 2f64.powi(75));
(value * 2f64.powi(DOUBLE_PRECISION_MANTISSA as i32)) as i128
}
pub fn f32_to_i128(a: f32) -> i128 {
let value = a.clamp(-(2f32.powi(75)), 2f32.powi(75));
(value * 2f32.powi(DOUBLE_PRECISION_MANTISSA as i32)) as i128
}
pub fn f64_to_number(a: f64) -> Number {
let value = a.clamp(-(2f64.powi(75)), 2f64.powi(75));
Number::from(value * 2f64.powi(DOUBLE_PRECISION_MANTISSA as i32))
}
pub fn f32_to_number(a: f32) -> Number {
let value = a.clamp(-(2f32.powi(75)), 2f32.powi(75));
Number::from(value * 2f32.powi(DOUBLE_PRECISION_MANTISSA as i32))
}
}
impl From<ArcisValue> for ArcisFloatValue {
fn from(value: ArcisValue) -> Self {
Self(value)
}
}
impl From<ArcisFloatValue> for ArcisValue {
fn from(a: ArcisFloatValue) -> ArcisValue {
a.0
}
}
impl TryFrom<f64> for ArcisFloatValue {
type Error = &'static str;
fn try_from(value: f64) -> Result<Self, &'static str> {
if value < -(2f64.powi(75)) || value >= 2f64.powi(75) {
Err("Arcis only supports inputs in the range [-2**75, 2**75)")
} else {
Ok(ArcisFloatValue(FieldValue::new(FieldExpr::Val(
ArcisFloatValue::f64_to_number(value).into(),
))))
}
}
}
macro_rules! impl_float_comparison_op {
($op:ident, $opfn:ident) => {
impl $op<Self> for ArcisFloatValue {
type Output = ArcisValue;
fn $opfn(self, other: Self) -> Self::Output {
ArcisValue::from(self.0.$opfn(other.0))
}
}
impl $op<f64> for ArcisFloatValue {
type Output = ArcisValue;
fn $opfn(self, other: f64) -> Self::Output {
ArcisValue::from(self.0.$opfn(ArcisFloatValue::try_from(other).unwrap().0))
}
}
};
}
impl_float_comparison_op!(Equal, eq);
impl_float_comparison_op!(GreaterThan, gt);
impl_float_comparison_op!(GreaterEqual, ge);
macro_rules! impl_float_arithmetic_op {
($op:ident, $opfn:ident) => {
impl $op<ArcisFloatValue> for ArcisFloatValue {
type Output = Self;
fn $opfn(self, other: Self) -> Self {
Self(self.0.$opfn(other.0))
}
}
impl $op<f64> for ArcisFloatValue {
type Output = ArcisFloatValue;
fn $opfn(self, other: f64) -> Self::Output {
Self(self.0.$opfn(ArcisFloatValue::try_from(other).unwrap().0))
}
}
};
}
impl_float_arithmetic_op!(Add, add);
impl_float_arithmetic_op!(Sub, sub);
pub(crate) const DOUBLE_PRECISION_MANTISSA: usize = 52;
impl Mul for ArcisFloatValue {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let prod = self.0 * rhs.0;
Self(shift_right(prod, DOUBLE_PRECISION_MANTISSA, true))
}
}
impl Div for ArcisFloatValue {
type Output = Self;
fn div(self, rhs: ArcisFloatValue) -> Self::Output {
Self(ArithmeticCircuitId::Div.apply(vec![self.0, rhs.0])[0])
}
}