use std::fmt::Write as _;
use crate::circuit::Circuit;
use crate::op::Op;
use crate::qasm::gates::{gate_info_1q, gate_info_2q};
use crate::{Error, Result};
pub fn emit(circuit: &Circuit) -> Result<String> {
let mut buf = String::with_capacity(64 + circuit.ops().len() * 16);
buf.push_str("OPENQASM 3.0;\n");
buf.push_str("include \"stdgates.inc\";\n");
if circuit.num_qubits() > 0 {
let _ = writeln!(buf, "qubit[{}] q;", circuit.num_qubits());
}
if circuit.num_classical() > 0 {
let _ = writeln!(buf, "bit[{}] c;", circuit.num_classical());
}
buf.push('\n');
for op in circuit.ops() {
emit_op(&mut buf, op, 0)?;
buf.push('\n');
}
Ok(buf)
}
fn emit_op(buf: &mut String, op: &Op, depth: usize) -> Result<()> {
match op {
Op::Apply1 { gate, target } => {
let (name, args) = gate_info_1q(gate)?;
let _ = write!(buf, "{name}{args} q[{}];", target.index());
}
Op::Apply2 { gate, a, b } => {
let name = gate_info_2q(gate)?;
let _ = write!(buf, "{name} q[{}], q[{}];", a.index(), b.index());
}
Op::Controlled {
controls,
gate,
target,
} => {
let (name, args) = gate_info_1q(gate)?;
if controls.len() == 1 {
buf.push_str("ctrl @ ");
} else {
let _ = write!(buf, "ctrl({}) @ ", controls.len());
}
let _ = write!(buf, "{name}{args} ");
for c in controls {
let _ = write!(buf, "q[{}], ", c.index());
}
let _ = write!(buf, "q[{}];", target.index());
}
Op::Measure { qubit, into } => {
let _ = write!(buf, "c[{}] = measure q[{}];", into.index(), qubit.index());
}
Op::IfClassic { bit, then } => {
if depth > 0 {
return Err(Error::Qasm {
line: 0,
col: 0,
message: "nested IfClassic is not supported in OpenQASM 3 emit".into(),
});
}
let _ = write!(buf, "if (c[{}] == 1) ", bit.index());
emit_op(buf, then, depth + 1)?;
}
}
Ok(())
}