use crate::{
core::{
bounds::BoolBounds,
circuits::boolean::byte::Byte,
expressions::{expr::EvalFailure, field_expr::InputId, InputKind},
},
traits::Keccak,
utils::{ignore_for_equality::IgnoreForEquality, unique_id::UniqueId},
};
use arcis_internal_expr_macro::Expr;
use serde::{Deserialize, Serialize};
use std::{cell::Cell, marker::PhantomData, rc::Rc};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RandomBitId(UniqueId);
impl RandomBitId {
pub fn new() -> Self {
RandomBitId(UniqueId::new())
}
}
impl Default for RandomBitId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BitInputInfo {
pub kind: InputKind,
pub name: String,
pub has_already_been_found_unused: IgnoreForEquality<Cell<bool>>,
}
impl Default for BitInputInfo {
fn default() -> Self {
let kind = InputKind::Secret;
let name = "_".to_owned();
let has_already_been_found_unused = Default::default();
BitInputInfo {
kind,
name,
has_already_been_found_unused,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Expr)]
pub enum BitExpr<B: Clone> {
And(B, B),
Not(B),
Xor(B, B),
Reveal(B),
Val(bool),
Binop(B, B, [bool; 4]),
Random(RandomBitId),
KeccakF1600(Vec<B>, usize),
Input(InputId, Rc<BitInputInfo>),
}
impl BitExpr<bool> {
pub fn eval_binop(b1: bool, b2: bool, arr: [bool; 4]) -> bool {
arr[b1 as usize * 2 + b2 as usize]
}
pub fn eval(self) -> Result<bool, EvalFailure> {
let res = match self {
BitExpr::And(b1, b2) => b1 && b2,
BitExpr::Not(b) => !b,
BitExpr::Xor(b1, b2) => b1 ^ b2,
BitExpr::Reveal(b) => b,
BitExpr::Val(b) => b,
BitExpr::Binop(b1, b2, arr) => Self::eval_binop(b1, b2, arr),
BitExpr::Random(_) => EvalFailure::err_imp("Cannot evaluate random gate here.")?,
BitExpr::KeccakF1600(bits, i) => {
assert!(bits.len() == 1600 && i < 1600);
let mut state = [Byte::<bool>::from(0); 200];
bits.chunks(8).enumerate().for_each(|(i, chunk)| {
state[i] =
Byte::new(chunk.to_vec().try_into().unwrap_or_else(|v: Vec<bool>| {
panic!("Expected a Vec of length 8 (found {})", v.len())
}));
});
let res = Keccak::f1600(state);
res[i / 8].get_bits()[i % 8]
}
BitExpr::Input(_, _) => EvalFailure::err_imp("Input not evaluable here")?,
};
Ok(res)
}
pub fn is_plaintext(&self) -> bool {
match self {
BitExpr::Reveal(_) => true,
BitExpr::Val(_) => true,
BitExpr::Random(_) => false,
BitExpr::KeccakF1600(_, _) => true,
BitExpr::Input(_, info) => info.kind.is_plaintext(),
_ => self.get_deps().iter().all(|x| *x),
}
}
}
impl<T: Clone> BitExpr<T> {
pub fn is_eval_deterministic_fn_from_deps(&self) -> bool {
!matches!(self, BitExpr::Random(_) | BitExpr::Input(..))
}
pub fn get_input(&self) -> Option<InputId> {
if let BitExpr::Input(id, _) = self {
Some(*id)
} else {
None
}
}
pub fn get_input_name(&self) -> &str {
if let BitExpr::Input(_, info) = self {
info.name.as_str()
} else {
""
}
}
pub fn get_is_input_already_optimized_out(&self) -> Option<&Cell<bool>> {
if let BitExpr::Input(_, info) = self {
Some(&info.has_already_been_found_unused.0)
} else {
None
}
}
}
impl BitExpr<BoolBounds> {
pub fn bounds(self) -> BoolBounds {
match self {
BitExpr::And(b1, b2) => b1 & b2,
BitExpr::Not(b) => !b,
BitExpr::Xor(b1, b2) => b1 ^ b2,
BitExpr::Reveal(b) => b,
BitExpr::Val(b) => BoolBounds::from(b),
BitExpr::Binop(b1, b2, arr) => {
let mut can_be_false = false;
let mut can_be_true = false;
for bool_1 in b1 {
for bool_2 in b2 {
let res = BitExpr::eval_binop(bool_1, bool_2, arr);
if res {
can_be_true = true;
} else {
can_be_false = true;
}
}
}
BoolBounds::new(can_be_false, can_be_true)
}
BitExpr::Random(_) => BoolBounds::new(true, true),
BitExpr::KeccakF1600(..) => BoolBounds::new(true, true),
BitExpr::Input(_, _) => BoolBounds::new(true, true),
}
}
}