Skip to main content

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
9use crate::cfg_feature_alloc;
10cfg_feature_alloc! {
11    extern crate alloc;
12}
13#[macro_export]
14macro_rules! array {
15    (@accum (0, $($_es:expr),*) -> ($($body:tt)*))
16        => {array!(@as_expr [$($body)*])};
17    (@accum (1, $($es:expr),*) -> ($($body:tt)*))
18        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)*))};
19    (@accum (2, $($es:expr),*) -> ($($body:tt)*))
20        => {array!(@accum (0, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
21    (@accum (3, $($es:expr),*) -> ($($body:tt)*))
22        => {array!(@accum (2, $($es),*) -> ($($body)* $($es,)*))};
23    (@accum (4, $($es:expr),*) -> ($($body:tt)*))
24        => {array!(@accum (2, $($es,)* $($es),*) -> ($($body)*))};
25    (@accum (5, $($es:expr),*) -> ($($body:tt)*))
26        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)*))};
27    (@accum (6, $($es:expr),*) -> ($($body:tt)*))
28        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)*))};
29    (@accum (7, $($es:expr),*) -> ($($body:tt)*))
30        => {array!(@accum (4, $($es),*) -> ($($body)* $($es,)* $($es,)* $($es,)*))};
31    (@accum (8, $($es:expr),*) -> ($($body:tt)*))
32        => {array!(@accum (4, $($es,)* $($es),*) -> ($($body)*))};
33    (@accum (16, $($es:expr),*) -> ($($body:tt)*))
34        => {array!(@accum (8, $($es,)* $($es),*) -> ($($body)*))};
35    (@accum (32, $($es:expr),*) -> ($($body:tt)*))
36        => {array!(@accum (16, $($es,)* $($es),*) -> ($($body)*))};
37    (@accum (64, $($es:expr),*) -> ($($body:tt)*))
38        => {array!(@accum (32, $($es,)* $($es),*) -> ($($body)*))};
39
40    (@as_expr $e:expr) => {$e};
41
42    [$e:expr; $n:tt] => { array!(@accum ($n, $e) -> ()) };
43}
44
45///
46/// Scans through the array, looking for the maximum value contained within it, using the
47/// [`PartialOrd`] operation.  Returns the first index of the maximum detected value.
48///
49/// # Example
50/// ```
51/// use irox_tools::arrays::max_index;
52/// let arr = &[8,2,7,4,6,3,8,2,15,1,6];
53/// let max_idx = max_index(arr);
54///
55/// assert_eq!(max_idx, Some(8));
56/// ```
57pub fn max_index<T: PartialOrd>(arr: &[T]) -> Option<usize> {
58    let mut max_idx: Option<usize> = None;
59    let mut max_val: Option<&T> = None;
60    for (idx, val) in arr.iter().enumerate() {
61        if max_idx.is_none() {
62            max_idx = Some(idx);
63            max_val = Some(val);
64        } else if let Some(mv) = max_val {
65            if val.gt(mv) {
66                max_val = Some(val);
67                max_idx = Some(idx);
68            }
69        }
70    }
71    max_idx
72}
73
74///
75/// Copies from the provided source slice into a statically sized output array.  Uses [`&T::copy_from_slice`]
76/// under-the-hood, and will panic if `N` is greater than the provided slice length.
77pub fn copy_subset<T: Copy + Default, const N: usize>(arr: &[T]) -> [T; N] {
78    let mut out = [T::default(); N];
79    out.copy_from_slice(arr.split_at(N).0);
80    out
81}
82
83pub trait SliceTools<T> {
84    ///
85    /// Copies from the provided source slice into a statically sized output array.  Uses [`&T::copy_from_slice`]
86    /// under-the-hood, and will panic if `N` is greater than the provided slice length.
87    fn copy_subset<const N: usize>(&self) -> [T; N]
88    where
89        T: Copy + Default;
90    fn limit(&self, limit: usize) -> &[T];
91    cfg_feature_alloc! {
92        fn reversed(&self) -> alloc::boxed::Box<[T]> where T: Copy+Default;
93    }
94}
95impl<T> SliceTools<T> for [T]
96where
97    T: Copy + Default,
98{
99    fn copy_subset<const N: usize>(&self) -> [T; N]
100    where
101        T: Copy + Default,
102    {
103        copy_subset(self)
104    }
105
106    fn limit(&self, limit: usize) -> &[T] {
107        if limit < self.len() {
108            self.split_at(limit).0
109        } else {
110            self
111        }
112    }
113
114    cfg_feature_alloc! {
115        fn reversed(&self) -> alloc::boxed::Box<[T]>
116        where
117            T: Copy + Default
118        {
119            let mut v : alloc::boxed::Box<[T]> = alloc::boxed::Box::from(self);
120            v.as_mut().reverse();
121            v
122        }
123    }
124}
125
126pub trait ArrayTools<T: Copy + Default, const N: usize> {
127    fn reversed(&self) -> [T; N];
128}
129impl<T: Copy + Default, const N: usize> ArrayTools<T, N> for [T; N] {
130    fn reversed(&self) -> [T; N] {
131        let mut c = *self;
132        c.reverse();
133        c
134    }
135}
136
137///
138/// Searches the provided array for the longest consecutive repeated sequence of 'val'.
139///
140/// # Example
141/// ```
142/// # use irox_tools::arrays::longest_consecutive_values;
143/// let (position,length) = longest_consecutive_values(&[1,0,0,0,0,0,1,2,3,4,5,6], &0).unwrap();
144///
145/// assert_eq!(position, 1);
146/// assert_eq!(length, 5);
147/// ```
148pub fn longest_consecutive_values<T: PartialOrd>(arr: &[T], val: &T) -> Option<(usize, usize)> {
149    let len = arr.len();
150    let mut best_count: usize = 0;
151    let mut best_position: usize = 0;
152    let mut start_pos = 0;
153    while start_pos < len {
154        let mut count: usize = 0;
155        let arr = arr.get(start_pos..)?;
156        for (idx, v) in arr.iter().enumerate() {
157            if v.ne(val) {
158                start_pos += idx;
159                break;
160            }
161            count += 1;
162        }
163        if count > best_count {
164            best_count = count;
165            if start_pos != 0 {
166                best_position = start_pos - count;
167            }
168        }
169        start_pos += 1;
170    }
171    if best_count > 0 {
172        return Some((best_position, best_count));
173    }
174    None
175}
176
177#[cfg(test)]
178mod tests {
179    use crate::arrays::longest_consecutive_values;
180
181    #[test]
182    pub fn test1() {
183        let (position, length) =
184            longest_consecutive_values(&[1, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6], &0).unwrap();
185
186        assert_eq!(position, 1);
187        assert_eq!(length, 5);
188    }
189
190    #[test]
191    pub fn test2() {
192        let (position, length) =
193            longest_consecutive_values(&[1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 4, 5, 6], &0).unwrap();
194
195        assert_eq!(position, 6);
196        assert_eq!(length, 5);
197    }
198}