added successful unshuffle

fix shuffle stack overflow on debug and test builds due to lack of tail-call opt
rust
Avril 4 years ago
parent a4674cea6d
commit 55ea30fcf8
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -82,6 +84,7 @@ dependencies = [
"cfg-if", "cfg-if",
"lazy_static", "lazy_static",
"rand", "rand",
"rand_chacha",
] ]
[[package]] [[package]]

@ -6,6 +6,14 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.test]
# Needed for tail call opt, to not cause stack overflow.
opt-level = 1
[profile.dev]
# Needed for tail call opt, to not cause stack overflow.
opt-level = 1
[features] [features]
default = ["deferred-drop"] default = ["deferred-drop"]
@ -16,3 +24,6 @@ deferred-drop = ["lazy_static"]
cfg-if = "1.0.0" cfg-if = "1.0.0"
lazy_static = {version = "1.4.0", optional = true} lazy_static = {version = "1.4.0", optional = true}
rand = "0.8.3" rand = "0.8.3"
[dev-dependencies]
rand_chacha = "0.3.0"

@ -32,7 +32,7 @@ impl<T> ShuffleExt<T> for [T]
shuffle::shuffle(self, rng) shuffle::shuffle(self, rng)
} }
#[inline] fn unshuffle<R: Rng + ?Sized>(&mut self, rng: &mut R) { #[inline] fn unshuffle<R: Rng + ?Sized>(&mut self, rng: &mut R) {
todo!() shuffle::unshuffle(self, rng)
} }
} }
@ -41,7 +41,6 @@ impl<T> ShuffleExt<T> for [T]
pub struct Lag<I: ?Sized, T>(Duration, I) pub struct Lag<I: ?Sized, T>(Duration, I)
where I: Iterator<Item=T>; where I: Iterator<Item=T>;
impl<I: ?Sized, T> Lag<I, T> impl<I: ?Sized, T> Lag<I, T>
where I: Iterator<Item=T> where I: Iterator<Item=T>
{ {

@ -13,4 +13,29 @@ mod shuffle;
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
#[cfg(test)] {
let mut values: Vec<_> = (0..100000i32).collect();
let expected = values.clone();
let mut rng = {
use rand::prelude::*;
rand_chacha::ChaChaRng::from_seed([0xfa; 32])
};
let mut nrng = rng.clone();
println!("Start (first 10): {:?}", &values[..10]);
values.shuffle(&mut rng);
println!("Shuffled (first 10): {:?}", &values[..10]);
values.unshuffle(&mut nrng);
println!("Unshuffled (first 10): {:?}", &values[..10]);
assert_eq!(&values[..], &expected[..]);
} }
}
#[cfg(test)] mod test;

@ -22,6 +22,7 @@ pub fn element_in_mut<'a, T, R: Rng + ?Sized>(slice: &'a mut (impl AsMut<[T]> +
fn shuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R) fn shuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R)
{ {
//XXX: Without optimisations this recursion causes stack overflow. Cannot work without tail call optimisation
match slice match slice
{ {
&mut [] | &mut [_] => {}, &mut [] | &mut [_] => {},
@ -34,9 +35,12 @@ fn shuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R)
fn unshuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R) fn unshuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R)
{ {
let indecies: Vec<_> = (1..slice.len()).map(|idx| with.gen_range(0..idx)).collect(); let indecies: Vec<_> = (1..slice.len()).rev().map(|x| with.gen_range(0..x)).collect();
todo!(); for (i, &idx) in (1..slice.len()).zip(indecies.iter().rev())
{
slice.swap(i, idx);
}
drop!(indecies); drop!(indecies);
} }
@ -45,3 +49,8 @@ fn unshuffle_slice<T, R: Rng + ?Sized>(slice: &mut [T], with: &mut R)
{ {
shuffle_slice(slice.as_mut(), with) shuffle_slice(slice.as_mut(), with)
} }
#[inline(always)] pub fn unshuffle<T, R: Rng + ?Sized>(mut slice: impl AsMut<[T]>, with: &mut R)
{
unshuffle_slice(slice.as_mut(), with)
}

@ -0,0 +1,52 @@
use super::*;
#[test]
pub fn shuffle_then_back()
{
let mut values: Vec<_> = (0..10i32).collect();
let expected = values.clone();
let mut rng = {
use rand::prelude::*;
rand_chacha::ChaChaRng::from_seed([12; 32])
};
let mut nrng = rng.clone();
println!("Start: {:?}", values);
values.shuffle(&mut rng);
println!("Shuffled: {:?}", values);
values.unshuffle(&mut nrng);
println!("Unshuffled: {:?}", values);
assert_eq!(&values[..], &expected[..]);
}
#[test]
pub fn shuffle_then_back_large()
{
let mut values: Vec<_> = (0..10000i32).collect();
let expected = values.clone();
let mut rng = {
use rand::prelude::*;
rand_chacha::ChaChaRng::from_seed([0xfa; 32])
};
let mut nrng = rng.clone();
println!("Start (first 10): {:?}", &values[..10]);
values.shuffle(&mut rng);
println!("Shuffled (first 10): {:?}", &values[..10]);
values.unshuffle(&mut nrng);
println!("Unshuffled (first 10): {:?}", &values[..10]);
assert_eq!(&values[..], &expected[..]);
}
Loading…
Cancel
Save