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    /// Constructor for a new [InOrderIndex].
 20    pub fn new(idx: NonZeroUsize) -> InOrderIndex {
 21        InOrderIndex { idx: idx.get() }
 22    }
 23
 24    /// Constructs an index from a leaf position.
 25    ///
 26    /// Panics:
 27    ///
 28    /// If `leaf` is higher than or equal to `usize::MAX / 2`.
 29    pub fn from_leaf_pos(leaf: usize) -> InOrderIndex {
 30        // Convert the position from 0-indexed to 1-indexed, since the bit manipulation in this
 31        // implementation only works 1-indexed counting.
 32        let pos = leaf + 1;
 33        InOrderIndex { idx: pos * 2 - 1 }
 34    }
 35
 36    /// True if the index is pointing at a leaf.
 37    ///
 38    /// Every odd number represents a leaf.
 39    pub fn is_leaf(&self) -> bool {
 40        self.idx & 1 == 1
 41    }
 42
 43    /// Returns the level of the index.
 44    ///
 45    /// Starts at level zero for leaves and increases by one for each parent.
 46    pub fn level(&self) -> u32 {
 47        self.idx.trailing_zeros()
 48    }
 49
 50    /// Returns the index of the left child.
 51    ///
 52    /// Panics:
 53    ///
 54    /// If the index corresponds to a leaf.
 55    pub fn left_child(&self) -> InOrderIndex {
 56        // The left child is itself a parent, with an index that splits its left/right subtrees. To
 57        // go from the parent index to its left child, it is only necessary to subtract the count
 58        // of elements on the child's right subtree + 1.
 59        let els = 1 << (self.level() - 1);
 60        InOrderIndex { idx: self.idx - els }
 61    }
 62
 63    /// Returns the index of the right child.
 64    ///
 65    /// Panics:
 66    ///
 67    /// If the index corresponds to a leaf.
 68    pub fn right_child(&self) -> InOrderIndex {
 69        // To compute the index of the parent of the right subtree it is sufficient to add the size
 70        // of its left subtree + 1.
 71        let els = 1 << (self.level() - 1);
 72        InOrderIndex { idx: self.idx + els }
 73    }
 74
 75    /// Returns the index of the parent node.
 76    pub fn parent(&self) -> InOrderIndex {
 77        // If the current index corresponds to a node in a left tree, to go up a level it is
 78        // required to add the number of nodes of the right sibling, analogously if the node is a
 79        // right child, going up requires subtracting the number of nodes in its left subtree.
 80        //
 81        // Both of the above operations can be performed by bitwise manipulation. Below the mask
 82        // sets the number of trailing zeros to be equal the new level of the index, and the bit
 83        // marks the parent.
 84        let target = self.level() + 1;
 85        let bit = 1 << target;
 86        let mask = bit - 1;
 87        let idx = self.idx ^ (self.idx & mask);
 88        InOrderIndex { idx: idx | bit }
 89    }
 90
 91    /// Returns the index of the sibling node.
 92    pub fn sibling(&self) -> InOrderIndex {
 93        let parent = self.parent();
 94        if *self > parent {
 95            parent.left_child()
 96        } else {
 97            parent.right_child()
 98        }
 99    }
100}
101
102// CONVERSIONS FROM IN-ORDER INDEX
103// ------------------------------------------------------------------------------------------------
104
105impl From<InOrderIndex> for u64 {
106    fn from(index: InOrderIndex) -> Self {
107        index.idx as u64
108    }
109}
110
111// TESTS
112// ================================================================================================
113
114#[cfg(test)]
115mod test {
116    use super::InOrderIndex;
117    use proptest::prelude::*;
118
119    proptest! {
120        #[test]
121        fn proptest_inorder_index_random(count in 1..1000usize) {
122            let left_pos = count * 2;
123            let right_pos = count * 2 + 1;
124
125            let left = InOrderIndex::from_leaf_pos(left_pos);
126            let right = InOrderIndex::from_leaf_pos(right_pos);
127
128            assert!(left.is_leaf());
129            assert!(right.is_leaf());
130            assert_eq!(left.parent(), right.parent());
131            assert_eq!(left.parent().right_child(), right);
132            assert_eq!(left, right.parent().left_child());
133            assert_eq!(left.sibling(), right);
134            assert_eq!(left, right.sibling());
135        }
136    }
137
138    #[test]
139    fn test_inorder_index_basic() {
140        let left = InOrderIndex::from_leaf_pos(0);
141        let right = InOrderIndex::from_leaf_pos(1);
142
143        assert!(left.is_leaf());
144        assert!(right.is_leaf());
145        assert_eq!(left.parent(), right.parent());
146        assert_eq!(left.parent().right_child(), right);
147        assert_eq!(left, right.parent().left_child());
148        assert_eq!(left.sibling(), right);
149        assert_eq!(left, right.sibling());
150    }
151}