openvm_recursion_circuit/primitives/range/
air.rs1use core::borrow::Borrow;
2
3use openvm_circuit_primitives::{ColumnsAir, StructReflection, StructReflectionHelper};
4use openvm_recursion_circuit_derive::AlignedBorrow;
5use openvm_stark_backend::{
6 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
7};
8use p3_air::{Air, AirBuilder, BaseAir};
9use p3_field::PrimeCharacteristicRing;
10use p3_matrix::Matrix;
11
12use crate::primitives::bus::{RangeCheckerBus, RangeCheckerBusMessage};
13
14#[repr(C)]
15#[derive(AlignedBorrow, Debug, StructReflection)]
16pub struct RangeCheckerCols<T> {
17 pub value: T,
18 pub mult: T,
19}
20
21#[derive(Debug, ColumnsAir)]
22#[columns_via(RangeCheckerCols<u8>)]
23pub struct RangeCheckerAir<const NUM_BITS: usize> {
24 pub bus: RangeCheckerBus,
25}
26
27impl<F, const NUM_BITS: usize> BaseAir<F> for RangeCheckerAir<NUM_BITS> {
28 fn width(&self) -> usize {
29 RangeCheckerCols::<F>::width()
30 }
31}
32impl<F, const NUM_BITS: usize> BaseAirWithPublicValues<F> for RangeCheckerAir<NUM_BITS> {}
33impl<F, const NUM_BITS: usize> PartitionedBaseAir<F> for RangeCheckerAir<NUM_BITS> {}
34
35impl<AB: AirBuilder + InteractionBuilder, const NUM_BITS: usize> Air<AB>
36 for RangeCheckerAir<NUM_BITS>
37{
38 fn eval(&self, builder: &mut AB) {
39 let main = builder.main();
40
41 let (local, next) = (
42 main.row_slice(0).expect("window should have two elements"),
43 main.row_slice(1).expect("window should have two elements"),
44 );
45 let local: &RangeCheckerCols<AB::Var> = (*local).borrow();
46 let next: &RangeCheckerCols<AB::Var> = (*next).borrow();
47
48 builder.when_first_row().assert_zero(local.value);
49 builder
50 .when_transition()
51 .assert_eq(local.value + AB::F::ONE, next.value);
52 builder
53 .when_last_row()
54 .assert_eq(local.value, AB::F::from_usize((1 << NUM_BITS) - 1));
55
56 self.bus.add_key_with_lookups(
57 builder,
58 RangeCheckerBusMessage {
59 value: local.value.into(),
60 max_bits: AB::Expr::from_usize(NUM_BITS),
61 },
62 local.mult,
63 );
64 }
65}