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(¤t.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}