Skip to main content

radiate_core/domain/math/
indexes.rs

1use crate::{RdRand, random_provider};
2
3pub enum SubsetMode<'a> {
4    StratifiedCorrect,
5    FastRandom,
6    Exclude(&'a [usize]),
7    RangeList(&'a [(usize, usize)]),
8}
9
10/// * Generates a sorted vector of unique indices for a given size and order, ensuring the specified index is included.
11/// * Calls the subset function to get a subset of indices.
12/// * Replaces an index in the subset with the specified index if it fits the criteria.
13/// * Sorts and returns the result.
14pub fn individual_indexes(index: usize, max_index: usize, num_indices: usize) -> Vec<usize> {
15    let mut sub_set = subset(max_index, num_indices, SubsetMode::StratifiedCorrect);
16    let mut i = 0;
17    while i < sub_set.len() && sub_set[i] < index {
18        i += 1;
19    }
20    if i < sub_set.len() {
21        sub_set[i] = index;
22    }
23    let mut result = sub_set.iter().map(|&x| x).collect::<Vec<usize>>();
24    result.sort_unstable();
25    result
26}
27
28/// * Generates a subset of indices of size k from a total of n elements.
29/// * Calls the next function to fill the subset.
30pub fn subset(max_index: usize, num_indicies: usize, mode: SubsetMode) -> Vec<usize> {
31    if max_index < num_indicies {
32        panic!("n smaller than k: {} < {}.", max_index, num_indicies);
33    }
34
35    random_provider::with_rng(|rand| match mode {
36        SubsetMode::StratifiedCorrect => {
37            let mut sub = vec![0; num_indicies];
38            next(max_index, &mut sub, rand);
39            sub
40        }
41        SubsetMode::FastRandom => {
42            let mut sub = vec![0; num_indicies];
43            for i in 0..num_indicies {
44                sub[i] = rand.range(0..max_index);
45            }
46            sub
47        }
48        SubsetMode::Exclude(exclude) => {
49            let mut sub = vec![0; num_indicies];
50            for i in 0..num_indicies {
51                loop {
52                    let index = rand.range(0..max_index);
53                    if !exclude.contains(&index) {
54                        sub[i] = index;
55                        break;
56                    }
57                }
58            }
59            sub
60        }
61        SubsetMode::RangeList(range_list) => {
62            let mut sub = vec![0; num_indicies];
63            for i in 0..num_indicies {
64                let (start, end) = range_list[i % range_list.len()];
65                sub[i] = rand.range(start..end);
66            }
67            sub
68        }
69    })
70}
71
72/// * Fills the subset with indices.
73/// * If the subset size equals the total number of elements, it fills the subset with sequential indices.
74/// * Otherwise, it calls build_subset to generate the subset and invert if necessary.
75/// * build_subset Function:
76/// * Constructs a subset of indices using a random selection process.
77/// * Ensures the subset size and range are valid.
78/// * Initializes the subset with evenly spaced indices.
79/// * Adjusts the subset by randomly selecting indices and ensuring they are unique.
80fn next(max_index: usize, sub_set: &mut [usize], rand: &mut RdRand<'_>) {
81    let k = sub_set.len();
82    if k == max_index {
83        for i in 0..k {
84            sub_set[i] = i;
85        }
86        return;
87    }
88    build_subset(max_index, sub_set, rand);
89    if k > max_index - k {
90        invert(max_index, sub_set);
91    }
92}
93
94/// * Inverts the subset to ensure all indices are unique and within the specified range.
95/// * Uses a helper vector to track used indices and fills the subset with the remaining indices.
96fn build_subset(max_index: usize, sub: &mut [usize], rand: &mut RdRand<'_>) {
97    let k = sub.len();
98    check_subset(max_index, k);
99
100    for i in 0..k {
101        sub[i] = i * max_index / k;
102    }
103
104    for _ in 0..k {
105        let mut ix;
106        let mut l;
107        loop {
108            ix = rand.range(1..max_index);
109            l = (ix * k - 1) / max_index;
110            if sub[l] < ix {
111                break;
112            }
113        }
114        sub[l] += 1;
115    }
116
117    let mut ip = 0;
118    let mut is_ = k;
119    for i in 0..k {
120        let m = sub[i];
121        sub[i] = 0;
122        if m != i * max_index / k {
123            ip += 1;
124            sub[ip - 1] = m;
125        }
126    }
127
128    let ihi = ip;
129    for i in 1..=ihi {
130        ip = ihi + 1 - i;
131        let l = 1 + (sub[ip - 1] * k - 1) / max_index;
132        let ids = sub[ip - 1] - (l - 1) * max_index / k;
133        sub[ip - 1] = 0;
134        sub[is_ - 1] = l;
135        is_ -= ids;
136    }
137
138    for ll in 1..=k {
139        let l = k + 1 - ll;
140        if sub[l - 1] != 0 {
141            let ir = l;
142            let m0 = 1 + (sub[l - 1] - 1) * max_index / k;
143            let m = sub[l - 1] * max_index / k - m0 + 1;
144
145            let ix = rand.range(m0..m0 + m - 1);
146            let mut i = l + 1;
147            while i <= ir && ix >= sub[i - 1] {
148                sub[i - 2] = sub[i - 1];
149                i += 1;
150            }
151            sub[i - 2] = ix;
152        }
153    }
154}
155
156/// * Finds the index of a value in a subset.
157/// * Returns the index if found, otherwise returns -1.
158fn invert(n: usize, a: &mut [usize]) {
159    let k = a.len();
160    let mut v = n - 1;
161    let j = n - k - 1;
162    let mut ac = vec![0; k];
163    ac.copy_from_slice(a);
164
165    for i in (0..k).rev() {
166        while let Some(_) = index_of(&ac, j, v) {
167            v -= 1;
168        }
169        a[i] = v;
170        v -= 1;
171    }
172}
173
174fn index_of(a: &[usize], start: usize, value: usize) -> Option<usize> {
175    for i in (0..=start).rev() {
176        if a[i] == value {
177            return Some(i);
178        }
179    }
180
181    None
182}
183
184fn check_subset(n: usize, k: usize) {
185    if n < k {
186        panic!("n smaller than k: {} < {}.", n, k);
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_fast_random_subset() {
196        let n = 50;
197        let k = 20;
198        let result = subset(n, k, SubsetMode::FastRandom);
199        assert_eq!(result.len(), k);
200        assert!(result.iter().all(|&x| x < n));
201    }
202
203    #[test]
204    fn test_exclude_subset() {
205        let n = 10;
206        let k = 5;
207        let blacklist = vec![2, 3, 4];
208        let result = subset(n, k, SubsetMode::Exclude(&blacklist));
209        assert_eq!(result.len(), k);
210        assert!(result.iter().all(|&x| !blacklist.contains(&x)));
211    }
212
213    #[test]
214    fn test_range_list_subset() {
215        let ranges = vec![(0, 5), (10, 15)];
216        let result = subset(20, 6, SubsetMode::RangeList(&ranges));
217        assert_eq!(result.len(), 6);
218        assert!(
219            result
220                .iter()
221                .all(|&x| (0..5).contains(&x) || (10..15).contains(&x))
222        );
223    }
224
225    #[test]
226    fn test_individual_indexes_includes_index() {
227        let result = individual_indexes(7, 20, 5);
228        assert_eq!(result.len(), 5);
229        assert!(result.contains(&7));
230        let mut sorted = result.clone();
231        sorted.sort_unstable();
232        assert_eq!(sorted, result); // must be sorted
233    }
234}