use rand::{rng, Rng};
const MAX_NUMBER: usize = usize::MAX;
pub fn pick_from_slice<'a, T>(slice: &'a [T], weights: &'a [usize]) -> Option<&'a T> {
let slice_len = slice.len();
let index = gen_usize_with_weights(slice_len, weights)?;
Some(&slice[index])
}
pub fn pick_from_multiple_slices<'a, T>(slices: &[&'a [T]], weights: &'a [usize]) -> Option<&'a T> {
let len: usize = slices.iter().map(|slice| slice.len()).sum();
let mut index = gen_usize_with_weights(len, weights)?;
for slice in slices {
let len = slice.len();
if index < len {
return Some(&slice[index]);
} else {
index -= len;
}
}
None
}
pub fn pick_multiple_from_slice<'a, T>(
slice: &'a [T],
weights: &'a [usize],
count: usize,
) -> Vec<&'a T> {
let slice_len = slice.len();
gen_multiple_usize_with_weights(slice_len, weights, count)
.iter()
.map(|&index| &slice[index])
.collect()
}
pub fn pick_multiple_from_multiple_slices<'a, T>(
slices: &[&'a [T]],
weights: &'a [usize],
count: usize,
) -> Vec<&'a T> {
let len: usize = slices.iter().map(|slice| slice.len()).sum();
gen_multiple_usize_with_weights(len, weights, count)
.iter()
.map(|index| {
let mut index = *index;
let mut s = slices[0];
for slice in slices {
let len = slice.len();
if index < len {
s = slice;
break;
} else {
index -= len;
}
}
&s[index]
})
.collect()
}
pub fn gen_usize_with_weights(high: usize, weights: &[usize]) -> Option<usize> {
let weights_len = weights.len();
if weights_len == 0 || high == 0 {
return None;
} else if weights_len == 1 {
if weights[0] == 0 {
return None;
}
return Some(rng().random_range(0..high));
} else {
let mut weights_sum = 0f64;
let mut max_weight = 0;
for w in weights.iter().copied() {
weights_sum += w as f64;
if w > max_weight {
max_weight = w;
}
}
if max_weight == 0 {
return None;
}
let mut rng = rng();
let index_scale = (high as f64) / (weights_len as f64);
let weights_scale = (MAX_NUMBER as f64) / weights_sum;
let rnd = rng.random_range(0..=MAX_NUMBER) as f64;
let mut temp = 0f64;
for (i, w) in weights.iter().copied().enumerate() {
temp += (w as f64) * weights_scale;
if temp > rnd {
let index = ((i as f64) * index_scale) as usize;
return Some(rng.random_range(index..((((i + 1) as f64) * index_scale) as usize)));
}
}
}
None
}
pub fn gen_multiple_usize_with_weights(high: usize, weights: &[usize], count: usize) -> Vec<usize> {
let mut result: Vec<usize> = Vec::with_capacity(count);
let weights_len = weights.len();
if weights_len > 0 && high > 0 {
if weights_len == 1 {
if weights[0] != 0 {
let mut rng = rng();
for _ in 0..count {
result.push(rng.random_range(0..high));
}
}
} else {
let mut weights_sum = 0f64;
let mut max_weight = 0;
for w in weights.iter().copied() {
weights_sum += w as f64;
if w > max_weight {
max_weight = w;
}
}
if max_weight > 0 {
let index_scale = (high as f64) / (weights_len as f64);
let weights_scale = (MAX_NUMBER as f64) / weights_sum;
let mut rng = rng();
for _ in 0..count {
let rnd = rng.random_range(0..=MAX_NUMBER) as f64;
let mut temp = 0f64;
for (i, w) in weights.iter().copied().enumerate() {
temp += (w as f64) * weights_scale;
if temp > rnd {
let index = ((i as f64) * index_scale) as usize;
result.push(
rng.random_range(
index..((((i + 1) as f64) * index_scale) as usize),
),
);
break;
}
}
}
}
}
}
result
}