use std::sync::LazyLock;
pub const LHO: [usize; 4] = [1, 2, 3, 0];
pub const RHO: [usize; 4] = [3, 0, 1, 2];
pub const PARTNER: [usize; 4] = [2, 3, 0, 1];
pub const BIT_MAP_RANK: [u16; 16] = [
0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200,
0x0400, 0x0800, 0x1000, 0x2000,
];
pub static HIGHEST_RANK: LazyLock<[u8; 8192]> = LazyLock::new(|| {
let mut t = [0u8; 8192];
for (aggr, entry) in t.iter_mut().enumerate().skip(1) {
for r in (2..=14).rev() {
if (aggr as u16) & BIT_MAP_RANK[r] != 0 {
*entry = r as u8;
break;
}
}
}
t
});
pub static LOWEST_RANK: LazyLock<[u8; 8192]> = LazyLock::new(|| {
let mut t = [0u8; 8192];
for (aggr, entry) in t.iter_mut().enumerate().skip(1) {
for (r, &bit) in BIT_MAP_RANK.iter().enumerate().skip(2).take(13) {
if (aggr as u16) & bit != 0 {
*entry = r as u8;
break;
}
}
}
t
});
pub static COUNT_TABLE: LazyLock<[u8; 8192]> = LazyLock::new(|| {
let mut t = [0u8; 8192];
for (aggr, entry) in t.iter_mut().enumerate() {
*entry = (aggr as u32).count_ones() as u8;
}
t
});
pub static REL_RANK: LazyLock<[[i8; 15]; 8192]> = LazyLock::new(|| {
let mut t = [[0i8; 15]; 8192];
for (aggr, row) in t.iter_mut().enumerate().skip(1) {
let mut ord: i8 = 0;
for r in (2..=14).rev() {
if (aggr as u16) & BIT_MAP_RANK[r] != 0 {
ord += 1;
row[r] = ord;
}
}
}
t
});
pub static WIN_RANKS: LazyLock<[[u16; 14]; 8192]> = LazyLock::new(|| {
let mut t = [[0u16; 14]; 8192];
for (aggr, row) in t.iter_mut().enumerate() {
for (least_win, slot) in row.iter_mut().enumerate().skip(1) {
let mut res: u16 = 0;
let mut next_bit_no = 1;
for r in (2..=14).rev() {
if (aggr as u16) & BIT_MAP_RANK[r] != 0 {
if next_bit_no <= least_win {
res |= BIT_MAP_RANK[r];
next_bit_no += 1;
} else {
break;
}
}
}
*slot = res;
}
}
t
});
#[derive(Clone, Copy, Debug)]
pub struct MoveGroup {
pub last_group: i8,
pub rank: [u8; 7],
pub sequence: [u16; 7],
pub fullseq: [u16; 7],
pub gap: [u16; 7],
}
impl MoveGroup {
const fn empty() -> Self {
Self {
last_group: -1,
rank: [0; 7],
sequence: [0; 7],
fullseq: [0; 7],
gap: [0; 7],
}
}
}
pub static GROUP_DATA: LazyLock<Box<[MoveGroup; 8192]>> = LazyLock::new(|| {
const TOPSIDE: [u16; 15] = [
0x0000, 0x0000, 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff,
];
const BOTSIDE: [u16; 15] = [
0xffff, 0xffff, 0x1ffe, 0x1ffc, 0x1ff8, 0x1ff0, 0x1fe0, 0x1fc0, 0x1f80, 0x1f00, 0x1e00,
0x1c00, 0x1800, 0x1000, 0x0000,
];
let mut t = Box::new([MoveGroup::empty(); 8192]);
t[1].last_group = 0;
t[1].rank[0] = 2;
t[1].sequence[0] = 0;
t[1].fullseq[0] = 1;
t[1].gap[0] = 0;
let mut top_bit_rank: u16 = 1;
let mut next_bit_rank: u16 = 0;
let mut top_bit_no: usize = 2;
for ris in 2..8192 {
if (ris as u16) >= (top_bit_rank << 1) {
next_bit_rank = top_bit_rank;
top_bit_rank <<= 1;
top_bit_no += 1;
}
let prev = t[ris ^ top_bit_rank as usize];
t[ris] = prev;
if (ris as u16) & next_bit_rank != 0 {
let g = t[ris].last_group as usize;
t[ris].rank[g] += 1;
t[ris].sequence[g] |= next_bit_rank;
t[ris].fullseq[g] |= top_bit_rank;
} else {
t[ris].last_group += 1;
let g = t[ris].last_group as usize;
t[ris].rank[g] = top_bit_no as u8;
t[ris].sequence[g] = 0;
t[ris].fullseq[g] = top_bit_rank;
let prev_rank = if g == 0 { 0 } else { t[ris].rank[g - 1] };
t[ris].gap[g] = TOPSIDE[top_bit_no] & BOTSIDE[prev_rank as usize];
}
}
t
});
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn highest_rank_known_values() {
assert_eq!(HIGHEST_RANK[0x1e00], 14);
assert_eq!(HIGHEST_RANK[0x0001], 2);
assert_eq!(HIGHEST_RANK[0], 0);
}
#[test]
fn lowest_rank_known_values() {
assert_eq!(LOWEST_RANK[0x1e00], 11);
assert_eq!(LOWEST_RANK[0x1000], 14);
assert_eq!(LOWEST_RANK[0], 0);
}
#[test]
fn count_table_matches_popcount() {
for aggr in [0_usize, 1, 0x1fff, 0x1e00, 0xaaa] {
assert_eq!(u32::from(COUNT_TABLE[aggr]), (aggr as u32).count_ones());
}
}
#[test]
fn rel_rank_simple_case() {
let aggr = 0x1c00;
assert_eq!(REL_RANK[aggr][14], 1);
assert_eq!(REL_RANK[aggr][13], 2);
assert_eq!(REL_RANK[aggr][12], 3);
assert_eq!(REL_RANK[aggr][11], 0);
}
#[test]
fn win_ranks_take_top_n() {
let aggr: usize = 0x1f80;
assert_eq!(WIN_RANKS[aggr][0], 0);
assert_eq!(WIN_RANKS[aggr][1], 0x1000);
assert_eq!(WIN_RANKS[aggr][3], 0x1c00);
assert_eq!(WIN_RANKS[aggr][13], aggr as u16);
}
#[test]
fn group_data_single_bit() {
let g = GROUP_DATA[0x0001];
assert_eq!(g.last_group, 0);
assert_eq!(g.rank[0], 2);
}
#[test]
fn group_data_two_runs() {
let ris = 0x1008;
let g = GROUP_DATA[ris];
assert_eq!(g.last_group, 1);
assert_eq!(g.rank[0], 5); assert_eq!(g.rank[1], 14); }
}