use p3_binary_field::TowerLevel;
use p3_matrix::Matrix;
use p3_matrix::dense::RowMajorMatrix;
pub trait AdditiveNtt<F: TowerLevel> {
fn shifted_ntt_batch(&self, mat: RowMajorMatrix<F>, shift: F) -> RowMajorMatrix<F>;
fn shifted_intt_batch(&self, mat: RowMajorMatrix<F>, shift: F) -> RowMajorMatrix<F>;
fn ntt_batch(&self, mat: RowMajorMatrix<F>) -> RowMajorMatrix<F> {
self.shifted_ntt_batch(mat, F::ZERO)
}
fn ntt_batch_padded(&self, mat: RowMajorMatrix<F>, log_inv_rate: usize) -> RowMajorMatrix<F> {
let log_n = p3_util::log2_strict_usize(mat.height());
assert!(log_inv_rate <= log_n, "padding exceeds matrix height");
self.ntt_batch(mat)
}
fn intt_batch(&self, mat: RowMajorMatrix<F>) -> RowMajorMatrix<F> {
self.shifted_intt_batch(mat, F::ZERO)
}
fn lde_batch(&self, mat: RowMajorMatrix<F>, added_bits: usize) -> RowMajorMatrix<F> {
self.shifted_lde_batch(mat, added_bits, F::ZERO)
}
fn shifted_lde_batch(
&self,
mat: RowMajorMatrix<F>,
added_bits: usize,
shift: F,
) -> RowMajorMatrix<F> {
let log_n = p3_util::log2_strict_usize(mat.height());
assert!(log_n <= 1 << F::LOG_BITS, "domain exceeds field dimension");
if added_bits == 0 {
return mat;
}
let coeffs = self.shifted_intt_batch(mat, shift);
let width = coeffs.width;
let len = coeffs.values.len();
let padded_len = u32::try_from(added_bits)
.ok()
.and_then(|bits| len.checked_shl(bits))
.filter(|&padded| padded >> added_bits == len)
.expect("extended codeword length overflows usize");
let mut values = F::zero_vec(padded_len);
values[..len].copy_from_slice(&coeffs.values);
self.shifted_ntt_batch(RowMajorMatrix::new(values, width), shift)
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use p3_binary_field::BinaryField8;
use p3_field::PrimeCharacteristicRing;
use p3_matrix::dense::RowMajorMatrix;
use super::AdditiveNtt;
use crate::LchNtt;
#[test]
fn identity_lde_preserves_input_allocation() {
let mat = RowMajorMatrix::new(vec![BinaryField8::ONE; 32], 4);
let ptr = mat.values.as_ptr();
let result = LchNtt::default().lde_batch(mat, 0);
assert_eq!(result.values.as_ptr(), ptr);
assert_eq!(result.values, vec![BinaryField8::ONE; 32]);
}
#[test]
#[should_panic]
fn identity_lde_rejects_invalid_height() {
let _ = LchNtt::default().lde_batch(RowMajorMatrix::new(vec![BinaryField8::ONE; 3], 1), 0);
}
#[test]
#[should_panic]
fn identity_lde_rejects_domain_above_field_dimension() {
let _ =
LchNtt::default().lde_batch(RowMajorMatrix::new(vec![BinaryField8::ONE; 512], 1), 0);
}
}