arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/merkle/mmr/inorder.rs
src/merkle/mmr/inorder.rsRaw
  1//! Index for nodes of a binary tree based on an in-order tree walk.
  2//!
  3//! In-order walks have the parent node index split its left and right subtrees. All the left
  4//! children have indexes lower than the parent, meanwhile all the right subtree higher indexes.
  5//! This property makes it is easy to compute changes to the index by adding or subtracting the
  6//! leaves count.
  7use core::num::NonZeroUsize;
  8
  9// IN-ORDER INDEX
 10// ================================================================================================
 11
 12/// Index of nodes in a perfectly balanced binary tree based on an in-order tree walk.
 13#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
 14pub struct InOrderIndex {
 15    idx: usize,
 16}
 17
 18impl InOrderIndex {
 19    // CONSTRUCTORS
 20    // --------------------------------------------------------------------------------------------
 21
 22    /// Returns a new [InOrderIndex] instantiated from the provided value.
 23    pub fn new(idx: NonZeroUsize) -> InOrderIndex {
 24        InOrderIndex { idx: idx.get() }
 25    }
 26
 27    /// Return a new [InOrderIndex] instantiated from the specified leaf position.
 28    ///
 29    /// # Panics:
 30    /// If `leaf` is higher than or equal to `usize::MAX / 2`.
 31    pub fn from_leaf_pos(leaf: usize) -> InOrderIndex {
 32        // Convert the position from 0-indexed to 1-indexed, since the bit manipulation in this
 33        // implementation only works 1-indexed counting.
 34        let pos = leaf + 1;
 35        InOrderIndex { idx: pos * 2 - 1 }
 36    }
 37
 38    // PUBLIC ACCESSORS
 39    // --------------------------------------------------------------------------------------------
 40
 41    /// True if the index is pointing at a leaf.
 42    ///
 43    /// Every odd number represents a leaf.
 44    pub fn is_leaf(&self) -> bool {
 45        self.idx & 1 == 1
 46    }
 47
 48    /// Returns true if this note is a left child of its parent.
 49    pub fn is_left_child(&self) -> bool {
 50        self.parent().left_child() == *self
 51    }
 52
 53    /// Returns the level of the index.
 54    ///
 55    /// Starts at level zero for leaves and increases by one for each parent.
 56    pub fn level(&self) -> u32 {
 57        self.idx.trailing_zeros()
 58    }
 59
 60    /// Returns the index of the left child.
 61    ///
 62    /// # Panics:
 63    /// If the index corresponds to a leaf.
 64    pub fn left_child(&self) -> InOrderIndex {
 65        // The left child is itself a parent, with an index that splits its left/right subtrees. To
 66        // go from the parent index to its left child, it is only necessary to subtract the count
 67        // of elements on the child's right subtree + 1.
 68        let els = 1 << (self.level() - 1);
 69        InOrderIndex { idx: self.idx - els }
 70    }
 71
 72    /// Returns the index of the right child.
 73    ///
 74    /// # Panics:
 75    /// If the index corresponds to a leaf.
 76    pub fn right_child(&self) -> InOrderIndex {
 77        // To compute the index of the parent of the right subtree it is sufficient to add the size
 78        // of its left subtree + 1.
 79        let els = 1 << (self.level() - 1);
 80        InOrderIndex { idx: self.idx + els }
 81    }
 82
 83    /// Returns the index of the parent node.
 84    pub fn parent(&self) -> InOrderIndex {
 85        // If the current index corresponds to a node in a left tree, to go up a level it is
 86        // required to add the number of nodes of the right sibling, analogously if the node is a
 87        // right child, going up requires subtracting the number of nodes in its left subtree.
 88        //
 89        // Both of the above operations can be performed by bitwise manipulation. Below the mask
 90        // sets the number of trailing zeros to be equal the new level of the index, and the bit
 91        // marks the parent.
 92        let target = self.level() + 1;
 93        let bit = 1 << target;
 94        let mask = bit - 1;
 95        let idx = self.idx ^ (self.idx & mask);
 96        InOrderIndex { idx: idx | bit }
 97    }
 98
 99    /// Returns the index of the sibling node.
100    pub fn sibling(&self) -> InOrderIndex {
101        let parent = self.parent();
102        if *self > parent {
103            parent.left_child()
104        } else {
105            parent.right_child()
106        }
107    }
108
109    /// Returns the inner value of this [InOrderIndex].
110    pub fn inner(&self) -> u64 {
111        self.idx as u64
112    }
113}
114
115// CONVERSIONS FROM IN-ORDER INDEX
116// ------------------------------------------------------------------------------------------------
117
118impl From<InOrderIndex> for u64 {
119    fn from(index: InOrderIndex) -> Self {
120        index.idx as u64
121    }
122}
123
124// TESTS
125// ================================================================================================
126
127#[cfg(test)]
128mod test {
129    use proptest::prelude::*;
130
131    use super::InOrderIndex;
132
133    proptest! {
134        #[test]
135        fn proptest_inorder_index_random(count in 1..1000usize) {
136            let left_pos = count * 2;
137            let right_pos = count * 2 + 1;
138
139            let left = InOrderIndex::from_leaf_pos(left_pos);
140            let right = InOrderIndex::from_leaf_pos(right_pos);
141
142            assert!(left.is_leaf());
143            assert!(right.is_leaf());
144            assert_eq!(left.parent(), right.parent());
145            assert_eq!(left.parent().right_child(), right);
146            assert_eq!(left, right.parent().left_child());
147            assert_eq!(left.sibling(), right);
148            assert_eq!(left, right.sibling());
149        }
150    }
151
152    #[test]
153    fn test_inorder_index_basic() {
154        let left = InOrderIndex::from_leaf_pos(0);
155        let right = InOrderIndex::from_leaf_pos(1);
156
157        assert!(left.is_leaf());
158        assert!(right.is_leaf());
159        assert_eq!(left.parent(), right.parent());
160        assert_eq!(left.parent().right_child(), right);
161        assert_eq!(left, right.parent().left_child());
162        assert_eq!(left.sibling(), right);
163        assert_eq!(left, right.sibling());
164    }
165}