use crate::{
core::{
actually_used_field::ActuallyUsedField,
bounds::{FieldBounds, IsBounds},
circuits::traits::arithmetic_circuit::ArithmeticCircuit,
expressions::expr::EvalFailure,
global_value::value::FieldValue,
},
utils::used_field::UsedField,
};
use num_traits::Signed;
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct SignedDivide;
impl SignedDivide {
#[allow(dead_code)]
pub fn checked_run<F: ActuallyUsedField>(
a: FieldValue<F>,
b: FieldValue<F>,
) -> Result<[FieldValue<F>; 2], &'static str> {
let b_cst = b.as_constant();
if b_cst == Some(F::ZERO) {
Err("Division by zero.")
} else {
let res = SignedDivide.run(vec![a, b]);
Ok([res[0], res[1]])
}
}
}
#[allow(dead_code)]
fn div_bounds<F: UsedField>(b1: FieldBounds<F>, b2: FieldBounds<F>) -> FieldBounds<F> {
let (min1, max1) = b1.to_signed_number_pair();
let (min2, max2) = b2.to_signed_number_pair();
if min2 == 0 && max2 == 0 {
b2
} else {
let (min, max) = if min2 >= 0 {
(
(min1.clone() / max2.clone()).min(min1 / min2.clone().max(1.into())),
(max1.clone() / min2.clone().max(1.into())).max(max1 / max2.clone()),
)
} else if max2 <= 0 {
(
(max1.clone() / max2.clone().min((-1).into())).min(max1 / min2.clone()),
(min1.clone() / min2.clone()).max(min1 / max2.clone().min((-1).into())),
)
} else {
(
-(max1.abs().max(min1.abs())),
max1.abs().max(min1.abs()),
)
};
FieldBounds::new(min.into(), max.into())
}
}
#[allow(dead_code)]
fn rem_bounds<F: UsedField>(b1: FieldBounds<F>, b2: FieldBounds<F>) -> FieldBounds<F> {
if let (Some(c1), Some(c2)) = (b1.as_constant(), b2.as_constant()) {
if c2 == F::ZERO {
return FieldBounds::from(F::ZERO);
}
let res: F = (c1.to_signed_number() % c2.to_signed_number()).into();
FieldBounds::from(res)
} else {
let max_r = b2.max_abs();
if max_r == F::ZERO {
FieldBounds::from(F::ZERO)
} else {
FieldBounds::new(-max_r + F::ONE, max_r - F::ONE)
}
}
}
impl<F: UsedField> ArithmeticCircuit<F> for SignedDivide {
fn eval(&self, x: Vec<F>) -> Result<Vec<F>, EvalFailure> {
assert_eq!(x.len(), 2);
let a = x[0];
let b = x[1];
if b == F::ZERO {
EvalFailure::err_ub("division by zero")?;
}
let q = a.signed_euclidean_division(b);
let r = a - b * q;
Ok(vec![q, r])
}
fn bounds(&self, bounds: Vec<FieldBounds<F>>) -> Vec<FieldBounds<F>> {
let a_bounds = bounds[0];
let b_bounds = bounds[1];
vec![
div_bounds(a_bounds, b_bounds),
rem_bounds(a_bounds, b_bounds),
]
}
fn run(&self, vals: Vec<FieldValue<F>>) -> Vec<FieldValue<F>>
where
F: ActuallyUsedField,
{
let a = vals[0];
let b = vals[1];
let abs_div = a.abs() / b.abs();
let a_sign = a.sign();
let b_sign = b.sign();
let abs_rem = a.abs() % b.abs();
vec![abs_div * a_sign * b_sign, abs_rem * a_sign]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
core::circuits::traits::arithmetic_circuit::tests::TestedArithmeticCircuit,
utils::field::ScalarField,
};
use rand::Rng;
use std::marker::PhantomData;
impl<F: ActuallyUsedField> TestedArithmeticCircuit<F> for SignedDivide {
fn gen_desc<R: Rng + ?Sized>(_rng: &mut R) -> Self {
SignedDivide
}
fn gen_n_inputs<R: Rng + ?Sized>(&self, _rng: &mut R) -> usize {
2
}
}
#[test]
fn tested() {
SignedDivide::test_with_marker(64, 16, PhantomData::<ScalarField>)
}
}