use std::iter::zip;
use crate::{
halo2_proofs::{
circuit::{Layouter, Region, Value},
halo2curves::ff::Field,
plonk::{Advice, Column, ConstraintSystem, Fixed, Phase},
poly::Rotation,
},
utils::{
halo2::{constrain_virtual_equals_external, raw_assign_advice, raw_assign_fixed},
ScalarField,
},
virtual_region::copy_constraints::SharedCopyConstraintManager,
AssignedValue,
};
#[derive(Clone, Debug)]
pub struct BasicDynLookupConfig<const KEY_COL: usize> {
pub to_lookup: Vec<([Column<Advice>; KEY_COL], Column<Fixed>)>,
pub table: [Column<Advice>; KEY_COL],
pub table_is_enabled: Column<Fixed>,
}
impl<const KEY_COL: usize> BasicDynLookupConfig<KEY_COL> {
pub fn new<P: Phase, F: Field>(
meta: &mut ConstraintSystem<F>,
phase: impl Fn() -> P,
num_lu_sets: usize,
) -> Self {
let mut make_columns = || {
let advices = [(); KEY_COL].map(|_| {
let advice = meta.advice_column_in(phase());
meta.enable_equality(advice);
advice
});
let is_enabled = meta.fixed_column();
(advices, is_enabled)
};
let (table, table_is_enabled) = make_columns();
let to_lookup: Vec<_> = (0..num_lu_sets).map(|_| make_columns()).collect();
for (key, key_is_enabled) in &to_lookup {
meta.lookup_any("dynamic lookup table", |meta| {
let table = table.map(|c| meta.query_advice(c, Rotation::cur()));
let table_is_enabled = meta.query_fixed(table_is_enabled, Rotation::cur());
let key = key.map(|c| meta.query_advice(c, Rotation::cur()));
let key_is_enabled = meta.query_fixed(*key_is_enabled, Rotation::cur());
zip(key, table).chain([(key_is_enabled, table_is_enabled)]).collect()
});
}
Self { table_is_enabled, table, to_lookup }
}
pub fn assign_virtual_to_lookup_to_raw<F: ScalarField>(
&self,
mut layouter: impl Layouter<F>,
keys: impl IntoIterator<Item = [AssignedValue<F>; KEY_COL]>,
copy_manager: Option<&SharedCopyConstraintManager<F>>,
) {
layouter
.assign_region(
|| "[BasicDynLookupConfig] Advice cells to lookup",
|mut region| {
self.assign_virtual_to_lookup_to_raw_from_offset(
&mut region,
keys,
0,
copy_manager,
);
Ok(())
},
)
.unwrap();
}
pub fn assign_virtual_to_lookup_to_raw_from_offset<F: ScalarField>(
&self,
region: &mut Region<F>,
keys: impl IntoIterator<Item = [AssignedValue<F>; KEY_COL]>,
mut offset: usize,
copy_manager: Option<&SharedCopyConstraintManager<F>>,
) {
let mut copy_manager = copy_manager.map(|c| c.lock().unwrap());
let mut lookup_col = 0;
for key in keys {
if lookup_col >= self.to_lookup.len() {
lookup_col = 0;
offset += 1;
}
let (key_col, key_is_enabled_col) = self.to_lookup[lookup_col];
raw_assign_fixed(region, key_is_enabled_col, offset, F::ONE);
for (advice, column) in zip(key, key_col) {
let bcell = raw_assign_advice(region, column, offset, Value::known(advice.value));
if let Some(copy_manager) = copy_manager.as_mut() {
constrain_virtual_equals_external(region, advice, bcell.cell(), copy_manager);
}
}
lookup_col += 1;
}
}
pub fn assign_virtual_table_to_raw<F: ScalarField>(
&self,
mut layouter: impl Layouter<F>,
rows: impl IntoIterator<Item = [AssignedValue<F>; KEY_COL]>,
copy_manager: Option<&SharedCopyConstraintManager<F>>,
) {
layouter
.assign_region(
|| "[BasicDynLookupConfig] Dynamic Lookup Table",
|mut region| {
self.assign_virtual_table_to_raw_from_offset(
&mut region,
rows,
0,
copy_manager,
);
Ok(())
},
)
.unwrap();
}
pub fn assign_virtual_table_to_raw_from_offset<F: ScalarField>(
&self,
region: &mut Region<F>,
rows: impl IntoIterator<Item = [AssignedValue<F>; KEY_COL]>,
mut offset: usize,
copy_manager: Option<&SharedCopyConstraintManager<F>>,
) {
let mut copy_manager = copy_manager.map(|c| c.lock().unwrap());
for row in rows {
raw_assign_fixed(region, self.table_is_enabled, offset, F::ONE);
for (advice, column) in zip(row, self.table) {
let bcell = raw_assign_advice(region, column, offset, Value::known(advice.value));
if let Some(copy_manager) = copy_manager.as_mut() {
constrain_virtual_equals_external(region, advice, bcell.cell(), copy_manager);
}
}
offset += 1;
}
raw_assign_fixed(region, self.table_is_enabled, offset, F::ZERO);
for col in self.table {
raw_assign_advice(region, col, offset, Value::known(F::ZERO));
}
}
}