radiate_core/domain/math/
indexes.rs1use crate::{RdRand, random_provider};
2
3pub enum SubsetMode<'a> {
4 StratifiedCorrect,
5 FastRandom,
6 Exclude(&'a [usize]),
7 RangeList(&'a [(usize, usize)]),
8}
9
10pub fn create_individual_indexes(index: usize, max_index: usize, num_indices: usize) -> Vec<usize> {
15 let mut scratch = vec![0; num_indices];
16 subset(
17 max_index,
18 num_indices,
19 &mut scratch,
20 SubsetMode::StratifiedCorrect,
21 );
22
23 let mut i = 0;
24 while i < scratch.len() && scratch[i] < index {
25 i += 1;
26 }
27
28 if i < scratch.len() {
29 scratch[i] = index;
30 }
31
32 scratch.sort_unstable();
33 scratch
34}
35
36pub fn individual_indexes(index: usize, max_index: usize, num_indices: usize, buff: &mut [usize]) {
37 subset(max_index, num_indices, buff, SubsetMode::StratifiedCorrect);
38
39 let mut i = 0;
40 while i < buff.len() && buff[i] < index {
41 i += 1;
42 }
43
44 if i < buff.len() {
45 buff[i] = index;
46 }
47
48 buff.sort_unstable();
49}
50pub fn subset(max_index: usize, num_indicies: usize, buffer: &mut [usize], mode: SubsetMode) {
53 if max_index < num_indicies {
54 panic!("n smaller than k: {} < {}.", max_index, num_indicies);
55 }
56
57 random_provider::with_rng(|rand| match mode {
58 SubsetMode::StratifiedCorrect => {
59 next(max_index, buffer, rand);
60 }
61 SubsetMode::FastRandom => {
62 for item in buffer.iter_mut().take(num_indicies) {
63 *item = rand.range(0..max_index);
64 }
65 }
66 SubsetMode::Exclude(exclude) => {
67 for item in buffer.iter_mut().take(num_indicies) {
68 loop {
69 let index = rand.range(0..max_index);
70 if !exclude.contains(&index) {
71 *item = index;
72 break;
73 }
74 }
75 }
76 }
77 SubsetMode::RangeList(range_list) => {
78 for i in 0..num_indicies {
79 let (start, end) = range_list[i % range_list.len()];
80 buffer[i] = rand.range(start..end);
81 }
82 }
83 })
84}
85
86fn next(max_index: usize, sub_set: &mut [usize], rand: &mut RdRand<'_>) {
95 let k = sub_set.len();
96 if k == max_index {
97 for (i, item) in sub_set.iter_mut().enumerate() {
98 *item = i;
99 }
100
101 return;
102 }
103 build_subset(max_index, sub_set, rand);
104 if k > max_index - k {
105 invert(max_index, sub_set);
106 }
107}
108
109fn build_subset(max_index: usize, sub: &mut [usize], rand: &mut RdRand<'_>) {
112 let k = sub.len();
113 check_subset(max_index, k);
114
115 for (i, item) in sub.iter_mut().enumerate() {
116 *item = i * max_index / k;
117 }
118
119 for _ in 0..k {
120 let mut ix;
121 let mut l;
122 loop {
123 ix = rand.range(1..max_index);
124 l = (ix * k - 1) / max_index;
125 if sub[l] < ix {
126 break;
127 }
128 }
129 sub[l] += 1;
130 }
131
132 let mut ip = 0;
133 let mut is_ = k;
134 for i in 0..k {
135 let m = sub[i];
136 sub[i] = 0;
137 if m != i * max_index / k {
138 ip += 1;
139 sub[ip - 1] = m;
140 }
141 }
142
143 let ihi = ip;
144 for i in 1..=ihi {
145 ip = ihi + 1 - i;
146 let l = 1 + (sub[ip - 1] * k - 1) / max_index;
147 let ids = sub[ip - 1] - (l - 1) * max_index / k;
148 sub[ip - 1] = 0;
149 sub[is_ - 1] = l;
150 is_ -= ids;
151 }
152
153 for ll in 1..=k {
154 let l = k + 1 - ll;
155 if sub[l - 1] != 0 {
156 let ir = l;
157 let m0 = 1 + (sub[l - 1] - 1) * max_index / k;
158 let m = sub[l - 1] * max_index / k - m0 + 1;
159
160 let ix = rand.range(m0..m0 + m - 1);
161 let mut i = l + 1;
162 while i <= ir && ix >= sub[i - 1] {
163 sub[i - 2] = sub[i - 1];
164 i += 1;
165 }
166 sub[i - 2] = ix;
167 }
168 }
169}
170
171fn invert(n: usize, a: &mut [usize]) {
174 let k = a.len();
175 let mut v = n - 1;
176 let j = n - k - 1;
177 let mut ac = vec![0; k];
178 ac.copy_from_slice(a);
179
180 for i in (0..k).rev() {
181 while index_of(&ac, j, v).is_some() {
182 v -= 1;
183 }
184 a[i] = v;
185 v -= 1;
186 }
187}
188
189fn index_of(a: &[usize], start: usize, value: usize) -> Option<usize> {
190 (0..=start).rev().find(|&i| a[i] == value)
191}
192
193fn check_subset(n: usize, k: usize) {
194 if n < k {
195 panic!("n smaller than k: {} < {}.", n, k);
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_fast_random_subset() {
205 let n = 50;
206 let k = 20;
207 let mut result = vec![0; k];
208 subset(n, k, &mut result, SubsetMode::FastRandom);
209 assert_eq!(result.len(), k);
210 assert!(result.iter().all(|&x| x < n));
211 }
212
213 #[test]
214 fn test_exclude_subset() {
215 let n = 10;
216 let k = 5;
217 let blacklist = vec![2, 3, 4];
218 let mut result = vec![0; k];
219 subset(n, k, &mut result, SubsetMode::Exclude(&blacklist));
220 assert_eq!(result.len(), k);
221 assert!(result.iter().all(|&x| !blacklist.contains(&x)));
222 }
223
224 #[test]
225 fn test_range_list_subset() {
226 let ranges = vec![(0, 5), (10, 15)];
227 let mut result = vec![0; 6];
228 subset(20, 6, &mut result, SubsetMode::RangeList(&ranges));
229 assert_eq!(result.len(), 6);
230 assert!(
231 result
232 .iter()
233 .all(|&x| (0..5).contains(&x) || (10..15).contains(&x))
234 );
235 }
236
237 #[test]
238 fn test_individual_indexes_includes_index() {
239 let result = create_individual_indexes(7, 20, 5);
240 assert_eq!(result.len(), 5);
241 assert!(result.contains(&7));
242 let mut sorted = result.clone();
243 sorted.sort_unstable();
244 assert_eq!(sorted, result); }
246}