use cmov::Cmov;
use num_traits::ops::overflowing::OverflowingAdd;
pub const CACHE_LINE_WIDTH: u32 = 64; pub const FLOAT_WIDTH: u32 = 8; pub const ITEMS_PER_CACHE_LINE: u32 = CACHE_LINE_WIDTH / FLOAT_WIDTH; pub const ITEMS_PER_CACHE_LINE_MASK: u32 = ITEMS_PER_CACHE_LINE - 1;
pub const ITEMS_PER_CACHE_LINE_MASK_INV: u32 = !ITEMS_PER_CACHE_LINE_MASK;
pub const LOG2_ITEMS_PER_CACHE_LINE: u32 = ITEMS_PER_CACHE_LINE.ilog2();
#[allow(dead_code)]
#[cfg_attr(not(feature = "no_inline"), inline)]
#[cfg_attr(feature = "no_inline", inline(never))]
pub fn donnelly_get_idx_v2(curr_idx: u32, is_right_child: bool, level: u32) -> u32 {
let minor_level = level % LOG2_ITEMS_PER_CACHE_LINE;
let maj_idx = curr_idx >> LOG2_ITEMS_PER_CACHE_LINE;
let min_idx = curr_idx & ITEMS_PER_CACHE_LINE_MASK;
let is_right_child = u32::from(is_right_child);
if (minor_level + 1) == LOG2_ITEMS_PER_CACHE_LINE {
let min_row_idx = min_idx - minor_level - 2;
(((maj_idx << LOG2_ITEMS_PER_CACHE_LINE) + (min_row_idx << 1) + 1 + is_right_child)
<< LOG2_ITEMS_PER_CACHE_LINE) + 1
} else {
(maj_idx << LOG2_ITEMS_PER_CACHE_LINE) + (min_idx << 1) + is_right_child
}
}
#[allow(dead_code)]
#[cfg_attr(feature = "no_inline", inline(never))]
#[inline(never)]
pub fn donnelly_get_idx_v2_branchless(
curr_idx: u32,
is_right_child: bool,
minor_level: u32,
) -> u32 {
let min_idx = curr_idx & ITEMS_PER_CACHE_LINE_MASK;
let min_row_idx = min_idx.overflowing_sub(minor_level).0.overflowing_sub(2).0;
let is_right_child = u32::from(is_right_child);
let inc_major_level = u8::from((minor_level.overflowing_add(1).0) == LOG2_ITEMS_PER_CACHE_LINE);
let mut result: u32 = (curr_idx & ITEMS_PER_CACHE_LINE_MASK_INV)
.overflowing_add(is_right_child)
.0;
result.cmovnz(
&result
.overflowing_add(min_row_idx.overflowing_shl(1).0)
.0
.overflowing_add(1)
.0
.overflowing_shl(LOG2_ITEMS_PER_CACHE_LINE)
.0
.overflowing_add(1)
.0,
inc_major_level,
);
result.cmovz(
&result.overflowing_add(min_idx.overflowing_shl(1).0).0,
inc_major_level,
);
result
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case((1, 0, false), 2)] #[case((1, 0, true), 3)] #[case((2, 1, false), 4)] #[case((2, 1, true), 5)] #[case((3, 1, false), 6)] #[case((3, 1, true), 7)] #[case((4, 2, false), 9)] #[case((4, 2, true), 17)] #[case((5, 2, false), 25)] #[case((5, 2, true), 33)] #[case((6, 2, false), 41)] #[case((6, 2, true), 49)] #[case((7, 2, false), 57)] #[case((7, 2, true), 65)] #[case((9, 3, false), 10)] #[case((9, 3, true), 11)] #[case((10, 4, false), 12)] #[case((10, 4, true), 13)] #[case((11, 4, false), 14)] #[case((11, 4, true), 15)] #[case((12, 5, false), 73)] #[case((12, 5, true), 81)] #[case((13, 5, false), 89)] #[case((13, 5, true), 97)] #[case((14, 5, false), 105)] #[case((14, 5, true), 113)] #[case((15, 5, false), 121)] #[case((15, 5, true), 129)] #[case((17, 3, false), 18)] #[case((17, 3, true), 19)] #[case((18, 4, false), 20)] #[case((18, 4, true), 21)] #[case((19, 4, false), 22)] #[case((19, 4, true), 23)] #[case((20, 5, false), 137)] #[case((20, 5, true), 145)] #[case((21, 5, false), 153)] #[case((21, 5, true), 161)] #[case((22, 5, false), 169)] #[case((22, 5, true), 177)] #[case((23, 5, false), 185)] #[case((23, 5, true), 193)] fn donnelly_v2_get_child_idx_produces_correct_values(
#[case] input: (u32, u32, bool),
#[case] expected: u32,
) {
let (curr_idx, level, is_right_child) = input;
let next_idx = donnelly_get_idx_v2(curr_idx, is_right_child, level);
assert_eq!(next_idx, expected);
}
#[rstest]
#[case((1, 0, false), 2)] #[case((1, 0, true), 3)] #[case((2, 1, false), 4)] #[case((2, 1, true), 5)] #[case((3, 1, false), 6)] #[case((3, 1, true), 7)] #[case((4, 2, false), 9)] #[case((4, 2, true), 17)] #[case((5, 2, false), 25)] #[case((5, 2, true), 33)] #[case((6, 2, false), 41)] #[case((6, 2, true), 49)] #[case((7, 2, false), 57)] #[case((7, 2, true), 65)] #[case((9, 3, false), 10)] #[case((9, 3, true), 11)] #[case((10, 4, false), 12)] #[case((10, 4, true), 13)] #[case((11, 4, false), 14)] #[case((11, 4, true), 15)] #[case((12, 5, false), 73)] #[case((12, 5, true), 81)] #[case((13, 5, false), 89)] #[case((13, 5, true), 97)] #[case((14, 5, false), 105)] #[case((14, 5, true), 113)] #[case((15, 5, false), 121)] #[case((15, 5, true), 129)] #[case((17, 3, false), 18)] #[case((17, 3, true), 19)] #[case((18, 4, false), 20)] #[case((18, 4, true), 21)] #[case((19, 4, false), 22)] #[case((19, 4, true), 23)] #[case((20, 5, false), 137)] #[case((20, 5, true), 145)] #[case((21, 5, false), 153)] #[case((21, 5, true), 161)] #[case((22, 5, false), 169)] #[case((22, 5, true), 177)] #[case((23, 5, false), 185)] #[case((23, 5, true), 193)] fn donnelly_v2_branchless_get_child_idx_branchless_produces_correct_values(
#[case] input: (u32, u32, bool),
#[case] expected: u32,
) {
let (curr_idx, minor_level, is_right_child) = input;
let next_idx = donnelly_get_idx_v2_branchless(curr_idx, is_right_child, minor_level);
assert_eq!(next_idx, expected);
}
}