use num_bigint::BigUint;
pub fn path_to_atom(path: &BigUint) -> Vec<u8> {
let bytes = path.to_bytes_be();
let first_nonzero = bytes
.iter()
.position(|&byte| byte != 0)
.unwrap_or(bytes.len());
bytes[first_nonzero..].to_vec()
}
pub fn first_path(path: &BigUint) -> BigUint {
add_path(path, BigUint::from(2u8))
}
pub fn rest_path(path: &BigUint) -> BigUint {
add_path(path, BigUint::from(3u8))
}
fn add_path(a: &BigUint, b: BigUint) -> BigUint {
let depth = a.bits().saturating_sub(1);
let mask = (BigUint::from(1u8) << depth) - 1u8;
(b << depth) | (a & mask)
}
pub fn parent_path(path: &BigUint) -> Option<BigUint> {
let result_depth = path.bits().saturating_sub(1);
if result_depth == 0 {
return None;
}
let original_depth = result_depth - 1;
let mask = (BigUint::from(1u8) << original_depth) - 1u8;
let lower_bits = path & mask;
let result = (BigUint::from(1u8) << original_depth) | lower_bits;
Some(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_to_atom() {
assert_eq!(path_to_atom(&0u8.into()), Vec::<u8>::new());
assert_eq!(path_to_atom(&1u8.into()), [0x01]);
assert_eq!(path_to_atom(&3u8.into()), [0x03]);
assert_eq!(path_to_atom(&128u8.into()), [0x80]);
assert_eq!(path_to_atom(&300u16.into()), [0x01, 0x2C]);
}
#[test]
fn test_first_path() {
assert_eq!(first_path(&1u8.into()), 2u8.into());
assert_eq!(first_path(&2u8.into()), 4u8.into());
assert_eq!(first_path(&5u8.into()), 9u8.into());
}
#[test]
fn test_rest_path() {
assert_eq!(rest_path(&1u8.into()), 3u8.into());
assert_eq!(rest_path(&2u8.into()), 6u8.into());
assert_eq!(rest_path(&5u8.into()), 13u8.into());
}
#[test]
fn test_parent_path() {
assert_eq!(parent_path(&0u8.into()), None);
assert_eq!(parent_path(&1u8.into()), None);
assert_eq!(parent_path(&2u8.into()), Some(1u8.into()));
assert_eq!(parent_path(&3u8.into()), Some(1u8.into()));
assert_eq!(parent_path(&4u8.into()), Some(2u8.into()));
assert_eq!(parent_path(&6u8.into()), Some(2u8.into()));
assert_eq!(parent_path(&5u8.into()), Some(3u8.into()));
assert_eq!(parent_path(&7u8.into()), Some(3u8.into()));
assert_eq!(parent_path(&8u8.into()), Some(4u8.into()));
assert_eq!(parent_path(&12u8.into()), Some(4u8.into()));
assert_eq!(parent_path(&10u8.into()), Some(6u8.into()));
assert_eq!(parent_path(&14u8.into()), Some(6u8.into()));
assert_eq!(parent_path(&9u8.into()), Some(5u8.into()));
assert_eq!(parent_path(&13u8.into()), Some(5u8.into()));
assert_eq!(parent_path(&11u8.into()), Some(7u8.into()));
assert_eq!(parent_path(&15u8.into()), Some(7u8.into()));
for path in 1..u32::from(u16::MAX) {
let path = BigUint::from(path);
assert_eq!(parent_path(&first_path(&path)), Some(path.clone()));
assert_eq!(parent_path(&rest_path(&path)), Some(path));
}
}
#[test]
fn test_arbitrarily_large_path() {
let mut path = BigUint::from(1u8);
for _ in 0..100 {
path = rest_path(&path);
}
assert_eq!(path.bits(), 101);
assert_eq!(path_to_atom(&path).len(), 13);
for _ in 0..100 {
path = parent_path(&path).unwrap();
}
assert_eq!(path, BigUint::from(1u8));
}
}