use super::total_float::TotalFloat;
use crate::Integer;
#[cfg(feature = "from_slice")]
use crate::RangeSetBlaze;
use core::{
cmp::Ordering,
fmt::Debug,
hash::{Hash, Hasher},
mem,
ops::RangeInclusive,
slice::from_raw_parts,
};
pub type TotalF64 = Total<f64>;
pub type TotalF32 = Total<f32>;
#[cfg(feature = "float_nightly_experimental")]
pub type TotalF16 = Total<f16>;
#[cfg(feature = "float_nightly_experimental")]
pub type TotalF128 = Total<f128>;
#[must_use]
pub const fn tf64(x: f64) -> TotalF64 {
TotalF64::new(x)
}
#[must_use]
pub const fn tf32(x: f32) -> TotalF32 {
TotalF32::new(x)
}
#[cfg(feature = "float_nightly_experimental")]
#[must_use]
pub const fn tf16(x: f16) -> TotalF16 {
TotalF16::new(x)
}
#[cfg(feature = "float_nightly_experimental")]
#[must_use]
pub const fn tf128(x: f128) -> TotalF128 {
TotalF128::new(x)
}
#[repr(transparent)]
#[derive(Copy, Clone, Default, Debug)]
pub struct Total<T: TotalFloat>(T);
impl<T: TotalFloat> Total<T> {
pub const MIN: Self = Self(T::MIN);
pub const MAX: Self = Self(T::MAX);
pub const MAX_SIZE: T::SafeLen = T::MAX_SIZE;
#[must_use]
pub const fn new(x: T) -> Self {
Self(x)
}
#[must_use]
pub fn inclusive_end_from_start(self, b: T::SafeLen) -> Self {
Self(T::inclusive_end_from_start(self.0, b))
}
#[must_use]
pub fn start_from_inclusive_end(self, b: T::SafeLen) -> Self {
Self(T::start_from_inclusive_end(self.0, b))
}
#[must_use]
pub const fn into_inner(self) -> T {
self.0
}
#[must_use]
pub fn after(self) -> Self {
debug_assert!(self != Self::MAX, "after() called on maximum value");
Self(T::after(self.0))
}
#[must_use]
pub fn before(self) -> Self {
debug_assert!(self != Self::MIN, "before() called on minimum value");
Self(T::before(self.0))
}
#[must_use]
pub fn checked_after(self) -> Option<Self> {
if self == Self::MAX {
None
} else {
Some(self.after())
}
}
#[must_use]
pub fn checked_before(self) -> Option<Self> {
if self == Self::MIN {
None
} else {
Some(self.before())
}
}
#[must_use]
pub fn from_primitive_range(range: RangeInclusive<T>) -> RangeInclusive<Self> {
let (start, end) = range.into_inner();
Self(start)..=Self(end)
}
pub fn from_primitive_ranges<I>(ranges: I) -> impl Iterator<Item = RangeInclusive<Self>>
where
I: IntoIterator<Item = RangeInclusive<T>>,
{
ranges.into_iter().map(Self::from_primitive_range)
}
pub fn values<I>(values: I) -> impl Iterator<Item = Self>
where
I: IntoIterator<Item = T>,
{
values.into_iter().map(Self)
}
#[must_use]
pub const fn from_primitive_slice(values: &[T]) -> &[Self] {
unsafe { mem::transmute::<&[T], &[Self]>(values) }
}
}
pub trait TotalSliceExt<T: TotalFloat> {
fn as_primitive_slice(&self) -> &[T];
}
impl<T: TotalFloat> TotalSliceExt<T> for [Total<T>] {
fn as_primitive_slice(&self) -> &[T] {
unsafe { from_raw_parts(self.as_ptr().cast::<T>(), self.len()) }
}
}
pub trait TotalRangeExt<T: TotalFloat> {
#[must_use]
fn into_primitive_range(self) -> RangeInclusive<T>;
#[must_use]
fn into_primitive_inner(self) -> (T, T);
}
impl<T: TotalFloat> TotalRangeExt<T> for RangeInclusive<Total<T>> {
fn into_primitive_range(self) -> RangeInclusive<T> {
let (start, end) = self.into_primitive_inner();
start..=end
}
fn into_primitive_inner(self) -> (T, T) {
let (start, end) = self.into_inner();
(start.into_inner(), end.into_inner())
}
}
impl<T: TotalFloat> PartialEq for Total<T> {
fn eq(&self, other: &Self) -> bool {
T::total_cmp(self.0, other.0) == Ordering::Equal
}
}
impl<T: TotalFloat> Eq for Total<T> {}
impl<T: TotalFloat> PartialOrd for Total<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: TotalFloat> Ord for Total<T> {
fn cmp(&self, other: &Self) -> Ordering {
T::total_cmp(self.0, other.0)
}
}
impl<T: TotalFloat> Hash for Total<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
T::hash(self.0, state);
}
}
impl<T: TotalFloat> Integer for Total<T> {
type SafeLen = T::SafeLen;
#[inline]
fn checked_add_one(self) -> Option<Self> {
self.checked_after()
}
#[inline]
fn add_one(self) -> Self {
self.after()
}
#[inline]
fn sub_one(self) -> Self {
self.before()
}
#[inline]
fn assign_sub_one(&mut self) {
*self = self.before();
}
#[inline]
fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
if range.is_empty() {
None
} else if range.start() == range.end() && *range.start() == Self::MAX {
let next = *range.start();
*range = next..=range.end().before();
Some(next)
} else {
let next = *range.start();
*range = (next.after())..=*range.end();
Some(next)
}
}
#[inline]
fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
if range.is_empty() {
None
} else if range.start() == range.end() && *range.start() == Self::MIN {
let last = *range.end();
*range = last.after()..=last;
Some(last)
} else {
let last = *range.end();
*range = *range.start()..=last.before();
Some(last)
}
}
#[inline]
fn min_value() -> Self {
Self::MIN
}
#[inline]
fn max_value() -> Self {
Self::MAX
}
#[cfg(feature = "from_slice")]
#[inline]
fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
RangeSetBlaze::from_iter(slice.as_ref())
}
fn safe_len(r: &RangeInclusive<Self>) -> Self::SafeLen {
let (start, end) = r.clone().into_primitive_inner();
T::prim_safe_len(start, end)
}
fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
T::safe_len_to_f64_lossy(len)
}
fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
T::f64_to_safe_len_lossy(f)
}
fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
self.inclusive_end_from_start(b)
}
fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
self.start_from_inclusive_end(b)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Integer;
use crate::float::total_float::{
from_ordered_32, from_ordered_64, to_ordered_32, to_ordered_64,
};
use std::collections::hash_map::DefaultHasher;
use std::vec;
use std::vec::Vec;
#[test]
fn ordering_agrees_with_total_cmp() {
let values = [
f64::NEG_INFINITY,
-f64::MAX,
-1.0,
-0.0,
0.0,
1.0,
f64::MAX,
f64::INFINITY,
f64::NAN,
f64::from_bits(0x7ff8_0000_0000_0001),
f64::from_bits(0xfff8_0000_0000_0001),
];
for left in values {
for right in values {
assert_eq!(tf64(left).cmp(&tf64(right)), left.total_cmp(&right));
}
}
}
#[test]
fn equality_agrees_with_total_cmp() {
assert_ne!(tf64(-0.0), tf64(0.0));
assert_eq!(tf64(f64::NAN), tf64(f64::NAN));
}
#[test]
fn equal_values_hash_equally() {
let left = hash(tf64(f64::NAN));
let right = hash(tf64(f64::NAN));
assert_eq!(left, right);
}
#[test]
fn converts_ranges() {
assert_eq!(
TotalF64::from_primitive_range(10.0..=20.0),
tf64(10.0)..=tf64(20.0)
);
assert_eq!(
TotalF64::from_primitive_ranges([10.0..=20.0, 30.0..=40.0]).collect::<Vec<_>>(),
vec![tf64(10.0)..=tf64(20.0), tf64(30.0)..=tf64(40.0)]
);
}
#[test]
fn after_and_before_step_through_zero_in_total_order() {
assert_eq!(tf64(-0.0).after(), tf64(0.0));
assert_eq!(tf64(0.0).before(), tf64(-0.0));
assert_eq!(tf64(0.0).after(), tf64(f64::from_bits(1)));
assert_eq!(
tf64(-0.0).before(),
tf64(f64::from_bits(0x8000_0000_0000_0001))
);
}
#[test]
fn checked_after_and_before_are_not_wrapping() {
assert_eq!(TotalF64::MAX.checked_after(), None);
assert_eq!(TotalF64::MIN.checked_before(), None);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "after() called on maximum value")]
fn total_after_panics_at_max_in_debug() {
let _ = TotalF64::MAX.after();
}
#[test]
#[cfg(not(debug_assertions))]
fn total_after_wraps_at_max_in_release() {
assert_eq!(TotalF64::MAX.after(), TotalF64::MIN);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "before() called on minimum value")]
fn total_before_panics_at_min_in_debug() {
let _ = TotalF64::MIN.before();
}
#[test]
#[cfg(not(debug_assertions))]
fn total_before_wraps_at_min_in_release() {
assert_eq!(TotalF64::MIN.before(), TotalF64::MAX);
}
#[test]
fn stable_ordered_round_trips() {
let edge_f64 = [
0,
1,
u64::MAX,
0x7ff0_0000_0000_0000,
0xfff0_0000_0000_0000,
0x7ff8_0000_0000_0001,
0xfff8_0000_0000_0001,
];
for bits in edge_f64 {
let value = f64::from_bits(bits);
assert_eq!(from_ordered_64(to_ordered_64(value)).to_bits(), bits);
}
let edge_f32 = [
0,
1,
u32::MAX,
0x7f80_0000,
0xff80_0000,
0x7fc0_0001,
0xffc0_0001,
];
for bits in edge_f32 {
let value = f32::from_bits(bits);
assert_eq!(from_ordered_32(to_ordered_32(value)).to_bits(), bits);
}
let mut state = 0x9e37_79b9_u64;
for _ in 0..10_000 {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1);
let value = f64::from_bits(state);
assert_eq!(from_ordered_64(to_ordered_64(value)).to_bits(), state);
let bytes = state.to_le_bytes();
let bits = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let value = f32::from_bits(bits);
assert_eq!(from_ordered_32(to_ordered_32(value)).to_bits(), bits);
}
}
#[test]
fn after_and_before_step_around_infinities() {
assert_eq!(tf64(f64::MAX).after(), tf64(f64::INFINITY));
assert_eq!(tf64(f64::INFINITY).before(), tf64(f64::MAX));
assert_eq!(tf64(f64::NEG_INFINITY).after(), tf64(-f64::MAX));
assert_eq!(tf64(-f64::MAX).before(), tf64(f64::NEG_INFINITY));
}
#[test]
fn checked_after_and_before_stop_at_total_order_boundaries() {
assert_eq!(TotalF64::MIN.checked_before(), None);
assert_eq!(TotalF64::MAX.checked_after(), None);
assert_eq!(TotalF64::MIN.checked_after(), Some(TotalF64::MIN.after()));
assert_eq!(TotalF64::MAX.checked_before(), Some(TotalF64::MAX.before()));
}
#[test]
fn min_and_max_are_total_order_boundaries() {
let values = [
tf64(f64::NEG_INFINITY),
tf64(-f64::MAX),
tf64(-1.0),
tf64(-0.0),
tf64(0.0),
tf64(1.0),
tf64(f64::MAX),
tf64(f64::INFINITY),
tf64(f64::NAN),
tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
tf64(f64::from_bits(0xfff8_0000_0000_0001)),
];
for value in values {
assert!(TotalF64::MIN <= value);
assert!(value <= TotalF64::MAX);
}
}
#[test]
fn after_and_before_are_neighbors_in_total_order() {
let values = [
tf64(f64::NEG_INFINITY),
tf64(-f64::MAX),
tf64(-1.0),
tf64(-0.0),
tf64(0.0),
tf64(1.0),
tf64(f64::MAX),
tf64(f64::INFINITY),
tf64(f64::NAN),
tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
tf64(f64::from_bits(0xfff8_0000_0000_0001)),
];
for value in values {
assert_eq!(value.after().before(), value);
assert_eq!(value.before().after(), value);
}
}
#[test]
fn adjacency_laws_cover_f32_and_f64_edges() {
macro_rules! check {
($wrapper:ident, $constructor:ident, $zero:expr, $negative_subnormal:expr, $positive_subnormal:expr, $min:expr, $max:expr) => {
let values = [
$constructor($zero),
$constructor($negative_subnormal),
$constructor($positive_subnormal),
$constructor(-1.0),
$constructor(1.0),
$constructor($min),
$constructor($max),
$constructor(f32::INFINITY),
$constructor(f32::NAN),
];
for value in values {
assert_eq!(value.after().before(), value);
assert_eq!(value.before().after(), value);
}
assert_eq!($wrapper::MIN.checked_before(), None);
assert_eq!($wrapper::MAX.checked_after(), None);
};
}
check!(
TotalF32,
tf32,
0.0_f32,
-f32::from_bits(1),
f32::from_bits(1),
f32::MIN,
f32::MAX
);
let values = [
tf64(-0.0),
tf64(0.0),
tf64(-f64::from_bits(1)),
tf64(f64::from_bits(1)),
tf64(-f64::MAX),
tf64(f64::MAX),
tf64(f64::NEG_INFINITY),
tf64(f64::INFINITY),
tf64(f64::from_bits(0x7ff8_0000_0000_0001)),
];
for value in values {
assert_eq!(value.after().before(), value);
assert_eq!(value.before().after(), value);
}
assert_eq!(TotalF64::MIN.checked_before(), None);
assert_eq!(TotalF64::MAX.checked_after(), None);
}
#[test]
fn range_length_laws_cover_f32_and_f64() {
let start = tf32(-f32::from_bits(1));
assert_eq!(TotalF32::safe_len(&(start..=start)), 1);
assert_eq!(TotalF32::safe_len(&(start..=start.after())), 2);
assert_eq!(
TotalF32::MAX_SIZE,
TotalF32::safe_len(&(TotalF32::MIN..=TotalF32::MAX))
);
let length = 17;
let end = start.inclusive_end_from_start(length);
assert_eq!(end.start_from_inclusive_end(length), start);
let start = tf64(-f64::from_bits(1));
assert_eq!(TotalF64::safe_len(&(start..=start)), 1);
assert_eq!(TotalF64::safe_len(&(start..=start.after())), 2);
assert_eq!(
TotalF64::MAX_SIZE,
TotalF64::safe_len(&(TotalF64::MIN..=TotalF64::MAX))
);
let length = 17;
let end = start.inclusive_end_from_start(length);
assert_eq!(end.start_from_inclusive_end(length), start);
}
#[cfg(feature = "float_nightly_experimental")]
#[test]
fn f16_total_adjacency_and_lengths_are_exhaustive() {
for bits in 0..=u16::MAX {
let value = TotalF16::new(f16::from_bits(bits));
if value != TotalF16::MAX {
assert_eq!(value.after().before(), value);
}
if value != TotalF16::MIN {
assert_eq!(value.before().after(), value);
}
assert_eq!(TotalF16::safe_len(&(value..=value)), 1);
}
assert_eq!(
TotalF16::MAX_SIZE,
TotalF16::safe_len(&(TotalF16::MIN..=TotalF16::MAX))
);
}
fn hash(value: TotalF64) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
#[test]
#[cfg(feature = "float_nightly_experimental")]
fn ordered_round_trip() {
use crate::float::total_float::from_ordered_16;
use crate::float::total_float::to_ordered_16;
for x in i16::MIN..=i16::MAX {
assert_eq!(to_ordered_16(from_ordered_16(x)), x);
}
}
}