ndarray_histogram/maybe_nan/
mod.rs

1use ndarray::prelude::*;
2use ndarray::{Data, DataMut, RemoveAxis, s};
3use ordered_float::{NotNan, OrderedFloat};
4use std::mem;
5
6/// A number exclusive NaN and hence *not* implementing [`Float`](`num_traits::Float`).
7pub type N32 = NotNan<f32>;
8/// A number exclusive NaN and hence *not* implementing [`Float`](`num_traits::Float`).
9pub type N64 = NotNan<f64>;
10
11/// Casts [`f32`] into a number.
12///
13/// # Panics
14///
15/// Panics if [`f32`] is NaN.
16#[inline]
17pub fn n32(num: f32) -> N32 {
18	N32::new(num).expect("NaN")
19}
20
21/// Casts [`f64`] into a number.
22///
23/// # Panics
24///
25/// Panics if [`f64`] is NaN.
26#[inline]
27pub fn n64(num: f64) -> N64 {
28	N64::new(num).expect("NaN")
29}
30
31/// Ordered [`f32`] inclusive NaN implementing [`Float`](`num_traits::Float`).
32pub type O32 = OrderedFloat<f32>;
33/// Ordered [`f64`] inclusive NaN implementing [`Float`](`num_traits::Float`).
34pub type O64 = OrderedFloat<f64>;
35
36/// Casts [`f32`] into an ordered float.
37#[must_use]
38#[inline]
39pub fn o32(num: f32) -> O32 {
40	OrderedFloat(num)
41}
42/// Casts [`f64`] into an ordered float.
43#[must_use]
44#[inline]
45pub fn o64(num: f64) -> O64 {
46	OrderedFloat(num)
47}
48
49/// A number type that can have not-a-number values.
50pub trait MaybeNan: Sized {
51	/// A type that is guaranteed not to be a NaN value.
52	type NotNan;
53
54	/// Returns `true` if the value is a NaN value.
55	#[must_use]
56	fn is_nan(&self) -> bool;
57
58	/// Tries to convert the value to `NotNan`.
59	///
60	/// Returns `None` if the value is a NaN value.
61	fn try_as_not_nan(&self) -> Option<&Self::NotNan>;
62
63	/// Converts the value.
64	///
65	/// If the value is `None`, a NaN value is returned.
66	#[must_use]
67	fn from_not_nan(_: Self::NotNan) -> Self;
68
69	/// Converts the value.
70	///
71	/// If the value is `None`, a NaN value is returned.
72	#[must_use]
73	fn from_not_nan_opt(_: Option<Self::NotNan>) -> Self;
74
75	/// Converts the value.
76	///
77	/// If the value is `None`, a NaN value is returned.
78	#[must_use]
79	fn from_not_nan_ref_opt(_: Option<&Self::NotNan>) -> &Self;
80
81	/// Returns a view with the NaN values removed.
82	///
83	/// This modifies the input view by moving elements as necessary. The final
84	/// order of the elements is unspecified. However, this method is
85	/// idempotent, and given the same input data, the result is always ordered
86	/// the same way.
87	#[must_use]
88	fn remove_nan_mut(_: ArrayViewMut1<'_, Self>) -> ArrayViewMut1<'_, Self::NotNan>;
89}
90
91/// Returns a view with the NaN values removed.
92///
93/// This modifies the input view by moving elements as necessary.
94fn remove_nan_mut<A: MaybeNan>(mut view: ArrayViewMut1<'_, A>) -> ArrayViewMut1<'_, A> {
95	if view.is_empty() {
96		return view.slice_move(s![..0]);
97	}
98	let mut i = 0;
99	let mut j = view.len() - 1;
100	loop {
101		// At this point, `i == 0 || !view[i-1].is_nan()`
102		// and `j == view.len() - 1 || view[j+1].is_nan()`.
103		while i <= j && !view[i].is_nan() {
104			i += 1;
105		}
106		// At this point, `view[i].is_nan() || i == j + 1`.
107		while j > i && view[j].is_nan() {
108			j -= 1;
109		}
110		// At this point, `!view[j].is_nan() || j == i`.
111		if i >= j {
112			return view.slice_move(s![..i]);
113		} else {
114			view.swap(i, j);
115			i += 1;
116			j -= 1;
117		}
118	}
119}
120
121/// Casts a view from one element type to another.
122///
123/// # Panics
124///
125/// Panics if `T` and `U` differ in size or alignment.
126///
127/// # Safety
128///
129/// The caller must ensure that qll elements in `view` are valid values for type `U`.
130unsafe fn cast_view_mut<T, U>(mut view: ArrayViewMut1<'_, T>) -> ArrayViewMut1<'_, U> {
131	unsafe {
132		assert_eq!(mem::size_of::<T>(), mem::size_of::<U>());
133		assert_eq!(mem::align_of::<T>(), mem::align_of::<U>());
134		let ptr: *mut U = view.as_mut_ptr().cast();
135		let len: usize = view.len_of(Axis(0));
136		let stride: isize = view.stride_of(Axis(0));
137		if len <= 1 {
138			// We can use a stride of `0` because the stride is irrelevant for the `len == 1` case.
139			let stride = 0;
140			ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
141		} else if stride >= 0 {
142			let stride = stride as usize;
143			ArrayViewMut1::from_shape_ptr([len].strides([stride]), ptr)
144		} else {
145			// At this point, stride < 0. We have to construct the view by using the inverse of the
146			// stride and then inverting the axis, since `ArrayViewMut::from_shape_ptr` requires the
147			// stride to be nonnegative.
148			let neg_stride = stride.checked_neg().unwrap() as usize;
149			// This is safe because `ndarray` guarantees that it's safe to offset the
150			// pointer anywhere in the array.
151			let neg_ptr = ptr.offset((len - 1) as isize * stride);
152			let mut v = ArrayViewMut1::from_shape_ptr([len].strides([neg_stride]), neg_ptr);
153			v.invert_axis(Axis(0));
154			v
155		}
156	}
157}
158
159macro_rules! impl_maybenan_for_fxx {
160	($fxx:ident, $Nxx:ident) => {
161		impl MaybeNan for $fxx {
162			type NotNan = $Nxx;
163
164			#[inline]
165			fn is_nan(&self) -> bool {
166				$fxx::is_nan(*self)
167			}
168
169			#[inline]
170			fn try_as_not_nan(&self) -> Option<&$Nxx> {
171				(!self.is_nan()).then(|| unsafe { mem::transmute(self) })
172			}
173
174			#[inline]
175			fn from_not_nan(value: $Nxx) -> $fxx {
176				*value
177			}
178
179			#[inline]
180			fn from_not_nan_opt(value: Option<$Nxx>) -> $fxx {
181				match value {
182					None => $fxx::NAN,
183					Some(num) => *num,
184				}
185			}
186
187			#[inline]
188			fn from_not_nan_ref_opt(value: Option<&$Nxx>) -> &$fxx {
189				match value {
190					None => &$fxx::NAN,
191					Some(num) => num.as_ref(),
192				}
193			}
194
195			#[inline]
196			fn remove_nan_mut(view: ArrayViewMut1<'_, $fxx>) -> ArrayViewMut1<'_, $Nxx> {
197				let not_nan = remove_nan_mut(view);
198				// This is safe because `remove_nan_mut` has removed the NaN values, and `$Nxx` is
199				// a thin wrapper around `$fxx`.
200				unsafe { cast_view_mut(not_nan) }
201			}
202		}
203	};
204}
205impl_maybenan_for_fxx!(f32, N32);
206impl_maybenan_for_fxx!(f64, N64);
207
208impl MaybeNan for O32 {
209	type NotNan = N32;
210
211	#[inline]
212	fn is_nan(&self) -> bool {
213		self.0.is_nan()
214	}
215
216	#[inline]
217	fn try_as_not_nan(&self) -> Option<&N32> {
218		(!self.is_nan()).then(|| unsafe { mem::transmute::<&O32, &N32>(self) })
219	}
220
221	#[inline]
222	fn from_not_nan(value: N32) -> O32 {
223		o32(*value)
224	}
225
226	#[inline]
227	fn from_not_nan_opt(value: Option<N32>) -> O32 {
228		match value {
229			None => o32(f32::NAN),
230			Some(num) => o32(*num),
231		}
232	}
233
234	#[inline]
235	fn from_not_nan_ref_opt(value: Option<&N32>) -> &O32 {
236		match value {
237			None => unsafe { mem::transmute::<&f64, &O32>(&f64::NAN) },
238			Some(num) => unsafe { mem::transmute::<&N32, &O32>(num) },
239		}
240	}
241
242	#[inline]
243	fn remove_nan_mut(view: ArrayViewMut1<'_, O32>) -> ArrayViewMut1<'_, N32> {
244		let not_nan = remove_nan_mut(view);
245		// This is safe because `remove_nan_mut` has removed the NaN values, and `N32` is
246		// a thin wrapper around `f32`.
247		unsafe { cast_view_mut(not_nan) }
248	}
249}
250
251impl MaybeNan for O64 {
252	type NotNan = N64;
253
254	#[inline]
255	fn is_nan(&self) -> bool {
256		self.0.is_nan()
257	}
258
259	#[inline]
260	fn try_as_not_nan(&self) -> Option<&N64> {
261		(!self.is_nan()).then(|| unsafe { mem::transmute::<&O64, &N64>(self) })
262	}
263
264	#[inline]
265	fn from_not_nan(value: N64) -> O64 {
266		o64(*value)
267	}
268
269	#[inline]
270	fn from_not_nan_opt(value: Option<N64>) -> O64 {
271		match value {
272			None => o64(f64::NAN),
273			Some(num) => o64(*num),
274		}
275	}
276
277	#[inline]
278	fn from_not_nan_ref_opt(value: Option<&N64>) -> &O64 {
279		match value {
280			None => unsafe { mem::transmute::<&f64, &O64>(&f64::NAN) },
281			Some(num) => unsafe { mem::transmute::<&N64, &O64>(num) },
282		}
283	}
284
285	#[inline]
286	fn remove_nan_mut(view: ArrayViewMut1<'_, O64>) -> ArrayViewMut1<'_, N64> {
287		let not_nan = remove_nan_mut(view);
288		// This is safe because `remove_nan_mut` has removed the NaN values, and `N64` is
289		// a thin wrapper around `f64`.
290		unsafe { cast_view_mut(not_nan) }
291	}
292}
293
294macro_rules! impl_maybenan_for_opt_never_nan {
295	($ty:ty) => {
296		impl MaybeNan for Option<$ty> {
297			type NotNan = NotNone<$ty>;
298
299			#[inline]
300			fn is_nan(&self) -> bool {
301				self.is_none()
302			}
303
304			#[inline]
305			fn try_as_not_nan(&self) -> Option<&NotNone<$ty>> {
306				if self.is_none() {
307					None
308				} else {
309					// This is safe because we have checked for the `None`
310					// case, and `NotNone<$ty>` is a thin wrapper around `Option<$ty>`.
311					Some(unsafe { &*(self as *const Option<$ty> as *const NotNone<$ty>) })
312				}
313			}
314
315			#[inline]
316			fn from_not_nan(value: NotNone<$ty>) -> Option<$ty> {
317				value.into_inner()
318			}
319
320			#[inline]
321			fn from_not_nan_opt(value: Option<NotNone<$ty>>) -> Option<$ty> {
322				value.and_then(|v| v.into_inner())
323			}
324
325			#[inline]
326			fn from_not_nan_ref_opt(value: Option<&NotNone<$ty>>) -> &Option<$ty> {
327				match value {
328					None => &None,
329					// This is safe because `NotNone<$ty>` is a thin wrapper around
330					// `Option<$ty>`.
331					Some(num) => unsafe { &*(num as *const NotNone<$ty> as *const Option<$ty>) },
332				}
333			}
334
335			#[inline]
336			fn remove_nan_mut(view: ArrayViewMut1<'_, Self>) -> ArrayViewMut1<'_, Self::NotNan> {
337				let not_nan = remove_nan_mut(view);
338				// This is safe because `remove_nan_mut` has removed the `None`
339				// values, and `NotNone<$ty>` is a thin wrapper around `Option<$ty>`.
340				unsafe {
341					ArrayViewMut1::from_shape_ptr(
342						not_nan.dim(),
343						not_nan.as_ptr() as *mut NotNone<$ty>,
344					)
345				}
346			}
347		}
348	};
349}
350impl_maybenan_for_opt_never_nan!(u8);
351impl_maybenan_for_opt_never_nan!(u16);
352impl_maybenan_for_opt_never_nan!(u32);
353impl_maybenan_for_opt_never_nan!(u64);
354impl_maybenan_for_opt_never_nan!(u128);
355impl_maybenan_for_opt_never_nan!(i8);
356impl_maybenan_for_opt_never_nan!(i16);
357impl_maybenan_for_opt_never_nan!(i32);
358impl_maybenan_for_opt_never_nan!(i64);
359impl_maybenan_for_opt_never_nan!(i128);
360impl_maybenan_for_opt_never_nan!(N32);
361impl_maybenan_for_opt_never_nan!(N64);
362
363/// A thin wrapper around `Option` that guarantees that the value is not
364/// `None`.
365#[derive(Clone, Copy, Debug)]
366#[repr(transparent)]
367pub struct NotNone<T>(Option<T>);
368
369impl<T> NotNone<T> {
370	/// Creates a new `NotNone` containing the given value.
371	#[inline]
372	pub fn new(value: T) -> NotNone<T> {
373		NotNone(Some(value))
374	}
375
376	/// Creates a new `NotNone` containing the given value.
377	///
378	/// Returns `None` if `value` is `None`.
379	#[inline]
380	pub fn try_new(value: Option<T>) -> Option<NotNone<T>> {
381		if value.is_some() {
382			Some(NotNone(value))
383		} else {
384			None
385		}
386	}
387
388	/// Returns the underling option.
389	#[inline]
390	pub fn into_inner(self) -> Option<T> {
391		self.0
392	}
393
394	/// Moves the value out of the inner option.
395	///
396	/// This method is guaranteed not to panic.
397	#[inline]
398	pub fn unwrap(self) -> T {
399		match self.0 {
400			Some(inner) => inner,
401			None => unsafe { ::std::hint::unreachable_unchecked() },
402		}
403	}
404
405	/// Maps an `NotNone<T>` to `NotNone<U>` by applying a function to the
406	/// contained value.
407	#[inline]
408	pub fn map<U, F>(self, f: F) -> NotNone<U>
409	where
410		F: FnOnce(T) -> U,
411	{
412		NotNone::new(f(self.unwrap()))
413	}
414}
415
416/// Extension trait for `ArrayBase` providing NaN-related functionality.
417pub trait MaybeNanExt<A, S, D>
418where
419	A: MaybeNan,
420	S: Data<Elem = A>,
421	D: Dimension,
422{
423	/// Traverse the non-NaN array elements and apply a fold, returning the
424	/// resulting value.
425	///
426	/// Elements are visited in arbitrary order.
427	fn fold_skipnan<'a, F, B>(&'a self, init: B, f: F) -> B
428	where
429		A: 'a,
430		F: FnMut(B, &'a A::NotNan) -> B;
431
432	/// Traverse the non-NaN elements and their indices and apply a fold,
433	/// returning the resulting value.
434	///
435	/// Elements are visited in arbitrary order.
436	fn indexed_fold_skipnan<'a, F, B>(&'a self, init: B, f: F) -> B
437	where
438		A: 'a,
439		F: FnMut(B, (D::Pattern, &'a A::NotNan)) -> B;
440
441	/// Visit each non-NaN element in the array by calling `f` on each element.
442	///
443	/// Elements are visited in arbitrary order.
444	fn visit_skipnan<'a, F>(&'a self, f: F)
445	where
446		A: 'a,
447		F: FnMut(&'a A::NotNan);
448
449	/// Fold non-NaN values along an axis.
450	///
451	/// Combine the non-NaN elements of each subview with the previous using
452	/// the fold function and initial value init.
453	fn fold_axis_skipnan<B, F>(&self, axis: Axis, init: B, fold: F) -> Array<B, D::Smaller>
454	where
455		D: RemoveAxis,
456		F: FnMut(&B, &A::NotNan) -> B,
457		B: Clone;
458
459	/// Reduce the values along an axis into just one value, producing a new
460	/// array with one less dimension.
461	///
462	/// The NaN values are removed from the 1-dimensional lanes, then they are
463	/// passed as mutable views to the reducer, allowing for side-effects.
464	///
465	/// **Warnings**:
466	///
467	/// * The lanes are visited in arbitrary order.
468	///
469	/// * The order of the elements within the lanes is unspecified. However,
470	///   if `mapping` is idempotent, this method is idempotent. Additionally,
471	///   given the same input data, the lane is always ordered the same way.
472	///
473	/// **Panics** if `axis` is out of bounds.
474	fn map_axis_skipnan_mut<'a, B, F>(&'a mut self, axis: Axis, mapping: F) -> Array<B, D::Smaller>
475	where
476		A: 'a,
477		S: DataMut,
478		D: RemoveAxis,
479		F: FnMut(ArrayViewMut1<'a, A::NotNan>) -> B;
480
481	private_decl! {}
482}
483
484impl<A, S, D> MaybeNanExt<A, S, D> for ArrayBase<S, D>
485where
486	A: MaybeNan,
487	S: Data<Elem = A>,
488	D: Dimension,
489{
490	fn fold_skipnan<'a, F, B>(&'a self, init: B, mut f: F) -> B
491	where
492		A: 'a,
493		F: FnMut(B, &'a A::NotNan) -> B,
494	{
495		self.fold(init, |acc, elem| {
496			if let Some(not_nan) = elem.try_as_not_nan() {
497				f(acc, not_nan)
498			} else {
499				acc
500			}
501		})
502	}
503
504	fn indexed_fold_skipnan<'a, F, B>(&'a self, init: B, mut f: F) -> B
505	where
506		A: 'a,
507		F: FnMut(B, (D::Pattern, &'a A::NotNan)) -> B,
508	{
509		self.indexed_iter().fold(init, |acc, (idx, elem)| {
510			if let Some(not_nan) = elem.try_as_not_nan() {
511				f(acc, (idx, not_nan))
512			} else {
513				acc
514			}
515		})
516	}
517
518	fn visit_skipnan<'a, F>(&'a self, mut f: F)
519	where
520		A: 'a,
521		F: FnMut(&'a A::NotNan),
522	{
523		self.for_each(|elem| {
524			if let Some(not_nan) = elem.try_as_not_nan() {
525				f(not_nan)
526			}
527		})
528	}
529
530	fn fold_axis_skipnan<B, F>(&self, axis: Axis, init: B, mut fold: F) -> Array<B, D::Smaller>
531	where
532		D: RemoveAxis,
533		F: FnMut(&B, &A::NotNan) -> B,
534		B: Clone,
535	{
536		self.fold_axis(axis, init, |acc, elem| {
537			if let Some(not_nan) = elem.try_as_not_nan() {
538				fold(acc, not_nan)
539			} else {
540				acc.clone()
541			}
542		})
543	}
544
545	fn map_axis_skipnan_mut<'a, B, F>(
546		&'a mut self,
547		axis: Axis,
548		mut mapping: F,
549	) -> Array<B, D::Smaller>
550	where
551		A: 'a,
552		S: DataMut,
553		D: RemoveAxis,
554		F: FnMut(ArrayViewMut1<'a, A::NotNan>) -> B,
555	{
556		self.map_axis_mut(axis, |lane| mapping(A::remove_nan_mut(lane)))
557	}
558
559	private_impl! {}
560}
561
562#[cfg(test)]
563mod tests {
564	use super::*;
565	use quickcheck_macros::quickcheck;
566
567	#[quickcheck]
568	fn remove_nan_mut_idempotent(is_nan: Vec<bool>) -> bool {
569		let mut values: Vec<_> = is_nan
570			.into_iter()
571			.map(|is_nan| if is_nan { None } else { Some(1) })
572			.collect();
573		let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
574		let removed = remove_nan_mut(view);
575		removed == remove_nan_mut(removed.to_owned().view_mut())
576	}
577
578	#[quickcheck]
579	fn remove_nan_mut_only_nan_remaining(is_nan: Vec<bool>) -> bool {
580		let mut values: Vec<_> = is_nan
581			.into_iter()
582			.map(|is_nan| if is_nan { None } else { Some(1) })
583			.collect();
584		let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
585		remove_nan_mut(view).iter().all(|elem| !elem.is_nan())
586	}
587
588	#[quickcheck]
589	fn remove_nan_mut_keep_all_non_nan(is_nan: Vec<bool>) -> bool {
590		let non_nan_count = is_nan.iter().filter(|&&is_nan| !is_nan).count();
591		let mut values: Vec<_> = is_nan
592			.into_iter()
593			.map(|is_nan| if is_nan { None } else { Some(1) })
594			.collect();
595		let view = ArrayViewMut1::from_shape(values.len(), &mut values).unwrap();
596		remove_nan_mut(view).len() == non_nan_count
597	}
598}
599
600mod impl_not_none;