pub(crate) const LOOKUP_BLOCK_SIZE: u64 = 8;
type LookupBlockType = u8;
type SignedLookupBlockType = i8;
const LOOKUP_MAX_VALUE: u32 = u8::MAX as u32;
#[allow(long_running_const_eval)]
static PAREN_BLOCK_LOOKUP_FWD: [u64; 1 << LOOKUP_BLOCK_SIZE] = calculate_lookup_table(true);
#[allow(long_running_const_eval)]
static PAREN_BLOCK_LOOKUP_BWD: [u64; 1 << LOOKUP_BLOCK_SIZE] = calculate_lookup_table(false);
const ENCODING_MASK: u64 = 0b11111;
const MINIMUM_EXCESS_POSITION: usize = 5;
const QUERY_BASE_POSITION: usize = 10;
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] const fn calculate_lookup_table(fwd: bool) -> [u64; 1 << LOOKUP_BLOCK_SIZE] {
const MORE_THAN_MAX: SignedLookupBlockType = (LOOKUP_BLOCK_SIZE + 1) as SignedLookupBlockType;
const LESS_THAN_MIN: SignedLookupBlockType = -(LOOKUP_BLOCK_SIZE as SignedLookupBlockType) - 1;
let mut lookup = [0; 1 << LOOKUP_BLOCK_SIZE];
let mut query_map = [-1i8; 17];
let mut v: u32 = 0;
while v <= LOOKUP_MAX_VALUE {
let mut minimum_excess = MORE_THAN_MAX;
let mut maximum_excess = LESS_THAN_MIN;
if fwd {
calculate_values_fwd(v, &mut minimum_excess, &mut maximum_excess, &mut query_map);
} else {
calculate_values_bwd(v, &mut minimum_excess, &mut maximum_excess, &mut query_map);
}
let mut encoded: u64 = ((minimum_excess as i32 + LOOKUP_BLOCK_SIZE as i32) as u64
& ENCODING_MASK)
<< MINIMUM_EXCESS_POSITION;
encoded |= (maximum_excess as i32 + LOOKUP_BLOCK_SIZE as i32) as u64 & ENCODING_MASK;
let mut relative_off = 0;
while relative_off <= (LOOKUP_BLOCK_SIZE * 2) as usize {
encoded |= ((query_map[relative_off] & 0b111) as u64)
<< (QUERY_BASE_POSITION + (relative_off * 3)) as u64;
query_map[relative_off] = -1;
relative_off += 1;
}
lookup[v as usize] = encoded;
v += 1;
}
lookup
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] const fn calculate_values_fwd(
v: u32,
minimum_excess: &mut SignedLookupBlockType,
maximum_excess: &mut SignedLookupBlockType,
query_map: &mut [i8; 17],
) {
let mut total_excess = 0;
let mut i = 0;
while i < LOOKUP_BLOCK_SIZE {
if ((v >> i) & 1) == 1 {
total_excess += 1;
} else {
total_excess -= 1;
}
*minimum_excess = min(*minimum_excess, total_excess);
*maximum_excess = max(*maximum_excess, total_excess);
if query_map[(total_excess + LOOKUP_BLOCK_SIZE as i8) as usize] == -1 {
query_map[(total_excess + LOOKUP_BLOCK_SIZE as i8) as usize] = i as i8;
}
i += 1;
}
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn calculate_values_bwd(
v: u32,
minimum_excess: &mut SignedLookupBlockType,
maximum_excess: &mut SignedLookupBlockType,
query_map: &mut [i8; 17],
) {
let mut total_excess = 0;
let mut i = LOOKUP_BLOCK_SIZE as i64 - 1;
while i >= 0 {
if ((v >> i) & 1) == 1 {
total_excess -= 1;
} else {
total_excess += 1;
}
*minimum_excess = min(*minimum_excess, total_excess);
*maximum_excess = max(*maximum_excess, total_excess);
if query_map[(total_excess + LOOKUP_BLOCK_SIZE as i8) as usize] == -1 {
query_map[(total_excess + LOOKUP_BLOCK_SIZE as i8) as usize] = i as i8;
}
i -= 1;
}
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn answer_query(value: u64, relative_excess: i64) -> u64 {
debug_assert!(relative_excess.abs() <= LOOKUP_BLOCK_SIZE as i64);
(value >> (QUERY_BASE_POSITION + ((relative_excess + 8) as usize * 3))) & 0b111
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn get_minimum_excess(value: u64) -> i64 {
((value >> MINIMUM_EXCESS_POSITION) & ENCODING_MASK) as i64 - LOOKUP_BLOCK_SIZE as i64
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn get_maximum_excess(value: u64) -> i64 {
(value & ENCODING_MASK) as i64 - LOOKUP_BLOCK_SIZE as i64
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn min(a: SignedLookupBlockType, b: SignedLookupBlockType) -> SignedLookupBlockType {
b + ((a - b)
& -(((a - b) as LookupBlockType >> (LOOKUP_BLOCK_SIZE - 1)) as SignedLookupBlockType))
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] #[allow(clippy::cast_possible_wrap)] const fn max(a: SignedLookupBlockType, b: SignedLookupBlockType) -> SignedLookupBlockType {
a - ((a - b)
& -(((a - b) as LookupBlockType >> (LOOKUP_BLOCK_SIZE - 1)) as SignedLookupBlockType))
}
#[inline(always)]
fn lookup_total_excess(block: LookupBlockType) -> i64 {
i64::from(block.count_ones()) - i64::from(block.count_zeros())
}
#[inline(always)]
fn lookup_maximum_excess(block: LookupBlockType) -> i64 {
get_maximum_excess(PAREN_BLOCK_LOOKUP_FWD[block as usize])
}
#[inline(always)]
fn lookup_minimum_excess(block: LookupBlockType) -> i64 {
get_minimum_excess(PAREN_BLOCK_LOOKUP_FWD[block as usize])
}
#[inline(always)]
pub(crate) fn process_block_fwd(
block: LookupBlockType,
relative_excess: &mut i64,
) -> Result<u64, ()> {
if *relative_excess <= lookup_maximum_excess(block)
&& lookup_minimum_excess(block) <= *relative_excess
{
Ok(answer_query(
PAREN_BLOCK_LOOKUP_FWD[block as usize],
*relative_excess,
))
} else {
*relative_excess -= lookup_total_excess(block);
Err(())
}
}
#[inline(always)]
pub(crate) fn process_block_bwd(
block: LookupBlockType,
relative_excess: &mut i64,
) -> Result<u64, ()> {
let total_excess = lookup_total_excess(block);
if (*relative_excess + total_excess == 0)
|| (lookup_minimum_excess(block) <= *relative_excess + total_excess
&& *relative_excess + total_excess <= lookup_maximum_excess(block))
{
Ok(answer_query(
PAREN_BLOCK_LOOKUP_BWD[block as usize],
*relative_excess,
))
} else {
*relative_excess += total_excess;
Err(())
}
}