irox_tools/
arrays.rs

1// SPDX-License-Identifier: MIT
2// Copyright 2025 IROX Contributors
3//
4
5//!
6//! Functions and tools for manipulating arrays of items.
7//!
8
9#[macro_export]
10macro_rules! array {
11    (@accum (0, $($_es:expr),*) -> ($($body:tt)*))
12        => {array!(@as_expr [$($body)*])};
13    (@accum (1, $($es:expr),*) -> ($($body:tt)*))
14        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)*))};
15    (@accum (2, $($es:expr),*) -> ($($body:tt)*))
16        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
17    (@accum (3, $($es:expr),*) -> ($($body:tt)*))
18        => {array!(@accum (2, $($es),*) -> ($($body)* $($es,)*))};
19    (@accum (4, $($es:expr),*) -> ($($body:tt)*))
20        => {array!(@accum (2, $($es,)* $($es),*) -> ($($body)*))};
21    (@accum (5, $($es:expr),*) -> ($($body:tt)*))
22        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)*))};
23    (@accum (6, $($es:expr),*) -> ($($body:tt)*))
24        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
25    (@accum (7, $($es:expr),*) -> ($($body:tt)*))
26        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)* $($es,)*))};
27    (@accum (8, $($es:expr),*) -> ($($body:tt)*))
28        => {array!(@accum (4, $($es,)* $($es),*) -> ($($body)*))};
29    (@accum (16, $($es:expr),*) -> ($($body:tt)*))
30        => {array!(@accum (8, $($es,)* $($es),*) -> ($($body)*))};
31    (@accum (32, $($es:expr),*) -> ($($body:tt)*))
32        => {array!(@accum (16, $($es,)* $($es),*) -> ($($body)*))};
33    (@accum (64, $($es:expr),*) -> ($($body:tt)*))
34        => {array!(@accum (32, $($es,)* $($es),*) -> ($($body)*))};
35
36    (@as_expr $e:expr) => {$e};
37
38    [$e:expr; $n:tt] => { array!(@accum ($n, $e) -> ()) };
39}
40
41///
42/// Scans through the array, looking for the maximum value contained within it, using the
43/// [`PartialOrd`] operation.  Returns the first index of the maximum detected value.
44///
45/// # Example
46/// ```
47/// use irox_tools::arrays::max_index;
48/// let arr = &[8,2,7,4,6,3,8,2,15,1,6];
49/// let max_idx = max_index(arr);
50///
51/// assert_eq!(max_idx, Some(8));
52/// ```
53pub fn max_index<T: PartialOrd>(arr: &[T]) -> Option<usize> {
54    let mut max_idx: Option<usize> = None;
55    let mut max_val: Option<&T> = None;
56    for (idx, val) in arr.iter().enumerate() {
57        if max_idx.is_none() {
58            max_idx = Some(idx);
59            max_val = Some(val);
60        } else if let Some(mv) = max_val {
61            if val.gt(mv) {
62                max_val = Some(val);
63                max_idx = Some(idx);
64            }
65        }
66    }
67    max_idx
68}
69
70///
71/// Copies from the provided source slice into a statically sized output array.  Uses [`&T::copy_from_slice`]
72/// under-the-hood, and will panic if `N` is greater than the provided slice length.
73pub fn copy_subset<T: Copy + Default, const N: usize>(arr: &[T]) -> [T; N] {
74    let mut out = [T::default(); N];
75    out.copy_from_slice(arr.split_at(N).0);
76    out
77}
78
79///
80/// Searches the provided array for the longest consecutive repeated sequence of 'val'.
81///
82/// # Example
83/// ```
84/// # use irox_tools::arrays::longest_consecutive_values;
85/// let (position,length) = longest_consecutive_values(&[1,0,0,0,0,0,1,2,3,4,5,6], &0).unwrap();
86///
87/// assert_eq!(position, 1);
88/// assert_eq!(length, 5);
89/// ```
90pub fn longest_consecutive_values<T: PartialOrd>(arr: &[T], val: &T) -> Option<(usize, usize)> {
91    let len = arr.len();
92    let mut best_count: usize = 0;
93    let mut best_position: usize = 0;
94    let mut start_pos = 0;
95    while start_pos < len {
96        let mut count: usize = 0;
97        let arr = arr.get(start_pos..)?;
98        for (idx, v) in arr.iter().enumerate() {
99            if v.ne(val) {
100                start_pos += idx;
101                break;
102            }
103            count += 1;
104        }
105        if count > best_count {
106            best_count = count;
107            if start_pos != 0 {
108                best_position = start_pos - count;
109            }
110        }
111        start_pos += 1;
112    }
113    if best_count > 0 {
114        return Some((best_position, best_count));
115    }
116    None
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::arrays::longest_consecutive_values;
122
123    #[test]
124    pub fn test1() {
125        let (position, length) =
126            longest_consecutive_values(&[1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6], &0).unwrap();
127
128        assert_eq!(position, 1);
129        assert_eq!(length, 5);
130    }
131
132    #[test]
133    pub fn test2() {
134        let (position, length) =
135            longest_consecutive_values(&[1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 4, 5, 6], &0).unwrap();
136
137        assert_eq!(position, 6);
138        assert_eq!(length, 5);
139    }
140}