openvm_circuit_primitives/bigint/
check_carry_to_zero.rsuse openvm_stark_backend::{interaction::InteractionBuilder, p3_field::AbstractField};
use super::{utils::range_check, OverflowInt};
use crate::SubAir;
pub struct CheckCarryToZeroCols<T> {
pub carries: Vec<T>,
}
#[derive(Clone, Debug)]
pub struct CheckCarryToZeroSubAir {
pub limb_bits: usize,
pub range_checker_bus: usize,
pub decomp: usize,
}
pub fn get_carry_max_abs_and_bits(max_overflow_bits: usize, limb_bits: usize) -> (usize, usize) {
let carry_bits = max_overflow_bits - limb_bits;
let carry_min_value_abs = 1 << carry_bits;
let carry_abs_bits = carry_bits + 1;
(carry_min_value_abs, carry_abs_bits)
}
impl CheckCarryToZeroSubAir {
pub fn new(limb_bits: usize, range_checker_bus: usize, decomp: usize) -> Self {
Self {
limb_bits,
range_checker_bus,
decomp,
}
}
}
impl<AB: InteractionBuilder> SubAir<AB> for CheckCarryToZeroSubAir {
type AirContext<'a>
= (
OverflowInt<AB::Expr>,
CheckCarryToZeroCols<AB::Var>,
AB::Expr,
)
where
AB::Var: 'a,
AB::Expr: 'a,
AB: 'a;
fn eval<'a>(
&'a self,
builder: &'a mut AB,
(expr, cols, is_valid): (
OverflowInt<AB::Expr>,
CheckCarryToZeroCols<AB::Var>,
AB::Expr,
),
) where
AB::Var: 'a,
AB::Expr: 'a,
{
assert_eq!(expr.limbs.len(), cols.carries.len());
builder.assert_bool(is_valid.clone());
let (carry_min_value_abs, carry_abs_bits) =
get_carry_max_abs_and_bits(expr.max_overflow_bits, self.limb_bits);
for &carry in cols.carries.iter() {
range_check(
builder,
self.range_checker_bus,
self.decomp,
carry_abs_bits,
carry + AB::F::from_canonical_usize(carry_min_value_abs),
is_valid.clone(),
);
}
let mut previous_carry = AB::Expr::ZERO;
for (i, limb) in expr.limbs.iter().enumerate() {
builder.assert_eq(
limb.clone() + previous_carry.clone(),
cols.carries[i] * AB::F::from_canonical_usize(1 << self.limb_bits),
);
previous_carry = cols.carries[i].into();
}
builder.assert_eq(previous_carry, AB::Expr::ZERO);
}
}