use crate::{
core::{
actually_used_field::ActuallyUsedField,
bounds::FieldBounds,
circuits::traits::arithmetic_circuit::ArithmeticCircuit,
expressions::expr::EvalFailure,
global_value::value::FieldValue,
},
traits::{GreaterThan, Select},
utils::used_field::UsedField,
};
#[derive(Debug, Clone)]
pub struct Max {
signed: bool,
}
impl Max {
pub const fn new(signed: bool) -> Self {
Self { signed }
}
fn empty_value<F: UsedField>(&self) -> F {
if self.signed {
F::TWO_INV
} else {
F::ZERO
}
}
fn max<F: UsedField>(&self, x: Vec<F>) -> F {
x.into_iter()
.fold(self.empty_value(), |x1, x2| x1.max(x2, self.signed))
}
}
impl<F: UsedField> ArithmeticCircuit<F> for Max {
fn eval(&self, x: Vec<F>) -> Result<Vec<F>, EvalFailure> {
let res = self.max(x);
Ok(vec![res])
}
fn bounds(&self, bounds: Vec<FieldBounds<F>>) -> Vec<FieldBounds<F>> {
let (min, max) = bounds
.iter()
.map(|bounds| bounds.min_and_max(self.signed))
.unzip();
let min = self.max(min);
let max = self.max(max);
vec![FieldBounds::new(min, max)]
}
fn run(&self, vals: Vec<FieldValue<F>>) -> Vec<FieldValue<F>>
where
F: ActuallyUsedField,
{
let res = if self.signed {
vals.into_iter()
.fold(FieldValue::from(self.empty_value::<F>()), |a, b| {
a.signed_gt(b).select(a, b)
})
} else {
vals.into_iter()
.fold(FieldValue::from(self.empty_value::<F>()), |a, b| {
a.le(b).select(b, a)
})
};
vec![res]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
core::circuits::traits::arithmetic_circuit::tests::TestedArithmeticCircuit,
ArcisField,
};
use rand::Rng;
impl TestedArithmeticCircuit<ArcisField> for Max {
fn gen_desc<R: Rng + ?Sized>(rng: &mut R) -> Self {
Max::new(rng.r#gen())
}
fn gen_n_inputs<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
let mut n_inputs = 1;
while rng.gen_bool(0.875) {
n_inputs += 1
}
n_inputs
}
}
#[test]
fn tested() {
Max::test(64, 4)
}
}