use std::collections::BTreeMap;
use std::sync::{Arc, Mutex, OnceLock};
use getset::{CopyGetters, Getters, Setters};
use crate::ff::Field;
use crate::halo2_proofs::{
circuit::{Region, Value},
plonk::{Advice, Column},
};
use crate::utils::halo2::{constrain_virtual_equals_external, raw_assign_advice};
use crate::{AssignedValue, ContextTag};
use super::copy_constraints::SharedCopyConstraintManager;
use super::manager::VirtualRegionManager;
pub mod basic;
#[derive(Clone, Debug, Getters, CopyGetters, Setters)]
pub struct LookupAnyManager<F: Field + Ord, const ADVICE_COLS: usize> {
#[allow(clippy::type_complexity)]
pub cells_to_lookup: Arc<Mutex<BTreeMap<ContextTag, Vec<[AssignedValue<F>; ADVICE_COLS]>>>>,
#[getset(get = "pub", set = "pub")]
copy_manager: SharedCopyConstraintManager<F>,
#[getset(get_copy = "pub")]
witness_gen_only: bool,
pub(crate) assigned: Arc<OnceLock<()>>,
}
impl<F: Field + Ord, const ADVICE_COLS: usize> LookupAnyManager<F, ADVICE_COLS> {
pub fn new(witness_gen_only: bool, copy_manager: SharedCopyConstraintManager<F>) -> Self {
Self {
witness_gen_only,
cells_to_lookup: Default::default(),
copy_manager,
assigned: Default::default(),
}
}
pub fn add_lookup(&self, tag: ContextTag, cells: [AssignedValue<F>; ADVICE_COLS]) {
self.cells_to_lookup
.lock()
.unwrap()
.entry(tag)
.and_modify(|thread| thread.push(cells))
.or_insert(vec![cells]);
}
pub fn total_rows(&self) -> usize {
self.cells_to_lookup.lock().unwrap().iter().flat_map(|(_, advices)| advices).count()
}
pub fn num_advice_chunks(&self, usable_rows: usize) -> usize {
let total = self.total_rows();
total.div_ceil(usable_rows)
}
pub fn clear(&mut self) {
self.cells_to_lookup.lock().unwrap().clear();
self.copy_manager.lock().unwrap().clear();
self.assigned = Arc::new(OnceLock::new());
}
pub fn deep_clone(&self, copy_manager: SharedCopyConstraintManager<F>) -> Self {
Self {
witness_gen_only: self.witness_gen_only,
cells_to_lookup: Arc::new(Mutex::new(self.cells_to_lookup.lock().unwrap().clone())),
copy_manager,
assigned: Default::default(),
}
}
}
impl<F: Field + Ord, const ADVICE_COLS: usize> Drop for LookupAnyManager<F, ADVICE_COLS> {
fn drop(&mut self) {
if Arc::strong_count(&self.cells_to_lookup) > 1 {
return;
}
if self.total_rows() > 0 && self.assigned.get().is_none() {
log::warn!("WARNING: LookupAnyManager was not assigned!");
}
}
}
impl<F: Field + Ord, const ADVICE_COLS: usize> VirtualRegionManager<F>
for LookupAnyManager<F, ADVICE_COLS>
{
type Config = Vec<[Column<Advice>; ADVICE_COLS]>;
type Assignment = ();
fn assign_raw(&self, config: &Self::Config, region: &mut Region<F>) {
let mut copy_manager =
(!self.witness_gen_only).then(|| self.copy_manager().lock().unwrap());
let cells_to_lookup = self.cells_to_lookup.lock().unwrap();
let mut lookup_offset = 0;
let mut lookup_col = 0;
for advices in cells_to_lookup.iter().flat_map(|(_, advices)| advices) {
if lookup_col >= config.len() {
lookup_col = 0;
lookup_offset += 1;
}
for (advice, &column) in advices.iter().zip(config[lookup_col].iter()) {
let bcell =
raw_assign_advice(region, column, lookup_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;
}
let _ = self.assigned.set(());
}
}