totally_ordered/
lib.rs

1//! This crate adds the `TotallyOrderable` trait for `f32` and `f64` values as well as the ABI-transparent `TotallyOrdered` type which adds `Ord + Eq + Hash` to wrapped floating point values.
2//! Main use case: sorting of floating-point arrays which may or may not contain not-a-numbers, infinities, and positive or negative zeros.
3//!
4//! ```
5//! use totally_ordered::TotallyOrdered;
6//! let mut values : [f64; 4] = [-0.0, 0.0, -1.0, 1.0];
7//! TotallyOrdered::new_slice_mut(&mut values).sort();
8//! # assert_eq!(values[0], -1.0);
9//! # assert_eq!(values[1].to_bits(), (-0.0f64).to_bits());
10//! # assert_eq!(values[2].to_bits(), (0.0f64).to_bits());
11//! # assert_eq!(values[3], 1.0);
12//! ```
13
14#![cfg_attr(not(feature = "std"), no_std)]
15
16use core::cmp::Ordering;
17use core::hash::{Hash, Hasher};
18use core::slice::{from_raw_parts, from_raw_parts_mut};
19
20/// Implement this for types that are not directly `Ord + Eq`, but
21/// can be at a slightly higher runtime cost. Implemented for `f32`
22/// and `f64`.
23pub trait TotallyOrderable {
24	/// A true equality comparison. Can be more expensive than standard
25	/// `PartialEq`.
26	fn total_eq(&self, other: &Self) -> bool;
27	/// A totally ordered comparison. Can be more expensive than standard
28	/// `PartialOrd`.
29	fn total_cmp(&self, other: &Self) -> Ordering;
30	/// A hashing function that matches `total_eq`. As the wrapped type
31	/// doesn't implement `Eq`, it can't be `Hash` directly.
32	fn total_hash<H: Hasher>(&self, state: &mut H);
33
34	type TotalOrdKey: Ord + Hash + Sized;
35	fn total_ord_key(&self) -> Self::TotalOrdKey;
36}
37
38/// Implement this for types that are not directly `Ord + Eq`, but
39/// can be transformed *in place* in such a way that the associated
40/// type `Transformed` is. Implemented for `f32` and `f64`.
41pub trait InPlaceTotallyOrderable: TotallyOrderable + Sized {
42	/// Associated type that is `Ord`, must be the same size as
43	/// `Self`
44	type Transformed: Ord + Hash + Sized;
45	/// Transform `Self` into `Transformed` in place.
46	/// # Safety
47	/// The resulting value must no longer be treated as a
48	/// `Self`, but as a `Transformed`, until the inverse
49	/// transform `total_order_inverse_transform` is called.
50	/// `Transformed` shall only be used for comparisons,
51	/// hashing and swapping / moves.
52	unsafe fn total_order_transform(&mut self);
53	/// Apply the inverse transformation of
54	/// `total_order_transform`
55	/// # Safety
56	/// This function may only be called on values previously
57	/// transformed via `total_order_transform`.
58	unsafe fn total_order_inverse_transform(&mut self);
59}
60
61#[cfg(feature = "std")]
62/// (Potentially) faster alternative to `TotallyOrdered::new_slice_mut` followed by
63/// `sort` for values that can be transformed in place.
64///
65/// ```
66/// # use totally_ordered::*;
67/// let mut values = vec![3.0, 2.0, 1.0];
68/// total_sort(&mut values);
69/// # assert_eq!(values[0], 1.0);
70/// # assert_eq!(values[1], 2.0);
71/// # assert_eq!(values[2], 3.0);
72/// ```
73pub fn total_sort<E: InPlaceTotallyOrderable, T: AsMut<[E]>>(container: &mut T) {
74	fn total_sort_impl<E: InPlaceTotallyOrderable>(s: &mut [E]) {
75		for e in s.iter_mut() {
76			unsafe {
77				e.total_order_transform();
78			}
79		}
80		{
81			let st = unsafe { from_raw_parts_mut(s.as_ptr() as *mut E::Transformed, s.len()) };
82			// as the transformed values are totally orderable, unstable == stable!
83			st.sort_unstable();
84		}
85		for e in s.iter_mut() {
86			unsafe {
87				e.total_order_inverse_transform();
88			}
89		}
90	}
91	total_sort_impl(container.as_mut());
92}
93
94macro_rules! implement_float_order {
95	($F:ident, $U:ident, $I:ident, $N:literal) => {
96		/// Implements the IEEE 754-2008 binary32/binary64 total ordering predicate.
97		impl TotallyOrderable for $F {
98			fn total_eq(&self, other: &Self) -> bool {
99				self.to_bits() == other.to_bits()
100			}
101
102			fn total_cmp(&self, other: &Self) -> Ordering {
103				let mut a = self.to_bits() as $I;
104				let mut b = other.to_bits() as $I;
105				a ^= (((a >> ($N - 1)) as $U) >> 1) as $I;
106				b ^= (((b >> ($N - 1)) as $U) >> 1) as $I;
107				a.cmp(&b)
108			}
109
110			fn total_hash<H: Hasher>(&self, state: &mut H) {
111				self.to_bits().hash(state);
112			}
113
114			type TotalOrdKey = $I;
115			fn total_ord_key(&self) -> Self::TotalOrdKey {
116				let a = self.to_bits() as $I;
117				a ^ (((a >> ($N - 1)) as $U) >> 1) as $I
118			}
119		}
120
121		impl InPlaceTotallyOrderable for $F {
122			type Transformed = $I;
123
124			unsafe fn total_order_transform(&mut self) {
125				let mut bits = self.to_bits();
126				bits ^= ((((bits as $I) >> ($N - 1)) as $U) >> 1);
127				*self = $F::from_bits(bits);
128			}
129
130			unsafe fn total_order_inverse_transform(&mut self) {
131				// forward and inverse transforms are identical for f32/f64!
132				self.total_order_transform();
133			}
134		}
135	};
136}
137
138implement_float_order!(f32, u32, i32, 32);
139implement_float_order!(f64, u64, i64, 64);
140
141/// An ABI-transparent newtype wrapper around types that
142/// adds Ord + Eq to TotallyOrderable types (f32 and f64).
143#[derive(Copy, Clone, Debug, Default)]
144#[repr(transparent)]
145pub struct TotallyOrdered<F: TotallyOrderable>(pub F);
146
147impl<F: TotallyOrderable> TotallyOrdered<F> {
148	/// Creates a wrapped value from an inner value
149	pub fn new(v: F) -> Self {
150		Self(v)
151	}
152
153	/// Creates a wrapped slice without copying data.
154	/// Not implemented as `From<&[F]> for &[Self]` as
155	/// slices are always foreign.
156	///
157	/// Follows the usual borrowing rules:
158	/// ```compile_fail
159	/// # use totally_ordered::*;
160	/// let mut values = [1.0, 2.0, 3.0];
161	/// let values_to = TotallyOrdered::new_slice(&values);
162	/// values[2] = 4.0; // error, can't mutate while borrowed
163	/// assert_eq!(values_to[2], TotallyOrdered::new(3.0));
164	/// ```
165	pub fn new_slice(s: &[F]) -> &[Self] {
166		// TotallyOrdered is repr(transparent)
167		unsafe { from_raw_parts(s.as_ptr() as *const _, s.len()) }
168	}
169
170	/// Creates a mutable wrapped slice without copying data.
171	/// Not implemented as `From<&mut [F]> for &mut [Self]` as
172	/// slices are always foreign.
173	///
174	/// Follows the usual borrowing rules:
175	/// ```compile_fail
176	/// # use totally_ordered::*;
177	/// let mut values = [3.0, 2.0, 1.0];
178	/// let values_to = TotallyOrdered::new_slice_mut(&mut values);
179	/// assert_eq!(values[2], 1.0); // error, can't borrow while mutably borrowed
180	/// values_to.sort();
181	/// ```
182	pub fn new_slice_mut(s: &mut [F]) -> &mut [Self] {
183		// TotallyOrdered is repr(transparent)
184		unsafe { from_raw_parts_mut(s.as_ptr() as *mut _, s.len()) }
185	}
186}
187
188impl<F: TotallyOrderable> PartialEq for TotallyOrdered<F> {
189	fn eq(&self, other: &Self) -> bool {
190		self.0.total_eq(&other.0)
191	}
192}
193
194impl<F: TotallyOrderable> Eq for TotallyOrdered<F> {}
195
196impl<F: TotallyOrderable> PartialOrd for TotallyOrdered<F> {
197	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
198		Some(self.0.total_cmp(&other.0))
199	}
200}
201
202impl<F: TotallyOrderable> Ord for TotallyOrdered<F> {
203	fn cmp(&self, other: &Self) -> Ordering {
204		self.0.total_cmp(&other.0)
205	}
206}
207
208impl<F: TotallyOrderable> Hash for TotallyOrdered<F> {
209	fn hash<H: Hasher>(&self, state: &mut H) {
210		self.0.total_hash(state);
211	}
212}
213
214impl<F: TotallyOrderable> From<F> for TotallyOrdered<F> {
215	fn from(v: F) -> Self {
216		Self(v)
217	}
218}
219
220impl<'a, F: TotallyOrderable> From<&'a F> for &'a TotallyOrdered<F> {
221	/// The explicit lifetime bound ensures that both From
222	/// ```compile_fail
223	/// # use totally_ordered::*;
224	/// let mut value : f32 = 1.0;
225	/// let value_to : &TotallyOrdered<_> = From::from(&value);
226	/// value = 2.0; // error, can't mutate while borrowed
227	/// assert_eq!(*value_to, TotallyOrdered::new(1.0));
228	/// ```
229	/// and Into
230	/// ```compile_fail
231	/// # use totally_ordered::*;
232	/// let mut value : f32 = 1.0;
233	/// let value_to : &TotallyOrdered<_> = (&value).into();
234	/// value = 2.0; // error, can't mutate while borrowed
235	/// assert_eq!(*value_to, TotallyOrdered::new(1.0));
236	/// ```
237	/// respect the lifetime of the borrow on the original value.
238	fn from(v: &F) -> Self {
239		// TotallyOrdered is repr(transparent)
240		unsafe { &*(v as *const _ as *const _) }
241	}
242}
243
244impl<'a, F: TotallyOrderable> From<&'a mut F> for &'a mut TotallyOrdered<F> {
245	/// The explicit lifetime bound ensures that both From
246	/// ```compile_fail
247	/// # use totally_ordered::*;
248	/// let mut value : f32 = 1.0;
249	/// let value_to : &mut TotallyOrdered<_> = From::from(&mut value);
250	/// assert_eq!(value, 1.0); // error, can't borrow while mutably borrowed
251	/// *value_to = TotallyOrdered::new(2.0);
252	/// ```
253	/// and Into
254	/// ```compile_fail
255	/// # use totally_ordered::*;
256	/// let mut value : f32 = 1.0;
257	/// let value_to : &mut TotallyOrdered<_> = (&mut value).into();
258	/// assert_eq!(value, 1.0); // error, can't borrow while mutably borrowed
259	/// *value_to = TotallyOrdered::new(2.0);
260	/// ```
261	/// respect the lifetime of the mutable borrow on the original value.
262	fn from(v: &mut F) -> Self {
263		// TotallyOrdered is repr(transparent)
264		unsafe { &mut *(v as *mut _ as *mut _) }
265	}
266}
267
268#[cfg(test)]
269mod test {
270	use crate::TotallyOrdered;
271	use core::{f32, f64};
272
273	#[test]
274	fn test_total_order_f32() {
275		let mut values = [
276			-0.0,
277			0.0,
278			-1.0,
279			1.0,
280			f32::NEG_INFINITY,
281			f32::INFINITY,
282			-f32::NAN,
283			f32::NAN,
284			-f32::MIN_POSITIVE,
285			f32::MIN_POSITIVE,
286			f32::MIN,
287			f32::MAX,
288		];
289
290		TotallyOrdered::new_slice_mut(&mut values).sort();
291		let values_total = TotallyOrdered::new_slice(&values);
292
293		assert_eq!(values_total[0], (-f32::NAN).into());
294		assert_eq!(values_total[1], f32::NEG_INFINITY.into());
295		assert_eq!(values_total[2], f32::MIN.into());
296		assert_eq!(values_total[3], (-1.0).into());
297		assert_eq!(values_total[4], (-f32::MIN_POSITIVE).into());
298		assert_eq!(values_total[5], (-0.0).into());
299		assert_eq!(values_total[6], 0.0.into());
300		assert_eq!(values_total[7], f32::MIN_POSITIVE.into());
301		assert_eq!(values_total[8], 1.0.into());
302		assert_eq!(values_total[9], f32::MAX.into());
303		assert_eq!(values_total[10], f32::INFINITY.into());
304		assert_eq!(values_total[11], f32::NAN.into());
305
306		assert_ne!(
307			TotallyOrdered::new(-f32::NAN),
308			TotallyOrdered::new(f32::NAN)
309		);
310		assert_ne!(TotallyOrdered::new(-0.0f32), TotallyOrdered::new(0.0f32));
311	}
312
313	#[test]
314	fn test_total_order_f64() {
315		let mut values = [
316			-0.0,
317			0.0,
318			-1.0,
319			1.0,
320			f64::NEG_INFINITY,
321			f64::INFINITY,
322			-f64::NAN,
323			f64::NAN,
324			-f64::MIN_POSITIVE,
325			f64::MIN_POSITIVE,
326			f64::MIN,
327			f64::MAX,
328		];
329
330		TotallyOrdered::new_slice_mut(&mut values).sort();
331		let values_total = TotallyOrdered::new_slice(&values);
332
333		assert_eq!(values_total[0], (-f64::NAN).into());
334		assert_eq!(values_total[1], f64::NEG_INFINITY.into());
335		assert_eq!(values_total[2], f64::MIN.into());
336		assert_eq!(values_total[3], (-1.0).into());
337		assert_eq!(values_total[4], (-f64::MIN_POSITIVE).into());
338		assert_eq!(values_total[5], (-0.0).into());
339		assert_eq!(values_total[6], 0.0.into());
340		assert_eq!(values_total[7], f64::MIN_POSITIVE.into());
341		assert_eq!(values_total[8], 1.0.into());
342		assert_eq!(values_total[9], f64::MAX.into());
343		assert_eq!(values_total[10], f64::INFINITY.into());
344		assert_eq!(values_total[11], f64::NAN.into());
345
346		assert_ne!(
347			TotallyOrdered::new(-f64::NAN),
348			TotallyOrdered::new(f64::NAN)
349		);
350		assert_ne!(TotallyOrdered::new(-0.0f64), TotallyOrdered::new(0.0f64));
351	}
352}