kimchi/circuits/lookup/tables/
mod.rs1use ark_ff::{FftField, One, Zero};
2use poly_commitment::PolyComm;
3use serde::{Deserialize, Serialize};
4
5pub mod range_check;
6pub mod xor;
7
8pub const XOR_TABLE_ID: i32 = 0;
14
15pub const RANGE_CHECK_TABLE_ID: i32 = 1;
17#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
21pub enum GateLookupTable {
22 Xor,
23 RangeCheck,
24}
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub struct GateLookupTables {
29 pub xor: bool,
30 pub range_check: bool,
31}
32
33impl core::ops::Index<GateLookupTable> for GateLookupTables {
34 type Output = bool;
35
36 fn index(&self, index: GateLookupTable) -> &Self::Output {
37 match index {
38 GateLookupTable::Xor => &self.xor,
39 GateLookupTable::RangeCheck => &self.range_check,
40 }
41 }
42}
43
44impl core::ops::IndexMut<GateLookupTable> for GateLookupTables {
45 fn index_mut(&mut self, index: GateLookupTable) -> &mut Self::Output {
46 match index {
47 GateLookupTable::Xor => &mut self.xor,
48 GateLookupTable::RangeCheck => &mut self.range_check,
49 }
50 }
51}
52
53impl IntoIterator for GateLookupTables {
54 type Item = GateLookupTable;
55 type IntoIter = std::vec::IntoIter<Self::Item>;
56
57 fn into_iter(self) -> Self::IntoIter {
58 let GateLookupTables { xor, range_check } = self;
60
61 let mut patterns = Vec::with_capacity(2);
62
63 if xor {
64 patterns.push(GateLookupTable::Xor)
65 }
66 if range_check {
67 patterns.push(GateLookupTable::RangeCheck)
68 }
69 patterns.into_iter()
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct LookupTable<F> {
76 pub id: i32,
77 pub data: Vec<Vec<F>>,
78}
79
80impl<F> LookupTable<F>
81where
82 F: FftField,
83{
84 pub fn has_zero_entry(&self) -> bool {
86 for row in 0..self.len() {
89 if self.data.iter().all(|col| col[row].is_zero()) {
90 return true;
91 }
92 }
93 false
94 }
95
96 pub fn width(&self) -> usize {
100 self.data.len()
101 }
102
103 pub fn len(&self) -> usize {
105 self.data[0].len()
106 }
107
108 pub fn is_empty(&self) -> bool {
110 self.data.is_empty()
111 }
112}
113
114pub fn get_table<F: FftField>(table_name: GateLookupTable) -> LookupTable<F> {
116 match table_name {
117 GateLookupTable::Xor => xor::xor_table(),
118 GateLookupTable::RangeCheck => range_check::range_check_table(),
119 }
120}
121
122impl GateLookupTable {
123 pub fn table_size(&self) -> usize {
125 match self {
126 GateLookupTable::Xor => xor::TABLE_SIZE,
127 GateLookupTable::RangeCheck => range_check::TABLE_SIZE,
128 }
129 }
130}
131
132pub fn combine_table_entry<'a, F, I>(
146 joint_combiner: &F,
147 table_id_combiner: &F,
148 v: I,
149 table_id: &F,
151) -> F
152where
153 F: 'a, F: Zero + One + Clone,
155 I: DoubleEndedIterator<Item = &'a F>,
156{
157 v.rev()
158 .fold(F::zero(), |acc, x| joint_combiner.clone() * acc + x.clone())
159 + table_id_combiner.clone() * table_id.clone()
160}
161
162pub fn combine_table<G>(
169 columns: &[&PolyComm<G>],
170 column_combiner: G::ScalarField,
171 table_id_combiner: G::ScalarField,
172 table_id_vector: Option<&PolyComm<G>>,
173 runtime_vector: Option<&PolyComm<G>>,
174) -> PolyComm<G>
175where
176 G: poly_commitment::commitment::CommitmentCurve,
177{
178 assert!(!columns.is_empty());
179
180 let mut j = G::ScalarField::one();
182 let mut scalars = vec![j];
183 let mut commitments = vec![columns[0]];
184 for comm in columns.iter().skip(1) {
185 j *= column_combiner;
186 scalars.push(j);
187 commitments.push(comm);
188 }
189
190 if let Some(table_id) = table_id_vector {
192 scalars.push(table_id_combiner);
193 commitments.push(table_id);
194 }
195
196 if let Some(runtime) = runtime_vector {
198 scalars.push(column_combiner); commitments.push(runtime);
200 }
201
202 PolyComm::multi_scalar_mul(&commitments, &scalars)
203}
204
205#[cfg(feature = "ocaml_types")]
206pub mod caml {
207 use ark_ff::PrimeField;
208 use ocaml;
209 use ocaml_gen;
210
211 use super::LookupTable;
212
213 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
218 pub struct CamlLookupTable<CamlF> {
219 pub id: i32,
220 pub data: Vec<Vec<CamlF>>,
221 }
222
223 impl<F, CamlF> From<CamlLookupTable<CamlF>> for LookupTable<F>
224 where
225 F: PrimeField,
226 CamlF: Into<F>,
227 {
228 fn from(caml_lt: CamlLookupTable<CamlF>) -> Self {
229 Self {
230 id: caml_lt.id,
231 data: caml_lt
232 .data
233 .into_iter()
234 .map(|t| t.into_iter().map(Into::into).collect())
235 .collect(),
236 }
237 }
238 }
239
240 impl<F, CamlF> From<LookupTable<F>> for CamlLookupTable<CamlF>
241 where
242 F: PrimeField,
243 CamlF: From<F>,
244 {
245 fn from(lt: LookupTable<F>) -> Self {
246 Self {
247 id: lt.id,
248 data: lt
249 .data
250 .into_iter()
251 .map(|t| t.into_iter().map(Into::into).collect())
252 .collect(),
253 }
254 }
255 }
256}