use logicaffeine_kernel::Term;
#[derive(Debug, Clone)]
pub struct SynthesisConstraintConfig {
pub max_inputs: usize,
pub timeout_ms: u64,
}
impl Default for SynthesisConstraintConfig {
fn default() -> Self {
Self {
max_inputs: 8,
timeout_ms: 10000,
}
}
}
#[derive(Debug, Clone)]
pub enum SynthesisConstraint {
Satisfiable(Term),
Unrealizable,
Unsupported(String),
}
pub fn build_synthesis_constraint(
spec_type: &Term,
config: &SynthesisConstraintConfig,
) -> SynthesisConstraint {
let (inputs, output) = extract_io_from_spec(spec_type);
if inputs.is_empty() && output.is_none() {
return SynthesisConstraint::Unsupported(
"spec has no Pi binders — not a function type".to_string(),
);
}
for (name, ty) in &inputs {
if !is_hardware_type(ty) {
return SynthesisConstraint::Unsupported(format!(
"input '{}' has non-hardware type: {:?}",
name, ty
));
}
}
let bit_count = inputs.iter().filter(|(_, ty)| is_bit_type(ty)).count();
if bit_count > config.max_inputs {
return SynthesisConstraint::Unsupported(format!(
"too many Bit inputs ({}) exceeds max_inputs ({})",
bit_count, config.max_inputs
));
}
if let Some(ref out_ty) = output {
if !is_hardware_type(out_ty) {
return SynthesisConstraint::Unsupported(format!(
"output has non-hardware type: {:?}",
out_ty
));
}
}
SynthesisConstraint::Satisfiable(spec_type.clone())
}
pub fn extract_io_from_spec(spec_type: &Term) -> (Vec<(String, Term)>, Option<Term>) {
let mut inputs = Vec::new();
let mut current = spec_type;
loop {
match current {
Term::Pi {
param,
param_type,
body_type,
} => {
inputs.push((param.clone(), *param_type.clone()));
current = body_type;
}
other => {
return (inputs, Some(other.clone()));
}
}
}
}
fn is_hardware_type(ty: &Term) -> bool {
match ty {
Term::Global(name) => matches!(
name.as_str(),
"Bit" | "Unit" | "BVec" | "Circuit"
),
Term::App(func, _) => {
if let Term::Global(name) = func.as_ref() {
name == "BVec" || name == "Circuit"
} else {
false
}
}
_ => false,
}
}
fn is_bit_type(ty: &Term) -> bool {
matches!(ty, Term::Global(name) if name == "Bit")
}