use whasher::{HashSet, HashSetExt};
pub const SAMPLE_STACK_CAP: usize = 64;
#[inline]
pub fn sample_distinct_indices(total: usize, count: usize) -> Vec<usize> {
if count == 0 {
return Vec::new();
}
if count >= total {
return (0..total).collect();
}
if count == 1 {
return vec![fastrand::usize(0..total)];
}
if total <= 64 {
let mut mask = 0u64;
if count.saturating_mul(2) <= total {
let mut picked = 0;
while picked < count {
let idx = fastrand::usize(0..total);
let bit = 1u64 << idx;
if (mask & bit) == 0 {
mask |= bit;
picked += 1;
}
}
} else {
let exclude = total - count;
let mut excluded = 0;
while excluded < exclude {
let idx = fastrand::usize(0..total);
let bit = 1u64 << idx;
if (mask & bit) == 0 {
mask |= bit;
excluded += 1;
}
}
let valid_mask = if total == 64 {
!0u64
} else {
(1u64 << total) - 1
};
mask = (!mask) & valid_mask;
}
let mut res = Vec::with_capacity(count);
while mask != 0 {
let tz = mask.trailing_zeros() as usize;
res.push(tz);
mask &= mask - 1;
}
return res;
}
if total <= 512 {
let mut mask = [0u64; 8];
if count.saturating_mul(2) <= total {
let mut picked = 0;
while picked < count {
let idx = fastrand::usize(0..total);
let word = idx / 64;
let bit = 1u64 << (idx % 64);
if (mask[word] & bit) == 0 {
mask[word] |= bit;
picked += 1;
}
}
} else {
let exclude = total - count;
let mut excluded = 0;
while excluded < exclude {
let idx = fastrand::usize(0..total);
let word = idx / 64;
let bit = 1u64 << (idx % 64);
if (mask[word] & bit) == 0 {
mask[word] |= bit;
excluded += 1;
}
}
for (i, word) in mask.iter_mut().enumerate() {
let base = i * 64;
if base >= total {
*word = 0;
} else if base + 64 > total {
let valid_bits = total - base;
let valid_mask = if valid_bits == 64 {
!0u64
} else {
(1u64 << valid_bits) - 1
};
*word = (!*word) & valid_mask;
} else {
*word = !*word;
}
}
}
let mut res = Vec::with_capacity(count);
for (i, &word) in mask.iter().enumerate() {
let mut w = word;
let base = i * 64;
while w != 0 {
let tz = w.trailing_zeros() as usize;
res.push(base + tz);
w &= w - 1;
}
}
return res;
}
if count <= SAMPLE_STACK_CAP {
let mut stack_buf = [0usize; SAMPLE_STACK_CAP];
let mut picked = 0;
while picked < count {
let r = fastrand::usize(0..total);
if !stack_buf[..picked].contains(&r) {
stack_buf[picked] = r;
picked += 1;
}
}
stack_buf[..count].sort_unstable();
return stack_buf[..count].to_vec();
}
if count.saturating_mul(2) <= total {
let mut picked = HashSet::with_capacity(count);
for j in (total - count)..total {
let t = fastrand::usize(0..=j);
if !picked.insert(t) {
picked.insert(j);
}
}
let mut v: Vec<usize> = picked.into_iter().collect();
v.sort_unstable();
v
} else {
let exclude = total - count;
let mut excluded = HashSet::with_capacity(exclude);
for j in (total - exclude)..total {
let t = fastrand::usize(0..=j);
if !excluded.insert(t) {
excluded.insert(j);
}
}
let mut sorted_ex: Vec<usize> = excluded.into_iter().collect();
sorted_ex.sort_unstable();
let mut v = Vec::with_capacity(count);
let mut cur = 0;
for ex in sorted_ex {
if ex > cur {
v.extend(cur..ex);
}
cur = ex + 1;
}
if cur < total {
v.extend(cur..total);
}
v
}
}