Skip to main content

logicaffeine_proof/
aig.rs

1//! And-inverter graphs: a circuit as two-input AND gates over complemented edges, read and written in the AIGER
2//! format (Biere, *The AIGER And-Inverter Graph Format, Version 20071012*, and its 1.9 extension).
3//!
4//! An [`Aig`] is built bottom-up and kept in topological order: every gate is created after its operands, so a
5//! gate's node index exceeds both of theirs. [`Aig::and`] hashes structurally (Kuehlmann & Krohm, DAC 1997): an
6//! operand pair it has seen returns the existing gate, and a constant, repeated or complementary operand folds away,
7//! so two circuits built into one graph share every gate they compute the same way. Inputs and latches are leaves;
8//! a latch's next-state literal may name any node, which is how a sequential circuit closes its loops without
9//! breaking the order.
10//!
11//! [`Aig::parse`] reads the ASCII (`aag`) and binary (`aig`) forms, including latch initial values, bad-state and
12//! invariant-constraint sections and the symbol table, and refuses justice and fairness properties by name. ASCII
13//! gates may be defined in any order; they are rebuilt in dependency order without recursion, so a chain of any
14//! depth reads. [`Aig::to_ascii`] and [`Aig::to_binary`] write the canonical numbering: inputs, then latches, then
15//! gates in node order.
16//!
17//! [`Aig::eval`] evaluates one assignment and [`Aig::simulate`] sixty-four assignments a word over any number of
18//! words: the signatures SAT sweeping ([`crate::sweep`]) groups candidate equivalences by.
19
20use std::collections::HashMap;
21
22/// A literal of an and-inverter graph: node `n` as `2n`, its complement as `2n + 1`, the AIGER encoding. Node 0 is
23/// the constant false.
24#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
25pub struct AigLit(u32);
26
27impl AigLit {
28    pub const FALSE: AigLit = AigLit(0);
29    pub const TRUE: AigLit = AigLit(1);
30
31    pub fn new(node: u32, complemented: bool) -> AigLit {
32        AigLit(node << 1 | complemented as u32)
33    }
34
35    /// The literal whose AIGER code is `code`.
36    pub fn from_code(code: u32) -> AigLit {
37        AigLit(code)
38    }
39
40    pub fn node(self) -> u32 {
41        self.0 >> 1
42    }
43
44    pub fn is_complemented(self) -> bool {
45        self.0 & 1 == 1
46    }
47
48    pub fn code(self) -> u32 {
49        self.0
50    }
51
52    /// This literal, complemented when `flip` is set.
53    pub fn with_phase(self, flip: bool) -> AigLit {
54        AigLit(self.0 ^ flip as u32)
55    }
56}
57
58impl std::ops::Not for AigLit {
59    type Output = AigLit;
60    fn not(self) -> AigLit {
61        AigLit(self.0 ^ 1)
62    }
63}
64
65/// What a node is.
66#[derive(Clone, Copy, PartialEq, Eq, Debug)]
67pub enum Node {
68    False,
69    /// The input at this index of [`Aig::inputs`].
70    Input(usize),
71    /// The latch at this index of [`Aig::latches`].
72    Latch(usize),
73    /// The conjunction of two literals of lower-numbered nodes, the larger code first.
74    And(AigLit, AigLit),
75}
76
77/// A latch's value before the first clock.
78#[derive(Clone, Copy, PartialEq, Eq, Debug)]
79pub enum Init {
80    Zero,
81    One,
82    /// Any value: the AIGER file writes the latch's own literal.
83    Free,
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct Latch {
88    pub node: u32,
89    pub next: AigLit,
90    pub init: Init,
91    pub name: Option<String>,
92}
93
94/// An input (its node, uncomplemented), output, bad-state property or invariant constraint, with its symbol.
95#[derive(Clone, Debug, PartialEq, Eq)]
96pub struct Port {
97    pub lit: AigLit,
98    pub name: Option<String>,
99}
100
101/// A leaf of a graph being imported into another ([`Aig::import`]).
102#[derive(Clone, Copy, PartialEq, Eq, Debug)]
103pub enum Leaf {
104    Input(usize),
105    Latch(usize),
106}
107
108/// Why an AIGER file was refused. Lines count from 1.
109#[derive(Clone, Debug, PartialEq, Eq)]
110pub enum AigerError {
111    Header(String),
112    /// A literal outside the range the header allows, or of the wrong parity for its place.
113    Literal { line: usize, literal: u64 },
114    /// A literal whose variable nothing defines.
115    Undefined { line: usize, var: u64 },
116    /// A variable defined twice.
117    Redefined { line: usize, var: u64 },
118    /// A gate that depends on itself.
119    Cycle { var: u64 },
120    /// A line that is not what its place in the file calls for.
121    Malformed { line: usize },
122    Unsupported(String),
123    Truncated,
124}
125
126impl std::fmt::Display for AigerError {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        match self {
129            AigerError::Header(why) => write!(f, "AIGER header: {why}"),
130            AigerError::Literal { line, literal } => write!(f, "line {line}: literal {literal} is out of place"),
131            AigerError::Undefined { line, var } => write!(f, "line {line}: variable {var} is never defined"),
132            AigerError::Redefined { line, var } => write!(f, "line {line}: variable {var} is defined twice"),
133            AigerError::Cycle { var } => write!(f, "gate {var} depends on itself"),
134            AigerError::Malformed { line } => write!(f, "line {line} is malformed"),
135            AigerError::Unsupported(what) => write!(f, "unsupported: {what}"),
136            AigerError::Truncated => write!(f, "the file ends early"),
137        }
138    }
139}
140
141impl std::error::Error for AigerError {}
142
143/// An and-inverter graph with its inputs, latches, outputs, bad-state properties, invariant constraints and
144/// comments.
145#[derive(Clone, Debug)]
146pub struct Aig {
147    nodes: Vec<Node>,
148    inputs: Vec<Port>,
149    latches: Vec<Latch>,
150    outputs: Vec<Port>,
151    bad: Vec<Port>,
152    constraints: Vec<Port>,
153    comments: Vec<String>,
154    strash: HashMap<(AigLit, AigLit), u32>,
155}
156
157impl Default for Aig {
158    fn default() -> Aig {
159        Aig::new()
160    }
161}
162
163impl Aig {
164    /// A graph holding only the constant node.
165    pub fn new() -> Aig {
166        Aig {
167            nodes: vec![Node::False],
168            inputs: Vec::new(),
169            latches: Vec::new(),
170            outputs: Vec::new(),
171            bad: Vec::new(),
172            constraints: Vec::new(),
173            comments: Vec::new(),
174            strash: HashMap::new(),
175        }
176    }
177
178    fn push(&mut self, node: Node) -> u32 {
179        let n = self.nodes.len() as u32;
180        self.nodes.push(node);
181        n
182    }
183
184    pub fn add_input(&mut self, name: Option<String>) -> AigLit {
185        let n = self.push(Node::Input(self.inputs.len()));
186        let lit = AigLit::new(n, false);
187        self.inputs.push(Port { lit, name });
188        lit
189    }
190
191    /// A latch whose next state is false until [`Aig::set_next`]; returns its index.
192    pub fn add_latch(&mut self, name: Option<String>, init: Init) -> usize {
193        let k = self.latches.len();
194        let node = self.push(Node::Latch(k));
195        self.latches.push(Latch { node, next: AigLit::FALSE, init, name });
196        k
197    }
198
199    /// The latch's current-state literal.
200    pub fn latch_lit(&self, latch: usize) -> AigLit {
201        AigLit::new(self.latches[latch].node, false)
202    }
203
204    pub fn set_next(&mut self, latch: usize, next: AigLit) {
205        self.latches[latch].next = next;
206    }
207
208    /// `a ∧ b`, folded and structurally hashed.
209    pub fn and(&mut self, a: AigLit, b: AigLit) -> AigLit {
210        let (hi, lo) = if a >= b { (a, b) } else { (b, a) };
211        if lo == AigLit::FALSE || hi == !lo {
212            return AigLit::FALSE;
213        }
214        if lo == AigLit::TRUE || hi == lo {
215            return hi;
216        }
217        if let Some(&n) = self.strash.get(&(hi, lo)) {
218            return AigLit::new(n, false);
219        }
220        let n = self.push(Node::And(hi, lo));
221        self.strash.insert((hi, lo), n);
222        AigLit::new(n, false)
223    }
224
225    pub fn or(&mut self, a: AigLit, b: AigLit) -> AigLit {
226        !self.and(!a, !b)
227    }
228
229    pub fn xor(&mut self, a: AigLit, b: AigLit) -> AigLit {
230        let l = self.and(a, !b);
231        let r = self.and(!a, b);
232        self.or(l, r)
233    }
234
235    pub fn xnor(&mut self, a: AigLit, b: AigLit) -> AigLit {
236        !self.xor(a, b)
237    }
238
239    /// `s ? t : e`.
240    pub fn mux(&mut self, s: AigLit, t: AigLit, e: AigLit) -> AigLit {
241        let l = self.and(s, t);
242        let r = self.and(!s, e);
243        self.or(l, r)
244    }
245
246    pub fn add_output(&mut self, lit: AigLit, name: Option<String>) {
247        self.outputs.push(Port { lit, name });
248    }
249
250    pub fn add_bad(&mut self, lit: AigLit, name: Option<String>) {
251        self.bad.push(Port { lit, name });
252    }
253
254    pub fn add_constraint(&mut self, lit: AigLit, name: Option<String>) {
255        self.constraints.push(Port { lit, name });
256    }
257
258    pub fn node(&self, n: u32) -> Node {
259        self.nodes[n as usize]
260    }
261
262    pub fn num_nodes(&self) -> usize {
263        self.nodes.len()
264    }
265
266    pub fn num_ands(&self) -> usize {
267        self.nodes.iter().filter(|n| matches!(n, Node::And(..))).count()
268    }
269
270    pub fn inputs(&self) -> &[Port] {
271        &self.inputs
272    }
273
274    pub fn latches(&self) -> &[Latch] {
275        &self.latches
276    }
277
278    pub fn outputs(&self) -> &[Port] {
279        &self.outputs
280    }
281
282    pub fn bad(&self) -> &[Port] {
283        &self.bad
284    }
285
286    pub fn constraints(&self) -> &[Port] {
287        &self.constraints
288    }
289
290    pub fn comments(&self) -> &[String] {
291        &self.comments
292    }
293
294    pub fn input_index(&self, name: &str) -> Option<usize> {
295        self.inputs.iter().position(|p| p.name.as_deref() == Some(name))
296    }
297
298    pub fn output_index(&self, name: &str) -> Option<usize> {
299        self.outputs.iter().position(|p| p.name.as_deref() == Some(name))
300    }
301
302    /// The value of every node under one assignment of the inputs and the latches.
303    pub fn eval(&self, inputs: &[bool], latches: &[bool]) -> Vec<bool> {
304        let mut v = vec![false; self.nodes.len()];
305        for (n, node) in self.nodes.iter().enumerate() {
306            v[n] = match *node {
307                Node::False => false,
308                Node::Input(i) => inputs[i],
309                Node::Latch(k) => latches[k],
310                Node::And(a, b) => Aig::value(&v, a) && Aig::value(&v, b),
311            };
312        }
313        v
314    }
315
316    /// A literal's value among [`Aig::eval`]'s node values.
317    pub fn value(values: &[bool], lit: AigLit) -> bool {
318        values[lit.node() as usize] ^ lit.is_complemented()
319    }
320
321    /// Every node's value under `64 · words` assignments at once: input `i`'s words are
322    /// `inputs[i·words .. (i+1)·words]`, likewise each latch's. The result holds node `n`'s words at
323    /// `n·words .. (n+1)·words`.
324    pub fn simulate(&self, words: usize, inputs: &[u64], latches: &[u64]) -> Vec<u64> {
325        let mut v = vec![0u64; self.nodes.len() * words];
326        for (n, node) in self.nodes.iter().enumerate() {
327            match *node {
328                Node::False => {}
329                Node::Input(i) => v[n * words..(n + 1) * words].copy_from_slice(&inputs[i * words..(i + 1) * words]),
330                Node::Latch(k) => {
331                    v[n * words..(n + 1) * words].copy_from_slice(&latches[k * words..(k + 1) * words])
332                }
333                Node::And(a, b) => {
334                    for w in 0..words {
335                        v[n * words + w] = Aig::word(&v, words, a, w) & Aig::word(&v, words, b, w);
336                    }
337                }
338            }
339        }
340        v
341    }
342
343    /// Word `w` of a literal's value in a [`Aig::simulate`] result.
344    pub fn word(sim: &[u64], words: usize, lit: AigLit, w: usize) -> u64 {
345        let x = sim[lit.node() as usize * words + w];
346        if lit.is_complemented() {
347            !x
348        } else {
349            x
350        }
351    }
352
353    /// The nodes the roots depend on, themselves included, in increasing order.
354    pub fn cone(&self, roots: &[AigLit]) -> Vec<u32> {
355        let mut seen = vec![false; self.nodes.len()];
356        let mut stack: Vec<u32> = roots.iter().map(|l| l.node()).collect();
357        let mut out = Vec::new();
358        while let Some(n) = stack.pop() {
359            if std::mem::replace(&mut seen[n as usize], true) {
360                continue;
361            }
362            out.push(n);
363            if let Node::And(a, b) = self.nodes[n as usize] {
364                stack.push(a.node());
365                stack.push(b.node());
366            }
367        }
368        out.sort_unstable();
369        out
370    }
371
372    /// The graph with only the nodes something reads: every input and latch in order, the outputs, bad and
373    /// constraint properties and latches' next states, and the nodes in their cones; with each node's literal in the
374    /// result, `None` for one nothing reads.
375    pub fn compact(&self) -> (Aig, Vec<Option<AigLit>>) {
376        self.compact_keeping(&[])
377    }
378
379    /// [`Aig::compact`], the cones of `keep` kept as well.
380    pub fn compact_keeping(&self, keep: &[AigLit]) -> (Aig, Vec<Option<AigLit>>) {
381        let mut live = vec![false; self.nodes.len()];
382        let mut stack: Vec<u32> = self
383            .outputs
384            .iter()
385            .chain(&self.bad)
386            .chain(&self.constraints)
387            .map(|p| p.lit.node())
388            .chain(self.latches.iter().map(|l| l.next.node()))
389            .chain(keep.iter().map(|l| l.node()))
390            .collect();
391        while let Some(n) = stack.pop() {
392            if std::mem::replace(&mut live[n as usize], true) {
393                continue;
394            }
395            if let Node::And(a, b) = self.nodes[n as usize] {
396                stack.push(a.node());
397                stack.push(b.node());
398            }
399        }
400        let mut out = Aig::new();
401        let inputs: Vec<AigLit> = self.inputs.iter().map(|p| out.add_input(p.name.clone())).collect();
402        let latches: Vec<usize> = self.latches.iter().map(|l| out.add_latch(l.name.clone(), l.init)).collect();
403        let mut map: Vec<Option<AigLit>> = vec![None; self.nodes.len()];
404        map[0] = Some(AigLit::FALSE);
405        for (n, node) in self.nodes.iter().enumerate() {
406            map[n] = match *node {
407                Node::False => Some(AigLit::FALSE),
408                Node::Input(i) => Some(inputs[i]),
409                Node::Latch(k) => Some(out.latch_lit(latches[k])),
410                Node::And(a, b) if live[n] => {
411                    let at = |l: AigLit, map: &[Option<AigLit>]| map[l.node() as usize].expect("a live node's fan-ins are live").with_phase(l.is_complemented());
412                    let (x, y) = (at(a, &map), at(b, &map));
413                    Some(out.and(x, y))
414                }
415                Node::And(..) => None,
416            };
417        }
418        let at = |l: AigLit| map[l.node() as usize].expect("read, so live").with_phase(l.is_complemented());
419        for (k, l) in self.latches.iter().enumerate() {
420            out.set_next(latches[k], at(l.next));
421        }
422        for p in &self.outputs {
423            out.add_output(at(p.lit), p.name.clone());
424        }
425        for p in &self.bad {
426            out.add_bad(at(p.lit), p.name.clone());
427        }
428        for p in &self.constraints {
429            out.add_constraint(at(p.lit), p.name.clone());
430        }
431        out.comments = self.comments.clone();
432        (out, map)
433    }
434
435    /// The graph the outputs `outputs` (by index) read through any number of clocks: those outputs, in the order given;
436    /// every latch whose value reaches one of them (a latch a kept latch's next state reads, to a fixpoint), in order,
437    /// with its name and initial value; every input, in order; and the nodes their cones need.
438    pub fn sequential_cone(&self, outputs: &[usize]) -> Aig {
439        let mut live = vec![false; self.nodes.len()];
440        let mut kept = vec![false; self.latches.len()];
441        let mut stack: Vec<u32> = outputs.iter().map(|&o| self.outputs[o].lit.node()).collect();
442        while let Some(n) = stack.pop() {
443            if std::mem::replace(&mut live[n as usize], true) {
444                continue;
445            }
446            match self.nodes[n as usize] {
447                Node::And(a, b) => {
448                    stack.push(a.node());
449                    stack.push(b.node());
450                }
451                Node::Latch(k) => {
452                    kept[k] = true;
453                    stack.push(self.latches[k].next.node());
454                }
455                Node::False | Node::Input(_) => {}
456            }
457        }
458        let mut out = Aig::new();
459        let inputs: Vec<AigLit> = self.inputs.iter().map(|p| out.add_input(p.name.clone())).collect();
460        let latches: Vec<Option<usize>> =
461            self.latches.iter().zip(&kept).map(|(l, &k)| k.then(|| out.add_latch(l.name.clone(), l.init))).collect();
462        let mut map: Vec<Option<AigLit>> = vec![None; self.nodes.len()];
463        for (n, node) in self.nodes.iter().enumerate() {
464            map[n] = match *node {
465                Node::False => Some(AigLit::FALSE),
466                Node::Input(i) => Some(inputs[i]),
467                Node::Latch(k) => latches[k].map(|j| out.latch_lit(j)),
468                Node::And(a, b) if live[n] => {
469                    let at = |l: AigLit, map: &[Option<AigLit>]| map[l.node() as usize].expect("a live node's fan-ins are live").with_phase(l.is_complemented());
470                    let (x, y) = (at(a, &map), at(b, &map));
471                    Some(out.and(x, y))
472                }
473                Node::And(..) => None,
474            };
475        }
476        let at = |l: AigLit| map[l.node() as usize].expect("in the cone, so live").with_phase(l.is_complemented());
477        for (k, l) in self.latches.iter().enumerate() {
478            if let Some(j) = latches[k] {
479                out.set_next(j, at(l.next));
480            }
481        }
482        for &o in outputs {
483            out.add_output(at(self.outputs[o].lit), self.outputs[o].name.clone());
484        }
485        out
486    }
487
488    /// Copy every node of `other` into this graph, each leaf replaced by the literal `leaves` gives it. Returns the
489    /// literal each of `other`'s nodes became, indexed by node.
490    pub fn import(&mut self, other: &Aig, leaves: &mut dyn FnMut(Leaf) -> AigLit) -> Vec<AigLit> {
491        let mut map: Vec<AigLit> = Vec::with_capacity(other.nodes.len());
492        for node in &other.nodes {
493            let lit = match *node {
494                Node::False => AigLit::FALSE,
495                Node::Input(i) => leaves(Leaf::Input(i)),
496                Node::Latch(k) => leaves(Leaf::Latch(k)),
497                Node::And(a, b) => {
498                    let ma = map[a.node() as usize].with_phase(a.is_complemented());
499                    let mb = map[b.node() as usize].with_phase(b.is_complemented());
500                    self.and(ma, mb)
501                }
502            };
503            map.push(lit);
504        }
505        map
506    }
507
508    /// The AIGER variable each node is written as: inputs, then latches, then gates in node order.
509    fn numbering(&self) -> Vec<u32> {
510        let mut var = vec![0u32; self.nodes.len()];
511        for (i, p) in self.inputs.iter().enumerate() {
512            var[p.lit.node() as usize] = i as u32 + 1;
513        }
514        let base = self.inputs.len() as u32;
515        for (k, l) in self.latches.iter().enumerate() {
516            var[l.node as usize] = base + k as u32 + 1;
517        }
518        let mut next = base + self.latches.len() as u32 + 1;
519        for (n, node) in self.nodes.iter().enumerate() {
520            if let Node::And(..) = node {
521                var[n] = next;
522                next += 1;
523            }
524        }
525        var
526    }
527
528    fn header(&self, magic: &str) -> String {
529        let (i, l, a) = (self.inputs.len(), self.latches.len(), self.num_ands());
530        let mut h = format!("{magic} {} {i} {l} {} {a}", i + l + a, self.outputs.len());
531        if !self.bad.is_empty() || !self.constraints.is_empty() {
532            h.push_str(&format!(" {} {}", self.bad.len(), self.constraints.len()));
533        }
534        h.push('\n');
535        h
536    }
537
538    fn symbols_and_comments(&self, out: &mut String) {
539        let tables: [(char, Vec<&Option<String>>); 5] = [
540            ('i', self.inputs.iter().map(|p| &p.name).collect()),
541            ('l', self.latches.iter().map(|l| &l.name).collect()),
542            ('o', self.outputs.iter().map(|p| &p.name).collect()),
543            ('b', self.bad.iter().map(|p| &p.name).collect()),
544            ('c', self.constraints.iter().map(|p| &p.name).collect()),
545        ];
546        for (kind, names) in tables {
547            for (k, name) in names.into_iter().enumerate() {
548                if let Some(name) = name {
549                    out.push_str(&format!("{kind}{k} {name}\n"));
550                }
551            }
552        }
553        if !self.comments.is_empty() {
554            out.push_str("c\n");
555            for c in &self.comments {
556                out.push_str(c);
557                out.push('\n');
558            }
559        }
560    }
561
562    fn latch_line(&self, var: &[u32], l: &Latch) -> String {
563        let code = |lit: AigLit| var[lit.node() as usize] * 2 + lit.is_complemented() as u32;
564        match l.init {
565            Init::Zero => format!("{}", code(l.next)),
566            Init::One => format!("{} 1", code(l.next)),
567            Init::Free => format!("{} {}", code(l.next), var[l.node as usize] * 2),
568        }
569    }
570
571    /// The canonical ASCII AIGER text.
572    pub fn to_ascii(&self) -> String {
573        let var = self.numbering();
574        let code = |lit: AigLit| var[lit.node() as usize] * 2 + lit.is_complemented() as u32;
575        let mut out = self.header("aag");
576        for p in &self.inputs {
577            out.push_str(&format!("{}\n", code(p.lit)));
578        }
579        for l in &self.latches {
580            out.push_str(&format!("{} {}\n", var[l.node as usize] * 2, self.latch_line(&var, l)));
581        }
582        for p in self.outputs.iter().chain(&self.bad).chain(&self.constraints) {
583            out.push_str(&format!("{}\n", code(p.lit)));
584        }
585        for (n, node) in self.nodes.iter().enumerate() {
586            if let Node::And(a, b) = *node {
587                let (x, y) = (code(a), code(b));
588                out.push_str(&format!("{} {} {}\n", var[n] * 2, x.max(y), x.min(y)));
589            }
590        }
591        self.symbols_and_comments(&mut out);
592        out
593    }
594
595    /// The canonical binary AIGER bytes.
596    pub fn to_binary(&self) -> Vec<u8> {
597        let var = self.numbering();
598        let code = |lit: AigLit| var[lit.node() as usize] * 2 + lit.is_complemented() as u32;
599        let mut text = self.header("aig");
600        for l in &self.latches {
601            text.push_str(&self.latch_line(&var, l));
602            text.push('\n');
603        }
604        for p in self.outputs.iter().chain(&self.bad).chain(&self.constraints) {
605            text.push_str(&format!("{}\n", code(p.lit)));
606        }
607        let mut out = text.into_bytes();
608        for (n, node) in self.nodes.iter().enumerate() {
609            if let Node::And(a, b) = *node {
610                let (x, y) = (code(a), code(b));
611                let (r0, r1) = (x.max(y), x.min(y));
612                let lhs = var[n] * 2;
613                push_varint(&mut out, lhs - r0);
614                push_varint(&mut out, r0 - r1);
615            }
616        }
617        let mut tail = String::new();
618        self.symbols_and_comments(&mut tail);
619        out.extend_from_slice(tail.as_bytes());
620        out
621    }
622
623    /// Read an ASCII (`aag`) or binary (`aig`) AIGER file.
624    pub fn parse(bytes: &[u8]) -> Result<Aig, AigerError> {
625        Parser { bytes, pos: 0, line: 0 }.parse()
626    }
627}
628
629fn push_varint(out: &mut Vec<u8>, mut x: u32) {
630    while x >= 0x80 {
631        out.push((x & 0x7f) as u8 | 0x80);
632        x >>= 7;
633    }
634    out.push(x as u8);
635}
636
637/// What an AIGER variable is, as the file defines it.
638#[derive(Clone, Copy)]
639enum Def {
640    None,
641    Input,
642    Latch,
643    And { r0: u64, r1: u64, line: usize },
644}
645
646struct Parser<'a> {
647    bytes: &'a [u8],
648    pos: usize,
649    line: usize,
650}
651
652impl Parser<'_> {
653    /// The next line's text without its newline, or `None` at the end.
654    fn next_line(&mut self) -> Option<String> {
655        if self.pos >= self.bytes.len() {
656            return None;
657        }
658        let end = self.bytes[self.pos..].iter().position(|&b| b == b'\n').map_or(self.bytes.len(), |k| self.pos + k);
659        let text = String::from_utf8_lossy(&self.bytes[self.pos..end]).into_owned();
660        self.pos = end + 1;
661        self.line += 1;
662        Some(text)
663    }
664
665    fn numbers(&mut self, want: std::ops::RangeInclusive<usize>) -> Result<(Vec<u64>, usize), AigerError> {
666        let text = self.next_line().ok_or(AigerError::Truncated)?;
667        let nums: Result<Vec<u64>, _> = text.split_whitespace().map(str::parse::<u64>).collect();
668        match nums {
669            Ok(n) if want.contains(&n.len()) => Ok((n, self.line)),
670            _ => Err(AigerError::Malformed { line: self.line }),
671        }
672    }
673
674    fn varint(&mut self) -> Result<u64, AigerError> {
675        let mut x: u64 = 0;
676        let mut shift = 0;
677        loop {
678            let &b = self.bytes.get(self.pos).ok_or(AigerError::Truncated)?;
679            self.pos += 1;
680            if shift > 56 {
681                return Err(AigerError::Malformed { line: self.line });
682            }
683            x |= ((b & 0x7f) as u64) << shift;
684            if b & 0x80 == 0 {
685                return Ok(x);
686            }
687            shift += 7;
688        }
689    }
690
691    fn parse(mut self) -> Result<Aig, AigerError> {
692        let header = self.next_line().ok_or(AigerError::Truncated)?;
693        let toks: Vec<&str> = header.split_whitespace().collect();
694        let binary = match toks.first() {
695            Some(&"aag") => false,
696            Some(&"aig") => true,
697            _ => return Err(AigerError::Header(format!("expected `aag` or `aig`, read `{header}`"))),
698        };
699        let nums: Vec<u64> = toks[1..]
700            .iter()
701            .map(|t| t.parse::<u64>())
702            .collect::<Result<_, _>>()
703            .map_err(|_| AigerError::Header(format!("non-numeric field in `{header}`")))?;
704        if !(5..=9).contains(&nums.len()) {
705            return Err(AigerError::Header(format!("expected M I L O A [B C J F], read `{header}`")));
706        }
707        let field = |k: usize| nums.get(k).copied().unwrap_or(0);
708        let (m, ni, nl, no, na) = (field(0), field(1), field(2), field(3), field(4));
709        let (nb, nc, nj, nf) = (field(5), field(6), field(7), field(8));
710        if nj > 0 || nf > 0 {
711            return Err(AigerError::Unsupported("justice and fairness properties".into()));
712        }
713        if binary && m != ni + nl + na {
714            return Err(AigerError::Header(format!("a binary file has M = I + L + A, read `{header}`")));
715        }
716        let max_lit = 2 * m + 1;
717        let mut def = vec![Def::None; m as usize + 1];
718        let define = |var: u64, d: Def, line: usize, def: &mut Vec<Def>| -> Result<(), AigerError> {
719            if var == 0 || var > m {
720                return Err(AigerError::Literal { line, literal: var * 2 });
721            }
722            if !matches!(def[var as usize], Def::None) {
723                return Err(AigerError::Redefined { line, var });
724            }
725            def[var as usize] = d;
726            Ok(())
727        };
728        let mut input_vars = Vec::with_capacity(ni as usize);
729        for k in 0..ni {
730            let var = if binary {
731                k + 1
732            } else {
733                let (n, line) = self.numbers(1..=1)?;
734                if n[0] % 2 == 1 || n[0] == 0 || n[0] > 2 * m {
735                    return Err(AigerError::Literal { line, literal: n[0] });
736                }
737                define(n[0] / 2, Def::Input, line, &mut def)?;
738                n[0] / 2
739            };
740            if binary {
741                define(var, Def::Input, 0, &mut def)?;
742            }
743            input_vars.push(var);
744        }
745        // (latch variable, next code, init, line)
746        let mut latch_defs: Vec<(u64, u64, Init, usize)> = Vec::with_capacity(nl as usize);
747        for k in 0..nl {
748            let (n, line) = if binary { self.numbers(1..=2)? } else { self.numbers(2..=3)? };
749            let (lit, rest) = if binary { (2 * (ni + k + 1), &n[..]) } else { (n[0], &n[1..]) };
750            if lit % 2 == 1 || lit == 0 || lit > 2 * m {
751                return Err(AigerError::Literal { line, literal: lit });
752            }
753            let next = rest[0];
754            if next > max_lit {
755                return Err(AigerError::Literal { line, literal: next });
756            }
757            let init = match rest.get(1).copied() {
758                None | Some(0) => Init::Zero,
759                Some(1) => Init::One,
760                Some(x) if x == lit => Init::Free,
761                Some(x) => return Err(AigerError::Literal { line, literal: x }),
762            };
763            define(lit / 2, Def::Latch, line, &mut def)?;
764            latch_defs.push((lit / 2, next, init, line));
765        }
766        let read_ports = |count: u64, this: &mut Self| -> Result<Vec<(u64, usize)>, AigerError> {
767            let mut out = Vec::with_capacity(count as usize);
768            for _ in 0..count {
769                let (n, line) = this.numbers(1..=1)?;
770                if n[0] > max_lit {
771                    return Err(AigerError::Literal { line, literal: n[0] });
772                }
773                out.push((n[0], line));
774            }
775            Ok(out)
776        };
777        let outputs = read_ports(no, &mut self)?;
778        let bad = read_ports(nb, &mut self)?;
779        let constraints = read_ports(nc, &mut self)?;
780        let mut and_vars = Vec::with_capacity(na as usize);
781        for k in 0..na {
782            if binary {
783                let lhs = 2 * (ni + nl + k + 1);
784                let d0 = self.varint()?;
785                let d1 = self.varint()?;
786                if d0 == 0 || d0 > lhs || d1 > lhs - d0 {
787                    return Err(AigerError::Malformed { line: self.line });
788                }
789                let r0 = lhs - d0;
790                let r1 = r0 - d1;
791                define(lhs / 2, Def::And { r0, r1, line: self.line }, self.line, &mut def)?;
792                and_vars.push(lhs / 2);
793            } else {
794                let (n, line) = self.numbers(3..=3)?;
795                let (lhs, r0, r1) = (n[0], n[1], n[2]);
796                if lhs % 2 == 1 || lhs == 0 || lhs > 2 * m {
797                    return Err(AigerError::Literal { line, literal: lhs });
798                }
799                for r in [r0, r1] {
800                    if r > max_lit {
801                        return Err(AigerError::Literal { line, literal: r });
802                    }
803                }
804                define(lhs / 2, Def::And { r0, r1, line }, line, &mut def)?;
805                and_vars.push(lhs / 2);
806            }
807        }
808        let mut aig = Aig::new();
809        let mut map: Vec<Option<AigLit>> = vec![None; m as usize + 1];
810        map[0] = Some(AigLit::FALSE);
811        for &v in &input_vars {
812            map[v as usize] = Some(aig.add_input(None));
813        }
814        for &(v, _, init, _) in &latch_defs {
815            let k = aig.add_latch(None, init);
816            map[v as usize] = Some(aig.latch_lit(k));
817        }
818        let resolve = |map: &[Option<AigLit>], code: u64, line: usize| -> Result<AigLit, AigerError> {
819            map[(code / 2) as usize]
820                .map(|l| l.with_phase(code % 2 == 1))
821                .ok_or(AigerError::Undefined { line, var: code / 2 })
822        };
823        let mut on_path = vec![false; m as usize + 1];
824        for &root in &and_vars {
825            let mut stack: Vec<(u64, bool)> = vec![(root, false)];
826            while let Some((v, expanded)) = stack.pop() {
827                if map[v as usize].is_some() {
828                    continue;
829                }
830                let Def::And { r0, r1, line } = def[v as usize] else {
831                    return Err(AigerError::Undefined { line: 0, var: v });
832                };
833                if expanded {
834                    let a = resolve(&map, r0, line)?;
835                    let b = resolve(&map, r1, line)?;
836                    map[v as usize] = Some(aig.and(a, b));
837                    on_path[v as usize] = false;
838                    continue;
839                }
840                if on_path[v as usize] {
841                    return Err(AigerError::Cycle { var: v });
842                }
843                on_path[v as usize] = true;
844                stack.push((v, true));
845                for r in [r1, r0] {
846                    let c = r / 2;
847                    if map[c as usize].is_some() {
848                        continue;
849                    }
850                    match def[c as usize] {
851                        Def::And { .. } => {
852                            if on_path[c as usize] {
853                                return Err(AigerError::Cycle { var: c });
854                            }
855                            stack.push((c, false));
856                        }
857                        _ => return Err(AigerError::Undefined { line, var: c }),
858                    }
859                }
860            }
861        }
862        for (k, &(_, next, _, line)) in latch_defs.iter().enumerate() {
863            let lit = resolve(&map, next, line)?;
864            aig.set_next(k, lit);
865        }
866        for (code, line) in outputs {
867            let lit = resolve(&map, code, line)?;
868            aig.add_output(lit, None);
869        }
870        for (code, line) in bad {
871            let lit = resolve(&map, code, line)?;
872            aig.add_bad(lit, None);
873        }
874        for (code, line) in constraints {
875            let lit = resolve(&map, code, line)?;
876            aig.add_constraint(lit, None);
877        }
878        while let Some(text) = self.next_line() {
879            if text == "c" {
880                while let Some(c) = self.next_line() {
881                    aig.comments.push(c);
882                }
883                break;
884            }
885            if text.is_empty() {
886                continue;
887            }
888            let malformed = AigerError::Malformed { line: self.line };
889            let mut chars = text.chars();
890            let kind = chars.next().ok_or(malformed.clone())?;
891            let rest = chars.as_str();
892            let (pos, name) = rest.split_once(' ').ok_or(malformed.clone())?;
893            let pos: usize = pos.parse().map_err(|_| malformed.clone())?;
894            let slot = match kind {
895                'i' => aig.inputs.get_mut(pos).map(|p| &mut p.name),
896                'l' => aig.latches.get_mut(pos).map(|l| &mut l.name),
897                'o' => aig.outputs.get_mut(pos).map(|p| &mut p.name),
898                'b' => aig.bad.get_mut(pos).map(|p| &mut p.name),
899                'c' => aig.constraints.get_mut(pos).map(|p| &mut p.name),
900                _ => None,
901            };
902            *slot.ok_or(malformed)? = Some(name.to_string());
903        }
904        Ok(aig)
905    }
906}
907
908#[cfg(test)]
909#[path = "../../logicaffeine_tests/unit/logicaffeine_proof/aig/tests.rs"]
910mod tests;