mod3d_base/
hierarchy.rs

1//a Documentation
2/*!
3This module provides a hierarchy of nodes and iterators over them
4 */
5
6//a Imports
7use indent_display::{IndentedDisplay, IndentedOptions, Indenter};
8
9//a Constants
10/// Compile-time setting for adding extra debugging information
11const DEBUG_ITERATOR: bool = false;
12
13//a Node
14//tp Node
15/// A node in the hierarchy
16#[derive(Debug)]
17pub struct Node<T>
18where
19    T: std::fmt::Debug,
20{
21    /// An optional parent index - if None, this is a root
22    parent: Option<usize>,
23    /// Array of child indices
24    children: Vec<usize>,
25    /// Data associated with the node
26    pub data: T,
27}
28
29//ip Clone for Node<T:Clone>
30impl<T> Clone for Node<T>
31where
32    T: Clone + std::fmt::Debug,
33{
34    fn clone(&self) -> Self {
35        let parent = self.parent;
36        let children = self.children.clone();
37        let data = self.data.clone();
38        Self {
39            parent,
40            children,
41            data,
42        }
43    }
44}
45
46//ip Node
47impl<T> Node<T>
48where
49    T: std::fmt::Debug,
50{
51    //fp new
52    /// Create a new node in the hierarchy
53    pub fn new(data: T, parent: Option<usize>) -> Self {
54        let children = Vec::new();
55        Self {
56            parent,
57            children,
58            data,
59        }
60    }
61
62    //fp has_parent
63    /// Returns true if the node has a parent - i.e. it is not the
64    /// root of the hierarchy
65    pub fn has_parent(&self) -> bool {
66        self.parent.is_some()
67    }
68
69    //fp set_parent
70    /// Set the parent of a node
71    pub fn set_parent(&mut self, parent: Option<usize>) {
72        self.parent = parent;
73    }
74
75    //mp add_child
76    /// Add a child of this node
77    pub fn add_child(&mut self, child: usize) {
78        self.children.push(child);
79    }
80
81    //mp has_children
82    /// Return true if the node has children
83    pub fn has_children(&self) -> bool {
84        !self.children.is_empty()
85    }
86
87    //zz All done
88}
89
90//a Hierarchy
91//tp Hierarchy
92/// A hierarchy of nodes, each of which has a data of the type of the
93/// tree
94#[derive(Debug)]
95pub struct Hierarchy<T>
96where
97    T: std::fmt::Debug,
98{
99    /// The elements in the hierarchy
100    elements: Vec<Node<T>>,
101    /// The roots in the hierarchy - more than one tree can be stored
102    /// in the hierarchy
103    roots: Vec<usize>,
104}
105
106//ip Default for Hierarchy<T>
107impl<T> std::default::Default for Hierarchy<T>
108where
109    T: std::fmt::Debug,
110{
111    fn default() -> Self {
112        let elements = vec![];
113        let roots = vec![];
114        Self { elements, roots }
115    }
116}
117
118//ip Clone for Hierarchy<T:Clone>
119impl<T> Clone for Hierarchy<T>
120where
121    T: Clone + std::fmt::Debug,
122{
123    fn clone(&self) -> Self {
124        let elements = self.elements.clone();
125        let roots = self.roots.clone();
126        Self { elements, roots }
127    }
128}
129
130//ip Hierarchy
131impl<T> Hierarchy<T>
132where
133    T: std::fmt::Debug,
134{
135    //fp new
136    /// Create a new hierarchy
137    pub fn new() -> Self {
138        Self {
139            elements: Vec::new(),
140            roots: Vec::new(),
141        }
142    }
143
144    //ap len
145    /// Return the number of elements in the hierarchy
146    pub fn len(&self) -> usize {
147        self.elements.len()
148    }
149
150    //ap is_empty
151    /// Return true if there are no elements
152    pub fn is_empty(&self) -> bool {
153        self.elements.is_empty()
154    }
155
156    //mp add_node
157    /// Add a node to the hierarchy
158    pub fn add_node(&mut self, data: T) -> usize {
159        let n = self.elements.len();
160        self.elements.push(Node::new(data, None));
161        n
162    }
163
164    //mp relate
165    /// Add a relation from a parent to a child in the hierarchy
166    pub fn relate(&mut self, parent: usize, child: usize) {
167        self.elements[parent].add_child(child);
168        self.elements[child].set_parent(Some(parent));
169    }
170
171    //mp find_roots
172    /// Find all the roots of the hierarchy and record it
173    pub fn find_roots(&mut self) {
174        self.roots = Vec::new();
175        for (i, e) in self.elements.iter().enumerate() {
176            if !e.has_parent() {
177                self.roots.push(i);
178            }
179        }
180    }
181
182    //mp borrow_node
183    /// Borrow a node in the hierarchy
184    pub fn borrow_node(&self, index: usize) -> &T {
185        &self.elements[index].data
186    }
187
188    //mp borrow_mut
189    /// Mutuably borrow a node in the hierarchy
190    pub fn borrow_mut(&mut self) -> (&Vec<usize>, &mut Vec<Node<T>>) {
191        (&self.roots, &mut self.elements)
192    }
193
194    //mp borrow_roots
195    /// Borrow the roots of the hierarchy
196    pub fn borrow_roots(&self) -> &Vec<usize> {
197        &self.roots
198    }
199
200    //mp enum_from
201    /// Enumerate the nodes from a particular node
202    pub fn enum_from(&self, node: usize) -> NodeEnum<T> {
203        NodeEnum::new(&self.elements, node)
204    }
205
206    //mp iter_from
207    /// Iterate the nodes from a particular node
208    pub fn iter_from(&self, node: usize) -> NodeIter<T> {
209        NodeIter::new(&self.elements, node)
210    }
211
212    //mp borrow_elements
213    /// Borrow all the elements
214    pub fn borrow_elements(&self) -> &Vec<Node<T>> {
215        &self.elements
216    }
217
218    //mp take_elements
219    /// Take the elements as a vec
220    pub fn take_elements(self) -> Vec<T> {
221        self.elements.into_iter().map(|n| n.data).collect()
222    }
223
224    //zz All done
225}
226
227//ip IndentedDisplay for Hierarchy
228impl<'a, Opt, T> IndentedDisplay<'a, Opt> for Hierarchy<T>
229where
230    Opt: IndentedOptions<'a>,
231    T: std::fmt::Debug + IndentedDisplay<'a, Opt>,
232{
233    //mp fmt
234    /// Display for humans with indent
235    fn indent(&self, f: &mut Indenter<'a, Opt>) -> std::fmt::Result {
236        use std::fmt::Write;
237        let mut sub = f.sub();
238        for (i, e) in self.elements.iter().enumerate() {
239            if !e.has_parent() {
240                for x in self.enum_from(i) {
241                    #[allow(unreachable_patterns)]
242                    match x {
243                        NodeEnumOp::Push(x, _) => {
244                            self.elements[x].data.indent(&mut sub)?;
245                            writeln!(sub)?;
246                            sub = sub.sub();
247                        }
248                        NodeEnumOp::Pop(_, _) => {
249                            sub = sub.pop();
250                        }
251                        _ => {}
252                    };
253                }
254                // expliticly drop sub for cleanliness
255            }
256        }
257        drop(sub);
258        Ok(())
259    }
260}
261
262//a NodeEnumOp
263//tp NodeEnumOp
264/// This enumeration is used as a node hierarchy is enumerated: a node
265/// is pushed into, then children are pushed/popped, then the node is
266/// popped.
267#[derive(Debug, Clone, Copy, PartialEq)]
268pub enum NodeEnumOp<T> {
269    /// Pushing in to the hierarchy to new node index, and true if node has children
270    Push(T, bool),
271    /// Popping out to the hierarchy to node index
272    Pop(T, bool),
273}
274
275//ip NodeEnumOp
276impl<T> NodeEnumOp<T> {
277    //mp unpack
278    /// Unpack the data
279    #[inline]
280    pub fn unpack(&self) -> (bool, &T, bool) {
281        match &self {
282            Self::Pop(d, c) => (false, d, *c),
283            Self::Push(d, c) => (true, d, *c),
284        }
285    }
286
287    //mp is_pop
288    /// Return true if this is a Pop, false if it is a Push
289    #[inline]
290    pub fn is_pop(&self) -> bool {
291        matches!(self, Self::Pop(_, _))
292    }
293
294    //zz All done
295}
296
297//a Recipe
298//tp Recipe
299/// Create a recipe from traversing a hierarchy from a node
300///
301/// The recipe is a [Vec] of [NodeEnumOp]s which describe entirely how
302/// to traverse the hierarchy; essentially it is a record of an
303/// enumeration of a hierarchy or part of a hierarchy
304#[derive(Debug)]
305pub struct Recipe {
306    /// The [NodeEnumOp]s that make up the traversal
307    ops: Vec<NodeEnumOp<usize>>,
308    /// The maximum depth required (maximum 'tree' depth from the initial node)
309    max_depth: usize,
310    /// The current depth (used in generating the recipe)
311    cur_depth: usize,
312}
313
314//ip Default for Recipe
315impl Default for Recipe {
316    fn default() -> Self {
317        Self::new()
318    }
319}
320
321//ip Recipe
322impl Recipe {
323    //fp new
324    /// Create a new recipe
325    pub fn new() -> Self {
326        Self {
327            ops: Vec::new(),
328            max_depth: 0,
329            cur_depth: 0,
330        }
331    }
332
333    //mp add_op
334    /// Add a new operation to the recipe
335    pub fn add_op(&mut self, op: NodeEnumOp<usize>) {
336        if op.is_pop() {
337            self.cur_depth -= 1;
338        } else {
339            self.cur_depth += 1;
340            if self.cur_depth > self.max_depth {
341                self.max_depth = self.cur_depth;
342            }
343        }
344        self.ops.push(op);
345    }
346
347    //dp take
348    /// Deconstruct the recipe
349    pub fn take(self) -> (usize, Vec<NodeEnumOp<usize>>) {
350        (self.max_depth, self.ops)
351    }
352
353    //mp depth
354    /// Find the maximum depth of the recipe
355    pub fn depth(&self) -> usize {
356        self.max_depth
357    }
358
359    //mp borrow_ops
360    /// Borrow the operations that make the recipe
361    pub fn borrow_ops(&self) -> &Vec<NodeEnumOp<usize>> {
362        &self.ops
363    }
364
365    //mp of_ops
366    /// Create a recipe from a [NodeEnum] iterator
367    pub fn of_ops<T>(iter: NodeEnum<T>) -> Self
368    where
369        T: std::fmt::Debug,
370    {
371        let mut r = Self::new();
372        for op in iter {
373            r.add_op(op);
374        }
375        r
376    }
377
378    //zz Al done
379}
380
381//a NodeEnum
382//ti NodeEnumState
383/// This enumeration provides
384#[derive(Debug, Clone, Copy)]
385enum NodeEnumState {
386    /// PreNode indicates that the element has not been returned yet
387    PreNode(usize),
388    PreChildren(usize),
389    Child(usize, usize),
390    PostChildren(usize),
391}
392
393//tp NodeEnum
394/// An iterator structure to permit iteration over a hierarchy of nodes
395///
396/// The iterator yields pairs of (NodeEnumState, usize)
397/// For a hierarchy of nodes:
398///   A -> B -> C0
399///             C1
400///        D
401///        E  -> F
402/// the iterator will provide
403///
404///    Push(A,true)
405///    Push(B,true)
406///    Push(C0,false)
407///    Pop(C0)
408///    Push(C1,false)
409///    Pop(C1)
410///    Pop(B)
411///    Push(D,false)
412///    Pop(D)
413///    Push(E,true)
414///    Push(F,false)
415///    Pop(F)
416///    Pop(E)
417///    Pop(A)
418pub struct NodeEnum<'a, T>
419where
420    T: std::fmt::Debug,
421{
422    /// Hierarchy of nodes that is being iterated over
423    pub hierarchy: &'a [Node<T>],
424    /// Stack of indices in to the hierarchy and whether the node at that point has been handled
425    stack: Vec<NodeEnumState>,
426}
427
428//ip NodeEnum
429impl<'a, T> NodeEnum<'a, T>
430where
431    T: std::fmt::Debug,
432{
433    //fp new
434    /// Create a new hierarchy node iterator
435    pub fn new(hierarchy: &'a [Node<T>], root: usize) -> Self {
436        let stack = vec![NodeEnumState::PreNode(root)];
437        Self { hierarchy, stack }
438    }
439}
440
441//ip Iterator for NodeEnum
442impl<'a, T> Iterator for NodeEnum<'a, T>
443where
444    T: std::fmt::Debug,
445{
446    type Item = NodeEnumOp<usize>;
447    fn next(&mut self) -> Option<Self::Item> {
448        if self.stack.is_empty() {
449            None
450        } else {
451            let se = self.stack.pop().unwrap();
452            // Track the state for debugging
453            if DEBUG_ITERATOR {
454                println!("{se:?}");
455            }
456            match se {
457                NodeEnumState::PreNode(x) => {
458                    self.stack.push(NodeEnumState::PreChildren(x));
459                    let has_children = self.hierarchy[x].has_children();
460                    Some(NodeEnumOp::Push(x, has_children))
461                }
462                NodeEnumState::PreChildren(x) => {
463                    // Push(x) has happened
464                    self.stack.push(NodeEnumState::Child(x, 0));
465                    self.next()
466                }
467                NodeEnumState::Child(x, n) => {
468                    // Children of x prior to n have happened
469                    if let Some(c) = self.hierarchy[x].children.get(n) {
470                        self.stack.push(NodeEnumState::Child(x, n + 1));
471                        self.stack.push(NodeEnumState::PreNode(*c));
472                    } else {
473                        // run out of children
474                        self.stack.push(NodeEnumState::PostChildren(x));
475                    }
476                    self.next()
477                }
478                NodeEnumState::PostChildren(x) => {
479                    // Push(x) and all children ops have happened
480                    let has_children = self.hierarchy[x].has_children();
481                    Some(NodeEnumOp::Pop(x, has_children))
482                }
483            }
484        }
485    }
486}
487
488//ip NodeIter
489/// An iterator over part of a [Hierarchy] that returns a reference to
490/// the node as it traverses
491pub struct NodeIter<'a, T>
492where
493    T: std::fmt::Debug,
494{
495    node_enum: NodeEnum<'a, T>,
496}
497
498//ip NodeIter
499impl<'a, T> NodeIter<'a, T>
500where
501    T: std::fmt::Debug,
502{
503    //fp new
504    /// Create a new hierarchy node iterator
505    pub fn new(hierarchy: &'a [Node<T>], root: usize) -> Self {
506        Self {
507            node_enum: NodeEnum::new(hierarchy, root),
508        }
509    }
510}
511
512//ip Iterator for NodeIter
513impl<'a, T> Iterator for NodeIter<'a, T>
514where
515    T: std::fmt::Debug,
516{
517    type Item = NodeEnumOp<(usize, &'a T)>;
518    fn next(&mut self) -> Option<Self::Item> {
519        match self.node_enum.next() {
520            Some(NodeEnumOp::Push(x, c)) => {
521                Some(NodeEnumOp::Push((x, &self.node_enum.hierarchy[x].data), c))
522            }
523            Some(NodeEnumOp::Pop(x, c)) => {
524                Some(NodeEnumOp::Pop((x, &self.node_enum.hierarchy[x].data), c))
525            }
526            None => None,
527        }
528    }
529}
530
531//a Test
532#[cfg(test)]
533mod test_node {
534    use super::*;
535    use indent_display::NullOptions;
536
537    //fi basic_hierarchy
538    pub fn basic_hierarchy() -> Hierarchy<&'static str> {
539        let mut h = Hierarchy::new();
540        let a = h.add_node("A");
541        let b = h.add_node("B");
542        let c0 = h.add_node("C0");
543        let c1 = h.add_node("C1");
544        let d = h.add_node("D");
545        let e = h.add_node("E");
546        let f = h.add_node("F");
547        h.relate(a, b);
548        h.relate(a, d);
549        h.relate(a, e);
550        h.relate(b, c0);
551        h.relate(b, c1);
552        h.relate(e, f);
553        h.find_roots();
554        h
555    }
556
557    //fi test_0
558    #[test]
559    fn test_0() {
560        let h = basic_hierarchy();
561        assert_eq!(h.borrow_roots(), &[0], "Expect roots to just be A");
562    }
563
564    //fi test_display
565    #[test]
566    fn test_display() {
567        let h = basic_hierarchy();
568        let mut f = Vec::<u8>::new();
569        let opt = NullOptions {};
570        let mut ind = Indenter::new(&mut f, " ", &opt);
571        h.indent(&mut ind).unwrap();
572        drop(ind);
573        assert_eq!(
574            f,
575            b" A
576  B
577   C0
578   C1
579  D
580  E
581   F
582"
583        );
584    }
585
586    //fi test_recipe
587    #[test]
588    fn test_recipe() {
589        let h = basic_hierarchy();
590        let mut r = Recipe::new();
591        for op in h.enum_from(0) {
592            r.add_op(op);
593        }
594        let (max_depth, ops) = r.take();
595        assert_eq!(max_depth, 3, "Max depth of tree is 3");
596        assert_eq!(
597            ops,
598            vec![
599                NodeEnumOp::Push(0, true),
600                NodeEnumOp::Push(1, true),
601                NodeEnumOp::Push(2, false),
602                NodeEnumOp::Pop(2, false),
603                NodeEnumOp::Push(3, false),
604                NodeEnumOp::Pop(3, false),
605                NodeEnumOp::Pop(1, true),
606                NodeEnumOp::Push(4, false),
607                NodeEnumOp::Pop(4, false),
608                NodeEnumOp::Push(5, true),
609                NodeEnumOp::Push(6, false),
610                NodeEnumOp::Pop(6, false),
611                NodeEnumOp::Pop(5, true),
612                NodeEnumOp::Pop(0, true),
613            ],
614            "Recipe mismatch"
615        );
616    }
617}