1use super::{util::*, AssignedBits};
2
3use group::ff::{Field, PrimeField};
4use halo2_proofs::{
5 circuit::{Chip, Layouter, Region, Value},
6 pasta::pallas,
7 plonk::{Advice, Column, ConstraintSystem, Error, TableColumn},
8 poly::Rotation,
9};
10use std::convert::TryInto;
11use std::marker::PhantomData;
12
13const BITS_7: usize = 1 << 7;
14const BITS_10: usize = 1 << 10;
15const BITS_11: usize = 1 << 11;
16const BITS_13: usize = 1 << 13;
17const BITS_14: usize = 1 << 14;
18
19#[derive(Copy, Clone, Debug)]
21pub(super) struct SpreadWord<const DENSE: usize, const SPREAD: usize> {
22 pub tag: u8,
23 pub dense: [bool; DENSE],
24 pub spread: [bool; SPREAD],
25}
26
27pub fn get_tag(input: u16) -> u8 {
29 let input = input as usize;
30 if input < BITS_7 {
31 0
32 } else if input < BITS_10 {
33 1
34 } else if input < BITS_11 {
35 2
36 } else if input < BITS_13 {
37 3
38 } else if input < BITS_14 {
39 4
40 } else {
41 5
42 }
43}
44
45impl<const DENSE: usize, const SPREAD: usize> SpreadWord<DENSE, SPREAD> {
46 pub(super) fn new(dense: [bool; DENSE]) -> Self {
47 assert!(DENSE <= 16);
48 SpreadWord {
49 tag: get_tag(lebs2ip(&dense) as u16),
50 dense,
51 spread: spread_bits(dense),
52 }
53 }
54
55 pub(super) fn try_new<T: TryInto<[bool; DENSE]> + std::fmt::Debug>(dense: T) -> Self
56 where
57 <T as TryInto<[bool; DENSE]>>::Error: std::fmt::Debug,
58 {
59 assert!(DENSE <= 16);
60 let dense: [bool; DENSE] = dense.try_into().unwrap();
61 SpreadWord {
62 tag: get_tag(lebs2ip(&dense) as u16),
63 dense,
64 spread: spread_bits(dense),
65 }
66 }
67}
68
69#[derive(Clone, Debug)]
71pub(super) struct SpreadVar<const DENSE: usize, const SPREAD: usize> {
72 pub _tag: Value<u8>,
73 pub dense: AssignedBits<DENSE>,
74 pub spread: AssignedBits<SPREAD>,
75}
76
77impl<const DENSE: usize, const SPREAD: usize> SpreadVar<DENSE, SPREAD> {
78 pub(super) fn with_lookup(
79 region: &mut Region<'_, pallas::Base>,
80 cols: &SpreadInputs,
81 row: usize,
82 word: Value<SpreadWord<DENSE, SPREAD>>,
83 ) -> Result<Self, Error> {
84 let tag = word.map(|word| word.tag);
85 let dense_val = word.map(|word| word.dense);
86 let spread_val = word.map(|word| word.spread);
87
88 region.assign_advice(
89 || "tag",
90 cols.tag,
91 row,
92 || tag.map(|tag| pallas::Base::from(tag as u64)),
93 )?;
94
95 let dense =
96 AssignedBits::<DENSE>::assign_bits(region, || "dense", cols.dense, row, dense_val)?;
97
98 let spread =
99 AssignedBits::<SPREAD>::assign_bits(region, || "spread", cols.spread, row, spread_val)?;
100
101 Ok(SpreadVar {
102 _tag: tag,
103 dense,
104 spread,
105 })
106 }
107
108 pub(super) fn without_lookup(
109 region: &mut Region<'_, pallas::Base>,
110 dense_col: Column<Advice>,
111 dense_row: usize,
112 spread_col: Column<Advice>,
113 spread_row: usize,
114 word: Value<SpreadWord<DENSE, SPREAD>>,
115 ) -> Result<Self, Error> {
116 let tag = word.map(|word| word.tag);
117 let dense_val = word.map(|word| word.dense);
118 let spread_val = word.map(|word| word.spread);
119
120 let dense = AssignedBits::<DENSE>::assign_bits(
121 region,
122 || "dense",
123 dense_col,
124 dense_row,
125 dense_val,
126 )?;
127
128 let spread = AssignedBits::<SPREAD>::assign_bits(
129 region,
130 || "spread",
131 spread_col,
132 spread_row,
133 spread_val,
134 )?;
135
136 Ok(SpreadVar {
137 _tag: tag,
138 dense,
139 spread,
140 })
141 }
142}
143
144#[derive(Clone, Debug)]
145pub(super) struct SpreadInputs {
146 pub(super) tag: Column<Advice>,
147 pub(super) dense: Column<Advice>,
148 pub(super) spread: Column<Advice>,
149}
150
151#[derive(Clone, Debug)]
152pub(super) struct SpreadTable {
153 pub(super) tag: TableColumn,
154 pub(super) dense: TableColumn,
155 pub(super) spread: TableColumn,
156}
157
158#[derive(Clone, Debug)]
159pub(super) struct SpreadTableConfig {
160 pub input: SpreadInputs,
161 pub table: SpreadTable,
162}
163
164#[derive(Clone, Debug)]
165pub(super) struct SpreadTableChip<F: Field> {
166 config: SpreadTableConfig,
167 _marker: PhantomData<F>,
168}
169
170impl<F: Field> Chip<F> for SpreadTableChip<F> {
171 type Config = SpreadTableConfig;
172 type Loaded = ();
173
174 fn config(&self) -> &Self::Config {
175 &self.config
176 }
177
178 fn loaded(&self) -> &Self::Loaded {
179 &()
180 }
181}
182
183impl<F: PrimeField> SpreadTableChip<F> {
184 pub fn configure(
185 meta: &mut ConstraintSystem<F>,
186 input_tag: Column<Advice>,
187 input_dense: Column<Advice>,
188 input_spread: Column<Advice>,
189 ) -> <Self as Chip<F>>::Config {
190 let table_tag = meta.lookup_table_column();
191 let table_dense = meta.lookup_table_column();
192 let table_spread = meta.lookup_table_column();
193
194 meta.lookup(|meta| {
195 let tag_cur = meta.query_advice(input_tag, Rotation::cur());
196 let dense_cur = meta.query_advice(input_dense, Rotation::cur());
197 let spread_cur = meta.query_advice(input_spread, Rotation::cur());
198
199 vec![
200 (tag_cur, table_tag),
201 (dense_cur, table_dense),
202 (spread_cur, table_spread),
203 ]
204 });
205
206 SpreadTableConfig {
207 input: SpreadInputs {
208 tag: input_tag,
209 dense: input_dense,
210 spread: input_spread,
211 },
212 table: SpreadTable {
213 tag: table_tag,
214 dense: table_dense,
215 spread: table_spread,
216 },
217 }
218 }
219
220 pub fn load(
221 config: SpreadTableConfig,
222 layouter: &mut impl Layouter<F>,
223 ) -> Result<<Self as Chip<F>>::Loaded, Error> {
224 layouter.assign_table(
225 || "spread table",
226 |mut table| {
227 let mut rows = SpreadTableConfig::generate::<F>();
229
230 for index in 0..(1 << 16) {
231 let mut row = None;
232 table.assign_cell(
233 || "tag",
234 config.table.tag,
235 index,
236 || {
237 row = rows.next();
238 Value::known(row.map(|(tag, _, _)| tag).unwrap())
239 },
240 )?;
241 table.assign_cell(
242 || "dense",
243 config.table.dense,
244 index,
245 || Value::known(row.map(|(_, dense, _)| dense).unwrap()),
246 )?;
247 table.assign_cell(
248 || "spread",
249 config.table.spread,
250 index,
251 || Value::known(row.map(|(_, _, spread)| spread).unwrap()),
252 )?;
253 }
254
255 Ok(())
256 },
257 )
258 }
259}
260
261impl SpreadTableConfig {
262 fn generate<F: PrimeField>() -> impl Iterator<Item = (F, F, F)> {
263 (1..=(1 << 16)).scan((F::ZERO, F::ZERO, F::ZERO), |(tag, dense, spread), i| {
264 let res = (*tag, *dense, *spread);
266
267 match i {
269 BITS_7 | BITS_10 | BITS_11 | BITS_13 | BITS_14 => *tag += F::ONE,
270 _ => (),
271 }
272 *dense += F::ONE;
273 if i & 1 == 0 {
274 *spread = F::ZERO;
276 for b in 0..16 {
277 if (i >> b) & 1 != 0 {
278 *spread += F::from(1 << (2 * b));
279 }
280 }
281 } else {
282 *spread += F::ONE;
284 }
285
286 Some(res)
287 })
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::{get_tag, SpreadTableChip, SpreadTableConfig};
294 use rand::Rng;
295
296 use group::ff::PrimeField;
297 use halo2_proofs::{
298 circuit::{Layouter, SimpleFloorPlanner, Value},
299 dev::MockProver,
300 pasta::Fp,
301 plonk::{Advice, Circuit, Column, ConstraintSystem, Error},
302 };
303
304 #[test]
305 fn lookup_table() {
306 #[derive(Copy, Clone, Debug)]
308 pub struct Variable(Column<Advice>, usize);
309
310 struct MyCircuit {}
311
312 impl<F: PrimeField> Circuit<F> for MyCircuit {
313 type Config = SpreadTableConfig;
314 type FloorPlanner = SimpleFloorPlanner;
315
316 fn without_witnesses(&self) -> Self {
317 MyCircuit {}
318 }
319
320 fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
321 let input_tag = meta.advice_column();
322 let input_dense = meta.advice_column();
323 let input_spread = meta.advice_column();
324
325 SpreadTableChip::configure(meta, input_tag, input_dense, input_spread)
326 }
327
328 fn synthesize(
329 &self,
330 config: Self::Config,
331 mut layouter: impl Layouter<F>,
332 ) -> Result<(), Error> {
333 SpreadTableChip::load(config.clone(), &mut layouter)?;
334
335 layouter.assign_region(
336 || "spread_test",
337 |mut gate| {
338 let mut row = 0;
339 let mut add_row = |tag, dense, spread| -> Result<(), Error> {
340 gate.assign_advice(
341 || "tag",
342 config.input.tag,
343 row,
344 || Value::known(tag),
345 )?;
346 gate.assign_advice(
347 || "dense",
348 config.input.dense,
349 row,
350 || Value::known(dense),
351 )?;
352 gate.assign_advice(
353 || "spread",
354 config.input.spread,
355 row,
356 || Value::known(spread),
357 )?;
358 row += 1;
359 Ok(())
360 };
361
362 add_row(F::ZERO, F::from(0b000), F::from(0b000000))?;
364 add_row(F::ZERO, F::from(0b001), F::from(0b000001))?;
365 add_row(F::ZERO, F::from(0b010), F::from(0b000100))?;
366 add_row(F::ZERO, F::from(0b011), F::from(0b000101))?;
367 add_row(F::ZERO, F::from(0b100), F::from(0b010000))?;
368 add_row(F::ZERO, F::from(0b101), F::from(0b010001))?;
369
370 add_row(F::ZERO, F::from(0b1111111), F::from(0b01010101010101))?;
373 add_row(F::ONE, F::from(0b10000000), F::from(0b0100000000000000))?;
374 add_row(
376 F::ONE,
377 F::from(0b1111111111),
378 F::from(0b01010101010101010101),
379 )?;
380 add_row(
381 F::from(2),
382 F::from(0b10000000000),
383 F::from(0b0100000000000000000000),
384 )?;
385 add_row(
387 F::from(2),
388 F::from(0b11111111111),
389 F::from(0b0101010101010101010101),
390 )?;
391 add_row(
392 F::from(3),
393 F::from(0b100000000000),
394 F::from(0b010000000000000000000000),
395 )?;
396 add_row(
398 F::from(3),
399 F::from(0b1111111111111),
400 F::from(0b01010101010101010101010101),
401 )?;
402 add_row(
403 F::from(4),
404 F::from(0b10000000000000),
405 F::from(0b0100000000000000000000000000),
406 )?;
407 add_row(
409 F::from(4),
410 F::from(0b11111111111111),
411 F::from(0b0101010101010101010101010101),
412 )?;
413 add_row(
414 F::from(5),
415 F::from(0b100000000000000),
416 F::from(0b010000000000000000000000000000),
417 )?;
418
419 let mut rng = rand::thread_rng();
421
422 fn interleave_u16_with_zeros(word: u16) -> u32 {
423 let mut word: u32 = word.into();
424 word = (word ^ (word << 8)) & 0x00ff00ff;
425 word = (word ^ (word << 4)) & 0x0f0f0f0f;
426 word = (word ^ (word << 2)) & 0x33333333;
427 word = (word ^ (word << 1)) & 0x55555555;
428 word
429 }
430
431 for _ in 0..10 {
432 let word: u16 = rng.gen();
433 add_row(
434 F::from(u64::from(get_tag(word))),
435 F::from(u64::from(word)),
436 F::from(u64::from(interleave_u16_with_zeros(word))),
437 )?;
438 }
439
440 Ok(())
441 },
442 )
443 }
444 }
445
446 let circuit: MyCircuit = MyCircuit {};
447
448 let prover = match MockProver::<Fp>::run(17, &circuit, vec![]) {
449 Ok(prover) => prover,
450 Err(e) => panic!("{:?}", e),
451 };
452 assert_eq!(prover.verify(), Ok(()));
453 }
454}