#[cfg(test)]
use alloc::vec::Vec;
use miden_air::trace::MIN_TRACE_LEN;
use super::chiplets::Chiplets;
use crate::{Felt, ONE};
#[cfg(test)]
use crate::{operation::Operation, utils::ToElements};
#[derive(Debug)]
pub struct RowMajorTraceWriter<'a, E> {
data: &'a mut [E],
payload: usize,
stride: usize,
}
impl<'a, E: Copy> RowMajorTraceWriter<'a, E> {
#[cfg(test)]
pub fn new(data: &'a mut [E], width: usize) -> Self {
Self::with_stride(data, width, width)
}
pub fn with_stride(data: &'a mut [E], payload: usize, stride: usize) -> Self {
debug_assert!(stride >= payload, "stride must be >= payload");
debug_assert_eq!(data.len() % stride, 0, "buffer length must be a multiple of stride");
Self { data, payload, stride }
}
#[inline(always)]
pub fn write_row(&mut self, row: usize, values: &[E]) {
debug_assert_eq!(values.len(), self.payload);
let start = row * self.stride;
self.data[start..start + self.payload].copy_from_slice(values);
}
}
pub const S_00_COL: usize = 0;
pub const S_01_COL: usize = 1;
pub const CHIP_CLK_COL: usize = 2;
pub const DATA_COL_START: usize = 3;
pub struct ChipletTraceFragment<'a> {
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_rows: usize,
num_cols: usize,
row_offset: usize,
prefix_one_cols: &'static [usize],
clk_col: usize,
write_clk: bool,
scatter_last: Option<usize>,
}
impl<'a> ChipletTraceFragment<'a> {
pub fn row_major(
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_cols: usize,
) -> Self {
debug_assert_eq!(band.len() % stride, 0, "band length must be a multiple of stride");
debug_assert!(col_start + num_cols <= stride, "column band overruns the row stride");
let num_rows = band.len() / stride;
Self {
band,
stride,
col_start,
num_rows,
num_cols,
row_offset: 0,
prefix_one_cols: &[],
clk_col: CHIP_CLK_COL,
write_clk: false,
scatter_last: None,
}
}
pub fn with_overheads(
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_cols: usize,
row_offset: usize,
prefix_one_cols: &'static [usize],
) -> Self {
debug_assert_eq!(band.len() % stride, 0, "band length must be a multiple of stride");
debug_assert!(col_start + num_cols <= stride, "column band overruns the row stride");
let num_rows = band.len() / stride;
Self {
band,
stride,
col_start,
num_rows,
num_cols,
row_offset,
prefix_one_cols,
clk_col: CHIP_CLK_COL,
write_clk: true,
scatter_last: None,
}
}
pub fn with_scattered_last(
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_cols: usize,
row_offset: usize,
scatter_last: usize,
) -> Self {
debug_assert_eq!(band.len() % stride, 0, "band length must be a multiple of stride");
debug_assert!(num_cols >= 1, "scattered fragment needs at least one column");
debug_assert!(col_start + num_cols - 1 <= stride, "column band overruns the row stride",);
let num_rows = band.len() / stride;
Self {
band,
stride,
col_start,
num_rows,
num_cols,
row_offset,
prefix_one_cols: &[],
clk_col: CHIP_CLK_COL,
write_clk: true,
scatter_last: Some(scatter_last),
}
}
pub fn width(&self) -> usize {
self.num_cols
}
pub fn len(&self) -> usize {
self.num_rows
}
pub fn set_s_01(&mut self, row: usize) {
if self.col_start < DATA_COL_START {
return;
}
self.band[row * self.stride + S_01_COL] = ONE;
}
pub fn copy_rows_from(&mut self, src: &[Felt]) {
debug_assert_eq!(src.len(), self.num_rows * self.num_cols, "source buffer size mismatch");
self.copy_rows_into(0, src);
}
pub fn copy_rows_into(&mut self, row_offset: usize, src: &[Felt]) {
debug_assert_eq!(src.len() % self.num_cols, 0, "source buffer size not row-aligned");
let chunk_rows = src.len() / self.num_cols;
debug_assert!(
row_offset + chunk_rows <= self.num_rows,
"chunk overruns fragment row range",
);
let contiguous_cols = match self.scatter_last {
Some(_) => self.num_cols - 1,
None => self.num_cols,
};
for r in 0..chunk_rows {
let dst_row = row_offset + r;
let row_start = dst_row * self.stride;
let row = &mut self.band[row_start..row_start + self.stride];
for &col in self.prefix_one_cols {
row[col] = ONE;
}
let src_row = &src[r * self.num_cols..(r + 1) * self.num_cols];
row[self.col_start..self.col_start + contiguous_cols]
.copy_from_slice(&src_row[..contiguous_cols]);
if let Some(scatter_col) = self.scatter_last {
row[scatter_col] = src_row[self.num_cols - 1];
}
if self.write_clk {
row[self.clk_col] = Felt::from_u32((self.row_offset + dst_row + 1) as u32);
}
}
}
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct TraceLenSummary {
core_trace_len: usize,
range_trace_len: usize,
chiplets_trace_len: ChipletsLengths,
padded_trace_len: Option<usize>,
}
impl TraceLenSummary {
pub fn new(
core_trace_len: usize,
range_trace_len: usize,
chiplets_trace_len: ChipletsLengths,
) -> Self {
TraceLenSummary {
core_trace_len,
range_trace_len,
chiplets_trace_len,
padded_trace_len: None,
}
}
pub fn new_with_padded(
core_trace_len: usize,
range_trace_len: usize,
chiplets_trace_len: ChipletsLengths,
padded_trace_len: usize,
) -> Self {
TraceLenSummary {
core_trace_len,
range_trace_len,
chiplets_trace_len,
padded_trace_len: Some(padded_trace_len),
}
}
pub fn core_trace_len(&self) -> usize {
self.core_trace_len
}
pub fn range_trace_len(&self) -> usize {
self.range_trace_len
}
pub fn chiplets_trace_len(&self) -> ChipletsLengths {
self.chiplets_trace_len
}
pub fn trace_len(&self) -> usize {
self.range_trace_len
.max(self.core_trace_len)
.max(self.chiplets_trace_len.trace_len())
}
pub fn padded_trace_len(&self) -> usize {
self.padded_trace_len
.unwrap_or_else(|| self.trace_len().next_power_of_two().max(MIN_TRACE_LEN))
}
pub fn padding_percentage(&self) -> usize {
(self.padded_trace_len() - self.trace_len()) * 100 / self.padded_trace_len()
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChipletsLengths {
hash_chiplet_len: usize,
bitwise_chiplet_len: usize,
memory_chiplet_len: usize,
ace_chiplet_len: usize,
kernel_rom_len: usize,
}
impl ChipletsLengths {
pub fn new(chiplets: &Chiplets) -> Self {
ChipletsLengths {
hash_chiplet_len: chiplets.bitwise_start().into(),
bitwise_chiplet_len: chiplets.memory_start() - chiplets.bitwise_start(),
memory_chiplet_len: chiplets.ace_start() - chiplets.memory_start(),
ace_chiplet_len: chiplets.kernel_rom_start() - chiplets.ace_start(),
kernel_rom_len: chiplets.padding_start() - chiplets.kernel_rom_start(),
}
}
pub fn from_parts(
hash_len: usize,
bitwise_len: usize,
memory_len: usize,
ace_len: usize,
kernel_len: usize,
) -> Self {
ChipletsLengths {
hash_chiplet_len: hash_len,
bitwise_chiplet_len: bitwise_len,
memory_chiplet_len: memory_len,
ace_chiplet_len: ace_len,
kernel_rom_len: kernel_len,
}
}
pub fn hash_chiplet_len(&self) -> usize {
self.hash_chiplet_len
}
pub fn bitwise_chiplet_len(&self) -> usize {
self.bitwise_chiplet_len
}
pub fn memory_chiplet_len(&self) -> usize {
self.memory_chiplet_len
}
pub fn ace_chiplet_len(&self) -> usize {
self.ace_chiplet_len
}
pub fn kernel_rom_len(&self) -> usize {
self.kernel_rom_len
}
pub fn trace_len(&self) -> usize {
self.hash_chiplet_len()
+ self.bitwise_chiplet_len()
+ self.memory_chiplet_len()
+ self.ace_chiplet_len()
+ self.kernel_rom_len()
+ 1
}
}
pub(crate) fn split_element_u32_into_u16(value: Felt) -> (Felt, Felt) {
let (hi, lo) = split_u32_into_u16(value.as_canonical_u64());
(Felt::new_unchecked(hi as u64), Felt::new_unchecked(lo as u64))
}
pub(crate) fn split_u32_into_u16(value: u64) -> (u16, u16) {
const U32MAX: u64 = u32::MAX as u64;
debug_assert!(value <= U32MAX, "not a 32-bit value");
let lo = value as u16;
let hi = (value >> 16) as u16;
(hi, lo)
}
#[cfg(test)]
pub fn build_span_with_respan_ops() -> (Vec<Operation>, Vec<Felt>) {
let iv = [1, 3, 5, 7, 9, 11, 13, 15, 17].to_elements();
let ops = alloc::vec![
Operation::Push(iv[0]),
Operation::Push(iv[1]),
Operation::Push(iv[2]),
Operation::Push(iv[3]),
Operation::Push(iv[4]),
Operation::Push(iv[5]),
Operation::Push(iv[6]),
Operation::Push(iv[7]),
Operation::Push(iv[8]),
Operation::Add,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
];
(ops, iv)
}