use super::hash_tables::constants::{BLS_SCALAR_REAL, DECOMPOSITION_S_I, SBOX};
use crate::error::Error;
use crate::plonkup::MultiSet;
use crate::prelude::BlsScalar;
use alloc::vec::Vec;
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct LookupTable(pub Vec<[BlsScalar; 4]>);
impl Default for LookupTable {
fn default() -> Self {
LookupTable::new()
}
}
impl LookupTable {
pub fn new() -> Self {
LookupTable(vec![])
}
pub fn insert_add_row(&mut self, a: u64, b: u64, upper_bound: u64) {
let c = (a + b) % upper_bound;
self.0.push([
BlsScalar::from(a),
BlsScalar::from(b),
BlsScalar::from(c),
BlsScalar::zero(),
]);
}
pub fn insert_special_row(
&mut self,
a: BlsScalar,
b: BlsScalar,
c: BlsScalar,
d: BlsScalar,
) {
self.0.push([a, b, c, d]);
}
pub fn insert_mul_row(&mut self, a: u64, b: u64, upper_bound: u64) {
let c = (a * b) % upper_bound;
self.0.push([
BlsScalar::from(a),
BlsScalar::from(b),
BlsScalar::from(c),
BlsScalar::one(),
]);
}
pub fn insert_xor_row(&mut self, a: u64, b: u64, upper_bound: u64) {
let c = (a ^ b) % upper_bound;
self.0.push([
BlsScalar::from(a),
BlsScalar::from(b),
BlsScalar::from(c),
-BlsScalar::one(),
]);
}
pub fn insert_and_row(&mut self, a: u64, b: u64, upper_bound: u64) {
let c = (a & b) % upper_bound;
self.0.push([
BlsScalar::from(a),
BlsScalar::from(b),
BlsScalar::from(c),
BlsScalar::from(2u64),
]);
}
pub fn insert_multi_add(&mut self, lower_bound: u64, n: u8) {
let upper_bound = 2u64.pow(n.into());
let range = lower_bound..upper_bound;
for a in range.clone() {
range
.clone()
.for_each(|b| self.insert_add_row(a, b, upper_bound));
}
}
pub fn insert_multi_mul(&mut self, lower_bound: u64, n: u8) {
let upper_bound = 2u64.pow(n.into());
let range = lower_bound..upper_bound;
for a in range.clone() {
range
.clone()
.for_each(|b| self.insert_mul_row(a, b, upper_bound));
}
}
pub fn insert_multi_xor(&mut self, lower_bound: u64, n: u8) {
let upper_bound = 2u64.pow(n.into());
let range = lower_bound..upper_bound;
for a in range.clone() {
range
.clone()
.for_each(|b| self.insert_xor_row(a, b, upper_bound));
}
}
pub fn insert_multi_and(&mut self, lower_bound: u64, n: u8) {
let upper_bound = 2u64.pow(n.into());
let range = lower_bound..upper_bound;
for a in range.clone() {
range
.clone()
.for_each(|b| self.insert_and_row(a, b, upper_bound));
}
}
pub fn vec_to_multiset(&self) -> (MultiSet, MultiSet, MultiSet, MultiSet) {
let mut multiset_a = MultiSet::new();
let mut multiset_b = MultiSet::new();
let mut multiset_c = MultiSet::new();
let mut multiset_d = MultiSet::new();
self.0.iter().for_each(|row| {
multiset_a.push(row[0]);
multiset_b.push(row[1]);
multiset_c.push(row[2]);
multiset_d.push(row[3]);
});
(multiset_a, multiset_b, multiset_c, multiset_d)
}
pub fn lookup(
&self,
a: BlsScalar,
b: BlsScalar,
d: BlsScalar,
) -> Result<BlsScalar, Error> {
let pos = self
.0
.iter()
.position(|row| row[0] == a && row[1] == b && row[3] == d)
.ok_or(Error::ElementNotIndexed)?;
Ok(self.0[pos][2])
}
pub fn create_hash_table() -> Self {
let mut table = Vec::new();
let two = BlsScalar::from(2);
(0..2).for_each(|i| {
(0..2).for_each(|j| {
(0..2).for_each(|k| {
if (i, j, k) != (1, 1, 1) {
table.push([
BlsScalar::one(),
BlsScalar::from(i),
BlsScalar::from(j),
BlsScalar::from(k),
])
}
})
})
});
(0..2).for_each(|i| {
(0..2).for_each(|j| {
if (i, j) != (1, 1) {
table.push([
BlsScalar::zero(),
BlsScalar::one(),
BlsScalar::from(i),
BlsScalar::from(j),
])
}
})
});
table.push([
BlsScalar::zero(),
BlsScalar::zero(),
BlsScalar::one(),
BlsScalar::zero(),
]);
(0..2).for_each(|i| {
table.push([
BlsScalar::zero(),
BlsScalar::zero(),
BlsScalar::zero(),
BlsScalar::from(i),
])
});
(0..2).for_each(|i| {
(1..3).for_each(|j| {
table.push([
BlsScalar::zero(),
BlsScalar::from(i),
BlsScalar::one(),
BlsScalar::from(j),
])
})
});
(1..3).for_each(|i| {
table.push([
BlsScalar::zero(),
BlsScalar::one(),
two,
BlsScalar::from(i),
])
});
(1..3).for_each(|i| {
(1..3).for_each(|j| {
(1..3).for_each(|k| {
(1..3).for_each(|m| {
table.push([
BlsScalar::from(i),
BlsScalar::from(j),
BlsScalar::from(k),
BlsScalar::from(m),
])
})
})
})
});
for k in 0..659 {
let first = BlsScalar::from(k);
let third = BlsScalar::from_raw([SBOX[k as usize] as u64, 0, 0, 0]);
table.push([first, BlsScalar::zero(), third, BlsScalar::one()]);
}
for k in (0..27).rev() {
let s_rev_k = DECOMPOSITION_S_I[k].0[0];
let v_rev_k = BLS_SCALAR_REAL[k] as u64;
if k == 26 {
for j in 659..(v_rev_k + 1) {
let first = BlsScalar::from(j as u64);
let fourth = if j == v_rev_k {
BlsScalar::zero()
} else {
BlsScalar::one()
};
table.push([first, BlsScalar::one(), first, fourth]);
}
} else {
let second = BlsScalar::from((27 - k) as u64);
for j in 659..v_rev_k {
let first = BlsScalar::from(j);
table.push([first, second, first, BlsScalar::one()]);
}
let first = BlsScalar::from(v_rev_k);
table.push([first, second, first, BlsScalar::zero()]);
table.push([first, second, first, two]);
for j in (v_rev_k + 1)..s_rev_k {
let first = BlsScalar::from(j);
table.push([first, second, first, two]);
}
}
}
LookupTable(table)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_add_table() {
let n = 4;
let table = {
let mut table = LookupTable::default();
table.insert_multi_add(0, n);
table
};
let mut i = 0;
let p = 2u64.pow(n as u32);
(0..p).for_each(|a| {
(0..p).for_each(|b| {
let c = (a + b) % p;
assert_eq!(BlsScalar::from(c), table.0[i][2]);
i += 1;
})
});
assert_eq!(
table.0.len() as u64,
2u64.pow(n as u32) * 2u64.pow(n as u32)
);
}
#[test]
fn test_xor_table() {
let n = 4;
let table = {
let mut table = LookupTable::default();
table.insert_multi_xor(0, n);
table
};
let mut i = 0;
let p = 2u64.pow(n as u32);
(0..p).for_each(|a| {
(0..p).for_each(|b| {
let c = (a ^ b) % p;
assert_eq!(BlsScalar::from(c), table.0[i][2]);
i += 1;
})
});
assert_eq!(
table.0.len() as u64,
2u64.pow(n as u32) * 2u64.pow(n as u32)
);
}
#[test]
fn test_mul_table() {
let n = 4;
let table = {
let mut table = LookupTable::default();
table.insert_multi_mul(0, n);
table
};
let mut i = 0;
let p = 2u64.pow(n as u32);
(0..p).for_each(|a| {
(0..p).for_each(|b| {
let c = (a * b) % p;
assert_eq!(BlsScalar::from(c), table.0[i][2]);
i += 1;
})
});
assert_eq!(
table.0.len() as u64,
2u64.pow(n as u32) * 2u64.pow(n as u32)
);
}
#[test]
fn test_lookup() {
let add_table = {
let mut table = LookupTable::default();
table.insert_multi_add(0, 3);
table
};
assert!(add_table
.lookup(BlsScalar::from(2), BlsScalar::from(3), BlsScalar::zero())
.is_ok());
let output = add_table.0[1][0] + add_table.0[1][1] + add_table.0[1][2];
assert_eq!(output, BlsScalar::from(2));
let second_output =
add_table.0[12][0] + add_table.0[12][1] + add_table.0[12][2];
assert_eq!(second_output, BlsScalar::from(10));
}
#[test]
fn test_missing_lookup_value() {
let xor_table = {
let mut table = LookupTable::default();
table.insert_multi_xor(0, 5);
table
};
assert!(xor_table
.lookup(
BlsScalar::from(17),
BlsScalar::from(367),
BlsScalar::from(1)
)
.is_err());
}
#[test]
fn test_concatenated_table() {
let mut table = LookupTable::new();
table.insert_multi_xor(0, 5);
table.insert_multi_add(4, 7);
assert_eq!(table.0.last().unwrap()[2], BlsScalar::from(126u64));
let xor = table.0[36][0] ^ table.0[36][1] ^ table.0[36][2];
assert_eq!(xor, BlsScalar::zero());
}
}