use formualizer_common::Coord as AbsCoord;
use std::fmt;
const RESERVED_HIGH_MASK: u64 = 0xFFFFF000_00000000;
const SYMBOL_TAG: u64 = 1 << 63;
const SYMBOL_PAYLOAD_MASK: u64 = !RESERVED_HIGH_MASK;
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GridAddr(AbsCoord);
impl Default for GridAddr {
#[inline]
fn default() -> Self {
Self::new(0, 0)
}
}
impl Ord for GridAddr {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.row(), self.col()).cmp(&(other.row(), other.col()))
}
}
impl PartialOrd for GridAddr {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl GridAddr {
#[inline]
pub fn new(row: u32, col: u32) -> Self {
Self(AbsCoord::new(row, col))
}
#[inline]
pub const fn from_coord(coord: AbsCoord) -> Self {
Self(coord)
}
#[inline]
pub const fn coord(self) -> AbsCoord {
self.0
}
#[inline]
pub fn row(self) -> u32 {
self.0.row()
}
#[inline]
pub fn col(self) -> u32 {
self.0.col()
}
}
impl From<AbsCoord> for GridAddr {
#[inline]
fn from(coord: AbsCoord) -> Self {
Self(coord)
}
}
impl From<GridAddr> for AbsCoord {
#[inline]
fn from(addr: GridAddr) -> Self {
addr.0
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SymbolAddr(u32);
impl SymbolAddr {
#[inline]
pub const fn new(index: u32) -> Self {
Self(index)
}
#[inline]
pub const fn index(self) -> u32 {
self.0
}
}
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct VertexAddr(u64);
impl VertexAddr {
pub const INVALID: Self = Self(u64::MAX);
#[inline]
pub fn grid(addr: GridAddr) -> Self {
Self(addr.0.as_u64())
}
#[inline]
pub const fn symbol(addr: SymbolAddr) -> Self {
Self(SYMBOL_TAG | (addr.0 as u64))
}
#[inline]
pub fn as_grid(self) -> Option<GridAddr> {
(self.0 & RESERVED_HIGH_MASK == 0).then(|| GridAddr(unsafe_coord_from_raw(self.0)))
}
#[inline]
pub fn as_symbol(self) -> Option<SymbolAddr> {
(self.0 & RESERVED_HIGH_MASK == SYMBOL_TAG)
.then_some(SymbolAddr((self.0 & SYMBOL_PAYLOAD_MASK) as u32))
}
#[inline]
pub fn is_symbol(self) -> bool {
self.0 & RESERVED_HIGH_MASK == SYMBOL_TAG
}
#[inline]
pub fn is_grid(self) -> bool {
self.0 & RESERVED_HIGH_MASK == 0
}
#[inline]
pub const fn as_u64(self) -> u64 {
self.0
}
#[inline]
pub fn order_key(self) -> (u32, u32) {
match (self.as_grid(), self.as_symbol()) {
(Some(grid), _) => (grid.row(), grid.col()),
(_, Some(symbol)) => (u32::MAX, symbol.index()),
_ => (u32::MAX, u32::MAX),
}
}
}
#[inline]
fn unsafe_coord_from_raw(raw: u64) -> AbsCoord {
debug_assert!(raw & RESERVED_HIGH_MASK == 0);
AbsCoord::from_raw(raw).unwrap_or_else(|_| AbsCoord::new(0, 0))
}
impl From<GridAddr> for VertexAddr {
#[inline]
fn from(addr: GridAddr) -> Self {
Self::grid(addr)
}
}
impl From<SymbolAddr> for VertexAddr {
#[inline]
fn from(addr: SymbolAddr) -> Self {
Self::symbol(addr)
}
}
impl From<AbsCoord> for VertexAddr {
#[inline]
fn from(coord: AbsCoord) -> Self {
Self::grid(GridAddr(coord))
}
}
impl Default for VertexAddr {
#[inline]
fn default() -> Self {
Self::grid(GridAddr::default())
}
}
impl fmt::Debug for VertexAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(grid) = self.as_grid() {
write!(f, "Grid(r{}, c{})", grid.row(), grid.col())
} else if let Some(symbol) = self.as_symbol() {
write!(f, "Symbol({})", symbol.index())
} else {
write!(f, "VertexAddr::INVALID")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vertex_addr_is_eight_bytes() {
assert_eq!(std::mem::size_of::<VertexAddr>(), 8);
assert_eq!(std::mem::size_of::<VertexAddr>(), size_of::<AbsCoord>());
assert_eq!(std::mem::align_of::<VertexAddr>(), align_of::<AbsCoord>());
assert_eq!(size_of::<GridAddr>(), 8);
}
#[test]
fn grid_addresses_round_trip_and_are_never_symbols() {
for (row, col) in [(0, 0), (1, 1), (1_048_575, 16_383), (7, 0), (0, 16_383)] {
let addr = VertexAddr::grid(GridAddr::new(row, col));
assert!(addr.is_grid());
assert!(!addr.is_symbol());
assert_eq!(addr.as_symbol(), None);
let grid = addr.as_grid().expect("grid address must decode");
assert_eq!((grid.row(), grid.col()), (row, col));
assert_eq!(addr.order_key(), (row, col));
}
}
#[test]
fn symbol_addresses_round_trip_and_are_never_grid() {
for index in [0u32, 1, 16_384, u32::MAX] {
let addr = VertexAddr::symbol(SymbolAddr::new(index));
assert!(addr.is_symbol());
assert!(!addr.is_grid());
assert_eq!(addr.as_grid(), None);
assert_eq!(addr.as_symbol(), Some(SymbolAddr::new(index)));
assert_eq!(addr.order_key(), (u32::MAX, index));
}
}
#[test]
fn invalid_sentinel_is_neither_grid_nor_symbol() {
assert!(!VertexAddr::INVALID.is_grid());
assert!(!VertexAddr::INVALID.is_symbol());
assert_eq!(VertexAddr::INVALID.as_grid(), None);
assert_eq!(VertexAddr::INVALID.as_symbol(), None);
}
#[test]
fn symbols_order_after_every_grid_position() {
let last_cell = VertexAddr::grid(GridAddr::new(1_048_575, 16_383));
let first_symbol = VertexAddr::symbol(SymbolAddr::new(0));
assert!(last_cell.order_key() < first_symbol.order_key());
}
}