ndarray_histogram/maybe_nan/
mod.rs1use ndarray::prelude::*;
2use ndarray::{Data, DataMut, RemoveAxis, s};
3use ordered_float::{NotNan, OrderedFloat};
4use std::mem;
5
6pub type N32 = NotNan<f32>;
8pub type N64 = NotNan<f64>;
10
11#[inline]
17pub fn n32(num: f32) -> N32 {
18 N32::new(num).expect("NaN")
19}
20
21#[inline]
27pub fn n64(num: f64) -> N64 {
28 N64::new(num).expect("NaN")
29}
30
31pub type O32 = OrderedFloat<f32>;
33pub type O64 = OrderedFloat<f64>;
35
36#[must_use]
38#[inline]
39pub fn o32(num: f32) -> O32 {
40 OrderedFloat(num)
41}
42#[must_use]
44#[inline]
45pub fn o64(num: f64) -> O64 {
46 OrderedFloat(num)
47}
48
49pub trait MaybeNan: Sized {
51 type NotNan;
53
54 #[must_use]
56 fn is_nan(&self) -> bool;
57
58 fn try_as_not_nan(&self) -> Option<&Self::NotNan>;
62
63 #[must_use]
67 fn from_not_nan(_: Self::NotNan) -> Self;
68
69 #[must_use]
73 fn from_not_nan_opt(_: Option<Self::NotNan>) -> Self;
74
75 #[must_use]
79 fn from_not_nan_ref_opt(_: Option<&Self::NotNan>) -> &Self;
80
81 #[must_use]
88 fn remove_nan_mut(_: ArrayViewMut1<'_, Self>) -> ArrayViewMut1<'_, Self::NotNan>;
89}
90
91fn 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 while i <= j && !view[i].is_nan() {
104 i += 1;
105 }
106 while j > i && view[j].is_nan() {
108 j -= 1;
109 }
110 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
121unsafe 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 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 let neg_stride = stride.checked_neg().unwrap() as usize;
149 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 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 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 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 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 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 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#[derive(Clone, Copy, Debug)]
366#[repr(transparent)]
367pub struct NotNone<T>(Option<T>);
368
369impl<T> NotNone<T> {
370 #[inline]
372 pub fn new(value: T) -> NotNone<T> {
373 NotNone(Some(value))
374 }
375
376 #[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 #[inline]
390 pub fn into_inner(self) -> Option<T> {
391 self.0
392 }
393
394 #[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 #[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
416pub trait MaybeNanExt<A, S, D>
418where
419 A: MaybeNan,
420 S: Data<Elem = A>,
421 D: Dimension,
422{
423 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 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 fn visit_skipnan<'a, F>(&'a self, f: F)
445 where
446 A: 'a,
447 F: FnMut(&'a A::NotNan);
448
449 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 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;