use crate::merkle::{
mmb::{Family, Location, Position},
Family as _,
};
#[derive(Default)]
pub struct PeakIterator {
n: Location, current_i: u32, remaining: u32, start_leaf_cursor: Location, }
impl PeakIterator {
pub fn new(size: Position) -> Self {
let n = Location::try_from(size).expect("size is not a valid MMB size");
if n.as_u64() == 0 {
return Self::default();
}
let num_peaks = (n.as_u64() + 1).ilog2();
Self {
n,
current_i: num_peaks - 1,
remaining: num_peaks,
start_leaf_cursor: Location::new(0),
}
}
#[inline]
pub const fn leaves(&self) -> Location {
self.n
}
pub fn to_nearest_size(size: Position) -> Position {
assert!(size <= Family::MAX_NODES, "size exceeds MAX_NODES");
if size.as_u64() == 0 {
return size;
}
let s = size.as_u64();
let mut n = s / 2;
n = (s + (n + 1).ilog2() as u64) / 2;
let candidate = Family::location_to_position(Location::new(n + 1));
if *candidate <= s {
candidate
} else {
Family::location_to_position(Location::new(n))
}
}
}
impl Iterator for PeakIterator {
type Item = (Position, u32);
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.remaining as usize;
(len, Some(len))
}
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let i = self.current_i;
let n_val = self.n.as_u64();
let height = (i as u64) + (((n_val + 1) >> i) & 1);
let leaves_in_peak = 1u64 << height;
let last_leaf = Location::new(self.start_leaf_cursor.as_u64() + leaves_in_peak - 1);
self.start_leaf_cursor = Location::new(self.start_leaf_cursor.as_u64() + leaves_in_peak);
self.current_i = self.current_i.wrapping_sub(1);
self.remaining -= 1;
let n_birth = peak_birth_leaf(last_leaf, height as u32);
Some((birthed_node_pos(n_birth, height == 0), height as u32))
}
}
impl ExactSizeIterator for PeakIterator {}
pub(super) const fn child_leaves(parent_leaf: Location, height: u32) -> (Location, Location) {
let parent_leaf = parent_leaf.as_u64();
if height == 1 {
(Location::new(parent_leaf - 1), Location::new(parent_leaf))
} else {
let base = 1u64 << (height - 2);
(
Location::new(parent_leaf - 3 * base),
Location::new(parent_leaf - base),
)
}
}
pub(super) fn birthed_node_pos(birth_leaf: Location, is_leaf: bool) -> Position {
if is_leaf {
Family::location_to_position(birth_leaf)
} else {
Position::new(Family::location_to_position(birth_leaf).as_u64() + 1)
}
}
fn parent_birth_leaf(pos: Position) -> Option<Location> {
let p = pos.as_u64();
if p < 2 {
return None;
}
if Family::position_to_location(pos).is_some() {
return None;
}
Family::position_to_location(Position::new(p - 1))
}
pub(crate) fn children(pos: Position, height: u32) -> (Position, Position) {
assert!(height > 0, "height-0 nodes are leaves and have no children");
let s = parent_birth_leaf(pos).expect("pos is not a valid parent position");
let (left_leaf, right_leaf) = child_leaves(s, height);
let is_leaf = height == 1;
let left = birthed_node_pos(left_leaf, is_leaf);
let right = birthed_node_pos(right_leaf, is_leaf);
(left, right)
}
pub(super) const fn peak_birth_leaf(last_leaf: Location, height: u32) -> Location {
let last_leaf = last_leaf.as_u64();
if height == 0 {
Location::new(last_leaf)
} else {
Location::new(last_leaf + (1u64 << (height - 1)) - 1)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_location_to_position() {
assert_eq!(Family::location_to_position(Location::new(1)).as_u64(), 1);
assert_eq!(Family::location_to_position(Location::new(2)).as_u64(), 3);
assert_eq!(Family::location_to_position(Location::new(3)).as_u64(), 4);
assert_eq!(Family::location_to_position(Location::new(4)).as_u64(), 6);
}
#[test]
fn test_position_to_location_roundtrip() {
for n in 1u64..=1000 {
let size = Family::location_to_position(Location::new(n));
assert_eq!(
Family::position_to_location(size),
Some(Location::new(n)),
"N={n}"
);
}
}
#[test]
fn test_max_position_and_max_location_consistent() {
let max_pos = Family::MAX_NODES.as_u64();
let max_loc = Family::MAX_LEAVES.as_u64();
assert_eq!(
Location::try_from(Position::new(max_pos)).ok(),
Some(Location::new(max_loc)),
"MAX_NODES should correspond to MAX_LEAVES leaves"
);
assert_eq!(
Position::try_from(Location::new(max_loc)).unwrap().as_u64(),
max_pos,
"location_to_position(MAX_LEAVES) should equal MAX_NODES"
);
let over_size = *Family::location_to_position(Location::new(max_loc + 1));
assert!(over_size > max_pos, "one more leaf should exceed MAX_NODES");
assert!(!Position::new(max_pos + 1).is_valid());
}
#[test]
fn test_child_leaves_matches_children() {
for n in 1u64..=256 {
let size = Position::try_from(Location::new(n)).unwrap();
for (pos, height) in PeakIterator::new(size) {
if height == 0 {
continue;
}
let leaf = parent_birth_leaf(pos).unwrap();
let (left_leaf, right_leaf) = child_leaves(leaf, height);
let is_leaf = height == 1;
let left_pos = birthed_node_pos(left_leaf, is_leaf);
let right_pos = birthed_node_pos(right_leaf, is_leaf);
let (expected_left, expected_right) = children(pos, height);
assert_eq!(
(left_pos, right_pos),
(expected_left, expected_right),
"n={n}, pos={pos}, height={height}"
);
}
}
}
#[test]
fn test_birthed_node_pos() {
for n in 1u64..=256 {
let size = Position::try_from(Location::new(n)).unwrap();
let mut first_leaf = 0u64;
for (pos, height) in PeakIterator::new(size) {
let leaves_in_peak = 1u64 << height;
let last_leaf = first_leaf + leaves_in_peak - 1;
let leaf = peak_birth_leaf(Location::new(last_leaf), height);
let computed_pos = birthed_node_pos(leaf, height == 0);
assert_eq!(computed_pos, pos, "n={n}, height={height}");
first_leaf += leaves_in_peak;
}
}
}
#[test]
fn test_invalid_sizes_rejected() {
for n in 1u64..=500 {
let valid = *Position::try_from(Location::new(n)).unwrap();
let next_valid = *Position::try_from(Location::new(n + 1)).unwrap();
for gap in (valid + 1)..next_valid {
assert!(
Location::try_from(Position::new(gap)).is_err(),
"size={gap} (between n={n} and n={}) should be invalid",
n + 1
);
}
}
}
#[test]
fn test_location_position_roundtrip() {
use crate::merkle::mmb::{Location, Position};
use core::convert::TryFrom;
for n in 0u64..=1000 {
let loc = Location::new(n);
let pos = Position::try_from(loc).unwrap();
let loc2 = Location::try_from(pos).unwrap();
assert_eq!(loc, loc2, "roundtrip failed for leaf {n}");
if n > 0 {
let prev_loc = Location::new(n - 1);
let prev_pos = Position::try_from(prev_loc).unwrap();
for gap_pos in (*prev_pos + 1)..*pos {
assert!(
Location::try_from(Position::new(gap_pos)).is_err(),
"position {gap_pos} between leaves {prev_pos} and {pos} should not be a leaf"
);
}
}
}
}
#[test]
fn test_parent_birth_leaf_rejects_leaf_positions() {
assert_eq!(parent_birth_leaf(Position::new(4)), None);
assert_eq!(parent_birth_leaf(Position::new(11)), None);
}
#[test]
#[should_panic(expected = "pos is not a valid parent position")]
fn test_children_rejects_leaf_position() {
children(Position::new(4), 1);
}
#[test]
fn test_peak_birth_leaf() {
for n in 1u64..=256 {
let size = Position::try_from(Location::new(n)).unwrap();
let mut first_leaf = 0u64;
for (pos, height) in PeakIterator::new(size) {
let leaves_in_peak = 1u64 << height;
let last_leaf = first_leaf + leaves_in_peak - 1;
let leaf = peak_birth_leaf(Location::new(last_leaf), height);
let computed_pos = birthed_node_pos(leaf, height == 0);
assert_eq!(computed_pos, pos, "n={n}, height={height}");
first_leaf += leaves_in_peak;
}
}
}
#[test]
fn test_to_nearest_size() {
assert_eq!(
PeakIterator::to_nearest_size(Position::new(0)),
Position::new(0)
);
let mut prev = Position::new(0);
for s in 0u64..=2000 {
let nearest = PeakIterator::to_nearest_size(Position::new(s));
assert!(
nearest.is_valid_size(),
"result {nearest} not valid for input {s}"
);
assert!(
nearest <= Position::new(s),
"result {nearest} exceeds input {s}"
);
assert!(nearest >= prev, "not monotonic at {s}");
prev = nearest;
}
for n in 1u64..=500 {
let size = Position::try_from(Location::new(n)).unwrap();
assert_eq!(PeakIterator::to_nearest_size(size), size, "n={n}");
let next_size = Position::try_from(Location::new(n + 1)).unwrap();
for s in *size + 1..*next_size {
assert_eq!(
PeakIterator::to_nearest_size(Position::new(s)),
size,
"gap size {s} between n={n} and n={}",
n + 1
);
}
}
}
}