use alloc::{string::ToString, vec::Vec};
use core::{mem::MaybeUninit, slice};
use miden_air::trace::{Challenges, MIN_TRACE_LEN, MainTrace};
use super::chiplets::Chiplets;
use crate::{
Felt, RowIndex,
debug::BusDebugger,
field::ExtensionField,
utils::{assume_init_vec, uninit_vector},
};
#[cfg(test)]
use crate::{operation::Operation, utils::ToElements};
pub struct TraceFragment<'a> {
data: Vec<&'a mut [Felt]>,
num_rows: usize,
}
impl<'a> TraceFragment<'a> {
pub fn new(num_columns: usize, num_rows: usize) -> Self {
TraceFragment {
data: Vec::with_capacity(num_columns),
num_rows,
}
}
pub fn width(&self) -> usize {
self.data.len()
}
pub fn len(&self) -> usize {
self.num_rows
}
#[inline(always)]
pub fn set(&mut self, row_idx: RowIndex, col_idx: usize, value: Felt) {
self.data[col_idx][row_idx] = value;
}
pub fn columns(&mut self) -> slice::IterMut<'_, &'a mut [Felt]> {
self.data.iter_mut()
}
pub fn push_column_slice(&mut self, column: &'a mut [Felt]) -> &'a mut [Felt] {
let (column_fragment, rest) = column.split_at_mut(self.num_rows);
self.data.push(column_fragment);
rest
}
#[cfg(test)]
pub fn trace_to_fragment(trace: &'a mut [Vec<Felt>]) -> Self {
assert!(!trace.is_empty(), "expected trace to have at least one column");
let mut data = Vec::new();
for column in trace.iter_mut() {
data.push(column.as_mut_slice());
}
let num_rows = data[0].len();
Self { data, num_rows }
}
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct TraceLenSummary {
main_trace_len: usize,
range_trace_len: usize,
chiplets_trace_len: ChipletsLengths,
}
impl TraceLenSummary {
pub fn new(
main_trace_len: usize,
range_trace_len: usize,
chiplets_trace_len: ChipletsLengths,
) -> Self {
TraceLenSummary {
main_trace_len,
range_trace_len,
chiplets_trace_len,
}
}
pub fn main_trace_len(&self) -> usize {
self.main_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.main_trace_len)
.max(self.chiplets_trace_len.trace_len())
}
pub fn padded_trace_len(&self) -> usize {
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,
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.kernel_rom_start() - chiplets.memory_start(),
kernel_rom_len: chiplets.padding_start() - chiplets.kernel_rom_start(),
}
}
pub fn from_parts(
hash_len: usize,
bitwise_len: usize,
memory_len: usize,
kernel_len: usize,
) -> Self {
ChipletsLengths {
hash_chiplet_len: hash_len,
bitwise_chiplet_len: bitwise_len,
memory_chiplet_len: memory_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 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.kernel_rom_len()
+ 1
}
}
pub(crate) trait AuxColumnBuilder<E: ExtensionField<Felt>> {
fn get_requests_at(
&self,
main_trace: &MainTrace,
challenges: &Challenges<E>,
row: RowIndex,
debugger: &mut BusDebugger<E>,
) -> E;
fn get_responses_at(
&self,
main_trace: &MainTrace,
challenges: &Challenges<E>,
row: RowIndex,
debugger: &mut BusDebugger<E>,
) -> E;
#[cfg(any(test, feature = "bus-debugger"))]
fn enforce_bus_balance(&self) -> bool;
fn build_aux_column(&self, main_trace: &MainTrace, challenges: &Challenges<E>) -> Vec<E> {
let mut bus_debugger = BusDebugger::new("aux bus".to_string());
let mut requests: Vec<MaybeUninit<E>> = uninit_vector(main_trace.num_rows());
requests[0].write(E::ONE);
let mut responses_prod: Vec<MaybeUninit<E>> = uninit_vector(main_trace.num_rows());
responses_prod[0].write(E::ONE);
let mut requests_running_prod = E::ONE;
let mut prev_prod = E::ONE;
for row_idx in 0..main_trace.num_rows() - 1 {
let row = row_idx.into();
let response = self.get_responses_at(main_trace, challenges, row, &mut bus_debugger);
prev_prod *= response;
responses_prod[row_idx + 1].write(prev_prod);
let request = self.get_requests_at(main_trace, challenges, row, &mut bus_debugger);
requests[row_idx + 1].write(request);
requests_running_prod *= request;
}
let requests = unsafe { assume_init_vec(requests) };
let mut result_aux_column = unsafe { assume_init_vec(responses_prod) };
let mut requests_running_divisor = requests_running_prod.inverse();
for i in (0..main_trace.num_rows()).rev() {
result_aux_column[i] *= requests_running_divisor;
requests_running_divisor *= requests[i];
}
#[cfg(any(test, feature = "bus-debugger"))]
if self.enforce_bus_balance() {
assert!(bus_debugger.is_empty(), "{bus_debugger}");
}
result_aux_column
}
}
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(hi as u64), Felt::new(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 = 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)
}