pub mod lanes_x_row {
pub const BASIC: usize = 1;
pub const BASIC_LARGE: usize = 2;
pub const BASIC_HUGE: usize = 4;
pub const ADD: usize = 1;
pub const ADD_LARGE: usize = 2;
pub const ADD_HUGE: usize = 4;
pub const ADD_HI: usize = 2;
pub const ADD_HI_LARGE: usize = 4;
pub const ADD_HI_HUGE: usize = 8;
pub const EXT: usize = 1;
pub const EXT_LARGE: usize = 2;
pub const MAX_ADD_HI: usize = ADD_HI_HUGE;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BinaryLanes {
lanes: usize,
}
impl BinaryLanes {
pub fn new(lanes: usize) -> Self {
assert!(lanes > 0, "BinaryLanes: lanes_x_row must be greater than 0");
Self { lanes }
}
#[inline(always)]
pub fn lanes(&self) -> usize {
self.lanes
}
#[inline(always)]
pub fn split(&self, slot: usize) -> (usize, usize) {
(slot / self.lanes, slot % self.lanes)
}
#[inline(always)]
pub fn slots(&self, num_rows: usize) -> usize {
num_rows * self.lanes
}
#[inline(always)]
pub fn rows_for(&self, slots: usize) -> usize {
slots.div_ceil(self.lanes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_constants_match_the_generated_rows() {
use proofman_fields::Goldilocks;
use zisk_pil::*;
macro_rules! check {
($row:ident, $probe:ident, $konst:path) => {
assert_eq!(
$row::<Goldilocks>::default().$probe().len(),
$konst,
concat!(stringify!($konst), " does not match ", stringify!($row)),
);
};
}
check!(BinaryTraceRow, get_all_b_op, lanes_x_row::BASIC);
check!(BinaryLargeTraceRow, get_all_b_op, lanes_x_row::BASIC_LARGE);
check!(BinaryHugeTraceRow, get_all_b_op, lanes_x_row::BASIC_HUGE);
check!(BinaryAddTraceRow, get_all_a, lanes_x_row::ADD);
check!(BinaryAddLargeTraceRow, get_all_a, lanes_x_row::ADD_LARGE);
check!(BinaryAddHugeTraceRow, get_all_a, lanes_x_row::ADD_HUGE);
check!(BinaryAddHiTraceRow, get_all_a, lanes_x_row::ADD_HI);
check!(BinaryAddHiLargeTraceRow, get_all_a, lanes_x_row::ADD_HI_LARGE);
check!(BinaryAddHiHugeTraceRow, get_all_a, lanes_x_row::ADD_HI_HUGE);
check!(BinaryExtensionTraceRow, get_all_op, lanes_x_row::EXT);
check!(BinaryExtensionLargeTraceRow, get_all_op, lanes_x_row::EXT_LARGE);
check!(BinaryExtensionTraceRow, get_all_op_is_chain, lanes_x_row::EXT);
check!(BinaryExtensionLargeTraceRow, get_all_op_is_chain, lanes_x_row::EXT_LARGE);
check!(BinaryExtensionTraceRow, get_all_b, lanes_x_row::EXT);
check!(BinaryExtensionLargeTraceRow, get_all_b, lanes_x_row::EXT_LARGE);
}
#[test]
fn the_state_machines_pack_what_their_air_holds() {
use crate::{BinaryAddHiRow, BinaryAddRow, BinaryBasicRow, BinaryExtensionRow};
use proofman_fields::Goldilocks;
use zisk_pil::*;
type F = Goldilocks;
macro_rules! check {
($trait:ident, $row:ident, $trace:ident, $probe:ident, $konst:path) => {
assert_eq!(
<$row<F> as $trait<F, $trace<$row<F>>>>::LANES_X_ROW,
$row::<F>::default().$probe().len(),
concat!(stringify!($row), " packs a different width than its air"),
);
assert_eq!(
<$row<F> as $trait<F, $trace<$row<F>>>>::LANES_X_ROW,
$konst,
concat!(stringify!($row), " does not use ", stringify!($konst)),
);
};
}
check!(BinaryBasicRow, BinaryTraceRow, BinaryTrace, get_all_b_op, lanes_x_row::BASIC);
check!(
BinaryBasicRow,
BinaryLargeTraceRow,
BinaryLargeTrace,
get_all_b_op,
lanes_x_row::BASIC_LARGE
);
check!(
BinaryBasicRow,
BinaryHugeTraceRow,
BinaryHugeTrace,
get_all_b_op,
lanes_x_row::BASIC_HUGE
);
check!(BinaryAddRow, BinaryAddTraceRow, BinaryAddTrace, get_all_a, lanes_x_row::ADD);
check!(
BinaryAddRow,
BinaryAddLargeTraceRow,
BinaryAddLargeTrace,
get_all_a,
lanes_x_row::ADD_LARGE
);
check!(
BinaryAddRow,
BinaryAddHugeTraceRow,
BinaryAddHugeTrace,
get_all_a,
lanes_x_row::ADD_HUGE
);
check!(
BinaryAddHiRow,
BinaryAddHiTraceRow,
BinaryAddHiTrace,
get_all_a,
lanes_x_row::ADD_HI
);
check!(
BinaryAddHiRow,
BinaryAddHiLargeTraceRow,
BinaryAddHiLargeTrace,
get_all_a,
lanes_x_row::ADD_HI_LARGE
);
check!(
BinaryAddHiRow,
BinaryAddHiHugeTraceRow,
BinaryAddHiHugeTrace,
get_all_a,
lanes_x_row::ADD_HI_HUGE
);
check!(
BinaryExtensionRow,
BinaryExtensionTraceRow,
BinaryExtensionTrace,
get_all_op,
lanes_x_row::EXT
);
check!(
BinaryExtensionRow,
BinaryExtensionLargeTraceRow,
BinaryExtensionLargeTrace,
get_all_op,
lanes_x_row::EXT_LARGE
);
}
#[test]
fn single_lane_is_the_identity() {
let lanes = BinaryLanes::new(1);
assert_eq!(lanes.lanes(), 1);
assert_eq!(lanes.slots(2048), 2048);
assert_eq!(lanes.rows_for(2048), 2048);
for slot in 0..8 {
assert_eq!(lanes.split(slot), (slot, 0));
}
}
#[test]
fn slots_walk_lanes_before_rows() {
let lanes = BinaryLanes::new(3);
assert_eq!(lanes.slots(1024), 3072);
let walked: Vec<(usize, usize)> = (0..7).map(|s| lanes.split(s)).collect();
assert_eq!(walked, vec![(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0)]);
}
#[test]
fn split_matches_div_and_rem_for_every_lane_count_in_use() {
for &n in &[1usize, 2, 3, 4, 5, 6, 9] {
let lanes = BinaryLanes::new(n);
for slot in 0..(4 * n + 3) {
assert_eq!(lanes.split(slot), (slot / n, slot % n), "lanes={n} slot={slot}");
}
}
}
#[test]
fn rows_for_pads_the_last_row() {
let lanes = BinaryLanes::new(5);
assert_eq!(lanes.rows_for(0), 0);
assert_eq!(lanes.rows_for(1), 1, "one operation still takes a whole row");
assert_eq!(lanes.rows_for(5), 1);
assert_eq!(lanes.rows_for(6), 2);
}
#[test]
#[should_panic(expected = "greater than 0")]
fn rejects_zero_lanes() {
BinaryLanes::new(0);
}
}