simplicity/
dag.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! General DAG iteration utilities
4
5use std::collections::{hash_map::Entry, HashMap};
6use std::fmt;
7use std::sync::Arc;
8
9use crate::node::{self, Disconnectable, Node};
10
11/// Abstract node of a directed acyclic graph.
12///
13/// Tracks the arity (out-degree) of nodes in a Simplictiy program, as well
14/// as whether they represent `witness` or `disconnect` combinators, which
15/// are treated specially when working with the graph structure of
16/// Simplicty programs.
17#[derive(Clone, Debug)]
18pub enum Dag<T> {
19    /// Combinator with no children
20    Nullary,
21    /// Combinator with one child
22    Unary(T),
23    /// Combinator with two children
24    Binary(T, T),
25}
26
27impl<T> Dag<T> {
28    /// Given a DAG of one type, convert it to a DAG of another type.
29    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> Dag<U> {
30        match self {
31            Dag::Nullary => Dag::Nullary,
32            Dag::Unary(t) => Dag::Unary(f(t)),
33            Dag::Binary(tl, tr) => Dag::Binary(f(tl), f(tr)),
34        }
35    }
36}
37
38/// How much sharing/expansion to do when running an iterator over a DAG
39///
40/// This object works by recording and looking up nodes in a DAG as they are
41/// being iterated over. If the tracker says that an element has been seen
42/// before, it will not be yielded again; so for example, a tracker which
43/// records nodes by their IHR will implement full sharing, while a tracker
44/// which claims to have never seen any node before will implement no
45/// sharing at all.
46pub trait SharingTracker<D: DagLike> {
47    /// Marks an object as having been seen, and record the index
48    /// when it was seen.
49    ///
50    /// If the object was already seen, does **not** update the index,
51    /// and instead returns the original one.
52    fn record(&mut self, object: &D, index: usize) -> Option<usize>;
53
54    /// Check whether an object has been seen before; if so, return
55    /// the index it was recorded at.
56    fn seen_before(&self, object: &D) -> Option<usize>;
57}
58
59// Annoyingly we need to implement this explicitly
60impl<D: DagLike> SharingTracker<D> for &mut dyn SharingTracker<D> {
61    fn record(&mut self, object: &D, index: usize) -> Option<usize> {
62        (**self).record(object, index)
63    }
64    fn seen_before(&self, object: &D) -> Option<usize> {
65        (**self).seen_before(object)
66    }
67}
68
69/// Do not share at all; yield every node in the expanded DAG
70#[derive(Clone, Debug, Default)]
71pub struct NoSharing;
72
73impl<D: DagLike> SharingTracker<D> for NoSharing {
74    fn record(&mut self, _: &D, _: usize) -> Option<usize> {
75        None
76    }
77    fn seen_before(&self, _: &D) -> Option<usize> {
78        None
79    }
80}
81
82/// Share using pointer identity, i.e. yield each node only once, where two
83/// nodes are the same iff they both point to the same object.
84#[derive(Clone, Debug, Default)]
85pub struct InternalSharing {
86    map: HashMap<PointerId, usize>,
87}
88
89impl<D: DagLike> SharingTracker<D> for InternalSharing {
90    fn record(&mut self, d: &D, index: usize) -> Option<usize> {
91        match self.map.entry(PointerId::from(d)) {
92            Entry::Occupied(occ) => Some(*occ.get()),
93            Entry::Vacant(vac) => {
94                vac.insert(index);
95                None
96            }
97        }
98    }
99    fn seen_before(&self, d: &D) -> Option<usize> {
100        self.map.get(&PointerId::from(d)).copied()
101    }
102}
103
104/// Maximal sharing: share every node whose CMR and cached data match
105///
106/// For `RedeemNode`s, this coincides with `FullSharing`; for other
107/// types of nodes it represents "as much sharing as we can currently
108/// safely do".
109#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct MaxSharing<N: node::Marker> {
111    map: HashMap<N::SharingId, usize>,
112}
113
114// Annoyingly we have to implement Default by hand
115impl<N: node::Marker> Default for MaxSharing<N> {
116    fn default() -> Self {
117        MaxSharing {
118            map: HashMap::default(),
119        }
120    }
121}
122
123impl<N: node::Marker> SharingTracker<&Node<N>> for MaxSharing<N> {
124    fn record(&mut self, d: &&Node<N>, index: usize) -> Option<usize> {
125        let id = d.sharing_id()?;
126
127        match self.map.entry(id) {
128            Entry::Occupied(occ) => Some(*occ.get()),
129            Entry::Vacant(vac) => {
130                vac.insert(index);
131                None
132            }
133        }
134    }
135    fn seen_before(&self, d: &&Node<N>) -> Option<usize> {
136        d.sharing_id().and_then(|id| self.map.get(&id)).copied()
137    }
138}
139
140impl<N: node::Marker> SharingTracker<Arc<Node<N>>> for MaxSharing<N> {
141    fn record(&mut self, d: &Arc<Node<N>>, index: usize) -> Option<usize> {
142        let id = d.sharing_id()?;
143
144        match self.map.entry(id) {
145            Entry::Occupied(occ) => Some(*occ.get()),
146            Entry::Vacant(vac) => {
147                vac.insert(index);
148                None
149            }
150        }
151    }
152    fn seen_before(&self, d: &Arc<Node<N>>) -> Option<usize> {
153        d.sharing_id().and_then(|id| self.map.get(&id)).copied()
154    }
155}
156
157// Need to explicitly allow swapping children for `MaxSharing`; the other
158// sharing styles have blanket implementations for `D: DagLike` so they
159// automatically allow it.
160impl<N, D> SharingTracker<SwapChildren<D>> for MaxSharing<N>
161where
162    D: DagLike,
163    MaxSharing<N>: SharingTracker<D>,
164    N: node::Marker,
165{
166    fn record(&mut self, d: &SwapChildren<D>, index: usize) -> Option<usize> {
167        self.record(&d.0, index)
168    }
169
170    fn seen_before(&self, d: &SwapChildren<D>) -> Option<usize> {
171        self.seen_before(&d.0)
172    }
173}
174
175/// Object representing pointer identity of a DAG node
176///
177/// Used to populate a hashset to determine whether or not a given node has
178/// already been tracker by an iterator.
179#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
180struct PointerId(usize);
181
182impl<D: DagLike> From<&D> for PointerId {
183    fn from(dag: &D) -> Self {
184        PointerId(dag.data() as *const _ as usize)
185    }
186}
187
188/// Return type of the [`DagLike::rtl_post_order_iter`] method.
189pub type RtlPostOrderIter<D, S> = std::iter::Map<
190    PostOrderIter<SwapChildren<D>, S>,
191    fn(PostOrderIterItem<SwapChildren<D>>) -> PostOrderIterItem<D>,
192>;
193
194/// A trait for any structure which has the shape of a Simplicity DAG
195///
196/// This should be implemented on any reference type for `Node`, though it
197/// cannot be implemented on the actual structure because it assumes that
198/// the node type is the same as the type contained in each variant.
199pub trait DagLike: Sized {
200    /// The type of the DAG node, with no references or wrappers
201    type Node;
202
203    /// A pointer to the underlying data
204    fn data(&self) -> &Self::Node;
205
206    /// Interpret the node as a DAG node
207    fn as_dag_node(&self) -> Dag<Self>;
208
209    /// Accessor for the left child of the node, if one exists
210    fn left_child(&self) -> Option<Self> {
211        match self.as_dag_node() {
212            Dag::Nullary => None,
213            Dag::Unary(sub) => Some(sub),
214            Dag::Binary(left, _) => Some(left),
215        }
216    }
217
218    /// Accessor for the right child of the node, if one exists
219    fn right_child(&self) -> Option<Self> {
220        match self.as_dag_node() {
221            Dag::Nullary => None,
222            Dag::Unary(_) => None,
223            Dag::Binary(_, right) => Some(right),
224        }
225    }
226
227    /// Number of children that the node has.
228    ///
229    /// This has a default implementation which simply inspects the children, but
230    /// in some cases it may be more efficient for implementors to implement this
231    /// manually.
232    fn n_children(&self) -> usize {
233        match self.as_dag_node() {
234            Dag::Nullary => 0,
235            Dag::Unary(..) => 1,
236            Dag::Binary(..) => 2,
237        }
238    }
239
240    /// Obtains an iterator of all the nodes rooted at the DAG, in right-to-left post order.
241    ///
242    /// An ordinary post-order iterator yields children in the order
243    /// left-child, right-child, node. This one instead yields them in
244    /// the order right-child, left-child, node.
245    ///
246    /// This is useful when implementing satisfiers, specifically for
247    /// handling the `comp` combinator, by allowing the user to see the
248    /// right child (and e.g. make decisions about what branches to prune
249    /// or what witnesses to provide) before the left child (which may
250    /// involve populating witnesses consistent with that choice).
251    fn rtl_post_order_iter<S: SharingTracker<SwapChildren<Self>> + Default>(
252        self,
253    ) -> RtlPostOrderIter<Self, S> {
254        PostOrderIter {
255            index: 0,
256            stack: vec![IterStackItem::unprocessed(
257                SwapChildren(self),
258                Previous::Root,
259            )],
260            tracker: Default::default(),
261        }
262        .map(PostOrderIterItem::unswap)
263    }
264
265    /// Obtains an iterator of all the nodes rooted at the DAG, in pre-order.
266    fn pre_order_iter<S: SharingTracker<Self> + Default>(self) -> PreOrderIter<Self, S> {
267        PreOrderIter {
268            stack: vec![self],
269            tracker: Default::default(),
270        }
271    }
272
273    /// Obtains a verbose iterator of all the nodes rooted at the DAG, in pre-order.
274    ///
275    /// See the documentation of [`VerbosePreOrderIter`] for more information about what
276    /// this does. Essentially, if you find yourself using [`Self::pre_order_iter`] and
277    /// then adding a stack to manually track which items and their children have been
278    /// yielded, you may be better off using this iterator instead.
279    fn verbose_pre_order_iter<S: SharingTracker<Self> + Default>(
280        self,
281        max_depth: Option<usize>,
282    ) -> VerbosePreOrderIter<Self, S>
283    where
284        Self: Clone,
285    {
286        VerbosePreOrderIter {
287            stack: vec![PreOrderIterItem::initial(self, 0, None)],
288            index: 0,
289            max_depth,
290            tracker: Default::default(),
291        }
292    }
293
294    /// Obtains an iterator of all the nodes rooted at the DAG, in post order.
295    ///
296    /// Each node is only yielded once, at the leftmost position that it
297    /// appears in the DAG.
298    fn post_order_iter<S: SharingTracker<Self> + Default>(self) -> PostOrderIter<Self, S> {
299        PostOrderIter {
300            index: 0,
301            stack: vec![IterStackItem::unprocessed(self, Previous::Root)],
302            tracker: Default::default(),
303        }
304    }
305
306    /// Checks whether a DAG's internal sharing (as expressed by shared pointers)
307    /// matches the given target sharing.
308    #[allow(clippy::wrong_self_convention)] // clippy doesn't like is_* taking a pointer by value
309    fn is_shared_as<S: SharingTracker<Self> + Default>(self) -> bool
310    where
311        Self: Clone,
312    {
313        let iter_is = self.clone().post_order_iter::<InternalSharing>();
314        let iter_ought = self.post_order_iter::<S>();
315        for (data_is, data_ought) in iter_is.zip(iter_ought) {
316            if PointerId::from(&data_is.node) != PointerId::from(&data_ought.node) {
317                return false;
318            }
319        }
320        true
321    }
322
323    /// Obtains an post-order iterator of all the nodes rooted at DAG, using the
324    /// given tracker.
325    ///
326    /// Ordinary users will never need to use this method; but it is available to
327    /// enable obscure iteration use-cases.
328    fn post_order_iter_with_tracker<S: SharingTracker<Self>>(
329        self,
330        tracker: S,
331    ) -> PostOrderIter<Self, S> {
332        PostOrderIter {
333            index: 0,
334            stack: vec![IterStackItem::unprocessed(self, Previous::Root)],
335            tracker,
336        }
337    }
338}
339
340/// A wrapper around a DAG-like reference that swaps the two children
341///
342/// This can be useful to modify the `PostOrderIter` behaviour to yield
343/// right children before left children.
344///
345/// Since it works by relabelling the children, when iterating with this
346/// adaptor, the "left" and "right" child indices will be inconsistent
347/// with the returned nodes. To correct this, you need to call
348/// [`PostOrderIterItem::unswap`].
349///
350/// To avoid confusion, this structure cannot be directly costructed.
351/// Instead it is implicit in the [`DagLike::rtl_post_order_iter`]
352/// method.
353#[derive(Clone, Debug)]
354pub struct SwapChildren<D>(D);
355
356impl<D: DagLike> DagLike for SwapChildren<D> {
357    type Node = D::Node;
358
359    fn data(&self) -> &Self::Node {
360        self.0.data()
361    }
362
363    fn as_dag_node(&self) -> Dag<Self> {
364        match self.0.as_dag_node() {
365            Dag::Nullary => Dag::Nullary,
366            Dag::Unary(sub) => Dag::Unary(SwapChildren(sub)),
367            Dag::Binary(left, right) => Dag::Binary(SwapChildren(right), SwapChildren(left)),
368        }
369    }
370}
371
372impl<N: node::Marker> DagLike for &'_ Node<N> {
373    type Node = Node<N>;
374
375    fn data(&self) -> &Node<N> {
376        self
377    }
378
379    #[rustfmt::skip]
380    fn as_dag_node(&self) -> Dag<Self> {
381        match self.inner() {
382            node::Inner::Iden
383            | node::Inner::Unit
384            | node::Inner::Fail(..)
385            | node::Inner::Jet(..)
386            | node::Inner::Word(..) => Dag::Nullary,
387            node::Inner::InjL(ref sub)
388            | node::Inner::InjR(ref sub)
389            | node::Inner::Take(ref sub)
390            | node::Inner::Drop(ref sub)
391            | node::Inner::AssertL(ref sub, _)
392            | node::Inner::AssertR(_, ref sub) => Dag::Unary(sub),
393            node::Inner::Comp(ref left, ref right)
394            | node::Inner::Case(ref left, ref right)
395            | node::Inner::Pair(ref left, ref right) => Dag::Binary(left, right),
396            node::Inner::Disconnect(ref left, ref right) => right.disconnect_dag_ref(left),
397            node::Inner::Witness(..) => Dag::Nullary,
398        }
399    }
400}
401
402impl<N: node::Marker> DagLike for Arc<Node<N>> {
403    type Node = Node<N>;
404
405    fn data(&self) -> &Node<N> {
406        self
407    }
408
409    #[rustfmt::skip]
410    fn as_dag_node(&self) -> Dag<Self> {
411        match self.inner() {
412            node::Inner::Iden
413            | node::Inner::Unit
414            | node::Inner::Fail(..)
415            | node::Inner::Jet(..)
416            | node::Inner::Word(..) => Dag::Nullary,
417            node::Inner::InjL(ref sub)
418            | node::Inner::InjR(ref sub)
419            | node::Inner::Take(ref sub)
420            | node::Inner::Drop(ref sub)
421            | node::Inner::AssertL(ref sub, _)
422            | node::Inner::AssertR(_, ref sub) => Dag::Unary(Arc::clone(sub)),
423            node::Inner::Comp(ref left, ref right)
424            | node::Inner::Case(ref left, ref right)
425            | node::Inner::Pair(ref left, ref right) => Dag::Binary(Arc::clone(left), Arc::clone(right)),
426            node::Inner::Disconnect(ref left, ref right) => right.clone().disconnect_dag_arc(Arc::clone(left)),
427            node::Inner::Witness(..) => Dag::Nullary,
428        }
429    }
430}
431
432enum Child<D: DagLike> {
433    None,
434    Repeat { idx: usize },
435    New(D),
436}
437
438#[derive(Clone, Debug)]
439enum Previous {
440    /// This is the root element and there are no previous elements
441    Root,
442    /// This is a left child and the previous element is its parent
443    ParentLeft,
444    /// This is a left child and the previous element is its sibling
445    SiblingLeft,
446    /// This is a right child and the previous element is its parent
447    ParentRight,
448}
449
450#[derive(Clone, Debug)]
451struct IterStackItem<D> {
452    /// The element on the stack
453    elem: D,
454    /// Whether we have dealt with this item (and pushed its children,
455    /// if any) yet.
456    processed: bool,
457    /// If the item has been processed, the index of its left child, if known
458    left_idx: Option<usize>,
459    /// If the item has been processed, the index of its right child, if known
460    right_idx: Option<usize>,
461    /// Whether the element is a left- or right-child of its parent
462    previous: Previous,
463}
464
465impl<D: DagLike> IterStackItem<D> {
466    /// Constructor for a new stack item with a given element and relationship
467    /// to its parent.
468    fn unprocessed(elem: D, previous: Previous) -> Self {
469        IterStackItem {
470            elem,
471            processed: false,
472            left_idx: None,
473            right_idx: None,
474            previous,
475        }
476    }
477
478    fn left_child<V: SharingTracker<D>>(&self, tracker: &V) -> Child<D> {
479        match self.elem.left_child() {
480            Some(child) => match tracker.seen_before(&child) {
481                Some(idx) => Child::Repeat { idx },
482                None => Child::New(child),
483            },
484            None => Child::None,
485        }
486    }
487
488    fn right_child<V: SharingTracker<D>>(&self, tracker: &V) -> Child<D> {
489        match self.elem.right_child() {
490            Some(child) => match tracker.seen_before(&child) {
491                Some(idx) => Child::Repeat { idx },
492                None => Child::New(child),
493            },
494            None => Child::None,
495        }
496    }
497}
498
499/// Iterates over a DAG in _post order_.
500///
501/// That means nodes are yielded in the order (left child, right child, parent).
502/// Nodes may be repeated or not, based on the `S` parameter which defines how
503/// the iterator treats sharing.
504#[derive(Clone, Debug)]
505pub struct PostOrderIter<D, S> {
506    /// The index of the next item to be yielded
507    index: usize,
508    /// A stack of elements to be yielded; each element is a node, then its left
509    /// and right children (if they exist and if they have been yielded already)
510    stack: Vec<IterStackItem<D>>,
511    /// Data which tracks which nodes have been yielded already and therefore
512    /// should be skipped.
513    tracker: S,
514}
515
516/// A set of data yielded by a `PostOrderIter`
517#[derive(Clone, Debug)]
518pub struct PostOrderIterItem<D> {
519    /// The actual node data
520    pub node: D,
521    /// The index of this node (equivalent to if you'd called `.enumerate()` on
522    /// the iterator)
523    pub index: usize,
524    /// The index of this node's left child, if it has a left child
525    pub left_index: Option<usize>,
526    /// The index of this node's right child, if it has a left child
527    pub right_index: Option<usize>,
528}
529
530impl<D: DagLike> PostOrderIterItem<SwapChildren<D>> {
531    /// When iterating in right-to-left mode using the [`SwapChildren`] adaptor,
532    /// use this method to correct the child indices. See documentation on
533    /// [`SwapChildren`] or [`DagLike::rtl_post_order_iter`].
534    pub fn unswap(mut self) -> PostOrderIterItem<D> {
535        if matches!(self.node.as_dag_node(), Dag::Binary(..)) {
536            std::mem::swap(&mut self.left_index, &mut self.right_index);
537        }
538        PostOrderIterItem {
539            node: self.node.0,
540            index: self.index,
541            left_index: self.left_index,
542            right_index: self.right_index,
543        }
544    }
545}
546
547impl<D: DagLike, S: SharingTracker<D>> Iterator for PostOrderIter<D, S> {
548    type Item = PostOrderIterItem<D>;
549
550    fn next(&mut self) -> Option<Self::Item> {
551        loop {
552            // Look at the current top item on the stack
553            if let Some(mut current) = self.stack.pop() {
554                if !current.processed {
555                    current.processed = true;
556                    // When we first encounter an item, it is completely unknown; it is
557                    // nominally the next item to be yielded, but it might have children,
558                    // and if so, they come first
559                    match (
560                        current.left_child(&self.tracker),
561                        current.right_child(&self.tracker),
562                    ) {
563                        // No children is easy, just mark it processed and iterate.
564                        // (We match _ for the right child but we know that if the left one
565                        // is Child::None, then the right one will be Child::None as well.)
566                        (Child::None, _) => {
567                            self.stack.push(current);
568                        }
569                        // Only a left child, already yielded
570                        (Child::Repeat { idx }, Child::None) => {
571                            current.left_idx = Some(idx);
572                            self.stack.push(current);
573                        }
574                        // Only a left child, new
575                        (Child::New(child), Child::None) => {
576                            self.stack.push(current);
577                            self.stack
578                                .push(IterStackItem::unprocessed(child, Previous::ParentLeft));
579                        }
580                        // Two children, both already yielded
581                        (Child::Repeat { idx: lidx }, Child::Repeat { idx: ridx }) => {
582                            current.left_idx = Some(lidx);
583                            current.right_idx = Some(ridx);
584                            self.stack.push(current);
585                        }
586                        // Two children, one yielded already
587                        (Child::New(child), Child::Repeat { idx }) => {
588                            current.right_idx = Some(idx);
589                            self.stack.push(current);
590                            self.stack
591                                .push(IterStackItem::unprocessed(child, Previous::ParentLeft));
592                        }
593                        (Child::Repeat { idx }, Child::New(child)) => {
594                            current.left_idx = Some(idx);
595                            self.stack.push(current);
596                            self.stack
597                                .push(IterStackItem::unprocessed(child, Previous::ParentRight));
598                        }
599                        // Two children, neither yielded already
600                        (Child::New(lchild), Child::New(rchild)) => {
601                            self.stack.push(current);
602                            self.stack
603                                .push(IterStackItem::unprocessed(rchild, Previous::ParentRight));
604                            self.stack
605                                .push(IterStackItem::unprocessed(lchild, Previous::SiblingLeft));
606                        }
607                    }
608                } else {
609                    // The second time we encounter an item, we have dealt with its children,
610                    // updated the child indices for this item, and are now ready to yield it
611                    // rather than putting it back in the stack. However, while dealing with
612                    // its children, we may have already yielded it (which can happen because
613                    // this is a DAG, not a tree). In this case we want to skip it.
614                    //
615                    // Check whether this is the case, and record the item's new index if not.
616                    let current_index = self.tracker.record(&current.elem, self.index);
617                    let already_yielded = current_index.is_some();
618                    let current_index = current_index.unwrap_or(self.index);
619
620                    // Then, update the item's parents and/or sibling, to make sure that its
621                    // parent has the correct child index. We need to do this whether or not
622                    // we're going to skip the element.
623                    let stack_len = self.stack.len();
624                    match current.previous {
625                        Previous::Root => {
626                            assert_eq!(stack_len, 0);
627                        }
628                        Previous::ParentLeft => {
629                            assert!(self.stack[stack_len - 1].processed);
630                            self.stack[stack_len - 1].left_idx = Some(current_index);
631                        }
632                        Previous::ParentRight => {
633                            assert!(self.stack[stack_len - 1].processed);
634                            self.stack[stack_len - 1].right_idx = Some(current_index);
635                        }
636                        Previous::SiblingLeft => {
637                            assert!(self.stack[stack_len - 2].processed);
638                            self.stack[stack_len - 2].left_idx = Some(current_index);
639                        }
640                    }
641                    // Having updated the parent indices, so that our iterator is in a
642                    // consistent state, we can safely skip here if the current item is
643                    // already yielded.
644                    if already_yielded {
645                        continue;
646                    }
647
648                    self.index += 1;
649                    return Some(PostOrderIterItem {
650                        node: current.elem,
651                        index: current_index,
652                        left_index: current.left_idx,
653                        right_index: current.right_idx,
654                    });
655                }
656            } else {
657                // If there is nothing on the stack we are done.
658                return None;
659            }
660        }
661    }
662}
663
664impl<'a, N: node::Marker, S: SharingTracker<&'a Node<N>> + Clone> PostOrderIter<&'a Node<N>, S> {
665    /// Adapt the iterator to only yield witnesses
666    ///
667    /// The witnesses are yielded in the order in which they appear in the DAG
668    /// *except* that each witness is only yielded once, and future occurences
669    /// are skipped.
670    pub fn into_witnesses(self) -> impl Iterator<Item = &'a N::Witness> + Clone {
671        self.filter_map(|data| {
672            if let node::Inner::Witness(value) = data.node.inner() {
673                Some(value)
674            } else {
675                None
676            }
677        })
678    }
679}
680
681impl<D: DagLike, S: SharingTracker<D>> PostOrderIter<D, S> {
682    /// Display the DAG as an indexed list in post order.
683    ///
684    /// `display_body()` formats the node body in front of the node indices.
685    /// `display_aux()` formats auxiliary items after the node indices.
686    pub fn into_display<F, G>(
687        self,
688        f: &mut fmt::Formatter<'_>,
689        mut display_body: F,
690        mut display_aux: G,
691    ) -> fmt::Result
692    where
693        F: FnMut(&D, &mut fmt::Formatter<'_>) -> fmt::Result,
694        G: FnMut(&D, &mut fmt::Formatter<'_>) -> fmt::Result,
695    {
696        for data in self {
697            write!(f, "{}: ", data.index)?;
698            display_body(&data.node, f)?;
699
700            if let Some(i_abs) = data.left_index {
701                if let Some(j_abs) = data.right_index {
702                    write!(f, "({}, {})", i_abs, j_abs)?;
703                } else {
704                    write!(f, "({})", i_abs)?;
705                }
706            }
707            display_aux(&data.node, f)?;
708            f.write_str("\n")?;
709        }
710        Ok(())
711    }
712}
713
714/// Iterates over a DAG in _pre order_.
715///
716/// Unlike the post-order iterator, this one does not keep track of indices
717/// (this would be impractical since when we yield a node we have not yet
718/// yielded its children, so we cannot know their indices). If you do need
719/// the indices for some reason, the best strategy may be to run the
720/// post-order iterator, collect into a vector, then iterate through that
721/// backward.
722#[derive(Clone, Debug)]
723pub struct PreOrderIter<D, S> {
724    /// A stack of elements to be yielded. As items are yielded, their right
725    /// children are put onto the stack followed by their left, so that the
726    /// appropriate one will be yielded on the next iteration.
727    stack: Vec<D>,
728    /// Data which tracks which nodes have been yielded already and therefore
729    /// should be skipped.
730    tracker: S,
731}
732
733impl<D: DagLike, S: SharingTracker<D>> Iterator for PreOrderIter<D, S> {
734    type Item = D;
735
736    fn next(&mut self) -> Option<Self::Item> {
737        // This algorithm is _significantly_ simpler than the post-order one,
738        // mainly because we don't care about child indices.
739        while let Some(top) = self.stack.pop() {
740            // Only yield if this is the first time we have seen this node.
741            if self.tracker.record(&top, 0).is_none() {
742                // If so, mark its children as to-yield, then yield it.
743                if let Some(child) = top.right_child() {
744                    self.stack.push(child);
745                }
746                if let Some(child) = top.left_child() {
747                    self.stack.push(child);
748                }
749                return Some(top);
750            }
751        }
752        None
753    }
754}
755
756/// Iterates over a DAG in "verbose pre order", yielding extra state changes.
757///
758/// This yields nodes followed by their children, followed by the node *again*
759/// after each child. This means that each node will be yielded a total of
760/// (n+1) times, where n is its number of children.
761///
762/// The different times that a node is yielded can be distinguished by looking
763/// at the [`PreOrderIterItem::n_children_yielded`]  (which, in particular,
764/// will be 0 on the first yield) and [`PreOrderIterItem::is_complete`] (which
765/// will be true on the last yield) fields of the yielded item.
766///
767/// If you use this iterator with a non-trivial sharing tracker, then any
768/// items which have been initially yielded before will not be initially
769/// yielded again.
770///
771/// If node's *children* have been initially yielded before, they will be
772/// skipped, but the node itself will still be re-yielded as many times
773/// as it has children.
774#[derive(Clone, Debug)]
775pub struct VerbosePreOrderIter<D, S> {
776    /// A stack of elements to be yielded. As items are yielded, their right
777    /// children are put onto the stack followed by their left, so that the
778    /// appropriate one will be yielded on the next iteration.
779    stack: Vec<PreOrderIterItem<D>>,
780    /// Maximum depth (distance from root) that the iterator will go to. Any
781    /// children at a greater depth will not be yielded. However, the parent
782    /// of such children will still be yielded multiple times, one for each
783    /// (unyielded) child, with `n_children_yielded` incremented as though
784    /// the children had been yielded.
785    ///
786    /// To determine whether pruning has happened, you should manually check
787    /// the [`PreOrderIterItem::depth`] field against the maximum depth that
788    /// you set when constructing the iterator.
789    max_depth: Option<usize>,
790    /// The index of the next item to be yielded.
791    ///
792    /// Note that unlike the [`PostOrderIter`], this value is not monotonic
793    /// and not equivalent to just using `enumerate` on the iterator, because
794    /// elements may be yielded multiple times.
795    index: usize,
796    /// Data which tracks which nodes have been yielded already and therefore
797    /// should be skipped.
798    tracker: S,
799}
800
801impl<D: DagLike + Clone, S: SharingTracker<D>> Iterator for VerbosePreOrderIter<D, S> {
802    type Item = PreOrderIterItem<D>;
803
804    fn next(&mut self) -> Option<Self::Item> {
805        // This algorithm is still simpler than the post-order one, because while
806        // we care about node indices, we don't care about their childrens' indices.
807        while let Some(mut top) = self.stack.pop() {
808            // If this is the *first* time we'd be yielding this element, act
809            // like the non-verbose pre-order iterator.
810            if top.n_children_yielded == 0 {
811                // If we've seen the node before, skip it.
812                if self.tracker.record(&top.node, 0).is_some() {
813                    continue;
814                }
815                // Set its index.
816                top.index = self.index;
817                self.index += 1;
818            }
819            // Push the next child.
820            match (top.n_children_yielded, top.node.n_children()) {
821                (0, 0) => {}
822                (0, n) => {
823                    self.stack.push(top.clone().increment(n == 1));
824                    if top.depth < self.max_depth.unwrap_or(top.depth + 1) {
825                        let child = top.node.left_child().unwrap();
826                        self.stack.push(PreOrderIterItem::initial(
827                            child,
828                            top.depth + 1,
829                            Some(top.node.clone()),
830                        ));
831                    }
832                }
833                (1, 0) => unreachable!(),
834                (1, 1) => {}
835                (1, _) => {
836                    self.stack.push(top.clone().increment(true));
837                    if top.depth < self.max_depth.unwrap_or(top.depth + 1) {
838                        let child = top.node.right_child().unwrap();
839                        self.stack.push(PreOrderIterItem::initial(
840                            child,
841                            top.depth + 1,
842                            Some(top.node.clone()),
843                        ));
844                    }
845                }
846                (x, y) => {
847                    debug_assert_eq!((x, y), (2, 2));
848                }
849            }
850            // Then yield the element.
851            return Some(top);
852        }
853        None
854    }
855}
856
857/// A set of data yielded by a [`VerbosePreOrderIter`].
858#[derive(Clone, Debug)]
859pub struct PreOrderIterItem<D> {
860    /// The actual element being yielded.
861    pub node: D,
862    /// The parent of this node. `None` for the initial node, but will be
863    /// populated for all other nodes.
864    pub parent: Option<D>,
865    /// The index when the element was first yielded.
866    pub index: usize,
867    /// The distance of this element from the initial node. 0 for the initial
868    /// node itself.
869    pub depth: usize,
870    /// How many of this item's children have been yielded.
871    ///
872    /// This can also be interpreted as a count of how many times this
873    /// item has been yielded before.
874    pub n_children_yielded: usize,
875    /// Whether this item is done (will not be yielded again).
876    pub is_complete: bool,
877}
878
879impl<D: DagLike + Clone> PreOrderIterItem<D> {
880    /// Creates a `PreOrderIterItem` which yields a given element for the first time.
881    ///
882    /// Marks the index as 0. The index must be manually set before yielding.
883    fn initial(d: D, depth: usize, parent: Option<D>) -> Self {
884        PreOrderIterItem {
885            is_complete: matches!(d.as_dag_node(), Dag::Nullary),
886            node: d,
887            parent,
888            index: 0,
889            depth,
890            n_children_yielded: 0,
891        }
892    }
893
894    /// Creates a `PreOrderIterItem` which yields a given element again.
895    fn increment(self, is_complete: bool) -> Self {
896        PreOrderIterItem {
897            node: self.node,
898            index: self.index,
899            depth: self.depth,
900            parent: self.parent,
901            n_children_yielded: self.n_children_yielded + 1,
902            is_complete,
903        }
904    }
905}