#![deny(missing_docs)]
use std::cmp::Ordering;
pub trait NumCmp<Other: Copy>: Copy {
#[cfg(test)] fn num_cmp_strategy(self, other: Other) -> &'static str;
fn num_cmp(self, other: Other) -> Option<Ordering>;
fn num_eq(self, other: Other) -> bool;
fn num_ne(self, other: Other) -> bool;
fn num_lt(self, other: Other) -> bool;
fn num_gt(self, other: Other) -> bool;
fn num_le(self, other: Other) -> bool;
fn num_ge(self, other: Other) -> bool;
}
macro_rules! impl_for_equal_types {
($($ty:ty;)*) => ($(
impl NumCmp<$ty> for $ty {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $ty) -> &'static str {
"strategy 1"
}
#[inline]
fn num_cmp(self, other: $ty) -> Option<Ordering> {
self.partial_cmp(&other)
}
#[inline]
fn num_eq(self, other: $ty) -> bool {
self == other
}
#[inline]
fn num_ne(self, other: $ty) -> bool {
self != other
}
#[inline]
fn num_lt(self, other: $ty) -> bool {
self < other
}
#[inline]
fn num_gt(self, other: $ty) -> bool {
self > other
}
#[inline]
fn num_le(self, other: $ty) -> bool {
self <= other
}
#[inline]
fn num_ge(self, other: $ty) -> bool {
self >= other
}
}
)*);
}
macro_rules! impl_for_size_types {
($($size:ty => $nonsize:ty, $other:ty;)*) => ($(
impl NumCmp<$other> for $size {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $other) -> &'static str {
"strategy 2, size vs other"
}
#[inline]
fn num_cmp(self, other: $other) -> Option<Ordering> {
(self as $nonsize).num_cmp(other)
}
#[inline]
fn num_eq(self, other: $other) -> bool {
(self as $nonsize).num_eq(other)
}
#[inline]
fn num_ne(self, other: $other) -> bool {
(self as $nonsize).num_ne(other)
}
#[inline]
fn num_lt(self, other: $other) -> bool {
(self as $nonsize).num_lt(other)
}
#[inline]
fn num_gt(self, other: $other) -> bool {
(self as $nonsize).num_gt(other)
}
#[inline]
fn num_le(self, other: $other) -> bool {
(self as $nonsize).num_le(other)
}
#[inline]
fn num_ge(self, other: $other) -> bool {
(self as $nonsize).num_ge(other)
}
}
impl NumCmp<$size> for $other {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $size) -> &'static str {
"strategy 2, nonsize vs size"
}
#[inline]
fn num_cmp(self, other: $size) -> Option<Ordering> {
self.num_cmp(other as $nonsize)
}
#[inline]
fn num_eq(self, other: $size) -> bool {
self.num_eq(other as $nonsize)
}
#[inline]
fn num_ne(self, other: $size) -> bool {
self.num_ne(other as $nonsize)
}
#[inline]
fn num_lt(self, other: $size) -> bool {
self.num_lt(other as $nonsize)
}
#[inline]
fn num_gt(self, other: $size) -> bool {
self.num_gt(other as $nonsize)
}
#[inline]
fn num_le(self, other: $size) -> bool {
self.num_le(other as $nonsize)
}
#[inline]
fn num_ge(self, other: $size) -> bool {
self.num_ge(other as $nonsize)
}
}
)*);
}
macro_rules! impl_for_nonequal_types_with_casting {
($($big:ty, $small:ty;)*) => ($(
impl NumCmp<$small> for $big {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $small) -> &'static str {
"strategy 3, big vs small"
}
#[inline]
fn num_cmp(self, other: $small) -> Option<Ordering> {
self.partial_cmp(&(other as $big))
}
#[inline]
fn num_eq(self, other: $small) -> bool {
self == other as $big
}
#[inline]
fn num_ne(self, other: $small) -> bool {
self != other as $big
}
#[inline]
fn num_lt(self, other: $small) -> bool {
self < other as $big
}
#[inline]
fn num_gt(self, other: $small) -> bool {
self > other as $big
}
#[inline]
fn num_le(self, other: $small) -> bool {
self <= other as $big
}
#[inline]
fn num_ge(self, other: $small) -> bool {
self >= other as $big
}
}
impl NumCmp<$big> for $small {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $big) -> &'static str {
"strategy 3, small vs big"
}
#[inline]
fn num_cmp(self, other: $big) -> Option<Ordering> {
(self as $big).partial_cmp(&other)
}
#[inline]
fn num_eq(self, other: $big) -> bool {
self as $big == other
}
#[inline]
fn num_ne(self, other: $big) -> bool {
self as $big != other
}
#[inline]
fn num_lt(self, other: $big) -> bool {
(self as $big) < other
}
#[inline]
fn num_gt(self, other: $big) -> bool {
self as $big > other
}
#[inline]
fn num_le(self, other: $big) -> bool {
self as $big <= other
}
#[inline]
fn num_ge(self, other: $big) -> bool {
self as $big >= other
}
}
)*);
}
macro_rules! impl_for_nonequal_types_with_different_signedness {
($($unsigned:ty, $signed:ty;)*) => ($(
impl NumCmp<$signed> for $unsigned {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $signed) -> &'static str {
"strategy 4, unsigned vs signed"
}
#[inline]
fn num_cmp(self, other: $signed) -> Option<Ordering> {
if other < 0 {
Some(Ordering::Greater)
} else {
self.partial_cmp(&(other as $unsigned))
}
}
#[inline]
fn num_eq(self, other: $signed) -> bool {
other >= 0 && self == other as $unsigned
}
#[inline]
fn num_ne(self, other: $signed) -> bool {
other < 0 || self != other as $unsigned
}
#[inline]
fn num_lt(self, other: $signed) -> bool {
other > 0 && self < other as $unsigned
}
#[inline]
fn num_gt(self, other: $signed) -> bool {
other < 0 || self > other as $unsigned
}
#[inline]
fn num_le(self, other: $signed) -> bool {
other >= 0 && self <= other as $unsigned
}
#[inline]
fn num_ge(self, other: $signed) -> bool {
other <= 0 || self >= other as $unsigned
}
}
impl NumCmp<$unsigned> for $signed {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $unsigned) -> &'static str {
"strategy 4, signed vs unsigned"
}
#[inline]
fn num_cmp(self, other: $unsigned) -> Option<Ordering> {
if self < 0 {
Some(Ordering::Less)
} else {
(self as $unsigned).partial_cmp(&other)
}
}
#[inline]
fn num_eq(self, other: $unsigned) -> bool {
self >= 0 && self as $unsigned == other
}
#[inline]
fn num_ne(self, other: $unsigned) -> bool {
self < 0 || self as $unsigned != other
}
#[inline]
fn num_lt(self, other: $unsigned) -> bool {
self < 0 || (self as $unsigned) < other
}
#[inline]
fn num_gt(self, other: $unsigned) -> bool {
self > 0 && self as $unsigned > other
}
#[inline]
fn num_le(self, other: $unsigned) -> bool {
self <= 0 || self as $unsigned <= other
}
#[inline]
fn num_ge(self, other: $unsigned) -> bool {
self >= 0 && self as $unsigned >= other
}
}
)*);
}
macro_rules! trunc_cmp {
(int $lhs:expr, $method:ident, float $rhs:expr;
($lb:expr) <= ($intty:ty) < ($ub:expr)) => ({
let rhsint = $rhs.trunc();
debug_assert!($lb <= rhsint && rhsint < $ub);
($lhs, rhsint).$method(&(rhsint as $intty, $rhs))
});
(float $lhs:expr, $method:ident, int $rhs:expr;
($lb:expr) <= ($intty:ty) < ($ub:expr)) => ({
let lhsint = $lhs.trunc();
debug_assert!($lb <= lhsint && lhsint < $ub);
(lhsint as $intty, $lhs).$method(&($rhs, lhsint))
});
}
macro_rules! impl_for_int_and_float_types_with_bounds_check {
($($float:ty, $int:ty, ($lb:expr) <= _ < ($ub:expr);)*) => ($(
impl NumCmp<$int> for $float {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $int) -> &'static str {
"strategy 5, float vs int"
}
#[inline]
fn num_cmp(self, other: $int) -> Option<Ordering> {
if self < $lb {
Some(Ordering::Less)
} else if $ub <= self {
Some(Ordering::Greater)
} else if self == self {
trunc_cmp!(float self, partial_cmp, int other; ($lb) <= ($int) < ($ub))
} else {
None
}
}
#[inline]
fn num_eq(self, other: $int) -> bool {
$lb <= self && self < $ub && trunc_cmp!(float self, eq, int other;
($lb) <= ($int) < ($ub))
}
#[inline]
fn num_ne(self, other: $int) -> bool {
!($lb <= self && self < $ub) || trunc_cmp!(float self, ne, int other;
($lb) <= ($int) < ($ub))
}
#[inline]
fn num_lt(self, other: $int) -> bool {
self < $ub && (self < $lb || trunc_cmp!(float self, lt, int other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_gt(self, other: $int) -> bool {
$lb <= self && ($ub <= self || trunc_cmp!(float self, gt, int other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_le(self, other: $int) -> bool {
self < $ub && (self < $lb || trunc_cmp!(float self, le, int other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_ge(self, other: $int) -> bool {
$lb <= self && ($ub <= self || trunc_cmp!(float self, ge, int other;
($lb) <= ($int) < ($ub)))
}
}
impl NumCmp<$float> for $int {
#[cfg(test)]
fn num_cmp_strategy(self, _other: $float) -> &'static str {
"strategy 5, int vs float"
}
#[inline]
fn num_cmp(self, other: $float) -> Option<Ordering> {
if other < $lb {
Some(Ordering::Greater)
} else if $ub <= other {
Some(Ordering::Less)
} else if other == other {
trunc_cmp!(int self, partial_cmp, float other; ($lb) <= ($int) < ($ub))
} else {
None
}
}
#[inline]
fn num_eq(self, other: $float) -> bool {
$lb <= other && other < $ub && trunc_cmp!(int self, eq, float other;
($lb) <= ($int) < ($ub))
}
#[inline]
fn num_ne(self, other: $float) -> bool {
!($lb <= other && other < $ub) || trunc_cmp!(int self, ne, float other;
($lb) <= ($int) < ($ub))
}
#[inline]
fn num_lt(self, other: $float) -> bool {
$lb <= other && ($ub <= other || trunc_cmp!(int self, lt, float other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_gt(self, other: $float) -> bool {
other < $ub && (other < $lb || trunc_cmp!(int self, gt, float other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_le(self, other: $float) -> bool {
$lb <= other && ($ub <= other || trunc_cmp!(int self, le, float other;
($lb) <= ($int) < ($ub)))
}
#[inline]
fn num_ge(self, other: $float) -> bool {
other < $ub && (other < $lb || trunc_cmp!(int self, ge, float other;
($lb) <= ($int) < ($ub)))
}
}
)*);
}
impl_for_equal_types! {
u8; u16; u32; u64; usize;
i8; i16; i32; i64; isize;
f32; f64;
u128;
i128;
}
#[cfg(target_pointer_width = "32")]
impl_for_size_types! {
usize => u32, u8; isize => i32, u8;
usize => u32, u16; isize => i32, u16;
usize => u32, u32; isize => i32, u32;
usize => u32, u64; isize => i32, u64;
usize => u32, i8; isize => i32, i8;
usize => u32, i16; isize => i32, i16;
usize => u32, i32; isize => i32, i32;
usize => u32, i64; isize => i32, i64;
usize => u32, f32; isize => i32, f32;
usize => u32, f64; isize => i32, f64;
usize => u32, u128; isize => i32, u128;
usize => u32, i128; isize => i32, i128;
}
#[cfg(target_pointer_width = "64")]
impl_for_size_types! {
usize => u64, u8; isize => i64, u8;
usize => u64, u16; isize => i64, u16;
usize => u64, u32; isize => i64, u32;
usize => u64, u64; isize => i64, u64;
usize => u64, i8; isize => i64, i8;
usize => u64, i16; isize => i64, i16;
usize => u64, i32; isize => i64, i32;
usize => u64, i64; isize => i64, i64;
usize => u64, f32; isize => i64, f32;
usize => u64, f64; isize => i64, f64;
usize => u64, u128; isize => i64, u128;
usize => u64, i128; isize => i64, i128;
}
impl_for_nonequal_types_with_casting! {
u64, u8; u32, u8; u16, u8;
u64, u16; u32, u16;
u64, u32;
i64, i8; i32, i8; i16, i8;
i64, i16; i32, i16;
i64, i32;
i64, u8; i32, u8; i16, u8;
i64, u16; i32, u16;
i64, u32;
f64, f32;
f32, u8; f32, u16;
f64, u8; f64, u16; f64, u32;
f32, i8; f32, i16;
f64, i8; f64, i16; f64, i32;
u128, u8; u128, u16; u128, u32; u128, u64;
i128, u8; i128, u16; i128, u32; i128, u64;
i128, i8; i128, i16; i128, i32; i128, i64;
}
impl_for_nonequal_types_with_different_signedness! {
u64, i8; u32, i8; u16, i8; u8, i8;
u64, i16; u32, i16; u16, i16;
u64, i32; u32, i32;
u64, i64;
usize, isize;
u128, i8;
u128, i16;
u128, i32;
u128, i64;
u128, i128;
}
const U32_BOUND_IN_F32: f32 = 4294967296.0;
const I32_BOUND_IN_F32: f32 = 2147483648.0;
const U64_BOUND_IN_F32: f32 = 18446744073709551616.0;
const U64_BOUND_IN_F64: f64 = 18446744073709551616.0;
const I64_BOUND_IN_F32: f32 = 9223372036854775808.0;
const I64_BOUND_IN_F64: f64 = 9223372036854775808.0;
const U128_BOUND_IN_F32: f32 = std::f32::INFINITY;
const U128_BOUND_IN_F64: f64 = 340282366920938463463374607431768211456.0;
const I128_BOUND_IN_F32: f32 = 170141183460469231731687303715884105728.0;
const I128_BOUND_IN_F64: f64 = 170141183460469231731687303715884105728.0;
impl_for_int_and_float_types_with_bounds_check! {
f32, u32, (0.0) <= _ < (U32_BOUND_IN_F32);
f32, u64, (0.0) <= _ < (U64_BOUND_IN_F32);
f64, u64, (0.0) <= _ < (U64_BOUND_IN_F64);
f32, i32, (-I32_BOUND_IN_F32) <= _ < (I32_BOUND_IN_F32);
f32, i64, (-I64_BOUND_IN_F32) <= _ < (I64_BOUND_IN_F32);
f64, i64, (-I64_BOUND_IN_F64) <= _ < (I64_BOUND_IN_F64);
f32, u128, (0.0) <= _ < (U128_BOUND_IN_F32);
f64, u128, (0.0) <= _ < (U128_BOUND_IN_F64);
f32, i128, (-I128_BOUND_IN_F32) <= _ < (I128_BOUND_IN_F32);
f64, i128, (-I128_BOUND_IN_F64) <= _ < (I128_BOUND_IN_F64);
}
#[cfg(test)] mod tests;