use super::{
InputCounts, InputLayout, InputRegion, LayoutRegions, StarkVarIndices, plan::MultiAirVarIndices,
};
use crate::{EXT_DEGREE, randomness};
#[derive(Clone, Copy)]
enum Alignment {
Unaligned = 1,
Word = 2,
DoubleWord = 4,
QuadWord = 8,
}
#[derive(Clone, Copy)]
struct LayoutPolicy {
public_values: Alignment,
randomness: Alignment,
main: Alignment,
aux: Alignment,
quotient: Alignment,
aux_bus_boundary: Alignment,
stark_vars: Alignment,
end_align: Option<Alignment>,
}
impl LayoutPolicy {
fn native() -> Self {
Self {
public_values: Alignment::Unaligned,
randomness: Alignment::Unaligned,
main: Alignment::Unaligned,
aux: Alignment::Unaligned,
quotient: Alignment::Unaligned,
aux_bus_boundary: Alignment::Unaligned,
stark_vars: Alignment::Unaligned,
end_align: None,
}
}
fn masm() -> Self {
Self {
public_values: Alignment::QuadWord,
randomness: Alignment::Word,
main: Alignment::DoubleWord,
aux: Alignment::DoubleWord,
quotient: Alignment::DoubleWord,
aux_bus_boundary: Alignment::Word,
stark_vars: Alignment::Word,
end_align: Some(Alignment::Word),
}
}
}
struct LayoutBuilder {
offset: usize,
}
impl LayoutBuilder {
fn new() -> Self {
Self { offset: 0 }
}
fn align(&mut self, alignment: Alignment) {
self.offset = self.offset.next_multiple_of(alignment as usize);
}
fn alloc(&mut self, width: usize, alignment: Alignment) -> InputRegion {
self.align(alignment);
let region = InputRegion { offset: self.offset, width };
self.offset += width;
region
}
}
impl InputLayout {
pub fn new(counts: InputCounts) -> Self {
Self::build_with_policy(counts, LayoutPolicy::native(), 1)
}
pub fn new_masm(counts: InputCounts) -> Self {
Self::build_with_policy(counts, LayoutPolicy::masm(), 1)
}
pub fn new_multi_air(counts: InputCounts, num_airs: usize) -> Self {
Self::build_with_policy(counts, LayoutPolicy::native(), num_airs)
}
pub fn new_masm_multi_air(counts: InputCounts, num_airs: usize) -> Self {
Self::build_with_policy(counts, LayoutPolicy::masm(), num_airs)
}
fn build_with_policy(counts: InputCounts, policy: LayoutPolicy, num_airs: usize) -> Self {
assert!(num_airs >= 1, "layout requires at least one AIR");
const NUM_STARK_VARS_BASE: usize = 10;
let is_multi_air = num_airs >= 2;
let num_stark_vars = NUM_STARK_VARS_BASE + if is_multi_air { 4 * num_airs } else { 0 };
let mut builder = LayoutBuilder::new();
let public_values = builder.alloc(counts.num_public, policy.public_values);
const NUM_RANDOMNESS_INPUTS: usize = 2;
let randomness = builder.alloc(NUM_RANDOMNESS_INPUTS, policy.randomness);
let (aux_rand_alpha, aux_rand_beta) = randomness::aux_rand_indices(randomness);
let main_curr = builder.alloc(counts.width, policy.main);
let aux_coord_width = counts.aux_width * EXT_DEGREE;
let aux_curr = builder.alloc(aux_coord_width, policy.aux);
let quotient_curr = builder.alloc(counts.num_quotient_chunks * EXT_DEGREE, policy.quotient);
let main_next = builder.alloc(counts.width, policy.main);
let aux_next = builder.alloc(aux_coord_width, policy.aux);
let quotient_next = builder.alloc(counts.num_quotient_chunks * EXT_DEGREE, policy.quotient);
let aux_bus_boundary = builder.alloc(counts.num_aux_boundary, policy.aux_bus_boundary);
let stark_vars = builder.alloc(num_stark_vars, policy.stark_vars);
let b = stark_vars.offset;
let alpha = b;
let z_pow_n = b + 1;
let z_k = b + 2;
let is_first = b + 3;
let is_last = b + 4;
let is_transition = b + 5;
let reserved = b + 6;
let weight0 = b + 7;
let f = b + 8;
let s0 = b + 9;
let multi_air = is_multi_air.then_some(MultiAirVarIndices { base: b + 10, num_airs });
if let Some(end_align) = policy.end_align {
builder.align(end_align);
}
Self {
regions: LayoutRegions {
public_values,
randomness,
main_curr,
aux_curr,
quotient_curr,
main_next,
aux_next,
quotient_next,
aux_bus_boundary,
stark_vars,
},
aux_rand_alpha,
aux_rand_beta,
stark: StarkVarIndices {
alpha,
z_pow_n,
z_k,
is_first,
is_last,
is_transition,
reserved,
weight0,
f,
s0,
multi_air,
},
total_inputs: builder.offset,
counts,
}
}
}
#[cfg(test)]
mod tests {
use super::super::{InputCounts, InputKey, InputLayout};
#[test]
fn multi_air_layout_generalizes_over_num_airs() {
let counts = InputCounts {
width: 1,
aux_width: 1,
num_aux_boundary: 3,
num_public: 8,
num_randomness: 2,
num_periodic: 0,
num_quotient_chunks: 1,
};
let layout = InputLayout::new_multi_air(counts, 3);
let base = layout.index(InputKey::MultiAirBeta(0)).unwrap();
assert_eq!(layout.index(InputKey::MultiAirBeta(2)), Some(base + 2));
assert_eq!(layout.index(InputKey::IsFirstAir(0)), Some(base + 3));
assert_eq!(layout.index(InputKey::IsTransitionAir(2)), Some(base + 3 + 3 * 2 + 2));
assert_eq!(layout.index(InputKey::MultiAirBeta(3)), None, "AIR index out of range");
assert_eq!(layout.index(InputKey::IsFirstAir(3)), None, "AIR index out of range");
let layout2 = InputLayout::new_multi_air(counts, 2);
let base2 = layout2.index(InputKey::MultiAirBeta(0)).unwrap();
assert_eq!(layout2.index(InputKey::MultiAirBeta(1)), Some(base2 + 1));
assert_eq!(layout2.index(InputKey::IsFirstAir(0)), Some(base2 + 2));
assert_eq!(layout2.index(InputKey::IsTransitionAir(1)), Some(base2 + 7));
}
}