use alloc::collections::BTreeMap;
use crate::Felt;
#[cfg(test)]
mod tests;
pub struct RangeChecker {
lookups: BTreeMap<u16, usize>,
}
impl RangeChecker {
pub fn new() -> Self {
let mut lookups = BTreeMap::new();
lookups.insert(0, 0);
lookups.insert(u16::MAX, 0);
Self { lookups }
}
pub fn add_value(&mut self, value: u16) {
self.lookups.entry(value).and_modify(|v| *v += 1).or_insert(1);
}
pub fn add_range_checks(&mut self, values: &[u16]) {
debug_assert!(values.len() == 2 || values.len() == 4);
for value in values.iter() {
self.add_value(*value);
}
}
pub fn write_range_into_core(
self,
core_data: &mut [Felt],
stride: usize,
m_off: usize,
v_off: usize,
range_table_len: usize,
core_height: usize,
) {
let num_padding_rows = core_height - range_table_len;
let end = {
let mut sink = |step: usize, m: u64, v: u64| {
core_data[step * stride + m_off] = Felt::new_unchecked(m);
core_data[step * stride + v_off] = Felt::new_unchecked(v);
};
self.emit_table_rows(&mut sink, num_padding_rows)
};
assert_eq!(end, core_height, "range checker table length mismatch vs core height");
}
fn emit_table_rows<F: FnMut(usize, u64, u64)>(&self, sink: &mut F, start_step: usize) -> usize {
let mut step = start_step;
let mut prev_value = 0u16;
for (&value, &num_lookups) in self.lookups.iter() {
write_rows(sink, &mut step, num_lookups, value, prev_value);
prev_value = value;
}
write_trace_row(sink, &mut step, 0, (u16::MAX).into());
step
}
pub fn get_number_range_checker_rows(&self) -> usize {
let mut num_rows = 1;
let mut prev_value = 0u16;
for value in self.lookups.keys() {
num_rows += 1;
let delta = value - prev_value;
num_rows += get_num_bridge_rows(delta);
prev_value = *value;
}
num_rows
}
#[cfg(test)]
pub fn trace_len(&self) -> usize {
self.get_number_range_checker_rows()
}
}
impl Default for RangeChecker {
fn default() -> Self {
Self::new()
}
}
pub fn get_num_bridge_rows(delta: u16) -> usize {
let mut gap = delta;
let mut bridge_rows = 0_usize;
let mut stride = 3_u16.pow(7);
while gap != stride {
if gap > stride {
bridge_rows += 1;
gap -= stride;
} else {
stride /= 3;
}
}
bridge_rows
}
fn write_rows<F: FnMut(usize, u64, u64)>(
sink: &mut F,
step: &mut usize,
num_lookups: usize,
value: u16,
prev_value: u16,
) {
let mut gap = value - prev_value;
let mut prev_val = prev_value;
let mut stride = 3_u16.pow(7);
while gap != stride {
if gap > stride {
gap -= stride;
prev_val += stride;
write_trace_row(sink, step, 0, prev_val as u64);
} else {
stride /= 3;
}
}
write_trace_row(sink, step, num_lookups, value as u64);
}
fn write_trace_row<F: FnMut(usize, u64, u64)>(
sink: &mut F,
step: &mut usize,
num_lookups: usize,
value: u64,
) {
sink(*step, num_lookups as u64, value);
*step += 1;
}