use crate::{
core::{
actually_used_field::ActuallyUsedField,
bounds::{FieldBounds, IsBounds},
circuits::{
arithmetic::PowCircuit,
boolean::{boolean_value::BooleanValue, byte::Byte, utils::sign_bit},
traits::arithmetic_circuit::ArithmeticCircuit,
},
expressions::{
conversion_expr::ConversionExpr::{BitNumToBit, BitToBitNum, ScalarFromPlaintextBit},
expr::Expr,
field_expr::{FieldExpr, RandomValId},
other_expr::OtherExpr,
},
global_value::{
global_expr_store::{are_unbuilt_circuits_allowed, with_global_expr_store_as_local},
value::FieldValue,
},
ir_builder::{ExprStore, IRBuilder},
},
traits::{
Equal,
FromF25519,
FromLeBits,
GetBit,
GreaterEqual,
GreaterThan,
Invert,
MxeRescueKey,
MxeX25519PrivateKey,
Pow,
Random,
RandomBit,
Reveal,
Select,
Selectable,
ToLeBytes,
WithBooleanBounds,
},
utils::{
crypto::key::RESCUE_KEY_COUNT,
field::{BaseField, ScalarField},
number::Number,
},
MxeFieldInput,
MxeInput,
MxeScalarInput,
};
use ff::PrimeField;
use num_traits::Zero;
use std::{
iter::Sum,
ops::{
Add,
AddAssign,
Div,
DivAssign,
Mul,
MulAssign,
Neg,
Not,
Rem,
RemAssign,
Shr,
Sub,
SubAssign,
},
};
impl<F: ActuallyUsedField> FieldValue<F> {
pub fn expr(&self) -> Expr<usize> {
with_global_expr_store_as_local(|expr_store| expr_store.get_expr(self.get_id()).clone())
}
pub fn with_bounds(&self, bounds: impl Into<FieldBounds<F>>) -> FieldValue<F> {
Self::new(FieldExpr::Bounds(*self, bounds.into()))
}
pub fn bounds(&self) -> FieldBounds<F> {
F::bounds_to_field_bounds(with_global_expr_store_as_local(|expr_store| {
*expr_store.get_bounds(self.get_id())
}))
}
pub fn abs(&self) -> FieldValue<F> {
Self::new(FieldExpr::Abs(*self))
}
fn sign_bit(&self) -> FieldValue<F> {
sign_bit(*self).into()
}
pub fn sign(&self) -> FieldValue<F> {
1 - 2 * self.sign_bit()
}
pub fn is_plaintext(&self) -> bool {
with_global_expr_store_as_local(|expr_store| expr_store.get_is_plaintext(self.get_id()))
}
pub fn is_constant(&self) -> bool {
self.as_constant().is_some()
}
pub fn as_constant(&self) -> Option<F> {
self.bounds().as_constant()
}
pub fn signed_lt(self, other: FieldValue<F>) -> BooleanValue {
self.param_lt(other, true)
}
pub fn signed_le(self, other: FieldValue<F>) -> BooleanValue {
self.param_le(other, true)
}
pub fn signed_gt(self, other: FieldValue<F>) -> BooleanValue {
self.param_gt(other, true)
}
pub fn signed_ge(self, other: FieldValue<F>) -> BooleanValue {
self.param_ge(other, true)
}
pub fn param_lt(self, other: FieldValue<F>, signed: bool) -> BooleanValue {
!BooleanValue::from(FieldValue::new(FieldExpr::Ge(self, other, signed)))
}
pub fn param_le(self, other: FieldValue<F>, signed: bool) -> BooleanValue {
!BooleanValue::from(FieldValue::new(FieldExpr::Gt(self, other, signed)))
}
pub fn param_gt(self, other: FieldValue<F>, signed: bool) -> BooleanValue {
BooleanValue::from(FieldValue::new(FieldExpr::Gt(self, other, signed)))
}
pub fn param_ge(self, other: FieldValue<F>, signed: bool) -> BooleanValue {
BooleanValue::from(FieldValue::new(FieldExpr::Ge(self, other, signed)))
}
pub fn random() -> FieldValue<F> {
let len = with_global_expr_store_as_local(|expr_store| expr_store.len());
let res = FieldValue::new(FieldExpr::RandomVal(RandomValId::new()));
assert_eq!(len, res.get_id(), "Randomness was reused !");
res
}
}
macro_rules! random_unsigned {
($t: ident, $n: expr) => {
impl<F: ActuallyUsedField> FieldValue<F> {
pub fn $t() -> FieldValue<F> {
assert!($n < F::NUM_BITS);
FieldValue::<F>::from_le_bits(
(0..$n)
.map(|_| BooleanValue::random())
.collect::<Vec<BooleanValue>>(),
false,
)
}
}
};
}
random_unsigned!(random_u8, 8);
random_unsigned!(random_u16, 16);
random_unsigned!(random_u32, 32);
random_unsigned!(random_u64, 64);
random_unsigned!(random_u128, 128);
impl<F: ActuallyUsedField> WithBooleanBounds for FieldValue<F> {
fn with_boolean_bounds(&self) -> Self {
Self::new(FieldExpr::Bounds(*self, FieldBounds::new(F::ZERO, F::ONE)))
}
}
impl<F: ActuallyUsedField> GetBit for FieldValue<F> {
type Output = BooleanValue;
fn get_bit(&self, index: usize, signed: bool) -> Self::Output {
let bounds = self.bounds();
let circuit_size = bounds.bin_size(signed);
let id = self.get_id();
if circuit_size == 0 {
BooleanValue::from(false)
} else if index < circuit_size {
BooleanValue::new(with_global_expr_store_as_local(|expr_store| {
<IRBuilder as ExprStore<F>>::push_conversion(
expr_store,
BitNumToBit(id, index, signed),
)
}))
} else if signed {
BooleanValue::new(with_global_expr_store_as_local(|expr_store| {
<IRBuilder as ExprStore<F>>::push_conversion(
expr_store,
BitNumToBit(id, circuit_size - 1, signed),
)
}))
} else {
BooleanValue::from(false)
}
}
}
impl<F: ActuallyUsedField> FromLeBits<BooleanValue> for FieldValue<F> {
fn from_le_bits(bits: Vec<BooleanValue>, signed: bool) -> Self {
FieldValue::<F>::from_id(with_global_expr_store_as_local(|expr_store| {
<IRBuilder as ExprStore<F>>::push_conversion(
expr_store,
BitToBitNum(
bits.into_iter()
.map(|bit| bit.get_id())
.collect::<Vec<usize>>(),
signed,
),
)
}))
}
}
impl<F: ActuallyUsedField> ToLeBytes for FieldValue<F> {
type BooleanOutput = BooleanValue;
fn to_le_bytes(self) -> [Byte<Self::BooleanOutput>; 32] {
(0..256)
.map(|i| self.get_bit(i, false))
.collect::<Vec<BooleanValue>>()
.chunks(8)
.map(|chunk| {
Byte::new(
chunk
.to_vec()
.try_into()
.unwrap_or_else(|v: Vec<BooleanValue>| {
panic!("Expected a Vec of length 8 (found {})", v.len())
}),
)
})
.collect::<Vec<Byte<Self::BooleanOutput>>>()
.try_into()
.unwrap_or_else(|v: Vec<Byte<Self::BooleanOutput>>| {
panic!("Expected a Vec of length 32 (found {})", v.len())
})
}
}
impl<F: ActuallyUsedField> Random for FieldValue<F> {
fn random() -> Self {
FieldValue::<F>::random()
}
}
impl<F: ActuallyUsedField> Reveal for FieldValue<F> {
fn reveal(self) -> Self {
FieldValue::new(FieldExpr::Reveal(self))
}
}
impl MxeX25519PrivateKey for FieldValue<ScalarField> {
fn mxe_x25519_private_key() -> Self {
Self::from_expr(Expr::Other(OtherExpr::MxeKey(MxeInput::ScalarOnly(
MxeScalarInput::X25519PrivateKey(),
))))
}
}
impl MxeRescueKey for FieldValue<BaseField> {
fn mxe_rescue_key(i: usize) -> Self {
assert!(i < RESCUE_KEY_COUNT);
Self::from_expr(Expr::Other(OtherExpr::MxeKey(MxeInput::Base(
MxeFieldInput::RescueKey(i),
))))
}
}
impl MxeRescueKey for FieldValue<ScalarField> {
fn mxe_rescue_key(i: usize) -> Self {
assert!(i < RESCUE_KEY_COUNT);
Self::from_expr(Expr::Other(OtherExpr::MxeKey(MxeInput::Scalar(
MxeFieldInput::RescueKey(i),
))))
}
}
macro_rules! field_value_from {
($t: ty) => {
impl<F: ActuallyUsedField> From<$t> for FieldValue<F> {
fn from(number: $t) -> FieldValue<F> {
FieldValue::new(FieldExpr::Val(Number::from(number).into()))
}
}
impl<'a, F: ActuallyUsedField> From<&'a $t> for FieldValue<F> {
fn from(number: &'a $t) -> FieldValue<F> {
FieldValue::new(FieldExpr::Val(Number::from(number.clone()).into()))
}
}
};
}
field_value_from!(bool);
field_value_from!(i8);
field_value_from!(i16);
field_value_from!(i32);
field_value_from!(i64);
field_value_from!(i128);
field_value_from!(u8);
field_value_from!(u16);
field_value_from!(u32);
field_value_from!(u64);
field_value_from!(u128);
field_value_from!(usize);
field_value_from!(Number);
impl<F: ActuallyUsedField> From<F> for FieldValue<F> {
fn from(number: F) -> FieldValue<F> {
FieldValue::new(FieldExpr::Val(number))
}
}
impl<F: ActuallyUsedField> From<BooleanValue> for FieldValue<F> {
fn from(b: BooleanValue) -> Self {
if b.is_plaintext() {
FieldValue::<F>::from_id(with_global_expr_store_as_local(|expr_store| {
expr_store.push_conversion(ScalarFromPlaintextBit::<F, usize>(b.get_id()))
}))
} else {
FieldValue::<F>::from_le_bits(vec![b], false)
}
}
}
impl<F: ActuallyUsedField> From<Byte<BooleanValue>> for FieldValue<F> {
fn from(byte: Byte<BooleanValue>) -> Self {
FieldValue::<F>::from_le_bits(byte.to_vec(), false)
}
}
impl FromF25519<FieldValue<BaseField>> for FieldValue<BaseField> {
fn from_F25519(value: FieldValue<BaseField>) -> Vec<FieldValue<BaseField>> {
vec![value]
}
}
impl FromF25519<FieldValue<BaseField>> for FieldValue<ScalarField> {
fn from_F25519(value: FieldValue<BaseField>) -> Vec<FieldValue<ScalarField>> {
let bytes = value.to_le_bytes();
let chunk_size = ScalarField::NUM_BITS.div_ceil(8) as usize - 1;
bytes
.chunks(chunk_size)
.map(|chunk| {
FieldValue::<ScalarField>::from_le_bits(
chunk
.iter()
.flat_map(|byte| byte.to_vec())
.collect::<Vec<BooleanValue>>(),
false,
)
})
.collect::<Vec<FieldValue<ScalarField>>>()
}
}
macro_rules! impl_right_scalar_binop {
($t: ident, $op: ident, $e: ident, $s: ty) => {
impl<F: ActuallyUsedField> $t<$s> for FieldValue<F> {
type Output = FieldValue<F>;
fn $op(self, rhs: $s) -> Self::Output {
FieldValue::new(FieldExpr::$e(self, rhs.into()))
}
}
impl<'a, F: ActuallyUsedField> $t<$s> for &'a FieldValue<F> {
type Output = FieldValue<F>;
fn $op(self, rhs: $s) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.clone(), rhs.into()))
}
}
impl<'b, F: ActuallyUsedField> $t<&'b $s> for FieldValue<F> {
type Output = FieldValue<F>;
fn $op(self, rhs: &'b $s) -> Self::Output {
FieldValue::new(FieldExpr::$e(self, rhs.clone().into()))
}
}
impl<'a, 'b, F: ActuallyUsedField> $t<&'b $s> for &'a FieldValue<F> {
type Output = FieldValue<F>;
fn $op(self, rhs: &'b $s) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.clone(), rhs.clone().into()))
}
}
};
}
macro_rules! impl_right_trait_binop {
($t: ident, $op: ident, $e: ident) => {
impl<F: ActuallyUsedField, T: Into<FieldValue<F>>> $t<T> for FieldValue<F> {
type Output = FieldValue<F>;
fn $op(self, rhs: T) -> Self::Output {
FieldValue::new(FieldExpr::$e(self, rhs.into()))
}
}
};
}
macro_rules! impl_left_scalar_binop {
($t: ident, $op: ident, $e: ident, $s: ty) => {
impl<F: ActuallyUsedField> $t<FieldValue<F>> for $s {
type Output = FieldValue<F>;
fn $op(self, rhs: FieldValue<F>) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.into(), rhs))
}
}
impl<'a, F: ActuallyUsedField> $t<FieldValue<F>> for &'a $s {
type Output = FieldValue<F>;
fn $op(self, rhs: FieldValue<F>) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.clone().into(), rhs))
}
}
impl<'b, F: ActuallyUsedField> $t<&'b FieldValue<F>> for $s {
type Output = FieldValue<F>;
fn $op(self, rhs: &'b FieldValue<F>) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.into(), rhs.clone()))
}
}
impl<'a, 'b, F: ActuallyUsedField> $t<&'b FieldValue<F>> for &'a $s {
type Output = FieldValue<F>;
fn $op(self, rhs: &'b FieldValue<F>) -> Self::Output {
FieldValue::new(FieldExpr::$e(self.clone().into(), rhs.clone()))
}
}
};
}
macro_rules! impl_sym_binop {
($t: ident, $op: ident, $e: ident) => {
impl_right_trait_binop!($t, $op, $e);
impl_left_scalar_binop!($t, $op, $e, i32);
impl_left_scalar_binop!($t, $op, $e, Number);
};
}
macro_rules! impl_bool_right_trait_binop {
($t: ident, $op: ident, $e: ident, $($o:tt)*) => {
impl<F: ActuallyUsedField, T: Into<FieldValue<F>>> $t<T> for FieldValue<F> {
type Output = BooleanValue;
fn $op(self, rhs: T) -> Self::Output {
BooleanValue::from(FieldValue::new(FieldExpr::$e(self, rhs.into(), $($o)*)))
}
}
};
}
macro_rules! impl_bool_left_scalar_binop {
($t: ident, $op: ident, $e: ident, $s: ty, $($o:tt)*) => {
impl<F: ActuallyUsedField> $t<FieldValue<F>> for $s {
type Output = BooleanValue;
fn $op(self, rhs: FieldValue<F>) -> Self::Output {
BooleanValue::from(FieldValue::new(FieldExpr::$e(self.into(), rhs, $($o)*)))
}
}
impl<'a, F: ActuallyUsedField> $t<FieldValue<F>> for &'a $s {
type Output = BooleanValue;
fn $op(self, rhs: FieldValue<F>) -> Self::Output {
BooleanValue::from(FieldValue::new(FieldExpr::$e(self.clone().into(), rhs, $($o)*)))
}
}
impl<'b, F: ActuallyUsedField> $t<&'b FieldValue<F>> for $s {
type Output = BooleanValue;
fn $op(self, rhs: &'b FieldValue<F>) -> Self::Output {
BooleanValue::from(FieldValue::new(FieldExpr::$e(self.into(), rhs.clone(), $($o)*)))
}
}
impl<'a, 'b, F: ActuallyUsedField> $t<&'b FieldValue<F>> for &'a $s {
type Output = BooleanValue;
fn $op(self, rhs: &'b FieldValue<F>) -> Self::Output {
BooleanValue::from(FieldValue::new(FieldExpr::$e(
self.clone().into(),
rhs.clone(),
$($o)*
)))
}
}
};
}
macro_rules! impl_bool_sym_binop {
($t: ident, $op: ident, $e: ident, $($o:tt)*) => {
impl_bool_right_trait_binop!($t, $op, $e, $($o)*);
impl_bool_left_scalar_binop!($t, $op, $e, i32, $($o)*);
impl_bool_left_scalar_binop!($t, $op, $e, Number, $($o)*);
};
}
macro_rules! impl_assign_binop {
($t: ident, $assign_op: ident, $op: tt) => {
impl<F: ActuallyUsedField, T: Into<FieldValue<F>>> $t<T> for FieldValue<F> {
fn $assign_op(&mut self, rhs: T) {
*self = *self $op rhs;
}
}
};
}
impl_sym_binop!(Add, add, Add);
impl_sym_binop!(Sub, sub, Sub);
impl_sym_binop!(Mul, mul, Mul);
impl_sym_binop!(Div, div, Div);
impl_sym_binop!(Rem, rem, Rem);
impl_bool_sym_binop!(Equal, eq, Equal,);
impl_bool_sym_binop!(GreaterEqual, ge, Ge, false);
impl_bool_sym_binop!(GreaterThan, gt, Gt, false);
impl_right_scalar_binop!(Shr, shr, LogicalRightShift, usize);
impl_assign_binop!(AddAssign, add_assign, +);
impl_assign_binop!(SubAssign, sub_assign, -);
impl_assign_binop!(MulAssign, mul_assign, *);
impl_assign_binop!(DivAssign, div_assign, /);
impl_assign_binop!(RemAssign, rem_assign, %);
impl<F: ActuallyUsedField> Neg for FieldValue<F> {
type Output = FieldValue<F>;
fn neg(self) -> Self::Output {
FieldValue::new(FieldExpr::Neg(self))
}
}
impl<F: ActuallyUsedField> Neg for &FieldValue<F> {
type Output = FieldValue<F>;
fn neg(self) -> Self::Output {
FieldValue::new(FieldExpr::Neg(*self))
}
}
impl<F: ActuallyUsedField> Not for FieldValue<F> {
type Output = FieldValue<F>;
fn not(self) -> Self::Output {
1 - self
}
}
impl<F: ActuallyUsedField> Not for &FieldValue<F> {
type Output = FieldValue<F>;
fn not(self) -> Self::Output {
1 - self
}
}
impl<F: ActuallyUsedField> Selectable<FieldValue<F>> for FieldValue<F> {
type Conditional = FieldValue<F>;
type Output = FieldValue<F>;
fn construct_selection(condition: Self::Conditional, a: Self, b: Self) -> Self::Output {
FieldValue::new(FieldExpr::Where(condition, a, b))
}
}
impl<F: ActuallyUsedField> Selectable<FieldValue<F>> for &FieldValue<F> {
type Conditional = FieldValue<F>;
type Output = FieldValue<F>;
fn construct_selection(
condition: Self::Conditional,
a: Self,
b: FieldValue<F>,
) -> Self::Output {
FieldValue::new(FieldExpr::Where(condition, *a, b))
}
}
impl<F: ActuallyUsedField> Selectable<&FieldValue<F>> for FieldValue<F> {
type Conditional = FieldValue<F>;
type Output = FieldValue<F>;
fn construct_selection(
condition: Self::Conditional,
a: Self,
b: &FieldValue<F>,
) -> Self::Output {
FieldValue::new(FieldExpr::Where(condition, a, *b))
}
}
impl<'a, 'b, F: ActuallyUsedField> Selectable<&'b FieldValue<F>> for &'a FieldValue<F> {
type Conditional = FieldValue<F>;
type Output = FieldValue<F>;
fn construct_selection(
condition: Self::Conditional,
a: &'a FieldValue<F>,
b: &'b FieldValue<F>,
) -> Self::Output {
FieldValue::new(FieldExpr::Where(condition, *a, *b))
}
}
impl<F: ActuallyUsedField, T, U, V> Select<T, U, V> for FieldValue<F>
where
T: Selectable<V, Conditional = FieldValue<F>, Output = U>,
{
fn select(self, t: T, u: V) -> U {
T::construct_selection(self, t, u)
}
}
impl<F: ActuallyUsedField, T, U, V> Select<T, U, V> for &FieldValue<F>
where
T: Selectable<V, Conditional = FieldValue<F>, Output = U>,
{
fn select(self, t: T, u: V) -> U {
T::construct_selection(*self, t, u)
}
}
impl<F: ActuallyUsedField> Zero for FieldValue<F> {
fn zero() -> Self {
0.into()
}
fn is_zero(&self) -> bool {
F::expr_to_field_expr(self.expr()) == Some(FieldExpr::Val(F::ZERO))
}
}
impl<F: ActuallyUsedField> Sum for FieldValue<F> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(0.into(), |acc, x| acc + x)
}
}
impl<F: ActuallyUsedField> Invert for FieldValue<F> {
fn invert(self, is_expected_non_zero: bool) -> Self {
let is_zero_mask = if !is_expected_non_zero {
FieldValue::<F>::from(self.eq(FieldValue::<F>::from(0)))
} else {
FieldValue::<F>::from(0)
};
Self::new(FieldExpr::FieldInverse(self + is_zero_mask)) - is_zero_mask
}
}
impl<F: ActuallyUsedField> Pow for FieldValue<F> {
fn pow(self, e: &Number, is_expected_non_zero: bool) -> Self {
let e = e % (F::modulus() - 1);
if are_unbuilt_circuits_allowed() {
FieldValue::new(FieldExpr::Pow(self, e, is_expected_non_zero))
} else {
let pow_circuit = PowCircuit::new(e, is_expected_non_zero);
pow_circuit.run(vec![self])[0]
}
}
}