#![deny(missing_debug_implementations, missing_docs, warnings)]
#![cfg_attr(not(feature = "std"), no_std)]
use core::{
convert::{From, TryFrom},
fmt::{self, Debug, Display, Formatter},
num::TryFromIntError as StdTryFromIntError,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
};
#[cfg(feature = "serde")]
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
pub const MAX_SAFE_INT: i64 = 0x001F_FFFF_FFFF_FFFF;
pub const MIN_SAFE_INT: i64 = -MAX_SAFE_INT;
pub const MAX_SAFE_UINT: u64 = 0x001F_FFFF_FFFF_FFFF;
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Int(i64);
impl Int {
pub fn new(val: i64) -> Option<Self> {
if val >= MIN_SAFE_INT && val <= MAX_SAFE_INT {
Some(Self(val))
} else {
None
}
}
fn new_saturating(val: i64) -> Self {
if val < MIN_SAFE_INT {
Self::min_value()
} else if val > MAX_SAFE_INT {
Self::max_value()
} else {
Self(val)
}
}
pub const fn min_value() -> Self {
Self(MIN_SAFE_INT)
}
pub const fn max_value() -> Self {
Self(MAX_SAFE_INT)
}
pub fn abs(self) -> Self {
Self(self.0.abs())
}
pub const fn is_positive(self) -> bool {
self.0.is_positive()
}
pub const fn is_negative(self) -> bool {
self.0.is_negative()
}
}
macro_rules! int_op_impl {
($trait:ident, $method:ident, $assign_trait:ident, $assign_method:ident) => {
impl $trait for Int {
type Output = Self;
fn $method(self, rhs: Self) -> Self {
let result = <i64 as $trait>::$method(self.0, rhs.0);
assert!(result >= MIN_SAFE_INT);
assert!(result <= MAX_SAFE_INT);
Self(result)
}
}
impl $assign_trait for Int {
fn $assign_method(&mut self, other: Self) {
let result = <i64 as $trait>::$method(self.0, other.0);
assert!(result >= MIN_SAFE_INT);
assert!(result <= MAX_SAFE_INT);
*self = Self(result);
}
}
};
}
int_op_impl!(Add, add, AddAssign, add_assign);
int_op_impl!(Sub, sub, SubAssign, sub_assign);
int_op_impl!(Mul, mul, MulAssign, mul_assign);
int_op_impl!(Div, div, DivAssign, div_assign);
int_op_impl!(Rem, rem, RemAssign, rem_assign);
impl Neg for Int {
type Output = Self;
fn neg(self) -> Self {
Self(-self.0)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Int {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct IntVisitor;
impl<'de> Visitor<'de> for IntVisitor {
type Value = Int;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a signed integer between -(2**53) + 1 and (2**53) - 1")
}
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::from(value))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Int::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
}
deserializer.deserialize_any(IntVisitor)
}
}
#[derive(Clone, Copy, Default, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct UInt(u64);
impl UInt {
pub fn new(val: u64) -> Option<Self> {
if val <= MAX_SAFE_UINT {
Some(Self(val))
} else {
None
}
}
pub fn new_wrapping(val: u64) -> Self {
Self(val & MAX_SAFE_UINT)
}
fn new_saturating(val: u64) -> Self {
if val <= MAX_SAFE_UINT {
Self(val)
} else {
Self::max_value()
}
}
pub const fn min_value() -> Self {
Self(0)
}
pub const fn max_value() -> Self {
Self(MAX_SAFE_UINT)
}
pub fn is_power_of_two(self) -> bool {
self.0.is_power_of_two()
}
pub fn checked_next_power_of_two(self) -> Option<Self> {
self.0.checked_next_power_of_two().and_then(Self::new)
}
}
macro_rules! uint_op_impl {
($trait:ident, $method:ident, $assign_trait:ident, $assign_method:ident) => {
impl $trait for UInt {
type Output = Self;
fn $method(self, rhs: Self) -> Self {
let result = <u64 as $trait>::$method(self.0, rhs.0);
if cfg!(debug_assertions) {
assert!(result <= MAX_SAFE_UINT);
Self(result)
} else {
Self::new_wrapping(result)
}
}
}
impl $assign_trait for UInt {
fn $assign_method(&mut self, other: Self) {
let result = <u64 as $trait>::$method(self.0, other.0);
if cfg!(debug_assertions) {
assert!(result <= MAX_SAFE_UINT);
*self = Self(result);
} else {
*self = Self::new_wrapping(result);
}
}
}
};
}
uint_op_impl!(Add, add, AddAssign, add_assign);
uint_op_impl!(Sub, sub, SubAssign, sub_assign);
uint_op_impl!(Mul, mul, MulAssign, mul_assign);
uint_op_impl!(Div, div, DivAssign, div_assign);
uint_op_impl!(Rem, rem, RemAssign, rem_assign);
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for UInt {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct UIntVisitor;
impl<'de> Visitor<'de> for UIntVisitor {
type Value = UInt;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("an unsigned integer between 0 and (2**53) - 1")
}
fn visit_i8<E>(self, value: i8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
fn visit_i16<E>(self, value: i16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
fn visit_i32<E>(self, value: i32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
fn visit_u8<E>(self, value: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::from(value))
}
fn visit_u16<E>(self, value: u16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::from(value))
}
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::from(value))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(UInt::try_from(value).map_err(|_| E::custom("out of bounds"))?)
}
}
deserializer.deserialize_any(UIntVisitor)
}
}
macro_rules! fmt_impls {
($type:ident) => {
impl Display for $type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Debug for $type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
};
}
fmt_impls!(Int);
fmt_impls!(UInt);
macro_rules! common_methods {
($type:ident) => {
impl $type {
pub fn checked_add(self, rhs: Self) -> Option<Self> {
self.0.checked_add(rhs.0).and_then(Self::new)
}
pub fn checked_sub(self, rhs: Self) -> Option<Self> {
self.0.checked_sub(rhs.0).and_then(Self::new)
}
pub fn checked_mul(self, rhs: Self) -> Option<Self> {
self.0.checked_mul(rhs.0).and_then(Self::new)
}
pub fn checked_div(self, rhs: Self) -> Option<Self> {
self.0.checked_div(rhs.0).and_then(Self::new)
}
pub fn checked_rem(self, rhs: Self) -> Option<Self> {
self.0.checked_rem(rhs.0).and_then(Self::new)
}
pub fn checked_pow(self, exp: u32) -> Option<Self> {
self.0.checked_pow(exp).and_then(Self::new)
}
pub fn saturating_add(self, rhs: Self) -> Self {
self.checked_add(rhs).unwrap_or_else(Self::max_value)
}
pub fn saturating_sub(self, rhs: Self) -> Self {
self.checked_sub(rhs).unwrap_or_else(Self::min_value)
}
pub fn saturating_mul(self, rhs: Self) -> Self {
self.checked_mul(rhs).unwrap_or_else(Self::max_value)
}
pub fn saturating_pow(self, exp: u32) -> Self {
Self::new_saturating(self.0.saturating_pow(exp))
}
}
};
}
common_methods!(Int);
common_methods!(UInt);
#[derive(Clone)]
pub struct TryFromIntError {
_private: (),
}
impl TryFromIntError {
fn new() -> Self {
Self { _private: () }
}
}
impl Display for TryFromIntError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("out of range integral type conversion attempted")
}
}
impl Debug for TryFromIntError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("TryFromIntError")
}
}
#[cfg(feature = "std")]
impl std::error::Error for TryFromIntError {}
macro_rules! convert_impls {
($type:ident, $t8:ident, $t16:ident, $t32:ident, $t64:ident) => {
impl From<$t8> for $type {
fn from(val: $t8) -> Self {
Self($t64::from(val))
}
}
impl From<$t16> for $type {
fn from(val: $t16) -> Self {
Self($t64::from(val))
}
}
impl From<$t32> for $type {
fn from(val: $t32) -> Self {
Self($t64::from(val))
}
}
impl TryFrom<$t64> for $type {
type Error = TryFromIntError;
fn try_from(val: $t64) -> Result<Self, TryFromIntError> {
Self::new(val).ok_or_else(TryFromIntError::new)
}
}
impl TryFrom<$type> for $t8 {
type Error = StdTryFromIntError;
fn try_from(val: $type) -> Result<Self, StdTryFromIntError> {
Self::try_from(val.0)
}
}
impl TryFrom<$type> for $t16 {
type Error = StdTryFromIntError;
fn try_from(val: $type) -> Result<Self, StdTryFromIntError> {
Self::try_from(val.0)
}
}
impl TryFrom<$type> for $t32 {
type Error = StdTryFromIntError;
fn try_from(val: $type) -> Result<Self, StdTryFromIntError> {
Self::try_from(val.0)
}
}
impl From<$type> for $t64 {
fn from(val: $type) -> Self {
val.0
}
}
impl From<$type> for f64 {
fn from(val: $type) -> Self {
val.0 as f64
}
}
};
}
convert_impls!(Int, i8, i16, i32, i64);
convert_impls!(UInt, u8, u16, u32, u64);
impl From<u8> for Int {
fn from(val: u8) -> Self {
Self(i64::from(val))
}
}
impl From<u16> for Int {
fn from(val: u16) -> Self {
Self(i64::from(val))
}
}
impl From<u32> for Int {
fn from(val: u32) -> Self {
Self(i64::from(val))
}
}
impl TryFrom<u64> for Int {
type Error = TryFromIntError;
fn try_from(val: u64) -> Result<Self, TryFromIntError> {
if val <= MAX_SAFE_UINT {
Ok(Self(val as i64))
} else {
Err(TryFromIntError::new())
}
}
}
impl TryFrom<i8> for UInt {
type Error = TryFromIntError;
fn try_from(val: i8) -> Result<Self, TryFromIntError> {
if val >= 0 {
Ok(Self(val as u64))
} else {
Err(TryFromIntError::new())
}
}
}
impl TryFrom<i16> for UInt {
type Error = TryFromIntError;
fn try_from(val: i16) -> Result<Self, TryFromIntError> {
if val >= 0 {
Ok(Self(val as u64))
} else {
Err(TryFromIntError::new())
}
}
}
impl TryFrom<i32> for UInt {
type Error = TryFromIntError;
fn try_from(val: i32) -> Result<Self, TryFromIntError> {
if val >= 0 {
Ok(Self(val as u64))
} else {
Err(TryFromIntError::new())
}
}
}
impl TryFrom<i64> for UInt {
type Error = TryFromIntError;
fn try_from(val: i64) -> Result<Self, TryFromIntError> {
if val >= 0 && val <= MAX_SAFE_INT {
Ok(Self(val as u64))
} else {
Err(TryFromIntError::new())
}
}
}
#[cfg(test)]
mod tests {
use super::{Int, UInt, MAX_SAFE_INT, MAX_SAFE_UINT, MIN_SAFE_INT};
#[test]
fn limits() {
assert_eq!(MAX_SAFE_INT, 9_007_199_254_740_991);
assert_eq!(MIN_SAFE_INT, -9_007_199_254_740_991);
assert_eq!(MAX_SAFE_UINT, 9_007_199_254_740_991);
assert_eq!(f64::from(Int::min_value()) as i64, MIN_SAFE_INT);
assert_eq!(f64::from(Int::max_value()) as i64, MAX_SAFE_INT);
assert_eq!(f64::from(UInt::max_value()) as u64, MAX_SAFE_UINT);
}
#[test]
#[should_panic]
fn int_underflow_panic() {
let _ = Int::min_value() - Int::from(1);
}
#[test]
#[should_panic]
fn int_overflow_panic() {
let _ = Int::max_value() + Int::from(1);
}
#[test]
fn uint_wrapping_new() {
assert_eq!(UInt::new_wrapping(MAX_SAFE_UINT + 1), UInt::from(0u32));
}
#[test]
#[cfg_attr(debug_assertions, ignore)]
fn uint_underflow_wrap() {
assert_eq!(UInt::from(0u32) - UInt::from(1u32), UInt::max_value());
}
#[test]
#[cfg_attr(debug_assertions, ignore)]
fn uint_overflow_wrap() {
assert_eq!(UInt::max_value() + UInt::from(1u32), UInt::from(0u32));
assert_eq!(UInt::max_value() + UInt::from(5u32), UInt::from(4u32));
}
#[test]
#[should_panic]
#[cfg_attr(not(debug_assertions), ignore)]
fn uint_underflow_panic() {
let _ = UInt::from(0u32) - UInt::from(1u32);
}
#[test]
#[should_panic]
#[cfg_attr(not(debug_assertions), ignore)]
fn uint_overflow_panic() {
let _ = UInt::max_value() + UInt::from(1u32);
}
}