#![allow(clippy::incompatible_msrv)]
pub trait Integer: private::Sealed {}
mod private {
pub trait Sealed {
fn copy(&self) -> Self;
fn eq(self, other: Self) -> bool;
fn lt(self, other: Self) -> bool;
fn checked_ilog(self, base: Self) -> Option<u32>;
fn ilog(self, base: u32) -> u32;
fn checked_ilog10(self) -> Option<u32>;
fn ilog10(self) -> u32;
fn invpow(self, base: u32, exp: u32) -> Self;
}
}
macro_rules! sealed_common {
() => {
fn copy(&self) -> Self {
*self
}
fn eq(self, other: Self) -> bool {
self == other
}
fn lt(self, other: Self) -> bool {
self < other
}
fn checked_ilog(mut self, base: Self) -> Option<u32> {
if base <= 1 {
assert!(base > 0);
return Some(0);
}
let mut x = 0;
while self >= base {
self /= base;
x += 1;
}
Some(x)
}
#[allow(unstable_name_collisions)]
fn ilog(self, base: u32) -> u32 {
if let Some(x) = self.checked_ilog(base as _) {
x
} else {
0
}
}
#[allow(unstable_name_collisions)]
fn ilog10(self) -> u32 {
if let Some(x) = self.checked_ilog10() {
x
} else {
0
}
}
fn invpow(mut self, base: u32, mut exp: u32) -> Self {
if exp == 0 {
return self;
}
let mut base = base as Self;
while exp > 1 {
if (exp & 1) == 1 {
self /= base;
}
exp /= 2;
base = base * base;
}
self / base
}
};
}
impl private::Sealed for u32 {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(mut self) -> Option<u32> {
let x = if self >= 100_000 {
self /= 100_000;
5
} else {
0
};
assert!((!0_u32) / 100_000 <= (!0_u16) as u32);
debug_assert!(self <= (!0_u16) as u32);
Some((self as u16).ilog(10) + x)
}
}
impl private::Sealed for u64 {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(mut self) -> Option<u32> {
let x = if self >= 10_000_000_000 {
self /= 10_000_000_000;
10
} else {
0
};
assert!((!0_u64) / 10_000_000_000 <= (!0_u32) as u64);
debug_assert!(self <= (!0_u32) as u64);
Some((self as u32).ilog(10) + x)
}
}
impl private::Sealed for u128 {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(mut self) -> Option<u32> {
if self >= 100_000_000_000_000_000_000_000_000_000_000 {
self /= 100_000_000_000_000_000_000_000_000_000_000;
assert!((!0_u128) / 100_000_000_000_000_000_000_000_000_000_000 <= (!0_u32) as u128);
debug_assert!(self <= (!0_u32) as u128);
return Some((self as u32).ilog(10) + 32);
}
let x = if self >= 10_000_000_000_000_000 {
self /= 10_000_000_000_000_000;
16
} else {
0
};
assert!(
(100_000_000_000_000_000_000_000_000_000_000 - 1) / 10_000_000_000_000_000
<= (!0_u64) as u128
);
debug_assert!(self <= (!0_u64) as u128);
Some((self as u64).ilog(10) + x)
}
}
macro_rules! generic_ilog10 {
($($ty:ty)*) => {$(
impl private::Sealed for $ty {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(self) -> Option<u32> {
self.checked_ilog(10)
}
}
)*};
}
generic_ilog10! { u8 u16 }
#[cfg(target_pointer_width = "64")]
impl private::Sealed for usize {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(self) -> Option<u32> {
(self as u64).checked_ilog10()
}
}
#[cfg(target_pointer_width = "32")]
impl private::Sealed for usize {
sealed_common!();
#[allow(unstable_name_collisions)]
fn checked_ilog10(self) -> Option<u32> {
(self as u32).checked_ilog10()
}
}
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
generic_ilog10! { usize }
impl Integer for u8 {}
impl Integer for u16 {}
impl Integer for u32 {}
impl Integer for u64 {}
impl Integer for u128 {}
impl Integer for usize {}