use core::panic;
use air_ir::{Air, AlgebraicGraph, ConstraintDomain, NodeIndex, Operation, TraceAccess, Value};
use crate::air::call_bus_boundary_varlen_pubinput;
use super::{Codegen, ElemType, Impl};
pub(super) fn add_fn_get_assertions(impl_ref: &mut Impl, ir: &Air) {
let get_assertions = impl_ref
.new_fn("get_assertions")
.arg_ref_self()
.ret("Vec<Assertion<Felt>>");
add_main_trace_assertions(get_assertions, ir);
get_assertions.line("result");
}
pub(super) fn add_fn_get_aux_assertions(impl_ref: &mut Impl, ir: &Air) {
let get_aux_assertions = impl_ref
.new_fn("get_aux_assertions")
.generic("E: FieldElement<BaseField = Felt>")
.arg_ref_self()
.arg("aux_rand_elements", "&AuxRandElements<E>")
.ret("Vec<Assertion<E>>");
add_aux_trace_assertions(get_aux_assertions, ir);
get_aux_assertions.line("result");
}
fn add_main_trace_assertions(func_body: &mut codegen::Function, ir: &Air) {
let elem_type = ElemType::Base;
let main_trace_segment = 0;
func_body.line("let mut result = Vec::new();");
for constraint in ir.boundary_constraints(main_trace_segment) {
let (trace_access, expr_root) =
split_boundary_constraint(ir.constraint_graph(), constraint.node_index());
debug_assert_eq!(trace_access.segment, main_trace_segment);
let expr_root_string = expr_root.to_string(ir, elem_type, main_trace_segment);
let assertion = format!(
"result.push(Assertion::single({}, {}, {}));",
trace_access.column,
domain_to_str(constraint.domain()),
expr_root_string
);
func_body.line(assertion);
}
}
fn add_aux_trace_assertions(func_body: &mut codegen::Function, ir: &Air) {
let elem_type = ElemType::Ext;
let aux_trace_segment = 1;
func_body.line("let mut result = Vec::new();");
for constraint in ir.boundary_constraints(aux_trace_segment) {
let (trace_access, expr_root) =
split_boundary_constraint(ir.constraint_graph(), constraint.node_index());
debug_assert_eq!(trace_access.segment, aux_trace_segment);
let expr_root_string = expr_root.to_string(ir, elem_type, aux_trace_segment);
let assertion = format!(
"result.push(Assertion::single({}, {}, {}));",
trace_access.column,
domain_to_str(constraint.domain()),
expr_root_string
);
func_body.line(assertion);
}
let domains = [ConstraintDomain::FirstRow, ConstraintDomain::LastRow];
for domain in &domains {
for (index, bus) in ir.buses.values().enumerate() {
let bus_boundary = match domain {
ConstraintDomain::FirstRow => &bus.first,
ConstraintDomain::LastRow => &bus.last,
_ => unreachable!("Invalid domain for bus boundary constraint"),
};
match bus_boundary {
air_ir::BusBoundary::PublicInputTable(air_ir::PublicInputTableAccess {
bus_name,
table_name,
..
}) => {
let expr_root_string =
call_bus_boundary_varlen_pubinput(ir, *bus_name, *table_name);
let assertion = format!(
"result.push(Assertion::single({}, {}, {}));",
index,
domain_to_str(*domain),
expr_root_string
);
func_body.line(assertion);
}
air_ir::BusBoundary::Null | air_ir::BusBoundary::Unconstrained => {}
}
}
}
}
fn domain_to_str(domain: ConstraintDomain) -> String {
match domain {
ConstraintDomain::FirstRow => "0".to_string(),
ConstraintDomain::LastRow => "self.last_step()".to_string(),
_ => panic!("invalid constraint domain"),
}
}
pub fn split_boundary_constraint(
graph: &AlgebraicGraph,
index: &NodeIndex,
) -> (TraceAccess, NodeIndex) {
let node = graph.node(index);
match node.op() {
Operation::Sub(lhs, rhs) => {
if let Operation::Value(Value::TraceAccess(trace_access)) = graph.node(lhs).op() {
debug_assert_eq!(trace_access.row_offset, 0);
(*trace_access, *rhs)
} else {
panic!(
"InvalidUsage: index {index:?} is not the constraint root of a boundary constraint"
);
}
}
_ => panic!("InvalidUsage: index {index:?} is not the root index of a constraint"),
}
}