#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use binary_fields::BinaryFieldElement;
pub fn evaluate_lagrange_basis<F: BinaryFieldElement>(rs: &[F]) -> Vec<F> {
if rs.is_empty() {
return vec![F::one()];
}
let one = F::one();
let mut current_layer = vec![one.add(&rs[0]), rs[0]];
let mut len = 2;
for i in 1..rs.len() {
let mut next_layer = Vec::with_capacity(2 * len);
let ri_plus_one = one.add(&rs[i]);
for j in 0..len {
next_layer.push(current_layer[j].mul(&ri_plus_one));
next_layer.push(current_layer[j].mul(&rs[i]));
}
current_layer = next_layer;
len *= 2;
}
debug_assert!(
!current_layer.iter().all(|&x| x == F::zero()),
"Lagrange basis should not be all zeros"
);
current_layer
}
pub fn eval_sk_at_vks<F: BinaryFieldElement>(n: usize) -> Vec<F> {
assert!(n.is_power_of_two());
let num_subspaces = n.trailing_zeros() as usize;
let mut sks_vks = vec![F::zero(); num_subspaces + 1];
sks_vks[0] = F::one();
let mut layer: Vec<F> = (1..=num_subspaces)
.map(|i| F::from_bits(1u64 << i))
.collect();
let mut cur_len = num_subspaces;
for i in 0..num_subspaces {
for j in 0..cur_len {
let sk_at_vk = if j == 0 {
let val = layer[0].mul(&layer[0]).add(&sks_vks[i].mul(&layer[0]));
sks_vks[i + 1] = val;
val
} else {
layer[j].mul(&layer[j]).add(&sks_vks[i].mul(&layer[j]))
};
if j > 0 {
layer[j - 1] = sk_at_vk;
}
}
cur_len -= 1;
}
sks_vks
}
#[allow(dead_code)]
fn field_to_index<F: BinaryFieldElement>(elem: F) -> usize {
if elem == F::zero() {
return 0;
}
for i in 0..256 {
if F::from_bits(i as u64) == elem {
return i;
}
}
let elem_bytes = unsafe {
core::slice::from_raw_parts(&elem as *const F as *const u8, core::mem::size_of::<F>())
};
let mut result = 0usize;
let bytes_to_use = core::cmp::min(elem_bytes.len(), 8);
for i in 0..bytes_to_use {
result |= (elem_bytes[i] as usize) << (i * 8);
}
result % 4096 }
pub fn evaluate_scaled_basis_inplace<F: BinaryFieldElement, U: BinaryFieldElement>(
sks_x: &mut [F],
basis: &mut [U],
sks_vks: &[F],
qf: F,
scale: U,
) where
U: From<F>,
{
let n = basis.len();
let num_subspaces = n.trailing_zeros() as usize;
for b in basis.iter_mut() {
*b = U::zero();
}
let idx = extract_index_from_field(&qf, n);
if idx < n {
basis[idx] = scale;
}
if num_subspaces > 0 && sks_x.len() >= num_subspaces && sks_vks.len() >= num_subspaces {
sks_x[0] = qf;
for i in 1..num_subspaces {
let s_prev = sks_x[i - 1];
let s_prev_at_root = sks_vks[i - 1];
sks_x[i] = s_prev.mul(&s_prev).add(&s_prev_at_root.mul(&s_prev));
}
}
}
#[inline(always)]
fn extract_index_from_field<F: BinaryFieldElement>(elem: &F, max_n: usize) -> usize {
let elem_bytes = unsafe {
core::slice::from_raw_parts(elem as *const F as *const u8, core::mem::size_of::<F>())
};
let mut idx = 0usize;
let bytes_to_read = core::cmp::min(elem_bytes.len(), core::mem::size_of::<usize>());
for i in 0..bytes_to_read {
idx |= (elem_bytes[i] as usize) << (i * 8);
}
idx & (max_n - 1)
}
pub fn evaluate_multilinear_extension<F: BinaryFieldElement, U: BinaryFieldElement>(
basis: &mut [U],
qf: F,
scale: U,
) where
U: From<F>,
{
let n = basis.len();
if !n.is_power_of_two() {
panic!("Basis length must be power of 2");
}
evaluate_scaled_basis_inplace(&mut [], basis, &[], qf, scale);
}
pub fn is_power_of_two(n: usize) -> bool {
n > 0 && (n & (n - 1)) == 0
}
#[cfg(feature = "prover")]
pub fn encode_non_systematic<F: BinaryFieldElement + 'static>(
rs: &reed_solomon::ReedSolomon<F>,
data: &mut [F],
) {
reed_solomon::encode_in_place(rs, data);
}
pub fn partial_eval_multilinear<F: BinaryFieldElement>(poly: &mut Vec<F>, evals: &[F]) {
let mut n = poly.len();
for &e in evals {
n /= 2;
for i in 0..n {
let p0 = poly[2 * i];
let p1 = poly[2 * i + 1];
poly[i] = p0.add(&e.mul(&p1.add(&p0)));
}
}
poly.truncate(n);
}
#[cfg(test)]
mod tests {
use super::*;
use binary_fields::{BinaryElem128, BinaryElem16, BinaryElem32};
#[test]
fn test_field_element_conversion() {
println!("Testing field element conversions:");
let zero = BinaryElem32::zero();
let zero_index = field_to_index(zero);
assert_eq!(zero_index, 0, "Zero should map to index 0");
for i in 0..10 {
let elem = BinaryElem32::from_bits(i);
let converted_index = field_to_index(elem);
println!(
"from_bits({}) -> field_to_index() -> {}",
i, converted_index
);
if i < 256 {
assert_eq!(
converted_index, i as usize,
"Small values should convert exactly"
);
}
}
}
#[test]
fn test_lagrange_basis() {
let rs = vec![
BinaryElem16::from_bits(0x1234),
BinaryElem16::from_bits(0x5678),
BinaryElem16::from_bits(0x9ABC),
];
let basis = evaluate_lagrange_basis(&rs);
assert_eq!(basis.len(), 8); }
#[test]
fn test_lagrange_basis_all_ones() {
let rs = vec![
BinaryElem32::one(),
BinaryElem32::one(),
BinaryElem32::one(),
BinaryElem32::one(),
];
let basis = evaluate_lagrange_basis(&rs);
assert_eq!(basis.len(), 16);
let non_zero_count = basis.iter().filter(|&&x| x != BinaryElem32::zero()).count();
println!("Non-zero entries: {}/{}", non_zero_count, basis.len());
}
#[test]
fn test_power_of_two() {
assert!(is_power_of_two(1));
assert!(is_power_of_two(2));
assert!(is_power_of_two(1024));
assert!(!is_power_of_two(0));
assert!(!is_power_of_two(1023));
}
#[test]
fn test_multilinear_delta_function() {
let mut basis = vec![BinaryElem128::zero(); 8]; let mut sks_x = vec![BinaryElem32::zero(); 4];
let sks_vks = vec![BinaryElem32::one(); 4];
let qf = BinaryElem32::from_bits(5);
let scale = BinaryElem128::from_bits(42);
evaluate_scaled_basis_inplace(&mut sks_x, &mut basis, &sks_vks, qf, scale);
let non_zero_count = basis
.iter()
.filter(|&&x| x != BinaryElem128::zero())
.count();
assert_eq!(non_zero_count, 1, "Should have exactly one non-zero entry");
let sum = basis
.iter()
.fold(BinaryElem128::zero(), |acc, &x| acc.add(&x));
assert_eq!(sum, scale, "Sum should equal scale");
let non_zero_index = basis
.iter()
.position(|&x| x != BinaryElem128::zero())
.unwrap();
println!("Non-zero entry at index: {}", non_zero_index);
assert_eq!(
basis[non_zero_index], scale,
"Non-zero entry should equal scale"
);
}
#[test]
fn test_multilinear_extension_full() {
let mut basis = vec![BinaryElem128::zero(); 4]; let qf = BinaryElem32::from_bits(2);
let scale = BinaryElem128::from_bits(7);
evaluate_multilinear_extension(&mut basis, qf, scale);
let sum = basis
.iter()
.fold(BinaryElem128::zero(), |acc, &x| acc.add(&x));
assert_eq!(sum, scale, "Sum should equal scale");
let non_zero_count = basis
.iter()
.filter(|&&x| x != BinaryElem128::zero())
.count();
assert_eq!(non_zero_count, 1, "Should have exactly one non-zero entry");
println!("Multilinear extension for qf=2: {:?}", basis);
}
#[test]
fn test_sk_evaluation() {
let sks_vks = eval_sk_at_vks::<BinaryElem32>(16);
assert_eq!(sks_vks.len(), 5); assert_eq!(sks_vks[0], BinaryElem32::one());
let sks_vks = eval_sk_at_vks::<BinaryElem16>(8);
assert_eq!(sks_vks.len(), 4); assert_eq!(sks_vks[0], BinaryElem16::one());
}
#[test]
fn test_partial_eval() {
let mut poly = vec![
BinaryElem32::from_bits(1),
BinaryElem32::from_bits(2),
BinaryElem32::from_bits(3),
BinaryElem32::from_bits(4),
BinaryElem32::from_bits(5),
BinaryElem32::from_bits(6),
BinaryElem32::from_bits(7),
BinaryElem32::from_bits(8),
];
let original_len = poly.len();
let evals = vec![BinaryElem32::from_bits(2)];
partial_eval_multilinear(&mut poly, &evals);
assert_eq!(poly.len(), original_len / 2);
}
#[test]
fn test_delta_function_properties() {
let test_cases = vec![
(BinaryElem32::zero(), 8), (BinaryElem32::from_bits(1), 8), (BinaryElem32::from_bits(7), 8), (BinaryElem32::from_bits(15), 16), ];
for (qf, n) in test_cases {
let mut basis = vec![BinaryElem128::zero(); n];
let mut sks_x = vec![BinaryElem32::zero(); 4];
let sks_vks = vec![BinaryElem32::one(); 4];
let scale = BinaryElem128::from_bits(123);
evaluate_scaled_basis_inplace(&mut sks_x, &mut basis, &sks_vks, qf, scale);
let non_zero_count = basis
.iter()
.filter(|&&x| x != BinaryElem128::zero())
.count();
assert_eq!(
non_zero_count, 1,
"Should have exactly one non-zero entry for qf={:?}",
qf
);
let sum = basis
.iter()
.fold(BinaryElem128::zero(), |acc, &x| acc.add(&x));
assert_eq!(sum, scale, "Sum should equal scale for qf={:?}", qf);
}
}
}
pub fn hash_row<F: BinaryFieldElement>(row: &[F]) -> merkle_tree::Hash {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update((row.len() as u32).to_le_bytes());
let elem_size = core::mem::size_of::<F>();
for elem in row.iter() {
let bytes =
unsafe { core::slice::from_raw_parts(elem as *const F as *const u8, elem_size) };
hasher.update(bytes);
}
hasher.finalize().into()
}
pub fn verify_ligero<T, U>(queries: &[usize], opened_rows: &[Vec<T>], yr: &[T], challenges: &[U])
where
T: BinaryFieldElement,
U: BinaryFieldElement + From<T>,
{
let gr = evaluate_lagrange_basis(challenges);
let n = yr.len().trailing_zeros() as usize;
let sks_vks: Vec<T> = eval_sk_at_vks(1 << n);
if !queries.is_empty() && !opened_rows.is_empty() {
let _ = (yr, sks_vks, gr, opened_rows); }
}