use super::circle::PolyOps;
use super::BitReversedOrder;
use crate::core::circle::Coset;
pub struct TwiddleTree<B: PolyOps> {
pub root_coset: Coset,
pub twiddles: B::Twiddles,
pub itwiddles: B::Twiddles,
}
unsafe impl<B: PolyOps> Sync for TwiddleTree<B> {}
pub trait TwiddleBuffer<Order> {
fn empty() -> Self;
fn extract_subdomain_twiddles(&self, domain_log_size: u32, subdomain_log_size: u32) -> Self;
}
impl<T: Copy> TwiddleBuffer<BitReversedOrder> for Vec<T> {
fn empty() -> Self {
Vec::new()
}
fn extract_subdomain_twiddles(&self, domain_log_size: u32, subdomain_log_size: u32) -> Self {
let domain_half_log_size = domain_log_size - 1;
let subdomain_half_log_size = subdomain_log_size - 1;
let buf_half_log_size = self.len().ilog2();
assert!(
subdomain_half_log_size <= domain_half_log_size
&& domain_half_log_size <= buf_half_log_size,
"Invalid sizes: subdomain_half={subdomain_half_log_size}, \
domain_half={domain_half_log_size}, buf_half={buf_half_log_size}"
);
let skip_layers = buf_half_log_size - domain_half_log_size;
let buf_size = 1usize << buf_half_log_size;
let out_size = 1usize << subdomain_half_log_size;
let mut result = Vec::with_capacity(out_size);
for layer in 0..subdomain_half_log_size as usize {
let root_layer = skip_layers as usize + layer;
let layer_start = buf_size - (buf_size >> root_layer);
let subdomain_layer_size = 1usize << (subdomain_half_log_size as usize - 1 - layer);
result.extend_from_slice(&self[layer_start..layer_start + subdomain_layer_size]);
}
result.push(self[self.len() - 1]);
debug_assert_eq!(result.len(), out_size);
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::poly::circle::CanonicCoset;
use crate::prover::backend::cpu::circle::slow_precompute_twiddles;
#[test]
fn test_extract_subdomain_twiddles() {
let committed_log_size = 6;
let subdomain_log_size = 4;
let committed_domain = CanonicCoset::new(committed_log_size).circle_domain();
let subdomain = committed_domain
.split(committed_log_size - subdomain_log_size)
.0;
let committed_twiddles = slow_precompute_twiddles(committed_domain.half_coset);
let extracted: Vec<_> = TwiddleBuffer::<BitReversedOrder>::extract_subdomain_twiddles(
&committed_twiddles,
committed_log_size,
subdomain_log_size,
);
let expected = slow_precompute_twiddles(subdomain.half_coset);
assert_eq!(extracted.len(), expected.len());
assert_eq!(
extracted[..extracted.len() - 1],
expected[..expected.len() - 1]
);
}
#[test]
fn test_extract_subdomain_twiddles_from_larger_buffer() {
let root_log_size = 8;
let committed_log_size = 6;
let subdomain_log_size = 4;
let root_domain = CanonicCoset::new(root_log_size).circle_domain();
let committed_domain = CanonicCoset::new(committed_log_size).circle_domain();
let subdomain = committed_domain
.split(committed_log_size - subdomain_log_size)
.0;
let root_twiddles = slow_precompute_twiddles(root_domain.half_coset);
let extracted: Vec<_> = TwiddleBuffer::<BitReversedOrder>::extract_subdomain_twiddles(
&root_twiddles,
committed_log_size,
subdomain_log_size,
);
let expected = slow_precompute_twiddles(subdomain.half_coset);
assert_eq!(extracted.len(), expected.len());
assert_eq!(
extracted[..extracted.len() - 1],
expected[..expected.len() - 1]
);
}
}