use super::IndexMode;
use crate::{Direction, Edge, Resolution, Vertex};
use core::{cmp, num::NonZeroU8};
const MODE_OFFSET: usize = 59;
const MODE_MASK: u64 = 0b1111 << MODE_OFFSET;
const EDGE_OFFSET: usize = 56;
const EDGE_MASK: u64 = 0b111 << EDGE_OFFSET;
const VERTEX_OFFSET: usize = 56;
const VERTEX_MASK: u64 = 0b111 << VERTEX_OFFSET;
pub const DIRECTIONS_MASK: u64 = 0x0000_1fff_ffff_ffff;
#[must_use]
pub const fn get_mode(bits: u64) -> u8 {
((bits & MODE_MASK) >> MODE_OFFSET) as u8
}
#[must_use]
pub const fn clr_mode(bits: u64) -> u64 {
bits & !MODE_MASK
}
#[must_use]
pub const fn set_mode(bits: u64, mode: IndexMode) -> u64 {
clr_mode(bits) | ((mode as u64) << MODE_OFFSET)
}
#[must_use]
pub const fn get_edge(bits: u64) -> u8 {
((bits & EDGE_MASK) >> EDGE_OFFSET) as u8
}
#[must_use]
pub fn set_edge(bits: u64, edge: Edge) -> u64 {
clr_edge(bits) | (u64::from(edge) << EDGE_OFFSET)
}
#[must_use]
pub const fn clr_edge(bits: u64) -> u64 {
bits & !EDGE_MASK
}
#[must_use]
pub const fn get_vertex(bits: u64) -> u8 {
((bits & VERTEX_MASK) >> VERTEX_OFFSET) as u8
}
#[must_use]
pub fn set_vertex(bits: u64, vertex: Vertex) -> u64 {
clr_vertex(bits) | (u64::from(vertex) << VERTEX_OFFSET)
}
#[must_use]
pub const fn clr_vertex(bits: u64) -> u64 {
bits & !VERTEX_MASK
}
#[must_use]
pub const fn get_resolution(bits: u64) -> Resolution {
Resolution::new_unchecked(h3o_bit::get_resolution(bits))
}
#[must_use]
pub fn set_resolution(bits: u64, resolution: Resolution) -> u64 {
h3o_bit::set_resolution(bits, resolution.into())
}
#[must_use]
pub fn get_direction(bits: u64, resolution: Resolution) -> u8 {
debug_assert!(resolution != Resolution::Zero, "res 0 means no directions");
h3o_bit::get_direction(bits, resolution.into())
}
#[must_use]
pub fn clr_direction(bits: u64, resolution: Resolution) -> u64 {
debug_assert!(resolution != Resolution::Zero, "res 0 means no directions");
h3o_bit::clr_direction(bits, resolution.into())
}
pub fn set_direction(bits: u64, cell: u8, resolution: Resolution) -> u64 {
debug_assert!(resolution != Resolution::Zero, "res 0 means no directions");
h3o_bit::set_direction(bits, resolution.into(), cell)
}
#[must_use]
pub fn set_unused(bits: u64, resolution: Resolution) -> u64 {
let unused_end_offset = resolution.direction_offset();
let unused_bits = (1 << unused_end_offset) - 1;
bits | unused_bits
}
#[must_use]
pub fn first_axe(bits: u64) -> Option<NonZeroU8> {
const PREFIX_LENGTH: u32 = DIRECTIONS_MASK.leading_zeros();
let resolution = get_resolution(bits);
if resolution == Resolution::Zero {
return None;
}
let bit_index = (bits & DIRECTIONS_MASK).leading_zeros() - PREFIX_LENGTH;
#[expect(clippy::cast_possible_truncation, reason = "values are in range")]
let resolution = cmp::min(
((bit_index / h3o_bit::DIRECTION_BITSIZE as u32) + 1) as u8,
resolution.into(),
);
NonZeroU8::new(get_direction(bits, Resolution::new_unchecked(resolution)))
}
pub fn rotate60<const CCW: bool>(mut bits: u64, count: usize) -> u64 {
if count == 1 {
for resolution in
Resolution::range(Resolution::One, get_resolution(bits))
{
let direction =
Direction::new_unchecked(get_direction(bits, resolution));
bits = set_direction(
bits,
direction.rotate60_once::<CCW>().into(),
resolution,
);
}
} else {
for resolution in
Resolution::range(Resolution::One, get_resolution(bits))
{
let direction =
Direction::new_unchecked(get_direction(bits, resolution));
bits = set_direction(
bits,
direction.rotate60::<CCW>(count).into(),
resolution,
);
}
}
bits
}
pub fn pentagon_rotate60<const CCW: bool>(mut bits: u64) -> u64 {
let res = get_resolution(bits);
if res == Resolution::Zero {
return bits;
}
let direction = if CCW { Direction::JK } else { Direction::IK }.axe();
if first_axe(bits) == direction {
for resolution in Resolution::range(Resolution::One, res) {
let direction =
Direction::new_unchecked(get_direction(bits, resolution));
bits = set_direction(
bits,
direction.rotate60::<CCW>(2).into(),
resolution,
);
}
} else {
for resolution in Resolution::range(Resolution::One, res) {
let direction =
Direction::new_unchecked(get_direction(bits, resolution));
bits = set_direction(
bits,
direction.rotate60_once::<CCW>().into(),
resolution,
);
}
}
bits
}
#[cfg(test)]
#[path = "./bits_tests.rs"]
mod tests;