use super::OverflowInteger;
use halo2_base::{
gates::{GateInstructions, RangeInstructions},
utils::{bigint_to_fe, fe_to_bigint, BigPrimeField},
Context,
QuantumCell::{Constant, Existing, Witness},
};
use num_bigint::BigInt;
pub fn truncate<F: BigPrimeField>(
range: &impl RangeInstructions<F>,
ctx: &mut Context<F>,
a: OverflowInteger<F>,
limb_bits: usize,
limb_base: F,
limb_base_big: &BigInt,
) {
let k = a.limbs.len();
let max_limb_bits = a.max_limb_bits;
let mut carries = Vec::with_capacity(k);
for a_limb in a.limbs.iter() {
let a_val_big = fe_to_bigint(a_limb.value());
let carry = if let Some(carry_val) = carries.last() {
(a_val_big + carry_val) / limb_base_big
} else {
a_val_big / limb_base_big
};
carries.push(carry);
}
const EPSILON: usize = 1;
let range_bits = max_limb_bits - limb_bits + EPSILON;
let range_bits =
((range_bits + range.lookup_bits()) / range.lookup_bits()) * range.lookup_bits() - 1;
let shift_val = range.gate().pow_of_two()[range_bits];
let mut previous = None;
for (a_limb, carry) in a.limbs.into_iter().zip(carries) {
let neg_carry_val = bigint_to_fe(&-carry);
ctx.assign_region(
[
Existing(a_limb),
Witness(neg_carry_val),
Constant(limb_base),
previous.map(Existing).unwrap_or_else(|| Constant(F::ZERO)),
],
[0],
);
let neg_carry = ctx.get(-3);
let shifted_carry = range.gate().add(ctx, neg_carry, Constant(shift_val));
range.range_check(ctx, shifted_carry, range_bits + 1);
previous = Some(neg_carry);
}
}