use libm::log2;
use crate::error::ErrorBits;
use crate::shape::{InstanceShape, StarkAirParams};
pub fn deep_ali_error(air: &StarkAirParams, shape: &InstanceShape, list_size: f64) -> ErrorBits {
if shape.modulus_bits == 0
|| !air.num_quotient_chunks.is_power_of_two()
|| shape.log_trace_length >= u64::BITS as usize
|| !list_size.is_finite()
|| list_size <= 0.0
{
return ErrorBits::from_log2(0.0);
}
let k = (1u64 << shape.log_trace_length) as f64;
let max_deg = air.max_constraint_degree.max(1) as f64;
let combo = air.max_combo as f64;
let ethstark = max_deg * (k + combo - 1.0) + (k - 1.0);
let chunks = air.num_quotient_chunks as f64;
let chunked = (chunks + 1.0) * k + combo - 1.0;
let factor = ethstark.max(chunked).max(1.0);
let bits = shape.modulus_bits as f64 - log2(list_size) - log2(factor);
ErrorBits::from_log2(bits.max(0.0))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trace_length_shift_boundary_is_conservative() {
let air = StarkAirParams {
num_constraints: 1,
max_constraint_degree: 2,
num_quotient_chunks: 1,
max_combo: 2,
};
let mut shape = InstanceShape {
log_trace_length: 63,
modulus_bits: 128,
collision_resistance: 128,
num_batched_functions: 1,
};
let expected = 128.0 - 63.0 - log2(3.0);
assert!((deep_ali_error(&air, &shape, 1.0).bits() - expected).abs() < 1e-12);
for log_trace_length in [64, 65, usize::MAX] {
shape.log_trace_length = log_trace_length;
assert_eq!(deep_ali_error(&air, &shape, 1.0).bits(), 0.0);
}
}
}