From 55ea30fcf82a6fd969ffabe1e59225e057e90fd6 Mon Sep 17 00:00:00 2001 From: Avril Date: Wed, 3 Mar 2021 16:18:14 +0000 Subject: [PATCH] added successful unshuffle fix shuffle stack overflow on debug and test builds due to lack of tail-call opt --- shuffle3rs/Cargo.lock | 3 +++ shuffle3rs/Cargo.toml | 11 +++++++++ shuffle3rs/src/ext.rs | 3 +-- shuffle3rs/src/main.rs | 25 +++++++++++++++++++ shuffle3rs/src/shuffle.rs | 15 ++++++++--- shuffle3rs/src/test.rs | 52 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 shuffle3rs/src/test.rs diff --git a/shuffle3rs/Cargo.lock b/shuffle3rs/Cargo.lock index b8cd917..ff05771 100644 --- a/shuffle3rs/Cargo.lock +++ b/shuffle3rs/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "cfg-if" version = "1.0.0" @@ -82,6 +84,7 @@ dependencies = [ "cfg-if", "lazy_static", "rand", + "rand_chacha", ] [[package]] diff --git a/shuffle3rs/Cargo.toml b/shuffle3rs/Cargo.toml index 0421c4b..bda77e0 100644 --- a/shuffle3rs/Cargo.toml +++ b/shuffle3rs/Cargo.toml @@ -6,6 +6,14 @@ edition = "2018" # 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] default = ["deferred-drop"] @@ -16,3 +24,6 @@ deferred-drop = ["lazy_static"] cfg-if = "1.0.0" lazy_static = {version = "1.4.0", optional = true} rand = "0.8.3" + +[dev-dependencies] +rand_chacha = "0.3.0" \ No newline at end of file diff --git a/shuffle3rs/src/ext.rs b/shuffle3rs/src/ext.rs index 04ebeb6..43204db 100644 --- a/shuffle3rs/src/ext.rs +++ b/shuffle3rs/src/ext.rs @@ -32,7 +32,7 @@ impl ShuffleExt for [T] shuffle::shuffle(self, rng) } #[inline] fn unshuffle(&mut self, rng: &mut R) { - todo!() + shuffle::unshuffle(self, rng) } } @@ -41,7 +41,6 @@ impl ShuffleExt for [T] pub struct Lag(Duration, I) where I: Iterator; - impl Lag where I: Iterator { diff --git a/shuffle3rs/src/main.rs b/shuffle3rs/src/main.rs index 834bfa5..230f3ac 100644 --- a/shuffle3rs/src/main.rs +++ b/shuffle3rs/src/main.rs @@ -13,4 +13,29 @@ mod shuffle; fn main() { 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; diff --git a/shuffle3rs/src/shuffle.rs b/shuffle3rs/src/shuffle.rs index 8d84275..6ae1b55 100644 --- a/shuffle3rs/src/shuffle.rs +++ b/shuffle3rs/src/shuffle.rs @@ -22,6 +22,7 @@ pub fn element_in_mut<'a, T, R: Rng + ?Sized>(slice: &'a mut (impl AsMut<[T]> + fn shuffle_slice(slice: &mut [T], with: &mut R) { + //XXX: Without optimisations this recursion causes stack overflow. Cannot work without tail call optimisation match slice { &mut [] | &mut [_] => {}, @@ -34,9 +35,12 @@ fn shuffle_slice(slice: &mut [T], with: &mut R) fn unshuffle_slice(slice: &mut [T], with: &mut R) { - let indecies: Vec<_> = (1..slice.len()).map(|idx| with.gen_range(0..idx)).collect(); - - todo!(); + let indecies: Vec<_> = (1..slice.len()).rev().map(|x| with.gen_range(0..x)).collect(); + + for (i, &idx) in (1..slice.len()).zip(indecies.iter().rev()) + { + slice.swap(i, idx); + } drop!(indecies); } @@ -45,3 +49,8 @@ fn unshuffle_slice(slice: &mut [T], with: &mut R) { shuffle_slice(slice.as_mut(), with) } + +#[inline(always)] pub fn unshuffle(mut slice: impl AsMut<[T]>, with: &mut R) +{ + unshuffle_slice(slice.as_mut(), with) +} diff --git a/shuffle3rs/src/test.rs b/shuffle3rs/src/test.rs new file mode 100644 index 0000000..92f7829 --- /dev/null +++ b/shuffle3rs/src/test.rs @@ -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[..]); +}