use super::{BTreeMap, Felt, RangeChecker, ONE, ZERO};
use crate::{utils::get_trace_len, RangeCheckTrace};
use rand_utils::rand_array;
use vm_core::{utils::ToElements, StarkField};
#[test]
fn range_checks() {
let mut checker = RangeChecker::new();
let values = [0, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 100, 355, 620].to_elements();
for &value in values.iter() {
checker.add_value(value.as_int() as u16)
}
let RangeCheckTrace {
trace,
aux_builder: _,
} = checker.into_trace(1024, 0);
validate_trace(&trace, &values);
let mut i = 0;
while trace[0][i] == ZERO {
i += 1
}
validate_row(&trace, i, 0, 1);
validate_row(&trace, i + 1, 1, 1);
validate_row(&trace, i + 2, 2, 4);
validate_row(&trace, i + 3, 3, 2);
validate_row(&trace, i + 4, 3, 1);
validate_row(&trace, i + 5, 4, 2);
validate_row(&trace, i + 6, 100, 1);
validate_row(&trace, i + 7, 355, 1);
validate_row(&trace, i + 8, 610, 0);
validate_row(&trace, i + 9, 620, 1);
}
#[test]
fn range_checks_rand() {
let mut checker = RangeChecker::new();
let values = rand_array::<u64, 300>();
let values = values
.into_iter()
.map(|v| Felt::new(v as u16 as u64))
.collect::<Vec<_>>();
for &value in values.iter() {
checker.add_value(value.as_int() as u16);
}
let trace_len = checker.trace_len().next_power_of_two();
let RangeCheckTrace {
trace,
aux_builder: _,
} = checker.into_trace(trace_len, 0);
validate_trace(&trace, &values);
}
fn validate_row(trace: &[Vec<Felt>], row_idx: usize, value: u64, num_lookups: u64) {
let (s0, s1) = match num_lookups {
0 => (ZERO, ZERO),
1 => (ONE, ZERO),
2 => (ZERO, ONE),
4 => (ONE, ONE),
_ => panic!("invalid lookup value"),
};
assert_eq!(s0, trace[1][row_idx]);
assert_eq!(s1, trace[2][row_idx]);
assert_eq!(Felt::new(value), trace[3][row_idx]);
}
fn validate_trace(trace: &[Vec<Felt>], lookups: &[Felt]) {
assert_eq!(4, trace.len());
let trace_len = get_trace_len(trace);
assert!(trace_len.is_power_of_two());
assert_eq!(ZERO, trace[0][0]);
assert_eq!(ZERO, trace[3][0]);
let mut lookups_8bit = BTreeMap::new();
let mut i = 0;
let mut prev_value = 0u8;
while trace[0][i] == ZERO {
let value = trace[3][i].as_int();
assert!(value <= 255, "not an 8-bit value");
let value = value as u8;
let delta = value - prev_value;
assert!(delta <= 1);
let count = get_lookup_count(trace, i);
lookups_8bit
.entry(value)
.and_modify(|value| *value += count)
.or_insert(count);
i += 1;
prev_value = value;
}
let last_value = trace[3][i - 1].as_int();
assert_eq!(255, last_value);
assert_eq!(256, lookups_8bit.len());
let mut lookups_16bit = BTreeMap::new();
assert_eq!(ZERO, trace[3][i]);
let count = get_lookup_count(trace, i);
lookups_16bit.insert(0u16, count);
i += 1;
let mut prev_value = 0u16;
while i < trace_len {
assert_eq!(ONE, trace[0][i]);
let value = trace[3][i].as_int();
assert!(value <= 65535, "not an 8-bit value");
let value = value as u16;
let delta = value - prev_value;
assert!(delta <= 255);
let delta = delta as u8;
lookups_8bit.entry(delta).and_modify(|count| {
assert!(*count > 0);
*count -= 1;
});
let count = get_lookup_count(trace, i);
lookups_16bit
.entry(value)
.and_modify(|value| *value += count)
.or_insert(count);
i += 1;
prev_value = value;
}
let last_value = trace[3][i - 1].as_int();
assert_eq!(65535, last_value);
for &value in lookups_8bit.values() {
assert_eq!(0, value);
}
for value in lookups {
let value = value.as_int();
assert!(value <= 65535, "not a 16-bit value");
let value = value as u16;
assert!(lookups_16bit.contains_key(&value));
lookups_16bit.entry(value).and_modify(|count| {
assert!(*count > 0);
*count -= 1;
});
}
for &value in lookups_16bit.values() {
assert_eq!(0, value);
}
}
fn get_lookup_count(trace: &[Vec<Felt>], step: usize) -> usize {
if trace[1][step] == ZERO && trace[2][step] == ZERO {
0
} else if trace[1][step] == ONE && trace[2][step] == ZERO {
1
} else if trace[1][step] == ZERO && trace[2][step] == ONE {
2
} else if trace[1][step] == ONE && trace[2][step] == ONE {
4
} else {
panic!("not a valid count");
}
}