extern crate alloc;
use std::sync::Mutex;
use alloc::vec::Vec;
use cyb_lens_brakedown::Brakedown;
use cyb_lens_core::{Commitment, Lens, MultilinearPoly};
use nebu::Goldilocks;
use crate::data::{Reduction, Order};
use crate::call::{CallProvider, LookProvider};
pub struct LookOpening {
pub namespace: Goldilocks,
pub key: Goldilocks,
pub value: Goldilocks,
}
pub struct BrakedownLookProvider {
poly: MultilinearPoly<Goldilocks>,
commitment: Commitment,
commitment_field: Goldilocks,
openings: Mutex<Vec<LookOpening>>,
}
impl BrakedownLookProvider {
pub fn new(poly: MultilinearPoly<Goldilocks>) -> Self {
let commitment = Brakedown::commit(&poly);
let bytes = commitment.as_bytes();
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[0..8]);
let commitment_field = Goldilocks::new(u64::from_le_bytes(buf));
Self { poly, commitment, commitment_field, openings: Mutex::new(Vec::new()) }
}
pub fn drain_openings(&self) -> Vec<LookOpening> {
let mut guard = self.openings.lock().unwrap();
core::mem::take(&mut *guard)
}
pub fn poly(&self) -> &MultilinearPoly<Goldilocks> { &self.poly }
pub fn commitment(&self) -> &Commitment { &self.commitment }
pub fn commitment_field(&self) -> Goldilocks { self.commitment_field }
}
impl LookProvider for BrakedownLookProvider {
fn look(&self, _commitment: Goldilocks, namespace: Goldilocks, key: Goldilocks) -> Option<Goldilocks> {
let idx = key.as_u64() as usize;
if idx >= self.poly.evals.len() {
return None;
}
let value = self.poly.evals[idx];
self.openings.lock().unwrap().push(LookOpening { namespace, key, value });
Some(value)
}
}
impl<const N: usize> CallProvider<N> for BrakedownLookProvider {
fn provide(&self, _order: &mut Reduction<N>, _tag: Goldilocks, _object: Order) -> Option<Order> {
None
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use crate::reduce::{reduce, Outcome};
use crate::trace::NoTrace;
use crate::data::{Reduction};
fn g(v: u64) -> Goldilocks { Goldilocks::new(v) }
fn make_object<const N: usize>(
reduction: &mut Reduction<N>,
l0: Goldilocks, l1: Goldilocks, l2: Goldilocks, l3: Goldilocks,
rest: Order,
) -> Order {
let a0 = reduction.atom(l0).unwrap();
let a1 = reduction.atom(l1).unwrap();
let a2 = reduction.atom(l2).unwrap();
let a3 = reduction.atom(l3).unwrap();
let inner = reduction.pair(a2, a3).unwrap();
let mid = reduction.pair(a1, inner).unwrap();
let root_pair = reduction.pair(a0, mid).unwrap();
reduction.pair(root_pair, rest).unwrap()
}
fn make_look<const N: usize>(reduction: &mut Reduction<N>, ns: u64, key: u64) -> Order {
let t17 = reduction.atom(g(17)).unwrap();
let t1 = reduction.atom(g(1)).unwrap();
let vns = reduction.atom(g(ns)).unwrap();
let vkey = reduction.atom(g(key)).unwrap();
let ns_formula = reduction.pair(t1, vns).unwrap();
let key_formula = reduction.pair(t1, vkey).unwrap();
let body = reduction.pair(ns_formula, key_formula).unwrap();
reduction.pair(t17, body).unwrap()
}
#[test]
fn look_returns_table_value() {
let evals = alloc::vec![g(10), g(20), g(30), g(40)];
let provider = BrakedownLookProvider::new(MultilinearPoly::new(evals));
let l0 = provider.commitment_field();
let mut ar = Reduction::<512>::new();
let rest = ar.atom(g(0)).unwrap();
let object = make_object(&mut ar, l0, g(0), g(0), g(0), rest);
let formula = make_look(&mut ar, 0, 2);
match reduce(&mut ar, object, formula, 10_000, &provider, &mut NoTrace) {
Outcome::Ok(r, _) => {
let v = ar.atom_value(r).unwrap();
assert_eq!(v, g(30));
}
o => panic!("{:?}", o),
}
}
#[test]
fn drain_openings_cleared_after_drain() {
let evals = alloc::vec![g(1), g(2)];
let provider = BrakedownLookProvider::new(MultilinearPoly::new(evals));
provider.look(g(0), g(0), g(0));
provider.look(g(0), g(0), g(1));
let ops = provider.drain_openings();
assert_eq!(ops.len(), 2);
assert!(provider.drain_openings().is_empty());
}
#[test]
fn look_out_of_bounds_returns_none() {
let evals = alloc::vec![g(1), g(2)];
let provider = BrakedownLookProvider::new(MultilinearPoly::new(evals));
assert!(provider.look(g(0), g(0), g(99)).is_none());
}
}