use std::fmt::{self, Display, Formatter};
use std::hash::{Hash, Hasher};
use crate::{Circuit, Result, StimError};
#[derive(Clone, PartialEq, Eq)]
pub struct CircuitRepeatBlock {
repeat_count: u64,
body: Circuit,
tag: String,
}
impl CircuitRepeatBlock {
pub fn new(repeat_count: u64, body: &Circuit, tag: impl Into<String>) -> Result<Self> {
if repeat_count == 0 {
return Err(StimError::new("Can't repeat 0 times."));
}
Ok(Self {
repeat_count,
body: body.clone(),
tag: tag.into(),
})
}
#[must_use]
pub fn name(&self) -> &str {
"REPEAT"
}
#[must_use]
pub fn tag(&self) -> &str {
&self.tag
}
#[must_use]
pub fn repeat_count(&self) -> u64 {
self.repeat_count
}
#[must_use]
pub fn num_measurements(&self) -> u64 {
self.body.num_measurements() * self.repeat_count
}
#[must_use]
pub fn body_copy(&self) -> Circuit {
self.body.clone()
}
}
impl Circuit {
pub fn append_repeat_block(
&mut self,
repeat_count: u64,
body: &Circuit,
tag: &str,
) -> Result<()> {
self.inner
.append_repeat_block(repeat_count, &body.inner, tag)
.map_err(StimError::from)
}
}
impl Display for CircuitRepeatBlock {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("REPEAT")?;
if !self.tag.is_empty() {
write!(f, "[{}]", self.tag)?;
}
write!(f, " {} {{", self.repeat_count)?;
let body = self.body.to_string();
if !body.is_empty() {
f.write_str("\n")?;
for line in body.lines() {
writeln!(f, " {line}")?;
}
} else {
f.write_str("\n")?;
}
f.write_str("}")
}
}
impl fmt::Debug for CircuitRepeatBlock {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"stim::CircuitRepeatBlock(repeat_count={}, body={:?}, tag={:?})",
self.repeat_count, self.body, self.tag
)
}
}
impl PartialOrd for CircuitRepeatBlock {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CircuitRepeatBlock {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.repeat_count
.cmp(&other.repeat_count)
.then_with(|| self.tag.cmp(&other.tag))
.then_with(|| self.body.to_string().cmp(&other.body.to_string()))
}
}
impl Hash for CircuitRepeatBlock {
fn hash<H: Hasher>(&self, state: &mut H) {
self.repeat_count.hash(state);
self.tag.hash(state);
self.body.to_string().hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn constructor_and_field_accessors_preserve_values() {
let body = Circuit::from_str("M 0\nH 1").expect("body should parse");
let block =
CircuitRepeatBlock::new(5, &body, "look-at-me").expect("block should construct");
assert_eq!(block.name(), "REPEAT");
assert_eq!(block.tag(), "look-at-me");
assert_eq!(block.repeat_count(), 5);
assert_eq!(block.num_measurements(), 5);
assert_eq!(block.body_copy(), body);
}
#[test]
fn clone_and_equality_include_tag() {
let body = Circuit::from_str("X 0").expect("body should parse");
let first = CircuitRepeatBlock::new(3, &body, "tagged").expect("block should construct");
let same = CircuitRepeatBlock::new(3, &body, "tagged").expect("block should construct");
let different_tag =
CircuitRepeatBlock::new(3, &body, "other").expect("block should construct");
assert_eq!(first.clone(), same);
assert_ne!(first, different_tag);
}
#[test]
fn display_and_debug_match_upstream_like_shapes() {
let body = Circuit::from_str("X 0\nM 0").expect("body should parse");
let block = CircuitRepeatBlock::new(3, &body, "tagged").expect("block should construct");
assert_eq!(block.to_string(), "REPEAT[tagged] 3 {\n X 0\n M 0\n}");
assert_eq!(
format!("{block:?}"),
"stim::CircuitRepeatBlock(repeat_count=3, body=stim::Circuit(\"\"\"\nX 0\nM 0\n\"\"\"), tag=\"tagged\")"
);
}
#[test]
fn new_rejects_zero_repetitions() {
let error = CircuitRepeatBlock::new(0, &Circuit::new(), "")
.expect_err("repeat_count=0 should fail");
assert!(error.message().contains("repeat 0"));
}
#[test]
fn append_repeat_block_roundtrips_through_stim_text() {
let mut circuit = Circuit::from_str("H 0").expect("circuit should parse");
let body = Circuit::from_str("CX 0 1\nM 0").expect("body should parse");
circuit
.append_repeat_block(2, &body, "tagged")
.expect("append_repeat_block should succeed");
let text = circuit.to_string();
assert_eq!(text, "H 0\nREPEAT[tagged] 2 {\n CX 0 1\n M 0\n}");
assert_eq!(
Circuit::from_str(&text).expect("text should parse"),
circuit
);
}
#[test]
fn append_repeat_block_rejects_zero_repetitions() {
let mut circuit = Circuit::new();
let body = Circuit::from_str("M 0").expect("body should parse");
let error = circuit
.append_repeat_block(0, &body, "")
.expect_err("repeat_count=0 should fail");
assert!(error.message().contains("repeat 0"));
}
}