openvm_circuit_primitives/range/
mod.rs1use core::mem::size_of;
8use std::{
9 borrow::{Borrow, BorrowMut},
10 sync::atomic::AtomicU32,
11};
12
13use openvm_circuit_primitives_derive::AlignedBorrow;
14use openvm_stark_backend::{
15 interaction::InteractionBuilder,
16 p3_air::{Air, BaseAir, PairBuilder},
17 p3_field::Field,
18 p3_matrix::{dense::RowMajorMatrix, Matrix},
19 rap::{BaseAirWithPublicValues, PartitionedBaseAir},
20};
21
22mod bus;
23
24#[cfg(test)]
25pub mod tests;
26
27pub use bus::*;
28
29#[derive(Default, AlignedBorrow, Copy, Clone)]
30#[repr(C)]
31pub struct RangeCols<T> {
32 pub mult: T,
34}
35
36#[derive(Default, AlignedBorrow, Copy, Clone)]
37#[repr(C)]
38pub struct RangePreprocessedCols<T> {
39 pub counter: T,
41}
42
43pub const NUM_RANGE_COLS: usize = size_of::<RangeCols<u8>>();
44pub const NUM_RANGE_PREPROCESSED_COLS: usize = size_of::<RangePreprocessedCols<u8>>();
45
46#[derive(Clone, Copy, Debug, derive_new::new)]
47pub struct RangeCheckerAir {
48 pub bus: RangeCheckBus,
49}
50
51impl RangeCheckerAir {
52 pub fn range_max(&self) -> u32 {
53 self.bus.range_max
54 }
55}
56
57impl<F: Field> BaseAirWithPublicValues<F> for RangeCheckerAir {}
58impl<F: Field> PartitionedBaseAir<F> for RangeCheckerAir {}
59impl<F: Field> BaseAir<F> for RangeCheckerAir {
60 fn width(&self) -> usize {
61 NUM_RANGE_COLS
62 }
63
64 fn preprocessed_trace(&self) -> Option<RowMajorMatrix<F>> {
65 let column = (0..self.range_max()).map(F::from_u32).collect();
67 Some(RowMajorMatrix::new_col(column))
68 }
69}
70
71impl<AB: InteractionBuilder + PairBuilder> Air<AB> for RangeCheckerAir {
72 fn eval(&self, builder: &mut AB) {
73 let preprocessed = builder.preprocessed();
74 let prep_local = preprocessed
75 .row_slice(0)
76 .expect("window should have two elements");
77 let prep_local: &RangePreprocessedCols<AB::Var> = (*prep_local).borrow();
78 let main = builder.main();
79 let local = main.row_slice(0).expect("window should have two elements");
80 let local: &RangeCols<AB::Var> = (*local).borrow();
81 self.bus
83 .receive(prep_local.counter)
84 .eval(builder, local.mult);
85 }
86}
87
88pub struct RangeCheckerChip {
89 pub air: RangeCheckerAir,
90 count: Vec<AtomicU32>,
92}
93
94impl RangeCheckerChip {
95 pub fn new(bus: RangeCheckBus) -> Self {
96 let mut count = vec![];
97 for _ in 0..bus.range_max {
98 count.push(AtomicU32::new(0));
99 }
100
101 Self {
102 air: RangeCheckerAir::new(bus),
103 count,
104 }
105 }
106
107 pub fn bus(&self) -> RangeCheckBus {
108 self.air.bus
109 }
110
111 pub fn range_max(&self) -> u32 {
112 self.air.range_max()
113 }
114
115 pub fn add_count(&self, val: u32) {
116 let val_atomic = &self.count[val as usize];
118 val_atomic.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
119 }
120
121 pub fn generate_trace<F: Field>(&self) -> RowMajorMatrix<F> {
122 let mut rows = F::zero_vec(self.air.range_max() as usize * NUM_RANGE_COLS);
123 for (n, row) in rows.chunks_exact_mut(NUM_RANGE_COLS).enumerate() {
124 let cols: &mut RangeCols<F> = (*row).borrow_mut();
125 cols.mult = F::from_u32(self.count[n].swap(0, std::sync::atomic::Ordering::Relaxed));
127 }
128 RowMajorMatrix::new(rows, NUM_RANGE_COLS)
129 }
130}