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
26impl PartialOrd for GateLookupTable {
29 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
30 Some(self.cmp(other))
31 }
32}
33
34impl Ord for GateLookupTable {
35 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
36 fn sort_key(t: &GateLookupTable) -> u8 {
37 match t {
38 GateLookupTable::RangeCheck => 0,
39 GateLookupTable::Xor => 1,
40 }
41 }
42 sort_key(self).cmp(&sort_key(other))
43 }
44}
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
48pub struct GateLookupTables {
49 pub xor: bool,
50 pub range_check: bool,
51}
52
53impl core::ops::Index<GateLookupTable> for GateLookupTables {
54 type Output = bool;
55
56 fn index(&self, index: GateLookupTable) -> &Self::Output {
57 match index {
58 GateLookupTable::Xor => &self.xor,
59 GateLookupTable::RangeCheck => &self.range_check,
60 }
61 }
62}
63
64impl core::ops::IndexMut<GateLookupTable> for GateLookupTables {
65 fn index_mut(&mut self, index: GateLookupTable) -> &mut Self::Output {
66 match index {
67 GateLookupTable::Xor => &mut self.xor,
68 GateLookupTable::RangeCheck => &mut self.range_check,
69 }
70 }
71}
72
73impl IntoIterator for GateLookupTables {
74 type Item = GateLookupTable;
75 type IntoIter = std::vec::IntoIter<Self::Item>;
76
77 fn into_iter(self) -> Self::IntoIter {
78 let GateLookupTables { xor, range_check } = self;
80
81 let mut patterns = Vec::with_capacity(2);
82
83 if xor {
84 patterns.push(GateLookupTable::Xor)
85 }
86 if range_check {
87 patterns.push(GateLookupTable::RangeCheck)
88 }
89 patterns.into_iter()
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct LookupTable<F> {
96 pub id: i32,
97 pub data: Vec<Vec<F>>,
98}
99
100impl<F> LookupTable<F>
101where
102 F: FftField,
103{
104 pub fn has_zero_entry(&self) -> bool {
106 for row in 0..self.len() {
109 if self.data.iter().all(|col| col[row].is_zero()) {
110 return true;
111 }
112 }
113 false
114 }
115
116 pub fn width(&self) -> usize {
120 self.data.len()
121 }
122
123 pub fn len(&self) -> usize {
125 self.data[0].len()
126 }
127
128 pub fn is_empty(&self) -> bool {
130 self.data.is_empty()
131 }
132}
133
134pub fn get_table<F: FftField>(table_name: GateLookupTable) -> LookupTable<F> {
136 match table_name {
137 GateLookupTable::Xor => xor::xor_table(),
138 GateLookupTable::RangeCheck => range_check::range_check_table(),
139 }
140}
141
142impl GateLookupTable {
143 pub fn table_size(&self) -> usize {
145 match self {
146 GateLookupTable::Xor => xor::TABLE_SIZE,
147 GateLookupTable::RangeCheck => range_check::TABLE_SIZE,
148 }
149 }
150}
151
152pub fn combine_table_entry<'a, F, I>(
166 joint_combiner: &F,
167 table_id_combiner: &F,
168 v: I,
169 table_id: &F,
171) -> F
172where
173 F: 'a, F: Zero + One + Clone,
175 I: DoubleEndedIterator<Item = &'a F>,
176{
177 v.rev()
178 .fold(F::zero(), |acc, x| joint_combiner.clone() * acc + x.clone())
179 + table_id_combiner.clone() * table_id.clone()
180}
181
182pub fn combine_table<G>(
189 columns: &[&PolyComm<G>],
190 column_combiner: G::ScalarField,
191 table_id_combiner: G::ScalarField,
192 table_id_vector: Option<&PolyComm<G>>,
193 runtime_vector: Option<&PolyComm<G>>,
194) -> PolyComm<G>
195where
196 G: poly_commitment::commitment::CommitmentCurve,
197{
198 assert!(!columns.is_empty());
199
200 let mut j = G::ScalarField::one();
202 let mut scalars = vec![j];
203 let mut commitments = vec![columns[0]];
204 for comm in columns.iter().skip(1) {
205 j *= column_combiner;
206 scalars.push(j);
207 commitments.push(comm);
208 }
209
210 if let Some(table_id) = table_id_vector {
212 scalars.push(table_id_combiner);
213 commitments.push(table_id);
214 }
215
216 if let Some(runtime) = runtime_vector {
218 scalars.push(column_combiner); commitments.push(runtime);
220 }
221
222 PolyComm::multi_scalar_mul(&commitments, &scalars)
223}
224
225#[cfg(feature = "ocaml_types")]
226pub mod caml {
227 use ark_ff::PrimeField;
228 use ocaml;
229 use ocaml_gen;
230
231 use super::LookupTable;
232
233 #[derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)]
238 pub struct CamlLookupTable<CamlF> {
239 pub id: i32,
240 pub data: Vec<Vec<CamlF>>,
241 }
242
243 impl<F, CamlF> From<CamlLookupTable<CamlF>> for LookupTable<F>
244 where
245 F: PrimeField,
246 CamlF: Into<F>,
247 {
248 fn from(caml_lt: CamlLookupTable<CamlF>) -> Self {
249 Self {
250 id: caml_lt.id,
251 data: caml_lt
252 .data
253 .into_iter()
254 .map(|t| t.into_iter().map(Into::into).collect())
255 .collect(),
256 }
257 }
258 }
259
260 impl<F, CamlF> From<LookupTable<F>> for CamlLookupTable<CamlF>
261 where
262 F: PrimeField,
263 CamlF: From<F>,
264 {
265 fn from(lt: LookupTable<F>) -> Self {
266 Self {
267 id: lt.id,
268 data: lt
269 .data
270 .into_iter()
271 .map(|t| t.into_iter().map(Into::into).collect())
272 .collect(),
273 }
274 }
275 }
276}