use alloc::vec::Vec;
use miden_air::{
BaseAir, LiftedAir, MidenAir,
logup::{BusId, MIDEN_MAX_MESSAGE_WIDTH},
lookup::{Challenges, accumulate, build_lookup_fractions, debug::collect_column_oracle_folds},
};
use miden_core::{
field::{Field, QuadFelt},
utils::{Matrix, RowMajorMatrix},
};
use super::{Felt, build_trace_from_ops, rand_array};
use crate::operation::Operation;
fn tiny_span() -> Vec<Operation> {
vec![
Operation::Pad,
Operation::Pad,
Operation::Add,
Operation::Pad,
Operation::Mul,
Operation::Drop,
]
}
fn assert_prover_matches_oracle(
label: &str,
aux: &RowMajorMatrix<QuadFelt>,
oracle_folds: &[Vec<(QuadFelt, QuadFelt)>],
aux_width: usize,
) {
let num_rows = oracle_folds.len();
assert_eq!(aux.width(), aux_width, "{label}: aux width mismatch");
assert_eq!(aux.height(), num_rows + 1, "{label}: aux height mismatch");
let aux_values = &aux.values;
for (r, row_folds) in oracle_folds.iter().enumerate() {
assert_eq!(row_folds.len(), aux_width, "{label} row {r}: fold width mismatch");
let per_row_values: Vec<QuadFelt> = row_folds
.iter()
.enumerate()
.map(|(col, &(v_col, u_col))| {
let u_inv = u_col.try_inverse().unwrap_or_else(|| {
panic!(
"{label} row {r} col {col}: oracle U_col is zero — bus has a \
zero-denominator product, indicating a bug in the emitter or \
message encoding",
)
});
v_col * u_inv
})
.collect();
let expected_delta: QuadFelt = per_row_values.iter().copied().sum();
let actual_delta = aux_values[(r + 1) * aux_width] - aux_values[r * aux_width];
assert_eq!(
actual_delta, expected_delta,
"{label} row {r} col 0 (accumulator): prover vs constraint path mismatch",
);
for col in 1..aux_width {
let actual_value = aux_values[r * aux_width + col];
assert_eq!(
actual_value, per_row_values[col],
"{label} row {r} col {col} (fraction): prover vs constraint path mismatch",
);
}
}
}
#[test]
fn build_lookup_fractions_matches_constraint_path_oracle() {
let trace = build_trace_from_ops(tiny_span(), &[]);
let (core_matrix, chip_matrix) = trace.main_trace().to_core_chiplets_matrices();
let public_vals = trace.to_public_values();
let chip_periodic = MidenAir::CHIPLETS.periodic_columns();
let raw = rand_array::<Felt, 4>();
let alpha = QuadFelt::new([raw[0], raw[1]]);
let beta = QuadFelt::new([raw[2], raw[3]]);
let challenges =
Challenges::<QuadFelt>::new(alpha, beta, MIDEN_MAX_MESSAGE_WIDTH, BusId::COUNT);
let core_fractions = build_lookup_fractions(&MidenAir::CORE, &core_matrix, &[], &challenges);
assert!(
!core_fractions.fractions().is_empty(),
"no Core fractions collected — trace is degenerate or emitters are broken",
);
let core_aux = accumulate(&core_fractions);
let core_folds =
collect_column_oracle_folds(&MidenAir::CORE, &core_matrix, &[], &public_vals, &challenges);
assert_prover_matches_oracle(
"Core",
&core_aux,
&core_folds,
LiftedAir::<Felt, QuadFelt>::aux_width(&MidenAir::CORE),
);
let chip_fractions =
build_lookup_fractions(&MidenAir::CHIPLETS, &chip_matrix, &chip_periodic, &challenges);
assert!(
!chip_fractions.fractions().is_empty(),
"no Chiplets fractions collected — trace is degenerate or emitters are broken",
);
let chip_aux = accumulate(&chip_fractions);
let chip_folds = collect_column_oracle_folds(
&MidenAir::CHIPLETS,
&chip_matrix,
&chip_periodic,
&public_vals,
&challenges,
);
assert_prover_matches_oracle(
"Chiplets",
&chip_aux,
&chip_folds,
LiftedAir::<Felt, QuadFelt>::aux_width(&MidenAir::CHIPLETS),
);
}