Skip to main content

cqlib_core/circuit/classical_expr/
expr.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! Typed, side-effect-free runtime classical expressions.
14//!
15//! Expressions read [`ClassicalVar`] and [`ClassicalValue`] handles and combine
16//! them into typed runtime values. They do not measure qubits, write classical
17//! storage, or transfer control. Control-flow statements consume these
18//! expressions in a later layer.
19//!
20//! Build expressions with the typed constructors on [`ClassicalExpr`].
21//! [`ClassicalVar`] and [`ClassicalValue`] handles can be converted directly
22//! into expressions, so `var.expr()`, `ClassicalExpr::from(var)`, and passing a
23//! handle to a `try_*` builder are equivalent expression sources. Leaf
24//! constructors such as [`ClassicalExpr::bool_literal`] and
25//! [`ClassicalExpr::bit_literal`] are infallible. Constructors that validate
26//! operand types return `Result<ClassicalExpr, CircuitError>`; the fallible
27//! logical builders use a `try_` prefix, for example
28//! [`ClassicalExpr::try_not`], [`ClassicalExpr::try_and`],
29//! [`ClassicalExpr::try_or`], and [`ClassicalExpr::try_xor`].
30//!
31//! Prefer Rust bit operators when the expression types are already known:
32//! `!source`, `lhs & rhs`, `lhs | rhs`, and `lhs ^ rhs`. Each source may be a
33//! [`ClassicalExpr`], [`ClassicalVar`], or [`ClassicalValue`]. These operator
34//! overloads panic if the operands are not matching `Bool` or matching `Bit`
35//! expressions, because the standard operator traits cannot return `Result`.
36//! Use the fallible `try_*` builders when invalid input should be reported as
37//! [`CircuitError`] instead of a panic.
38//!
39//! ```rust
40//! use cqlib_core::circuit::{CircuitId, ClassicalExpr, ClassicalType, ClassicalVar};
41//!
42//! let cid = CircuitId::new();
43//! let a = ClassicalVar::new(cid, 0, ClassicalType::Bool);
44//! let b = ClassicalVar::new(cid, 1, ClassicalType::Bool);
45//!
46//! let condition = !a & b;
47//! assert_eq!(condition.ty(), ClassicalType::Bool);
48//!
49//! let checked = ClassicalExpr::try_and(ClassicalExpr::try_not(a)?, b)?;
50//! assert_eq!(checked.ty(), ClassicalType::Bool);
51//! # Ok::<(), Box<dyn std::error::Error>>(())
52//! ```
53
54use crate::circuit::classical::{ClassicalType, ClassicalValue, ClassicalVar};
55use crate::circuit::error::CircuitError;
56use std::collections::{BTreeSet, HashMap};
57use std::num::NonZeroU32;
58use std::ops::{BitAnd, BitOr, BitXor, Not};
59
60/// Unary operators for classical expressions.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62pub enum ClassicalUnaryOp {
63    /// Logical negation for `Bool`, or bit inversion for `Bit`.
64    Not,
65}
66
67/// Binary operators for boolean and bit expressions.
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69pub enum ClassicalBinaryOp {
70    /// Logical or bitwise conjunction.
71    And,
72    /// Logical or bitwise disjunction.
73    Or,
74    /// Logical or bitwise exclusive-or.
75    Xor,
76}
77
78/// Comparison operators for classical expressions.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80pub enum ClassicalCompareOp {
81    /// Equality.
82    Eq,
83    /// Inequality.
84    Ne,
85    /// Strictly less than.
86    Lt,
87    /// Less than or equal.
88    Le,
89    /// Strictly greater than.
90    Gt,
91    /// Greater than or equal.
92    Ge,
93}
94
95/// Explicit casts between classical expression types.
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
97pub enum ClassicalCast {
98    /// Interprets a runtime bit as a runtime boolean.
99    BitToBool,
100    /// Interprets an ordered bit-vector as a little-endian unsigned integer.
101    BitVecToUInt,
102}
103
104/// A typed, side-effect-free runtime classical expression.
105///
106/// The expression node kind is exposed through [`ClassicalExpr::kind`] for
107/// inspection and tooling. Prefer the typed constructors on this type when
108/// building expressions; they centralize type checks and keep the AST valid.
109#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110pub struct ClassicalExpr {
111    node: Box<ClassicalExprNode>,
112}
113
114/// Typed node stored inside a [`ClassicalExpr`].
115///
116/// Its fields are private so callers cannot construct an expression whose
117/// declared type disagrees with its node kind.
118#[derive(Debug, Clone, PartialEq, Eq, Hash)]
119pub struct ClassicalExprNode {
120    ty: ClassicalType,
121    kind: ClassicalExprKind,
122}
123
124/// Public node kind for inspecting a [`ClassicalExpr`] AST.
125///
126/// The variants mirror the expression builders on [`ClassicalExpr`]. External
127/// callers can pattern-match this enum through [`ClassicalExpr::kind`], while
128/// expression construction should continue to use the typed builders.
129#[derive(Debug, Clone, PartialEq, Eq, Hash)]
130pub enum ClassicalExprKind {
131    /// Read a mutable circuit-local variable.
132    Var(ClassicalVar),
133    /// Read an immutable circuit-local SSA value.
134    Value(ClassicalValue),
135    /// Boolean literal.
136    BoolLiteral(bool),
137    /// Single-bit literal.
138    BitLiteral(bool),
139    /// Fixed-width unsigned integer literal.
140    UIntLiteral {
141        /// Static integer width.
142        width: NonZeroU32,
143        /// Literal value, constrained to the declared width.
144        value: u128,
145    },
146    /// Fixed-width ordered bit-vector literal.
147    BitVecLiteral {
148        /// Static bit-vector width.
149        width: NonZeroU32,
150        /// Packed little-endian literal value.
151        value: u128,
152    },
153    /// Unary operation.
154    Unary {
155        /// Unary operator.
156        op: ClassicalUnaryOp,
157        /// Operand expression.
158        expr: ClassicalExpr,
159    },
160    /// Binary boolean or bit operation.
161    Binary {
162        /// Binary operator.
163        op: ClassicalBinaryOp,
164        /// Left operand.
165        lhs: ClassicalExpr,
166        /// Right operand.
167        rhs: ClassicalExpr,
168    },
169    /// Comparison producing a boolean result.
170    Compare {
171        /// Comparison operator.
172        op: ClassicalCompareOp,
173        /// Left operand.
174        lhs: ClassicalExpr,
175        /// Right operand.
176        rhs: ClassicalExpr,
177    },
178    /// Explicit conversion between classical types.
179    Cast {
180        /// Conversion operation.
181        cast: ClassicalCast,
182        /// Source expression.
183        expr: ClassicalExpr,
184    },
185    /// Conditional expression analogous to a typed ternary operator.
186    Select {
187        /// Boolean selection condition.
188        condition: ClassicalExpr,
189        /// Value selected when the condition is true.
190        then_expr: ClassicalExpr,
191        /// Value selected when the condition is false.
192        else_expr: ClassicalExpr,
193    },
194    /// Extract one bit from an integer or bit-vector expression.
195    ExtractBit {
196        /// Source expression.
197        value: ClassicalExpr,
198        /// Zero-based little-endian bit index.
199        index: u32,
200    },
201    /// Extract a contiguous bit range.
202    ExtractBits {
203        /// Source expression.
204        value: ClassicalExpr,
205        /// Zero-based little-endian starting offset.
206        offset: u32,
207        /// Number of bits in the result.
208        width: NonZeroU32,
209    },
210    /// Concatenate bit-vector expressions in the documented builder order.
211    Concat {
212        /// Expressions being concatenated.
213        parts: Box<[ClassicalExpr]>,
214    },
215    /// Pack individual `Bit` expressions into a bit vector.
216    PackBits {
217        /// Bits in increasing little-endian bit-index order.
218        bits: Box<[ClassicalExpr]>,
219    },
220}
221
222impl ClassicalExpr {
223    /// Creates an expression that reads the current runtime value of `var`.
224    pub fn var(var: ClassicalVar) -> Self {
225        Self {
226            node: Box::new(ClassicalExprNode {
227                ty: var.ty(),
228                kind: ClassicalExprKind::Var(var),
229            }),
230        }
231    }
232
233    /// Creates an expression that reads an immutable runtime classical value.
234    pub fn value(value: ClassicalValue) -> Self {
235        Self {
236            node: Box::new(ClassicalExprNode {
237                ty: value.ty(),
238                kind: ClassicalExprKind::Value(value),
239            }),
240        }
241    }
242
243    /// Creates a boolean literal expression.
244    pub fn bool_literal(value: bool) -> Self {
245        Self {
246            node: Box::new(ClassicalExprNode {
247                ty: ClassicalType::Bool,
248                kind: ClassicalExprKind::BoolLiteral(value),
249            }),
250        }
251    }
252
253    /// Creates a bit literal expression.
254    pub fn bit_literal(value: bool) -> Self {
255        Self {
256            node: Box::new(ClassicalExprNode {
257                ty: ClassicalType::Bit,
258                kind: ClassicalExprKind::BitLiteral(value),
259            }),
260        }
261    }
262
263    /// Creates an unsigned integer literal with a non-zero width.
264    ///
265    /// Literal construction currently supports constants up to 128 bits wide.
266    pub fn uint_literal(width: u32, value: u128) -> Result<Self, CircuitError> {
267        let Some(width) = NonZeroU32::new(width) else {
268            return Err(CircuitError::InvalidOperation(
269                "UInt literal width must be non-zero".to_string(),
270            ));
271        };
272        if width.get() > 128 {
273            return Err(CircuitError::InvalidOperation(format!(
274                "UInt literal width {} exceeds the 128-bit literal limit",
275                width.get()
276            )));
277        }
278        if width.get() < 128 && value >= (1u128 << width.get()) {
279            return Err(CircuitError::InvalidOperation(format!(
280                "UInt literal value {value} does not fit in width {}",
281                width.get()
282            )));
283        }
284        Ok(Self {
285            node: Box::new(ClassicalExprNode {
286                ty: ClassicalType::UInt(width),
287                kind: ClassicalExprKind::UIntLiteral { width, value },
288            }),
289        })
290    }
291
292    /// Creates a bit-vector literal with a non-zero width.
293    ///
294    /// Literal construction currently supports constants up to 128 bits wide.
295    pub fn bit_vec_literal(width: u32, value: u128) -> Result<Self, CircuitError> {
296        let Some(width) = NonZeroU32::new(width) else {
297            return Err(CircuitError::InvalidOperation(
298                "BitVec literal width must be non-zero".to_string(),
299            ));
300        };
301        if width.get() > 128 {
302            return Err(CircuitError::InvalidOperation(format!(
303                "BitVec literal width {} exceeds the 128-bit literal limit",
304                width.get()
305            )));
306        }
307        if width.get() < 128 && value >= (1u128 << width.get()) {
308            return Err(CircuitError::InvalidOperation(format!(
309                "BitVec literal value {value} does not fit in width {}",
310                width.get()
311            )));
312        }
313        Ok(Self {
314            node: Box::new(ClassicalExprNode {
315                ty: ClassicalType::BitVec(width),
316                kind: ClassicalExprKind::BitVecLiteral { width, value },
317            }),
318        })
319    }
320
321    /// Returns this expression's static type.
322    pub fn ty(&self) -> ClassicalType {
323        self.node.ty
324    }
325
326    /// Returns the expression node kind.
327    ///
328    /// This exposes the classical expression AST for inspection. Prefer the
329    /// typed constructors on [`ClassicalExpr`] when building expressions so
330    /// expression invariants remain centralized.
331    pub fn kind(&self) -> &ClassicalExprKind {
332        &self.node.kind
333    }
334
335    /// Returns a copy of this expression with circuit-local classical handles remapped.
336    ///
337    /// Every [`ClassicalVar`] and [`ClassicalValue`] referenced by this
338    /// expression must have an entry in the corresponding map. Literal and
339    /// structural nodes keep their static type and shape.
340    pub fn remap_classical_ids(
341        &self,
342        var_map: &HashMap<ClassicalVar, ClassicalVar>,
343        value_map: &HashMap<ClassicalValue, ClassicalValue>,
344    ) -> Result<Self, CircuitError> {
345        let kind = match &self.node.kind {
346            ClassicalExprKind::Var(var) => {
347                let mapped = var_map.get(var).copied().ok_or_else(|| {
348                    CircuitError::InvalidOperation(format!(
349                        "missing classical variable remap for id {}",
350                        var.id()
351                    ))
352                })?;
353                ClassicalExprKind::Var(mapped)
354            }
355            ClassicalExprKind::Value(value) => {
356                let mapped = value_map.get(value).copied().ok_or_else(|| {
357                    CircuitError::InvalidOperation(format!(
358                        "missing classical value remap for id {}",
359                        value.index()
360                    ))
361                })?;
362                ClassicalExprKind::Value(mapped)
363            }
364            ClassicalExprKind::BoolLiteral(value) => ClassicalExprKind::BoolLiteral(*value),
365            ClassicalExprKind::BitLiteral(value) => ClassicalExprKind::BitLiteral(*value),
366            ClassicalExprKind::UIntLiteral { width, value } => ClassicalExprKind::UIntLiteral {
367                width: *width,
368                value: *value,
369            },
370            ClassicalExprKind::BitVecLiteral { width, value } => ClassicalExprKind::BitVecLiteral {
371                width: *width,
372                value: *value,
373            },
374            ClassicalExprKind::Unary { op, expr } => ClassicalExprKind::Unary {
375                op: *op,
376                expr: expr.remap_classical_ids(var_map, value_map)?,
377            },
378            ClassicalExprKind::Binary { op, lhs, rhs } => ClassicalExprKind::Binary {
379                op: *op,
380                lhs: lhs.remap_classical_ids(var_map, value_map)?,
381                rhs: rhs.remap_classical_ids(var_map, value_map)?,
382            },
383            ClassicalExprKind::Compare { op, lhs, rhs } => ClassicalExprKind::Compare {
384                op: *op,
385                lhs: lhs.remap_classical_ids(var_map, value_map)?,
386                rhs: rhs.remap_classical_ids(var_map, value_map)?,
387            },
388            ClassicalExprKind::Cast { cast, expr } => ClassicalExprKind::Cast {
389                cast: *cast,
390                expr: expr.remap_classical_ids(var_map, value_map)?,
391            },
392            ClassicalExprKind::Select {
393                condition,
394                then_expr,
395                else_expr,
396            } => ClassicalExprKind::Select {
397                condition: condition.remap_classical_ids(var_map, value_map)?,
398                then_expr: then_expr.remap_classical_ids(var_map, value_map)?,
399                else_expr: else_expr.remap_classical_ids(var_map, value_map)?,
400            },
401            ClassicalExprKind::ExtractBit { value, index } => ClassicalExprKind::ExtractBit {
402                value: value.remap_classical_ids(var_map, value_map)?,
403                index: *index,
404            },
405            ClassicalExprKind::ExtractBits {
406                value,
407                offset,
408                width,
409            } => ClassicalExprKind::ExtractBits {
410                value: value.remap_classical_ids(var_map, value_map)?,
411                offset: *offset,
412                width: *width,
413            },
414            ClassicalExprKind::Concat { parts } => ClassicalExprKind::Concat {
415                parts: parts
416                    .iter()
417                    .map(|part| part.remap_classical_ids(var_map, value_map))
418                    .collect::<Result<Vec<_>, _>>()?
419                    .into_boxed_slice(),
420            },
421            ClassicalExprKind::PackBits { bits } => ClassicalExprKind::PackBits {
422                bits: bits
423                    .iter()
424                    .map(|bit| bit.remap_classical_ids(var_map, value_map))
425                    .collect::<Result<Vec<_>, _>>()?
426                    .into_boxed_slice(),
427            },
428        };
429
430        Ok(Self {
431            node: Box::new(ClassicalExprNode {
432                ty: self.node.ty,
433                kind,
434            }),
435        })
436    }
437
438    /// Returns all runtime classical variables read by this expression.
439    pub fn vars(&self) -> BTreeSet<ClassicalVar> {
440        let mut vars = BTreeSet::new();
441        self.collect_vars(&mut vars);
442        vars
443    }
444
445    /// Returns all immutable runtime classical values read by this expression.
446    pub fn values(&self) -> BTreeSet<ClassicalValue> {
447        let mut values = BTreeSet::new();
448        self.collect_values(&mut values);
449        values
450    }
451
452    /// Adds every runtime classical variable read by this expression to `out`.
453    pub fn collect_vars(&self, out: &mut BTreeSet<ClassicalVar>) {
454        match &self.node.kind {
455            ClassicalExprKind::Var(var) => {
456                out.insert(*var);
457            }
458            ClassicalExprKind::Value(_) => {}
459            ClassicalExprKind::BoolLiteral(_)
460            | ClassicalExprKind::BitLiteral(_)
461            | ClassicalExprKind::UIntLiteral { .. }
462            | ClassicalExprKind::BitVecLiteral { .. } => {}
463            ClassicalExprKind::Unary { expr, .. } | ClassicalExprKind::Cast { expr, .. } => {
464                expr.collect_vars(out);
465            }
466            ClassicalExprKind::Binary { lhs, rhs, .. }
467            | ClassicalExprKind::Compare { lhs, rhs, .. } => {
468                lhs.collect_vars(out);
469                rhs.collect_vars(out);
470            }
471            ClassicalExprKind::Select {
472                condition,
473                then_expr,
474                else_expr,
475            } => {
476                condition.collect_vars(out);
477                then_expr.collect_vars(out);
478                else_expr.collect_vars(out);
479            }
480            ClassicalExprKind::ExtractBit { value, .. }
481            | ClassicalExprKind::ExtractBits { value, .. } => {
482                value.collect_vars(out);
483            }
484            ClassicalExprKind::Concat { parts } => {
485                for part in parts.iter() {
486                    part.collect_vars(out);
487                }
488            }
489            ClassicalExprKind::PackBits { bits } => {
490                for bit in bits.iter() {
491                    bit.collect_vars(out);
492                }
493            }
494        }
495    }
496
497    /// Adds every immutable runtime classical value read by this expression to `out`.
498    pub fn collect_values(&self, out: &mut BTreeSet<ClassicalValue>) {
499        match &self.node.kind {
500            ClassicalExprKind::Value(value) => {
501                out.insert(*value);
502            }
503            ClassicalExprKind::Var(_)
504            | ClassicalExprKind::BoolLiteral(_)
505            | ClassicalExprKind::BitLiteral(_)
506            | ClassicalExprKind::UIntLiteral { .. }
507            | ClassicalExprKind::BitVecLiteral { .. } => {}
508            ClassicalExprKind::Unary { expr, .. } | ClassicalExprKind::Cast { expr, .. } => {
509                expr.collect_values(out);
510            }
511            ClassicalExprKind::Binary { lhs, rhs, .. }
512            | ClassicalExprKind::Compare { lhs, rhs, .. } => {
513                lhs.collect_values(out);
514                rhs.collect_values(out);
515            }
516            ClassicalExprKind::Select {
517                condition,
518                then_expr,
519                else_expr,
520            } => {
521                condition.collect_values(out);
522                then_expr.collect_values(out);
523                else_expr.collect_values(out);
524            }
525            ClassicalExprKind::ExtractBit { value, .. }
526            | ClassicalExprKind::ExtractBits { value, .. } => {
527                value.collect_values(out);
528            }
529            ClassicalExprKind::Concat { parts } => {
530                for part in parts.iter() {
531                    part.collect_values(out);
532                }
533            }
534            ClassicalExprKind::PackBits { bits } => {
535                for bit in bits.iter() {
536                    bit.collect_values(out);
537                }
538            }
539        }
540    }
541
542    /// Creates a `not` expression for `Bool` or `Bit` values.
543    pub fn try_not(expr: impl Into<Self>) -> Result<Self, CircuitError> {
544        let expr = expr.into();
545        match expr.ty() {
546            ClassicalType::Bool | ClassicalType::Bit => Ok(Self {
547                node: Box::new(ClassicalExprNode {
548                    ty: expr.ty(),
549                    kind: ClassicalExprKind::Unary {
550                        op: ClassicalUnaryOp::Not,
551                        expr,
552                    },
553                }),
554            }),
555            ty => Err(CircuitError::InvalidOperation(format!(
556                "not expects Bool or Bit, got {ty:?}"
557            ))),
558        }
559    }
560
561    /// Creates an `and` expression for matching `Bool` or matching `Bit` values.
562    pub fn try_and(lhs: impl Into<Self>, rhs: impl Into<Self>) -> Result<Self, CircuitError> {
563        let lhs = lhs.into();
564        let rhs = rhs.into();
565        let ty = lhs.ty();
566        if ty != rhs.ty() {
567            return Err(CircuitError::InvalidOperation(format!(
568                "binary operands must have the same type, got {:?} and {:?}",
569                lhs.ty(),
570                rhs.ty()
571            )));
572        }
573        match ty {
574            ClassicalType::Bool | ClassicalType::Bit => Ok(Self {
575                node: Box::new(ClassicalExprNode {
576                    ty,
577                    kind: ClassicalExprKind::Binary {
578                        op: ClassicalBinaryOp::And,
579                        lhs,
580                        rhs,
581                    },
582                }),
583            }),
584            ty => Err(CircuitError::InvalidOperation(format!(
585                "binary And expects Bool or Bit operands, got {ty:?}"
586            ))),
587        }
588    }
589
590    /// Creates an `or` expression for matching `Bool` or matching `Bit` values.
591    pub fn try_or(lhs: impl Into<Self>, rhs: impl Into<Self>) -> Result<Self, CircuitError> {
592        let lhs = lhs.into();
593        let rhs = rhs.into();
594        let ty = lhs.ty();
595        if ty != rhs.ty() {
596            return Err(CircuitError::InvalidOperation(format!(
597                "binary operands must have the same type, got {:?} and {:?}",
598                lhs.ty(),
599                rhs.ty()
600            )));
601        }
602        match ty {
603            ClassicalType::Bool | ClassicalType::Bit => Ok(Self {
604                node: Box::new(ClassicalExprNode {
605                    ty,
606                    kind: ClassicalExprKind::Binary {
607                        op: ClassicalBinaryOp::Or,
608                        lhs,
609                        rhs,
610                    },
611                }),
612            }),
613            ty => Err(CircuitError::InvalidOperation(format!(
614                "binary Or expects Bool or Bit operands, got {ty:?}"
615            ))),
616        }
617    }
618
619    /// Creates an `xor` expression for matching `Bool` or matching `Bit` values.
620    pub fn try_xor(lhs: impl Into<Self>, rhs: impl Into<Self>) -> Result<Self, CircuitError> {
621        let lhs = lhs.into();
622        let rhs = rhs.into();
623        let ty = lhs.ty();
624        if ty != rhs.ty() {
625            return Err(CircuitError::InvalidOperation(format!(
626                "binary operands must have the same type, got {:?} and {:?}",
627                lhs.ty(),
628                rhs.ty()
629            )));
630        }
631        match ty {
632            ClassicalType::Bool | ClassicalType::Bit => Ok(Self {
633                node: Box::new(ClassicalExprNode {
634                    ty,
635                    kind: ClassicalExprKind::Binary {
636                        op: ClassicalBinaryOp::Xor,
637                        lhs,
638                        rhs,
639                    },
640                }),
641            }),
642            ty => Err(CircuitError::InvalidOperation(format!(
643                "binary Xor expects Bool or Bit operands, got {ty:?}"
644            ))),
645        }
646    }
647
648    /// Creates an equality comparison. Operands must have the same type.
649    pub fn eq(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
650        if lhs.ty() != rhs.ty() {
651            return Err(CircuitError::InvalidOperation(format!(
652                "comparison operands must have the same type, got {:?} and {:?}",
653                lhs.ty(),
654                rhs.ty()
655            )));
656        }
657        Ok(Self {
658            node: Box::new(ClassicalExprNode {
659                ty: ClassicalType::Bool,
660                kind: ClassicalExprKind::Compare {
661                    op: ClassicalCompareOp::Eq,
662                    lhs,
663                    rhs,
664                },
665            }),
666        })
667    }
668
669    /// Creates an inequality comparison. Operands must have the same type.
670    pub fn ne(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
671        if lhs.ty() != rhs.ty() {
672            return Err(CircuitError::InvalidOperation(format!(
673                "comparison operands must have the same type, got {:?} and {:?}",
674                lhs.ty(),
675                rhs.ty()
676            )));
677        }
678        Ok(Self {
679            node: Box::new(ClassicalExprNode {
680                ty: ClassicalType::Bool,
681                kind: ClassicalExprKind::Compare {
682                    op: ClassicalCompareOp::Ne,
683                    lhs,
684                    rhs,
685                },
686            }),
687        })
688    }
689
690    /// Creates an unsigned less-than comparison.
691    pub fn lt(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
692        if lhs.ty() != rhs.ty() {
693            return Err(CircuitError::InvalidOperation(format!(
694                "comparison operands must have the same type, got {:?} and {:?}",
695                lhs.ty(),
696                rhs.ty()
697            )));
698        }
699        if !matches!(lhs.ty(), ClassicalType::UInt(_)) {
700            return Err(CircuitError::InvalidOperation(format!(
701                "ordered comparisons require UInt operands, got {:?}",
702                lhs.ty()
703            )));
704        }
705        Ok(Self {
706            node: Box::new(ClassicalExprNode {
707                ty: ClassicalType::Bool,
708                kind: ClassicalExprKind::Compare {
709                    op: ClassicalCompareOp::Lt,
710                    lhs,
711                    rhs,
712                },
713            }),
714        })
715    }
716
717    /// Creates an unsigned less-than-or-equal comparison.
718    pub fn le(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
719        if lhs.ty() != rhs.ty() {
720            return Err(CircuitError::InvalidOperation(format!(
721                "comparison operands must have the same type, got {:?} and {:?}",
722                lhs.ty(),
723                rhs.ty()
724            )));
725        }
726        if !matches!(lhs.ty(), ClassicalType::UInt(_)) {
727            return Err(CircuitError::InvalidOperation(format!(
728                "ordered comparisons require UInt operands, got {:?}",
729                lhs.ty()
730            )));
731        }
732        Ok(Self {
733            node: Box::new(ClassicalExprNode {
734                ty: ClassicalType::Bool,
735                kind: ClassicalExprKind::Compare {
736                    op: ClassicalCompareOp::Le,
737                    lhs,
738                    rhs,
739                },
740            }),
741        })
742    }
743
744    /// Creates an unsigned greater-than comparison.
745    pub fn gt(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
746        if lhs.ty() != rhs.ty() {
747            return Err(CircuitError::InvalidOperation(format!(
748                "comparison operands must have the same type, got {:?} and {:?}",
749                lhs.ty(),
750                rhs.ty()
751            )));
752        }
753        if !matches!(lhs.ty(), ClassicalType::UInt(_)) {
754            return Err(CircuitError::InvalidOperation(format!(
755                "ordered comparisons require UInt operands, got {:?}",
756                lhs.ty()
757            )));
758        }
759        Ok(Self {
760            node: Box::new(ClassicalExprNode {
761                ty: ClassicalType::Bool,
762                kind: ClassicalExprKind::Compare {
763                    op: ClassicalCompareOp::Gt,
764                    lhs,
765                    rhs,
766                },
767            }),
768        })
769    }
770
771    /// Creates an unsigned greater-than-or-equal comparison.
772    pub fn ge(lhs: Self, rhs: Self) -> Result<Self, CircuitError> {
773        if lhs.ty() != rhs.ty() {
774            return Err(CircuitError::InvalidOperation(format!(
775                "comparison operands must have the same type, got {:?} and {:?}",
776                lhs.ty(),
777                rhs.ty()
778            )));
779        }
780        if !matches!(lhs.ty(), ClassicalType::UInt(_)) {
781            return Err(CircuitError::InvalidOperation(format!(
782                "ordered comparisons require UInt operands, got {:?}",
783                lhs.ty()
784            )));
785        }
786        Ok(Self {
787            node: Box::new(ClassicalExprNode {
788                ty: ClassicalType::Bool,
789                kind: ClassicalExprKind::Compare {
790                    op: ClassicalCompareOp::Ge,
791                    lhs,
792                    rhs,
793                },
794            }),
795        })
796    }
797
798    /// Explicitly casts a `Bit` expression to `Bool`.
799    pub fn bit_to_bool(expr: Self) -> Result<Self, CircuitError> {
800        if expr.ty() != ClassicalType::Bit {
801            return Err(CircuitError::InvalidOperation(format!(
802                "bit_to_bool expects Bit, got {:?}",
803                expr.ty()
804            )));
805        }
806        Ok(Self {
807            node: Box::new(ClassicalExprNode {
808                ty: ClassicalType::Bool,
809                kind: ClassicalExprKind::Cast {
810                    cast: ClassicalCast::BitToBool,
811                    expr,
812                },
813            }),
814        })
815    }
816
817    /// Explicitly casts a `BitVec` expression to a little-endian `UInt`.
818    pub fn bit_vec_to_uint(expr: Self) -> Result<Self, CircuitError> {
819        match expr.ty() {
820            ClassicalType::BitVec(width) => Ok(Self {
821                node: Box::new(ClassicalExprNode {
822                    ty: ClassicalType::UInt(width),
823                    kind: ClassicalExprKind::Cast {
824                        cast: ClassicalCast::BitVecToUInt,
825                        expr,
826                    },
827                }),
828            }),
829            ty => Err(CircuitError::InvalidOperation(format!(
830                "bit_vec_to_uint expects BitVec, got {ty:?}"
831            ))),
832        }
833    }
834
835    /// Converts this `Bit` expression to `Bool`.
836    pub fn to_bool(self) -> Result<Self, CircuitError> {
837        Self::bit_to_bool(self)
838    }
839
840    /// Converts this `BitVec` expression to a little-endian `UInt`.
841    pub fn to_uint(self) -> Result<Self, CircuitError> {
842        Self::bit_vec_to_uint(self)
843    }
844
845    /// Creates an expression that chooses between two same-typed values.
846    pub fn select(condition: Self, then_expr: Self, else_expr: Self) -> Result<Self, CircuitError> {
847        if condition.ty() != ClassicalType::Bool {
848            return Err(CircuitError::InvalidOperation(format!(
849                "select condition expects Bool, got {:?}",
850                condition.ty()
851            )));
852        }
853        if then_expr.ty() != else_expr.ty() {
854            return Err(CircuitError::InvalidOperation(format!(
855                "select branches must have the same type, got {:?} and {:?}",
856                then_expr.ty(),
857                else_expr.ty()
858            )));
859        }
860        Ok(Self {
861            node: Box::new(ClassicalExprNode {
862                ty: then_expr.ty(),
863                kind: ClassicalExprKind::Select {
864                    condition,
865                    then_expr,
866                    else_expr,
867                },
868            }),
869        })
870    }
871
872    /// Extracts a single bit from a `BitVec` or `UInt`.
873    ///
874    /// Index `0` is the least-significant bit.
875    pub fn extract_bit(value: Self, index: u32) -> Result<Self, CircuitError> {
876        let width = match value.ty() {
877            ClassicalType::UInt(width) | ClassicalType::BitVec(width) => width.get(),
878            ty => {
879                return Err(CircuitError::InvalidOperation(format!(
880                    "extract_bit expects UInt or BitVec, got {ty:?}"
881                )));
882            }
883        };
884        if index >= width {
885            return Err(CircuitError::InvalidOperation(format!(
886                "extract_bit index {index} out of bounds for width {width}"
887            )));
888        }
889        Ok(Self {
890            node: Box::new(ClassicalExprNode {
891                ty: ClassicalType::Bit,
892                kind: ClassicalExprKind::ExtractBit { value, index },
893            }),
894        })
895    }
896
897    /// Extracts a bit range from a `BitVec` or `UInt`.
898    ///
899    /// `offset` is little-endian: offset `0` starts at the least-significant bit.
900    pub fn extract_bits(value: Self, offset: u32, width: u32) -> Result<Self, CircuitError> {
901        let source_width = match value.ty() {
902            ClassicalType::UInt(width) | ClassicalType::BitVec(width) => width.get(),
903            ty => {
904                return Err(CircuitError::InvalidOperation(format!(
905                    "extract_bits expects UInt or BitVec, got {ty:?}"
906                )));
907            }
908        };
909        let Some(width) = NonZeroU32::new(width) else {
910            return Err(CircuitError::InvalidOperation(
911                "extract_bits width must be non-zero".to_string(),
912            ));
913        };
914        let Some(end) = offset.checked_add(width.get()) else {
915            return Err(CircuitError::InvalidOperation(
916                "extract_bits range overflows u32".to_string(),
917            ));
918        };
919        if end > source_width {
920            return Err(CircuitError::InvalidOperation(format!(
921                "extract_bits range [{offset}, {end}) out of bounds for width {source_width}"
922            )));
923        }
924        Ok(Self {
925            node: Box::new(ClassicalExprNode {
926                ty: ClassicalType::BitVec(width),
927                kind: ClassicalExprKind::ExtractBits {
928                    value,
929                    offset,
930                    width,
931                },
932            }),
933        })
934    }
935
936    /// Concatenates bit-vector values into a larger bit-vector.
937    ///
938    /// The first part occupies the least-significant output bits.
939    pub fn concat(parts: impl IntoIterator<Item = Self>) -> Result<Self, CircuitError> {
940        let parts: Vec<Self> = parts.into_iter().collect();
941        if parts.is_empty() {
942            return Err(CircuitError::InvalidOperation(
943                "concat requires at least one part".to_string(),
944            ));
945        }
946
947        let mut total_width = 0u32;
948        for part in &parts {
949            match part.ty() {
950                ClassicalType::Bit => {
951                    let Some(next_width) = total_width.checked_add(1) else {
952                        return Err(CircuitError::InvalidOperation(
953                            "concat width overflows u32".to_string(),
954                        ));
955                    };
956                    total_width = next_width;
957                }
958                ClassicalType::BitVec(width) => {
959                    let Some(next_width) = total_width.checked_add(width.get()) else {
960                        return Err(CircuitError::InvalidOperation(
961                            "concat width overflows u32".to_string(),
962                        ));
963                    };
964                    total_width = next_width;
965                }
966                ty => {
967                    return Err(CircuitError::InvalidOperation(format!(
968                        "concat expects Bit or BitVec parts, got {ty:?}"
969                    )));
970                }
971            }
972        }
973
974        let Some(width) = NonZeroU32::new(total_width) else {
975            return Err(CircuitError::InvalidOperation(
976                "concat width must be non-zero".to_string(),
977            ));
978        };
979        Ok(Self {
980            node: Box::new(ClassicalExprNode {
981                ty: ClassicalType::BitVec(width),
982                kind: ClassicalExprKind::Concat {
983                    parts: parts.into_boxed_slice(),
984                },
985            }),
986        })
987    }
988
989    /// Returns `true` when this expression is the boolean literal `true`.
990    pub fn is_bool_true(&self) -> bool {
991        matches!(self.kind(), ClassicalExprKind::BoolLiteral(true))
992    }
993
994    /// Returns `true` when this expression is the boolean literal `false`.
995    pub fn is_bool_false(&self) -> bool {
996        matches!(self.kind(), ClassicalExprKind::BoolLiteral(false))
997    }
998
999    /// Returns `true` when this expression is the bit literal `true` (1).
1000    pub fn is_bit_true(&self) -> bool {
1001        matches!(self.kind(), ClassicalExprKind::BitLiteral(true))
1002    }
1003
1004    /// Returns `true` when this expression is the bit literal `false` (0).
1005    pub fn is_bit_false(&self) -> bool {
1006        matches!(self.kind(), ClassicalExprKind::BitLiteral(false))
1007    }
1008
1009    /// Returns a structurally simplified copy of this expression.
1010    ///
1011    /// Simplification eliminates literal-driven redundancies and algebraic
1012    /// identities without evaluating runtime variable or value reads. It is
1013    /// idempotent: applying it twice yields the same expression.
1014    ///
1015    /// See [`simplify`](super::simplify::simplify) for the full rule set.
1016    pub fn simplified(&self) -> Self {
1017        super::simplify::simplify(self)
1018    }
1019
1020    /// Creates an expression from a type-tag and node kind.
1021    ///
1022    /// This is a low-level constructor used by the simplifier and other
1023    /// internal passes. Prefer the typed constructors for normal circuit
1024    /// building.
1025    pub(crate) fn from_kind(ty: ClassicalType, kind: ClassicalExprKind) -> Self {
1026        Self {
1027            node: Box::new(ClassicalExprNode { ty, kind }),
1028        }
1029    }
1030
1031    /// Packs single-bit expressions into a `BitVec`.
1032    ///
1033    /// The first bit becomes output index `0`, the least-significant bit.
1034    pub fn pack_bits(bits: impl IntoIterator<Item = Self>) -> Result<Self, CircuitError> {
1035        let bits: Vec<Self> = bits.into_iter().collect();
1036        if bits.is_empty() {
1037            return Err(CircuitError::InvalidOperation(
1038                "pack_bits requires at least one bit".to_string(),
1039            ));
1040        }
1041        for bit in &bits {
1042            if bit.ty() != ClassicalType::Bit {
1043                return Err(CircuitError::InvalidOperation(format!(
1044                    "pack_bits expects Bit expressions, got {:?}",
1045                    bit.ty()
1046                )));
1047            }
1048        }
1049
1050        let Some(width) = NonZeroU32::new(bits.len() as u32) else {
1051            return Err(CircuitError::InvalidOperation(
1052                "pack_bits width must be non-zero".to_string(),
1053            ));
1054        };
1055        Ok(Self {
1056            node: Box::new(ClassicalExprNode {
1057                ty: ClassicalType::BitVec(width),
1058                kind: ClassicalExprKind::PackBits {
1059                    bits: bits.into_boxed_slice(),
1060                },
1061            }),
1062        })
1063    }
1064
1065    /// Returns the target bit replaced by a single-bit measurement result.
1066    ///
1067    /// The expression must reconstruct the complete `BitVec` target in index
1068    /// order, preserving every other bit from the target itself and using
1069    /// `result` exactly once.
1070    pub(crate) fn measurement_store_bit(
1071        &self,
1072        target: ClassicalVar,
1073        result: ClassicalValue,
1074    ) -> Option<u32> {
1075        let ClassicalType::BitVec(width) = target.ty() else {
1076            return None;
1077        };
1078        let ClassicalExprKind::PackBits { bits } = self.kind() else {
1079            return None;
1080        };
1081        if bits.len() != width.get() as usize {
1082            return None;
1083        }
1084
1085        let mut measured_index = None;
1086        for (index, bit) in bits.iter().enumerate() {
1087            match bit.kind() {
1088                ClassicalExprKind::Value(value) if *value == result => {
1089                    if measured_index.replace(index as u32).is_some() {
1090                        return None;
1091                    }
1092                }
1093                ClassicalExprKind::ExtractBit {
1094                    value,
1095                    index: source_index,
1096                } if *source_index == index as u32
1097                    && matches!(value.kind(), ClassicalExprKind::Var(var) if *var == target) => {}
1098                _ => return None,
1099            }
1100        }
1101        measured_index
1102    }
1103}
1104
1105impl From<ClassicalVar> for ClassicalExpr {
1106    fn from(var: ClassicalVar) -> Self {
1107        Self::var(var)
1108    }
1109}
1110
1111impl From<ClassicalValue> for ClassicalExpr {
1112    fn from(value: ClassicalValue) -> Self {
1113        Self::value(value)
1114    }
1115}
1116
1117macro_rules! impl_expr_not {
1118    ($ty:ty) => {
1119        impl Not for $ty {
1120            type Output = ClassicalExpr;
1121
1122            fn not(self) -> Self::Output {
1123                ClassicalExpr::try_not(self)
1124                    .expect("ClassicalExpr ! operator requires a Bool or Bit expression")
1125            }
1126        }
1127    };
1128}
1129
1130macro_rules! impl_expr_binary_ops {
1131    ($ty:ty) => {
1132        impl<Rhs> BitAnd<Rhs> for $ty
1133        where
1134            Rhs: Into<ClassicalExpr>,
1135        {
1136            type Output = ClassicalExpr;
1137
1138            fn bitand(self, rhs: Rhs) -> Self::Output {
1139                ClassicalExpr::try_and(self, rhs).expect(
1140                    "ClassicalExpr & operator requires matching Bool or matching Bit expressions",
1141                )
1142            }
1143        }
1144
1145        impl<Rhs> BitOr<Rhs> for $ty
1146        where
1147            Rhs: Into<ClassicalExpr>,
1148        {
1149            type Output = ClassicalExpr;
1150
1151            fn bitor(self, rhs: Rhs) -> Self::Output {
1152                ClassicalExpr::try_or(self, rhs).expect(
1153                    "ClassicalExpr | operator requires matching Bool or matching Bit expressions",
1154                )
1155            }
1156        }
1157
1158        impl<Rhs> BitXor<Rhs> for $ty
1159        where
1160            Rhs: Into<ClassicalExpr>,
1161        {
1162            type Output = ClassicalExpr;
1163
1164            fn bitxor(self, rhs: Rhs) -> Self::Output {
1165                ClassicalExpr::try_xor(self, rhs).expect(
1166                    "ClassicalExpr ^ operator requires matching Bool or matching Bit expressions",
1167                )
1168            }
1169        }
1170    };
1171}
1172
1173impl_expr_not!(ClassicalExpr);
1174impl_expr_not!(ClassicalVar);
1175impl_expr_not!(ClassicalValue);
1176impl_expr_binary_ops!(ClassicalExpr);
1177impl_expr_binary_ops!(ClassicalVar);
1178impl_expr_binary_ops!(ClassicalValue);
1179
1180#[cfg(test)]
1181#[path = "expr_test.rs"]
1182mod expr_test;