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