use crate::{h2xy, xy2h, Unsigned, UnsignedBase};
use core::cmp::Ordering;
#[derive(Debug, PartialEq, Eq)]
pub enum OrderError<T: Unsigned> {
InvalidOrder {
order: u8,
the_type: &'static str,
max_order: u8,
},
OrderExceeded {
order: u8,
max_allowed_index: T::Key,
given_index: T::Key,
},
}
impl<T: Unsigned> core::fmt::Display for OrderError<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
OrderError::InvalidOrder {
order,
the_type,
max_order,
} => write!(
f,
"Type {} can at most support order {}, which {} exceeds",
the_type,
max_order,
order,
),
OrderError::OrderExceeded { order, max_allowed_index, given_index } => write!(f, "order {order} can at most index to {max_allowed_index:?}, which {given_index:?} exceeds"),
}
}
}
impl<T: Unsigned> core::error::Error for OrderError<T> {}
#[inline]
pub fn max_order<T: Unsigned>() -> u8 {
(size_of::<T>() << 3) as u8
}
#[inline]
pub fn max_coord<T: Unsigned>(order: u8) -> Result<T, OrderError<T>> {
let max_order = max_order::<T>();
match order.cmp(&max_order) {
Ordering::Greater => Err(OrderError::InvalidOrder {
order,
the_type: core::any::type_name::<T>(),
max_order,
}), Ordering::Equal => Ok(!T::ZERO),
Ordering::Less => Ok((T::from(1) << usize::from(order)) - 1.into()),
}
}
#[inline]
pub fn max_index<T: Unsigned>(order: u8) -> Result<T::Key, OrderError<T>> {
let max_order = max_order::<T>();
match order.cmp(&max_order) {
Ordering::Greater => Err(OrderError::InvalidOrder {
order,
the_type: core::any::type_name::<T>(),
max_order,
}),
Ordering::Equal => Ok(!T::Key::ZERO),
Ordering::Less => Ok((T::Key::from(1) << usize::from(order * 2)) - 1.into()),
}
}
pub fn xy2h_checked<T: Unsigned>(
x: T,
y: T,
order: u8,
) -> Result<<T as Unsigned>::Key, OrderError<T>> {
let max_coord = max_coord(order)?;
if (x | y).into() > max_coord.into() {
Err(OrderError::OrderExceeded {
order,
max_allowed_index: max_coord.into(),
given_index: x.max(y).into(),
})
} else {
Ok(xy2h(x, y, order))
}
}
pub fn h2xy_checked<T: Unsigned>(
h: <T as Unsigned>::Key,
order: u8,
) -> Result<(T, T), OrderError<T>> {
let max_index = max_index(order)?;
if h > max_index {
Err(OrderError::OrderExceeded {
order,
max_allowed_index: max_index,
given_index: h,
})
} else {
Ok(h2xy(h, order))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
assert_eq!(
&OrderError::InvalidOrder::<u8> {
order: 9,
the_type: "u8",
max_order: 8
}
.to_string(),
"Type u8 can at most support order 8, which 9 exceeds"
);
assert_eq!(
&OrderError::OrderExceeded::<u8> {
order: 1,
max_allowed_index: 3,
given_index: 4
}
.to_string(),
"order 1 can at most index to 3, which 4 exceeds"
);
}
#[test]
fn test_invalid_order_xy2h_checked() {
assert_eq!(xy2h_checked(5u8, 10u8, 8), Ok(119));
assert_eq!(
xy2h_checked(5u8, 10u8, 9).unwrap_err(),
OrderError::InvalidOrder {
order: 9,
the_type: &"u8",
max_order: 8
}
);
assert_eq!(
xy2h_checked(5u8, 10u8, 255).unwrap_err(),
OrderError::InvalidOrder {
order: 255,
the_type: &"u8",
max_order: 8
}
);
assert!(xy2h_checked(100u32, 200u32, 32).is_ok());
assert_eq!(
xy2h_checked(100u32, 200u32, 33).unwrap_err(),
OrderError::InvalidOrder {
order: 33,
the_type: &"u32",
max_order: 32
}
);
}
#[test]
fn test_invalid_order_h2xy_checked() {
assert_eq!(h2xy_checked::<u8>(100u16, 8).unwrap(), (4, 14));
assert_eq!(
h2xy_checked::<u8>(100u16, 9).unwrap_err(),
OrderError::InvalidOrder {
order: 9,
the_type: &"u8",
max_order: 8
}
);
assert_eq!(
h2xy_checked::<u8>(100u16, 255).unwrap_err(),
OrderError::InvalidOrder {
order: 255,
the_type: &"u8",
max_order: 8
}
);
assert_eq!(h2xy_checked::<u32>(1000u64, 32).unwrap(), (6, 30));
assert_eq!(
h2xy_checked::<u32>(1000u64, 33).unwrap_err(),
OrderError::InvalidOrder {
order: 33,
the_type: &"u32",
max_order: 32
}
);
}
#[test]
fn test_exceeds_order() {
assert_eq!(h2xy_checked::<u64>(3, 1).unwrap(), (1, 0));
assert_eq!(
h2xy_checked::<u64>(4, 1),
Err(OrderError::OrderExceeded {
order: 1,
max_allowed_index: 3,
given_index: 4
})
);
assert_eq!(xy2h_checked::<u64>(1, 1, 1).unwrap(), 2);
assert_eq!(
xy2h_checked::<u64>(1, 2, 1).unwrap_err(),
OrderError::OrderExceeded {
order: 1,
max_allowed_index: 1,
given_index: 2
}
);
assert_eq!(
xy2h_checked::<u64>(2, 1, 1).unwrap_err(),
OrderError::OrderExceeded {
order: 1,
max_allowed_index: 1,
given_index: 2
}
);
}
#[test]
fn test_edge_cases() {
assert_eq!(
xy2h_checked(u64::MAX, u64::MAX, 64),
Ok(226854911280625642308916404954512140970)
);
assert_eq!(
h2xy_checked::<u64>(u128::MAX, 64).unwrap(),
(18446744073709551615, 0)
);
assert_eq!(xy2h_checked(0u32, 0u32, 0).unwrap(), 0);
assert_eq!(h2xy_checked::<u32>(0u64, 0).unwrap(), (0, 0));
assert!(xy2h_checked(u32::MAX, u32::MAX, u8::MAX).is_err());
assert!(h2xy_checked::<u32>(u64::MAX, u8::MAX).is_err());
}
}