pub(crate) mod api;
mod einsum_kernels;
mod set_kernels;
mod signal_kernels;
mod stats_kernels;
use std::cmp::Ordering;
pub(crate) fn is_nan_like<T: PartialOrd>(x: &T) -> bool {
x.partial_cmp(x).is_none()
}
pub(crate) fn cmp_nan_last<T: PartialOrd>(a: &T, b: &T) -> Ordering {
match a.partial_cmp(b) {
Some(o) => o,
None => is_nan_like(a).cmp(&is_nan_like(b)),
}
}
pub(crate) fn searchsorted_left<T: PartialOrd>(sorted: &[T], v: &T) -> usize {
sorted.partition_point(|e| cmp_nan_last(e, v) == Ordering::Less)
}
pub(crate) fn searchsorted_right<T: PartialOrd>(sorted: &[T], v: &T) -> usize {
sorted.partition_point(|e| cmp_nan_last(e, v) != Ordering::Greater)
}
pub(crate) fn normalize_axis(axis: isize, ndim: usize) -> usize {
let n = ndim as isize;
if axis < -n || axis >= n {
panic!("axis {axis} is out of bounds for array of dimension {ndim}");
}
if axis < 0 {
(axis + n) as usize
} else {
axis as usize
}
}
pub(crate) fn axis_split(shape: &[usize], axis: usize) -> (usize, usize, usize) {
let outer = shape[..axis].iter().product();
let inner = shape[axis + 1..].iter().product();
(outer, shape[axis], inner)
}
#[cfg(test)]
pub(crate) fn assert_close(got: &[f64], want: &[f64]) {
assert_eq!(got.len(), want.len(), "length: got {got:?}, want {want:?}");
for (i, (g, w)) in got.iter().zip(want).enumerate() {
let ok = (g.is_nan() && w.is_nan()) || g == w || (g - w).abs() <= 1e-12 * w.abs().max(1.0);
assert!(
ok,
"element {i}: got {g}, want {w}
got {got:?}
want {want:?}"
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nan_last_order() {
let mut v = [3.0, f64::NAN, 1.0, f64::NAN, 2.0];
v.sort_by(cmp_nan_last);
assert_eq!(&v[..3], &[1.0, 2.0, 3.0]);
assert!(v[3].is_nan() && v[4].is_nan());
}
#[test]
fn searchsorted_sides() {
let s = [1.0, 2.0, 2.0, 4.0, f64::NAN];
assert_eq!(searchsorted_left(&s, &2.0), 1);
assert_eq!(searchsorted_right(&s, &2.0), 3);
assert_eq!(searchsorted_left(&s, &9.0), 4);
assert_eq!(searchsorted_left(&s, &f64::NAN), 4);
assert_eq!(searchsorted_right(&s, &f64::NAN), 5);
}
#[test]
fn axes() {
assert_eq!(normalize_axis(-1, 3), 2);
assert_eq!(axis_split(&[2, 3, 4], 1), (2, 3, 4));
}
#[test]
#[should_panic(expected = "axis 2 is out of bounds for array of dimension 2")]
fn bad_axis() {
normalize_axis(2, 2);
}
}