#[cfg(feature="alloc")] use core::slice;
#[cfg(feature="std")] use std::vec;
#[cfg(all(feature="alloc", not(feature="std")))] use alloc::vec::{self, Vec};
#[cfg(feature="std")] use std::collections::{HashSet};
#[cfg(all(feature="alloc", not(feature="std")))] use alloc::collections::BTreeSet;
#[cfg(feature="alloc")] use distributions::{Distribution, Uniform};
use Rng;
#[derive(Clone, Debug)]
pub enum IndexVec {
#[doc(hidden)] U32(Vec<u32>),
#[doc(hidden)] USize(Vec<usize>),
}
impl IndexVec {
pub fn len(&self) -> usize {
match self {
&IndexVec::U32(ref v) => v.len(),
&IndexVec::USize(ref v) => v.len(),
}
}
pub fn index(&self, index: usize) -> usize {
match self {
&IndexVec::U32(ref v) => v[index] as usize,
&IndexVec::USize(ref v) => v[index],
}
}
pub fn into_vec(self) -> Vec<usize> {
match self {
IndexVec::U32(v) => v.into_iter().map(|i| i as usize).collect(),
IndexVec::USize(v) => v,
}
}
pub fn iter<'a>(&'a self) -> IndexVecIter<'a> {
match self {
&IndexVec::U32(ref v) => IndexVecIter::U32(v.iter()),
&IndexVec::USize(ref v) => IndexVecIter::USize(v.iter()),
}
}
pub fn into_iter(self) -> IndexVecIntoIter {
match self {
IndexVec::U32(v) => IndexVecIntoIter::U32(v.into_iter()),
IndexVec::USize(v) => IndexVecIntoIter::USize(v.into_iter()),
}
}
}
impl PartialEq for IndexVec {
fn eq(&self, other: &IndexVec) -> bool {
use self::IndexVec::*;
match (self, other) {
(&U32(ref v1), &U32(ref v2)) => v1 == v2,
(&USize(ref v1), &USize(ref v2)) => v1 == v2,
(&U32(ref v1), &USize(ref v2)) => (v1.len() == v2.len())
&& (v1.iter().zip(v2.iter()).all(|(x, y)| *x as usize == *y)),
(&USize(ref v1), &U32(ref v2)) => (v1.len() == v2.len())
&& (v1.iter().zip(v2.iter()).all(|(x, y)| *x == *y as usize)),
}
}
}
impl From<Vec<u32>> for IndexVec {
fn from(v: Vec<u32>) -> Self {
IndexVec::U32(v)
}
}
impl From<Vec<usize>> for IndexVec {
fn from(v: Vec<usize>) -> Self {
IndexVec::USize(v)
}
}
#[derive(Debug)]
pub enum IndexVecIter<'a> {
#[doc(hidden)] U32(slice::Iter<'a, u32>),
#[doc(hidden)] USize(slice::Iter<'a, usize>),
}
impl<'a> Iterator for IndexVecIter<'a> {
type Item = usize;
fn next(&mut self) -> Option<usize> {
use self::IndexVecIter::*;
match self {
&mut U32(ref mut iter) => iter.next().map(|i| *i as usize),
&mut USize(ref mut iter) => iter.next().cloned(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
&IndexVecIter::U32(ref v) => v.size_hint(),
&IndexVecIter::USize(ref v) => v.size_hint(),
}
}
}
impl<'a> ExactSizeIterator for IndexVecIter<'a> {}
#[derive(Clone, Debug)]
pub enum IndexVecIntoIter {
#[doc(hidden)] U32(vec::IntoIter<u32>),
#[doc(hidden)] USize(vec::IntoIter<usize>),
}
impl Iterator for IndexVecIntoIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
use self::IndexVecIntoIter::*;
match self {
&mut U32(ref mut v) => v.next().map(|i| i as usize),
&mut USize(ref mut v) => v.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
use self::IndexVecIntoIter::*;
match self {
&U32(ref v) => v.size_hint(),
&USize(ref v) => v.size_hint(),
}
}
}
impl ExactSizeIterator for IndexVecIntoIter {}
pub fn sample<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec
where R: Rng + ?Sized,
{
if amount > length {
panic!("`amount` of samples must be less than or equal to `length`");
}
if length > (::core::u32::MAX as usize) {
return sample_rejection(rng, length, amount);
}
let amount = amount as u32;
let length = length as u32;
if amount < 163 {
const C: [[f32; 2]; 2] = [[1.6, 8.0/45.0], [10.0, 70.0/9.0]];
let j = if length < 500_000 { 0 } else { 1 };
let amount_fp = amount as f32;
let m4 = C[0][j] * amount_fp;
if amount > 11 && (length as f32) < (C[1][j] + m4) * amount_fp {
sample_inplace(rng, length, amount)
} else {
sample_floyd(rng, length, amount)
}
} else {
const C: [f32; 2] = [270.0, 330.0/9.0];
let j = if length < 500_000 { 0 } else { 1 };
if (length as f32) < C[j] * (amount as f32) {
sample_inplace(rng, length, amount)
} else {
sample_rejection(rng, length as usize, amount as usize)
}
}
}
fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec
where R: Rng + ?Sized,
{
let floyd_shuffle = amount < 50;
debug_assert!(amount <= length);
let mut indices = Vec::with_capacity(amount as usize);
for j in length - amount .. length {
let t = rng.gen_range(0, j + 1);
if floyd_shuffle {
if let Some(pos) = indices.iter().position(|&x| x == t) {
indices.insert(pos, j);
continue;
}
} else {
if indices.contains(&t) {
indices.push(j);
continue;
}
}
indices.push(t);
}
if !floyd_shuffle {
for i in (1..amount).rev() {
indices.swap(i as usize, rng.gen_range(0, i + 1) as usize);
}
}
IndexVec::from(indices)
}
fn sample_inplace<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec
where R: Rng + ?Sized,
{
debug_assert!(amount <= length);
let mut indices: Vec<u32> = Vec::with_capacity(length as usize);
indices.extend(0..length);
for i in 0..amount {
let j: u32 = rng.gen_range(i, length);
indices.swap(i as usize, j as usize);
}
indices.truncate(amount as usize);
debug_assert_eq!(indices.len(), amount as usize);
IndexVec::from(indices)
}
fn sample_rejection<R>(rng: &mut R, length: usize, amount: usize) -> IndexVec
where R: Rng + ?Sized,
{
debug_assert!(amount < length);
#[cfg(feature="std")] let mut cache = HashSet::with_capacity(amount);
#[cfg(not(feature="std"))] let mut cache = BTreeSet::new();
let distr = Uniform::new(0, length);
let mut indices = Vec::with_capacity(amount);
for _ in 0..amount {
let mut pos = distr.sample(rng);
while !cache.insert(pos) {
pos = distr.sample(rng);
}
indices.push(pos);
}
debug_assert_eq!(indices.len(), amount);
IndexVec::from(indices)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_sample_boundaries() {
let mut r = ::test::rng(404);
assert_eq!(sample_inplace(&mut r, 0, 0).len(), 0);
assert_eq!(sample_inplace(&mut r, 1, 0).len(), 0);
assert_eq!(sample_inplace(&mut r, 1, 1).into_vec(), vec![0]);
assert_eq!(sample_rejection(&mut r, 1, 0).len(), 0);
assert_eq!(sample_floyd(&mut r, 0, 0).len(), 0);
assert_eq!(sample_floyd(&mut r, 1, 0).len(), 0);
assert_eq!(sample_floyd(&mut r, 1, 1).into_vec(), vec![0]);
let sum: usize = sample_rejection(&mut r, 1 << 25, 10)
.into_iter().sum();
assert!(1 << 25 < sum && sum < (1 << 25) * 25);
let sum: usize = sample_floyd(&mut r, 1 << 25, 10)
.into_iter().sum();
assert!(1 << 25 < sum && sum < (1 << 25) * 25);
}
#[test]
fn test_sample_alg() {
let seed_rng = ::test::rng;
let (length, amount): (usize, usize) = (100, 50);
let v1 = sample(&mut seed_rng(420), length, amount);
let v2 = sample_inplace(&mut seed_rng(420), length as u32, amount as u32);
assert!(v1.iter().all(|e| e < length));
assert_eq!(v1, v2);
let v3 = sample_floyd(&mut seed_rng(420), length as u32, amount as u32);
assert!(v1 != v3);
let (length, amount): (usize, usize) = (1<<20, 50);
let v1 = sample(&mut seed_rng(421), length, amount);
let v2 = sample_floyd(&mut seed_rng(421), length as u32, amount as u32);
assert!(v1.iter().all(|e| e < length));
assert_eq!(v1, v2);
let (length, amount): (usize, usize) = (1<<20, 600);
let v1 = sample(&mut seed_rng(422), length, amount);
let v2 = sample_rejection(&mut seed_rng(422), length, amount);
assert!(v1.iter().all(|e| e < length));
assert_eq!(v1, v2);
}
}