Skip to main content

lox_core/math/
slices.rs

1// SPDX-FileCopyrightText: 2024 Angus Morrison <github@angus-morrison.com>
2// SPDX-FileCopyrightText: 2024 Helge Eichhorn <git@helgeeichhorn.de>
3//
4// SPDX-License-Identifier: MPL-2.0
5
6//! Utility traits for working with slices of numbers.
7
8use std::ops::Sub;
9
10/// Computes consecutive differences of a slice.
11pub trait Diff<T> {
12    /// Returns a vector of differences between consecutive elements.
13    fn diff(&self) -> Vec<T>;
14}
15
16impl<T> Diff<T> for [T]
17where
18    T: Copy + Sub<Output = T>,
19{
20    fn diff(&self) -> Vec<T> {
21        let n = self.len();
22        self.iter()
23            .take(n - 1)
24            .enumerate()
25            .map(|(idx, x)| self[idx + 1] - *x)
26            .collect()
27    }
28}
29
30/// Tests whether a slice is monotonically ordered.
31pub trait Monotonic {
32    /// Returns `true` if elements are non-decreasing.
33    fn is_increasing(&self) -> bool;
34    /// Returns `true` if elements are non-increasing.
35    fn is_decreasing(&self) -> bool;
36    /// Returns `true` if each element is strictly greater than the previous.
37    fn is_strictly_increasing(&self) -> bool;
38    /// Returns `true` if each element is strictly less than the previous.
39    fn is_strictly_decreasing(&self) -> bool;
40}
41
42impl<T> Monotonic for [T]
43where
44    T: PartialOrd,
45{
46    fn is_increasing(&self) -> bool {
47        self.as_ref().iter().is_sorted()
48    }
49
50    fn is_decreasing(&self) -> bool {
51        self.as_ref().iter().is_sorted_by(|a, b| a >= b)
52    }
53
54    fn is_strictly_increasing(&self) -> bool {
55        self.as_ref().iter().is_sorted_by(|a, b| a < b)
56    }
57
58    fn is_strictly_decreasing(&self) -> bool {
59        self.as_ref().iter().is_sorted_by(|a, b| a > b)
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn test_diff() {
69        let x: Vec<f64> = vec![1.0, 2.0, 3.0];
70        let exp: Vec<f64> = vec![1.0, 1.0];
71        assert_eq!(x.diff(), exp);
72    }
73
74    #[test]
75    fn test_monotonic() {
76        let x1: Vec<f64> = vec![1.0, 1.0, 3.0];
77        let x2: Vec<f64> = vec![1.0, 2.0, 3.0];
78        let x3: Vec<f64> = vec![3.0, 2.0, 2.0];
79        let x4: Vec<f64> = vec![3.0, 2.0, 1.0];
80        assert!(x1.is_increasing());
81        assert!(!x1.is_strictly_increasing());
82        assert!(x2.is_increasing());
83        assert!(x2.is_strictly_increasing());
84        assert!(x3.is_decreasing());
85        assert!(!x3.is_strictly_decreasing());
86        assert!(x4.is_decreasing());
87        assert!(x4.is_strictly_decreasing());
88    }
89}