use crate::{
error, index::bits, BaseCell, CellIndex, DIRECTION_BITSIZE, NUM_PENTAGONS,
};
use std::{ffi::c_int, fmt, iter::DoubleEndedIterator, str::FromStr};
pub const MAX: u8 = 15;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(u8)]
#[allow(clippy::exhaustive_enums)] #[cfg_attr(
feature = "serde",
derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr)
)]
pub enum Resolution {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Eleven = 11,
Twelve = 12,
Thirteen = 13,
Fourteen = 14,
Fifteen = 15,
}
impl Resolution {
#[must_use]
pub const fn is_class3(self) -> bool {
(self as u8) % 2 == 1
}
#[must_use]
pub fn succ(self) -> Option<Self> {
(self != Self::Fifteen).then(|| Self::new_unchecked(u8::from(self) + 1))
}
#[must_use]
pub fn pred(self) -> Option<Self> {
(self != Self::Zero).then(|| Self::new_unchecked(u8::from(self) - 1))
}
#[allow(unsafe_code)]
pub fn range(
start: Self,
end: Self,
) -> impl Iterator<Item = Self> + DoubleEndedIterator {
(u8::from(start)..=u8::from(end))
.map(|value| unsafe { std::mem::transmute::<u8, Self>(value) })
}
#[must_use]
pub const fn area_rads2(self) -> f64 {
match self {
Self::Zero => 0.10735348936216678,
Self::One => 0.015023219032163554,
Self::Two => 0.002138515704691145,
Self::Three => 0.00030533422843566127,
Self::Four => 4.361565217314979e-5,
Self::Five => 6.230734784621071e-6,
Self::Six => 8.90103480357314e-7,
Self::Seven => 1.2715760961973138e-7,
Self::Eight => 1.8165372181422116e-8,
Self::Nine => 2.5950531560902726e-9,
Self::Ten => 3.707218791825834e-10,
Self::Eleven => 5.2960268449371254e-11,
Self::Twelve => 7.565752635516638e-12,
Self::Thirteen => 1.0808218050716045e-12,
Self::Fourteen => 1.5440311501018426e-13,
Self::Fifteen => 2.205758785859684e-14,
}
}
#[must_use]
pub const fn area_km2(self) -> f64 {
match self {
Self::Zero => 4.357449416078383e6,
Self::One => 6.097884417941332e5,
Self::Two => 8.68017803989972e4,
Self::Three => 1.239343465508816e4,
Self::Four => 1.770347654491307e3,
Self::Five => 2.529038581819449e2,
Self::Six => 3.612906216441245e1,
Self::Seven => 5.161293359717191,
Self::Eight => 7.373275975944177e-1,
Self::Nine => 1.053325134272067e-1,
Self::Ten => 1.504750190766435e-2,
Self::Eleven => 2.149643129451879e-3,
Self::Twelve => 3.07091875631606e-4,
Self::Thirteen => 4.387026794728296e-5,
Self::Fourteen => 6.267181135324313e-6,
Self::Fifteen => 8.95311590760579e-7,
}
}
#[must_use]
pub const fn area_m2(self) -> f64 {
match self {
Self::Zero => 4.35744941607839e12,
Self::One => 6.097884417941339e11,
Self::Two => 8.680178039899731e10,
Self::Three => 1.239343465508818e10,
Self::Four => 1.770347654491309e9,
Self::Five => 2.529038581819452e8,
Self::Six => 3.61290621644125e7,
Self::Seven => 5.161293359717198e6,
Self::Eight => 7.373275975944188e5,
Self::Nine => 1.053325134272069e5,
Self::Ten => 1.504750190766437e4,
Self::Eleven => 2.149643129451882e3,
Self::Twelve => 3.070918756316063e2,
Self::Thirteen => 4.387026794728301e1,
Self::Fourteen => 6.267181135324322,
Self::Fifteen => 8.953115907605802e-1,
}
}
#[must_use]
pub const fn edge_length_rads(self) -> f64 {
match self {
Self::Zero => 0.17386773543713177,
Self::One => 0.06571582696594004,
Self::Two => 0.024838247910621048,
Self::Three => 0.00938797528264242,
Self::Four => 0.0035483211300887216,
Self::Five => 0.0013411393259123903,
Self::Six => 0.000506903018673795,
Self::Seven => 0.00019159133310284987,
Self::Eight => 7.241471731216742e-5,
Self::Nine => 2.7370188582154633e-5,
Self::Ten => 1.0344958831218647e-5,
Self::Eleven => 3.909987901851459e-6,
Self::Twelve => 1.4778708817343719e-6,
Self::Thirteen => 5.587645562011106e-7,
Self::Fourteen => 2.1167375294114534e-7,
Self::Fifteen => 8.000508954480841e-8,
}
}
#[must_use]
pub const fn edge_length_km(self) -> f64 {
match self {
Self::Zero => 1107.712591,
Self::One => 418.6760055,
Self::Two => 158.2446558,
Self::Three => 59.81085794,
Self::Four => 22.6063794,
Self::Five => 8.544408276,
Self::Six => 3.229482772,
Self::Seven => 1.220629759,
Self::Eight => 0.461354684,
Self::Nine => 0.174375668,
Self::Ten => 0.065907807,
Self::Eleven => 0.024910561,
Self::Twelve => 0.009415526,
Self::Thirteen => 0.003559893,
Self::Fourteen => 0.001348575,
Self::Fifteen => 0.000509713,
}
}
#[must_use]
#[allow(clippy::inconsistent_digit_grouping)]
pub const fn edge_length_m(self) -> f64 {
match self {
Self::Zero => 1_107_712.591,
Self::One => 418_676.0055,
Self::Two => 158_244.6558,
Self::Three => 59_810.85794,
Self::Four => 22_606.3794,
Self::Five => 8544.408276,
Self::Six => 3229.482772,
Self::Seven => 1220.629759,
Self::Eight => 461.3546837,
Self::Nine => 174.3756681,
Self::Ten => 65.90780749,
Self::Eleven => 24.9105614,
Self::Twelve => 9.415526211,
Self::Thirteen => 3.559893033,
Self::Fourteen => 1.348574562,
Self::Fifteen => 0.509713273,
}
}
#[must_use]
pub const fn cell_count(self) -> u64 {
match self {
Self::Zero => 122,
Self::One => 842,
Self::Two => 5882,
Self::Three => 41_162,
Self::Four => 288_122,
Self::Five => 2_016_842,
Self::Six => 14_117_882,
Self::Seven => 98_825_162,
Self::Eight => 691_776_122,
Self::Nine => 4_842_432_842,
Self::Ten => 33_897_029_882,
Self::Eleven => 237_279_209_162,
Self::Twelve => 1_660_954_464_122,
Self::Thirteen => 11_626_681_248_842,
Self::Fourteen => 81_386_768_741_882,
Self::Fifteen => 569_707_381_193_162,
}
}
#[must_use]
pub const fn pentagon_count() -> u8 {
NUM_PENTAGONS
}
pub fn pentagons(self) -> impl Iterator<Item = CellIndex> {
const TEMPLATE: u64 = 0x0800_0000_0000_0000;
BaseCell::iter().filter_map(move |base_cell| {
base_cell.is_pentagon().then(|| {
let bits = bits::set_base_cell(TEMPLATE, base_cell.into());
let bits = bits::set_resolution(bits, self);
CellIndex::new_unchecked(bits::set_unused(bits, self))
})
})
}
#[allow(unsafe_code)]
pub(crate) const fn new_unchecked(value: u8) -> Self {
assert!(value <= MAX, "resolution out of range");
unsafe { std::mem::transmute::<u8, Self>(value) }
}
pub(crate) fn direction_mask(self) -> u64 {
debug_assert!(self != Self::Zero, "res 0 means no directions");
0b111 << self.direction_offset()
}
pub(crate) fn direction_offset(self) -> usize {
usize::from(MAX - u8::from(self)) * DIRECTION_BITSIZE
}
}
impl From<Resolution> for usize {
fn from(value: Resolution) -> Self {
u8::from(value).into()
}
}
impl From<Resolution> for u64 {
fn from(value: Resolution) -> Self {
u8::from(value).into()
}
}
impl From<Resolution> for i16 {
fn from(value: Resolution) -> Self {
u8::from(value).into()
}
}
impl From<Resolution> for u8 {
fn from(value: Resolution) -> Self {
value as Self
}
}
impl TryFrom<c_int> for Resolution {
type Error = error::InvalidResolution;
fn try_from(value: c_int) -> Result<Self, Self::Error> {
u8::try_from(value)
.map_err(|_| Self::Error::new(None, "c_int out of range"))
.and_then(TryInto::try_into)
}
}
impl TryFrom<u8> for Resolution {
type Error = error::InvalidResolution;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Zero),
1 => Ok(Self::One),
2 => Ok(Self::Two),
3 => Ok(Self::Three),
4 => Ok(Self::Four),
5 => Ok(Self::Five),
6 => Ok(Self::Six),
7 => Ok(Self::Seven),
8 => Ok(Self::Eight),
9 => Ok(Self::Nine),
10 => Ok(Self::Ten),
11 => Ok(Self::Eleven),
12 => Ok(Self::Twelve),
13 => Ok(Self::Thirteen),
14 => Ok(Self::Fourteen),
15 => Ok(Self::Fifteen),
_ => Err(Self::Error::new(Some(value), "out of range")),
}
}
}
impl FromStr for Resolution {
type Err = error::InvalidResolution;
fn from_str(s: &str) -> Result<Self, Self::Err> {
u8::from_str(s)
.map_err(|_| Self::Err::new(None, "invalid 8-bit number"))
.and_then(Self::try_from)
}
}
impl fmt::Display for Resolution {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", u8::from(*self))
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Resolution {
fn arbitrary(
data: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<Self> {
u8::arbitrary(data).and_then(|byte| {
Self::try_from(byte).map_err(|_| arbitrary::Error::IncorrectFormat)
})
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ExtendedResolution(u8);
impl ExtendedResolution {
pub const fn is_class3(self) -> bool {
self.0 % 2 == 1
}
pub fn down(current: Resolution) -> Self {
Self(u8::from(current) + 1)
}
}
impl From<ExtendedResolution> for usize {
fn from(value: ExtendedResolution) -> Self {
value.0.into()
}
}
impl From<Resolution> for ExtendedResolution {
fn from(value: Resolution) -> Self {
Self(value.into())
}
}