From 9899b71dad963b7019eb12df406b4d87225420c5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 12:37:21 +0000 Subject: [PATCH 1/7] Add harness for doc tests --- .github/workflows/test.yml | 25 +++++++++++++++++++++++++ .gitignore | 2 ++ tests/Cargo.toml | 16 ++++++++++++++++ tests/generate.sh | 9 +++++++++ tests/src/lib.rs | 3 +++ 5 files changed, 55 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 tests/Cargo.toml create mode 100755 tests/generate.sh create mode 100644 tests/src/lib.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d81d2b6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Tests + +on: + push: + branches: [ master, '0.[0-9]+' ] + pull_request: + branches: [ master, '0.[0-9]+' ] + +jobs: + code-samples: + name: Test code samples + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + - name: Generate harness + working-directory: ./tests + run: ./generate.sh + - name: Test code samples + working-directory: ./tests + run: cargo test diff --git a/.gitignore b/.gitignore index 7585238..3506b15 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ book +tests/src/* +Cargo.lock diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000..423ce5a --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tests" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +doc-comment = "0.3.3" +rand = "0.8" +rand_0_7 = { package = "rand", version = "0.7" } +rand_0_5 = { package = "rand", version = "0.5" } +rand_pcg = "0.3" +rand_chacha = "0.3" +rand_distr = "0.4" +rand_distr_0_2 = { package = "rand_distr", version = "0.2" } +rand_seeder = "0.2" diff --git a/tests/generate.sh b/tests/generate.sh new file mode 100755 index 0000000..4eded4d --- /dev/null +++ b/tests/generate.sh @@ -0,0 +1,9 @@ +echo >> src/lib.rs +for doc in ../src/*.md +do + NAME=$(basename $doc .md) + NAME=${NAME//./_} + NAME=${NAME//-/_} + echo -e "doctest\041(\"../$doc\");" > src/$NAME.rs + echo "mod $NAME;" >> src/lib.rs +done diff --git a/tests/src/lib.rs b/tests/src/lib.rs new file mode 100644 index 0000000..31349b2 --- /dev/null +++ b/tests/src/lib.rs @@ -0,0 +1,3 @@ +#![allow(non_snake_case)] +#[macro_use] +extern crate doc_comment; From f67e6e55357a366b9e3397e3acf674df41424ec5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 12:48:57 +0000 Subject: [PATCH 2/7] Fix doc tests: updating --- src/update-0.5.md | 4 ++-- src/update-0.8.md | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/update-0.5.md b/src/update-0.5.md index 4f76daa..b425167 100644 --- a/src/update-0.5.md +++ b/src/update-0.5.md @@ -75,7 +75,7 @@ any type supporting `SeedableRng`, and provides construction from fresh, strong entropy: ```rust -use rand::{ChaChaRng, FromEntropy}; +use rand_0_5::{ChaChaRng, FromEntropy}; let mut rng = ChaChaRng::from_entropy(); ``` @@ -120,7 +120,7 @@ use cannot be guaranteed.* A new `Error` type has been added, designed explicitly for no-std compatibility, simplicity, and enough flexibility for our uses (carrying a `cause` when possible): -```rust +```ignore pub struct Error { pub kind: ErrorKind, pub msg: &'static str, diff --git a/src/update-0.8.md b/src/update-0.8.md index 2a9638a..e452288 100644 --- a/src/update-0.8.md +++ b/src/update-0.8.md @@ -46,15 +46,18 @@ deliberately excluded since these types are not portable. use-after-free in its thread-local destructor. Any code relying on `ThreadRng` being copied must be updated to use a mutable reference instead. For example, ``` -let rng = thread_rng(); -let a: u32 = Standard.sample(rng); -let b: u32 = Standard.sample(rng); +# use rand_0_7::distributions::{Distribution, Standard}; +let rng = rand_0_7::thread_rng(); +let a: u32 = Standard.sample_iter(rng).next().unwrap(); +let b: u32 = Standard.sample_iter(rng).next().unwrap(); ``` can be replaced with the following code: ``` +# use rand::prelude::*; +# use rand::distributions::Standard; let mut rng = thread_rng(); -let a: u32 = Standard.sample(&mut rng); -let b: u32 = Standard.sample(&mut rng); +let a: u32 = Standard.sample_iter(&mut rng).next().unwrap(); +let b: u32 = Standard.sample_iter(&mut rng).next().unwrap(); ``` #### `gen_range` @@ -116,14 +119,18 @@ Several smaller changes occurred to rand distributions: be adapted to perform the conversion from `u8` to `char`. For example, with Rand 0.7 you could write: ``` - let chars: String = iter::repeat(()) + # use rand_0_7::{distributions::Alphanumeric, Rng}; + # let mut rng = rand_0_7::thread_rng(); + let chars: String = std::iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .take(7) .collect(); ``` With Rand 0.8, this is equivalent to the following: ``` - let chars: String = iter::repeat(()) + # use rand::{distributions::Alphanumeric, Rng}; + # let mut rng = rand::thread_rng(); + let chars: String = std::iter::repeat(()) .map(|()| rng.sample(Alphanumeric)) .map(char::from) .take(7) @@ -148,10 +155,12 @@ In `rand_distr` v0.4, more changes occurred (since v0.2): Therefore, the weights are taken as a slice instead of a `Vec` as input. For example, the following `rand_distr 0.2` code ``` + # use rand_distr_0_2::Dirichlet; Dirichlet::new(vec![1.0, 2.0, 3.0]).unwrap(); ``` can be replaced with the following `rand_distr 0.3` code: ``` + # use rand_distr::Dirichlet; Dirichlet::new(&[1.0, 2.0, 3.0]).unwrap(); ``` - [`rand_distr::Poisson`] does no longer support sampling `u64` values directly. From a1f16e53e0c40d65e4153c0bc76146c6b13809c0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 12:58:19 +0000 Subject: [PATCH 3/7] Fix doc tests: guide --- src/guide-data.md | 5 ++++- src/guide-dist.md | 7 ++++++- src/guide-gen.md | 7 ++++++- src/guide-seeding.md | 50 +++++++++++++++++++++++++++++--------------- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/src/guide-data.md b/src/guide-data.md index b958769..a60e7da 100644 --- a/src/guide-data.md +++ b/src/guide-data.md @@ -3,9 +3,12 @@ ```rust # extern crate rand; # use rand::RngCore; +# fn main() { // get some random data: -let mut data = [0u8; 32]; +let mut data = [0u8; 8]; rand::thread_rng().fill_bytes(&mut data); +println!("{:?}", data) +# } ``` ## What is randomness? diff --git a/src/guide-dist.md b/src/guide-dist.md index 48c8dc0..f482193 100644 --- a/src/guide-dist.md +++ b/src/guide-dist.md @@ -4,6 +4,7 @@ For maximum flexibility when producing random values, we define the [`Distribution`] trait: ```rust +# use rand::{Rng, distributions::DistIter}; // a producer of data of type T: pub trait Distribution { // the key function: @@ -14,7 +15,10 @@ pub trait Distribution { where Self: Sized, R: Rng, - { ... } + { + // [has a default implementation] + # todo!() + } } ``` @@ -90,6 +94,7 @@ Lets go over the distributions by type: - For enums, you have to implement uniform sampling yourself. For example, you could use the following approach: ```rust + # use rand::{Rng, distributions::{Distribution, Standard}}; pub enum Food { Burger, Pizza, diff --git a/src/guide-gen.md b/src/guide-gen.md index 2c9d9a9..7263906 100644 --- a/src/guide-gen.md +++ b/src/guide-gen.md @@ -9,12 +9,17 @@ This section concerns theory; see also the chapter on ```rust # extern crate rand; # extern crate rand_pcg; +use rand::{Rng, SeedableRng}; + +# fn main() { // prepare a non-deterministic random number generator: let mut rng = rand::thread_rng(); +println!("{}", rng.gen::()); // prepare a deterministic generator: -use rand::SeedableRng; let mut rng = rand_pcg::Pcg32::seed_from_u64(123); +println!("{}", rng.gen::()); +# } ``` ## True random number generators diff --git a/src/guide-seeding.md b/src/guide-seeding.md index b7060c2..8e27655 100644 --- a/src/guide-seeding.md +++ b/src/guide-seeding.md @@ -27,7 +27,10 @@ Using a fresh seed (direct from the OS) is easy using [`SeedableRng::from_entrop use rand::prelude::*; use rand_chacha::ChaCha20Rng; -let rng = ChaCha20Rng::from_entropy(); +fn main() { + let mut rng = ChaCha20Rng::from_entropy(); + println!("{}", rng.gen::()); +} ``` Note that this requires `rand_core` has the feature `getrandom` enabled. @@ -41,7 +44,10 @@ convenience method for this: use rand::prelude::*; use rand_pcg::Pcg64; -let rng = Pcg64::from_rng(thread_rng()); +fn main() { + let mut rng = Pcg64::from_rng(thread_rng()).unwrap(); + println!("{}", rng.gen::()); +} ``` But, say you want to save a key and use it later. For that you need to be a @@ -51,9 +57,12 @@ little bit more explicit: use rand::prelude::*; use rand_chacha::ChaCha8Rng; -let mut seed: ::Seed = Default::default(); -thread_rng().fill(&mut seed); -let rng = ChaCha8Rng::from_seed(seed); +fn main() { + let mut seed: ::Seed = Default::default(); + thread_rng().fill(&mut seed); + let mut rng = ChaCha8Rng::from_seed(seed); + println!("{}", rng.gen::()); +} ``` **Obligatory warning**: a few simple PRNGs, notably [`XorShiftRng`], @@ -77,7 +86,10 @@ number while providing good bit-avalance (so that two similar numbers such as use rand::prelude::*; use rand_pcg::Pcg64; -let rng = Pcg64::seed_from_u64(2); +fn main() { + let mut rng = Pcg64::seed_from_u64(2); + println!("{}", rng.gen::()); +} ``` Note that a number with 64-bits or less **cannot be secure**, so this should @@ -98,20 +110,24 @@ use rand::prelude::*; use rand_seeder::{Seeder, SipHasher}; use rand_pcg::Pcg64; -// In one line: -let rng: Pcg64 = Seeder::from("stripy zebra").make_rng(); +fn main() { + // In one line: + let mut rng: Pcg64 = Seeder::from("stripy zebra").make_rng(); + println!("{}", rng.gen::()); -// If we want to be more explicit, first we create a SipRng: -let hasher = SipHasher::from("a sailboat"); -let mut hasher_rng = hasher.into_rng(); -// (Note: hasher_rng is a full RNG and can be used directly.) + // If we want to be more explicit, first we create a SipRng: + let hasher = SipHasher::from("a sailboat"); + let mut hasher_rng = hasher.into_rng(); + // (Note: hasher_rng is a full RNG and can be used directly.) -// Now, we use hasher_rng to create a seed: -let mut seed: ::Seed = Default::default(); -hasher_rng.fill(&mut seed); + // Now, we use hasher_rng to create a seed: + let mut seed: ::Seed = Default::default(); + hasher_rng.fill(&mut seed); -// And create our RNG from that seed: -let rng = Pcg64::from_seed(seed); + // And create our RNG from that seed: + let mut rng = Pcg64::from_seed(seed); + println!("{}", rng.gen::()); +} ``` Note that `rand_seeder` is **not suitable** for cryptographic usage. From 90776d84056667ae061d1ecdacbb8ea1080530df Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 12:57:06 +0000 Subject: [PATCH 4/7] Fix doc tests: other --- src/contrib-doc.md | 2 +- src/contrib-test.md | 2 +- src/crates.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contrib-doc.md b/src/contrib-doc.md index af0b93e..c49b01b 100644 --- a/src/contrib-doc.md +++ b/src/contrib-doc.md @@ -73,7 +73,7 @@ When referring to an item from within another crate, Examples: -``` +```ignore // We depend on rand_core, therefore can use the Rust path: /// [`BlockRngCore`]: rand_core::block::BlockRngCore diff --git a/src/contrib-test.md b/src/contrib-test.md index 85ee88b..1793727 100644 --- a/src/contrib-test.md +++ b/src/contrib-test.md @@ -48,7 +48,7 @@ Often test code needs some RNG to test with, but does not need any particular RNG. In this case, we prefer use of `::test::rng` which is simple, fast to initialise and deterministic: -```rust +```rust,ignore let mut rng = ::test::rng(528); // just pick some number ``` diff --git a/src/crates.md b/src/crates.md index b6fdc93..9451bc4 100644 --- a/src/crates.md +++ b/src/crates.md @@ -9,7 +9,7 @@ platform-dependent random number source, [`rand_core`] defines the API that generators must implement, and a number of crates like [`rand_chacha`] and [`rand_xoshiro`] provide pseudo-random generators. -``` +```plain getrandom ┐ └ rand_core ┐ ├ rand_chacha ┐ From d957922b9c25c81aa06c7a58e3e2f1298c70e887 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 16:03:30 +0000 Subject: [PATCH 5/7] Fix links; update sequences, weighted index --- src/contrib-doc.md | 2 +- src/contributing.md | 2 +- src/crates-gen.md | 2 +- src/guide-dist.md | 77 ++++++++++-------------------------- src/guide-err.md | 19 +++------ src/guide-gen.md | 2 +- src/guide-rngs.md | 16 ++++---- src/guide-seeding.md | 2 +- src/guide-seq.md | 92 ++++++++++++++++++++++++++++++++++++++++---- src/guide-values.md | 2 +- src/portability.md | 6 +-- src/update-0.6.md | 2 +- src/update-0.7.md | 9 +++-- src/update-0.8.md | 10 ++--- 14 files changed, 139 insertions(+), 104 deletions(-) diff --git a/src/contrib-doc.md b/src/contrib-doc.md index c49b01b..585f924 100644 --- a/src/contrib-doc.md +++ b/src/contrib-doc.md @@ -95,7 +95,7 @@ example. For the most part these files do not have any continuous testing. Where examples are included (currently only for the `rand_jitter` crate), we enable continuous testing via `doc_comment` (see -[lib.rs:62 onwards](https://github.com/rust-random/rand/blob/master/rand_jitter/src/lib.rs#L62)). +[lib.rs:62 onwards](https://github.com/rust-random/rngs/blob/master/rand_jitter/src/lib.rs#L62)). ### CHANGELOG files diff --git a/src/contributing.md b/src/contributing.md index 920f18c..38a63d0 100644 --- a/src/contributing.md +++ b/src/contributing.md @@ -47,5 +47,5 @@ thus we may take considerable time to get back to you. Our works are attributed to "The Rand Project Developers". This is not a formal entity but merely the collection of all contributors to this project. - For more, see the [COPYRIGHT](COPYRIGHT) file. + For more, see the `COPYRIGHT` file. - **Thank you!** diff --git a/src/crates-gen.md b/src/crates-gen.md index 1115016..70c45a8 100644 --- a/src/crates-gen.md +++ b/src/crates-gen.md @@ -41,7 +41,7 @@ The following crates implement pseudo-random number generators [`rand_chacha`]: https://rust-random.github.io/rand/rand_chacha/index.html -[`rand_hc`]: https://rust-random.github.io/rand/rand_hc/index.html +[`rand_hc`]: https://docs.rs/rand_hc/ [`rand_isaac`]: https://docs.rs/rand_isaac/ [`rand_pcg`]: https://rust-random.github.io/rand/rand_pcg/index.html [`rand_xoshiro`]: https://docs.rs/rand_xoshiro/ diff --git a/src/guide-dist.md b/src/guide-dist.md index f482193..767ab82 100644 --- a/src/guide-dist.md +++ b/src/guide-dist.md @@ -90,7 +90,7 @@ Lets go over the distributions by type: - For SIMD types, each element is sampled as above, for [`Standard`] and [`Uniform`] (for the latter, `low` and `high` parameters are *also* SIMD types, effectively sampling from multiple ranges simultaneously). SIMD - support is gated behind a [feature flag](../features.html#simd-support). + support requires using the `simd_support` feature flag and nightly `rustc`. - For enums, you have to implement uniform sampling yourself. For example, you could use the following approach: ```rust @@ -116,29 +116,15 @@ Lets go over the distributions by type: # Non-uniform distributions -Non-uniform distributions can be divided into two categories, as follows. -Some of these discrete and all of the continuous distributions have been moved -from the main [`rand`] crate to a dedicated [`rand_distr`] crate. +The [`rand`] crate provides only two non-uniform distributions: -## Discrete non-uniform distributions +- The [`Bernoulli`] distribution simply generates a boolean where the + probability of sampling `true` is some constant (`Bernoulli::new(0.5)`) or + ratio (`Bernoulli::from_ratio(1, 6)`). +- The [`WeightedIndex`] distribution may be used to sample from a sequence of + weighted values. See the [Sequences] section. -Discrete distributions sample from boolean or integer types. As above, these -can be sampled uniformly, or, as below, via a non-uniform distribution. - -Potentially a discrete distribution could sample directly from a set of discrete -values such as a slice or an `enum`. See the section on [Sequences] regarding -Rand's traits for slice and iterator types. Rand does not provide direct -sampling from `enum`s, with the exception of `Option` (see above). - -### Booleans - -The [`Bernoulli`] distribution is a fancy name for generating a boolean -with a given a probability `p` of being `true`, or defined via a -`success : failure` ratio. Often this is described as a *trial* with -probability `p` of *success* (`true`). - -The methods [`Rng::gen_bool`] and [`Rng::gen_ratio`] are short-cuts to this -distribution. +Many more distributions are provided by the [`rand_distr`] crate. ### Integers @@ -159,26 +145,6 @@ For example, `u64` values can be attained with `rng.sample(Poisson) as u64`. Note that out of range float to int conversions with `as` result in undefined behavior for Rust <1.45 and a saturating conversion for Rust >=1.45. -### Weighted sequences - -The [`WeightedIndex`] distribution samples an index from sequence of weights. -See the [Sequences] section for convenience wrappers directly sampling a slice -element. - -For example, weighted sampling could be used to model the colour of a marble -sampled from a bucket containing 5 green, 15 red and 80 blue marbles. - -Currently the Rand lib only implements *sampling with replacement*, i.e. -repeated sampling assumes the same distribution (that any sampled marble -has been replaced). An alternative distribution implementing -*sampling without replacement* has been -[requested](https://github.com/rust-random/rand/issues/596). - -Note also that two implementations of [`WeightedIndex`] are available; the -first is optimised for a small number of samples while -[`alias_method::WeightedIndex`] is optimised for a large number of samples -(where "large" may mean "> 1000"; benchmarks recommended). - ## Continuous non-uniform distributions Continuous distributions model samples drawn from the real number line ℝ, or in @@ -195,7 +161,7 @@ deviation. The [`LogNormal`] is related: for sample `X` from the log-normal distribution, `log(X)` is normally distributed; this "skews" the normal distribution to avoid negative values and to have a long positive tail. -The [`UnitCircle`] and [`UnitSphereSurface`] distributions simulate uniform +The [`UnitCircle`] and [`UnitSphere`] distributions simulate uniform sampling from the edge of a circle or surface of a sphere. The [`Cauchy`] distribution (also known as the Lorentz distribution) is the @@ -206,7 +172,7 @@ The [`Beta`] distribution is a two-parameter probability distribution, whose output values lie between 0 and 1. The [`Dirichlet`] distribution is a generalisation to any positive number of parameters. -[Sequences]: ../guide-seq.html +[Sequences]: guide-seq.html [`Distribution`]: ../rand/rand/distributions/trait.Distribution.html [`distributions`]: ../rand/rand/distributions/index.html [`rand`]: ../rand/rand/index.html @@ -224,16 +190,15 @@ generalisation to any positive number of parameters. [`Open01`]: ../rand/rand/distributions/struct.Open01.html [`OpenClosed01`]: ../rand/rand/distributions/struct.OpenClosed01.html [`Bernoulli`]: ../rand/rand/distributions/struct.Bernoulli.html -[`Binomial`]: ../rand/rand/distributions/struct.Binomial.html -[`Exp`]: ../rand/rand/distributions/struct.Exp.html -[`Normal`]: ../rand/rand/distributions/struct.Normal.html -[`LogNormal`]: ../rand/rand/distributions/struct.LogNormal.html -[`UnitCircle`]: ../rand/rand/distributions/struct.UnitCircle.html -[`UnitSphereSurface`]: ../rand/rand/distributions/struct.UnitSphereSurface.html -[`Cauchy`]: ../rand/rand/distributions/struct.Cauchy.html -[`Poisson`]: ../rand/rand/distributions/struct.Poisson.html -[`Beta`]: ../rand/rand/distributions/struct.Beta.html -[`Dirichlet`]: ../rand/rand/distributions/struct.Dirichlet.html -[`WeightedIndex`]: ../rand/rand/distributions/weighted/struct.WeightedIndex.html -[`alias_method::WeightedIndex`]: ../rand/rand/distributions/weighted/alias_method/struct.WeightedIndex.html +[`Binomial`]: ../rand/rand_distr/struct.Binomial.html +[`Exp`]: ../rand/rand_distr/struct.Exp.html +[`Normal`]: ../rand/rand_distr/struct.Normal.html +[`LogNormal`]: ../rand/rand_distr/struct.LogNormal.html +[`UnitCircle`]: ../rand/rand_distr/struct.UnitCircle.html +[`UnitSphere`]: ../rand/rand_distr/struct.UnitSphere.html +[`Cauchy`]: ../rand/rand_distr/struct.Cauchy.html +[`Poisson`]: ../rand/rand_distr/struct.Poisson.html +[`Beta`]: ../rand/rand_distr/struct.Beta.html +[`Dirichlet`]: ../rand/rand_distr/struct.Dirichlet.html [`statrs`]: https://github.com/statrs-dev/statrs/ +[`WeightedIndex`]: ../rand/rand/distributions/struct.WeightedIndex.html diff --git a/src/guide-err.md b/src/guide-err.md index 6c5de52..bf7c26d 100644 --- a/src/guide-err.md +++ b/src/guide-err.md @@ -19,17 +19,10 @@ reduce to calls to [`RngCore`]'s "infallible" methods. Since most RNGs cannot fail anyway this is usually not a problem, but the few generators which can may be forced to fail in this case: -- [`OsRng`] interfaces with the Operating System's generator; in rare cases - this may fail as "not ready" or simply "unavailable". -- [`JitterRng`] is a generator based on timer jitter; if the timer does not - appear to be capable of sufficient precision or is too predictable, this - will fail. -- [`EntropyRng`] is an abstraction over the above, falling back to the next - option when the first fails but ultimately failing if all sources fail -- [`thread_rng`] seeds itself via [`EntropyRng`], thus can potentially fail - on its first use on each thread (though it never fails after the first use) -- [`ReadRng`] tries to read data from its source but fails when the stream - ends or errors (though it retries on interrupt). +- [`OsRng`] is a wrapper over [`getrandom`]. "In general, on supported + platforms, failure is highly unlikely, though not impossible." +- [`thread_rng`] seeds itself via [`OsRng`] on first use and periodically + thereafter, thus can potentially fail, though unlikely [`Rng::try_fill`]: ../rand/rand/trait.Rng.html#method.try_fill [`RngCore::try_fill_bytes`]: ../rand/rand_core/trait.RngCore.html#tymethod.try_fill_bytes @@ -37,6 +30,4 @@ be forced to fail in this case: [`RngCore`]: ../rand/rand_core/trait.RngCore.html [`thread_rng`]: ../rand/rand/fn.thread_rng.html [`OsRng`]: ../rand/rand/rngs/struct.OsRng.html -[`JitterRng`]: ../rand/rand/rngs/struct.JitterRng.html -[`EntropyRng`]: ../rand/rand/rngs/struct.EntropyRng.html -[`ReadRng`]: ../rand/rand/rngs/adapter/struct.ReadRng.html +[`getrandom`]: https://docs.rs/getrandom/latest/getrandom/ diff --git a/src/guide-gen.md b/src/guide-gen.md index 7263906..622862f 100644 --- a/src/guide-gen.md +++ b/src/guide-gen.md @@ -130,4 +130,4 @@ couple of bits entropy is available per time-stamp, after running several tests on the timer's quality). [`RngCore`]: ../rand/rand_core/trait.RngCore.html -[`JitterRng`]: ../rand/rand/rngs/jitter/struct.JitterRng.html +[`JitterRng`]: https://docs.rs/rand_jitter/latest/rand_jitter/struct.JitterRng.html diff --git a/src/guide-rngs.md b/src/guide-rngs.md index 4bc0b83..74512db 100644 --- a/src/guide-rngs.md +++ b/src/guide-rngs.md @@ -245,11 +245,11 @@ for recommendations. It is worth noting that a CSPRNG's security relies absolutely on being seeded with a secure random key. Should the key be known or guessable, all output of the CSPRNG is easy to guess. This implies that the seed should -come from a trusted source; usually either the OS or another CSPRNG. Our -seeding helper trait, [`FromEntropy`], and the source it uses -([`EntropyRng`]), should be secure. Additionally, [`ThreadRng`] is a CSPRNG, -thus it is acceptable to seed from this (although for security applications -fresh/external entropy should be preferred). +come from a trusted source; usually either the OS or another CSPRNG. For this +purpose, we recommend using the [`getrandom`] crate which interfaces the OS's +secure random interface. [`SeedableRng::from_entropy`] is a wrapper around +[`getrandom`] for convenience. Alternatively, using a user-space CSPRNG such as +[`ThreadRng`] for seeding should be sufficient. Further, it should be obvious that the internal state of a CSPRNG must be kept secret. With that in mind, our implementations do not provide direct @@ -305,10 +305,10 @@ by P. Hellekalek. [`Xoshiro256PlusPlus`]: https://docs.rs/rand_xoshiro/latest/rand_xoshiro/struct.Xoshiro256PlusPlus.html [`Xoshiro256Plus`]: https://docs.rs/rand_xoshiro/latest/rand_xoshiro/struct.Xoshiro256Plus.html [`SplitMix64`]: https://docs.rs/rand_xoshiro/latest/rand_xoshiro/struct.SplitMix64.html -[`ChaChaRng`]: ../rand/rand_chacha/struct.ChaChaRng.html +[`ChaChaRng`]: ../rand/rand_chacha/type.ChaChaRng.html [`ChaCha20Rng`]: ../rand/rand_chacha/struct.ChaCha20Rng.html [`ChaCha8Rng`]: ../rand/rand_chacha/struct.ChaCha8Rng.html -[`Hc128Rng`]: ../rand/rand_hc/struct.Hc128Rng.html +[`Hc128Rng`]: https://docs.rs/rand_hc/latest/rand_hc/struct.Hc128Rng.html [`IsaacRng`]: https://docs.rs/rand_isaac/latest/rand_isaac/isaac/struct.IsaacRng.html [`Isaac64Rng`]: https://docs.rs/rand_isaac/latest/rand_isaac/isaac64/struct.Isaac64Rng.html [`ThreadRng`]: ../rand/rand/rngs/struct.ThreadRng.html @@ -325,3 +325,5 @@ by P. Hellekalek. [next-bit test]: https://en.wikipedia.org/wiki/Next-bit_test [NIST]: https://www.nist.gov/ [ECRYPT]: http://www.ecrypt.eu.org/ +[`getrandom`]: https://docs.rs/getrandom/ +[`SeedableRng::from_entropy`]: ../rand/rand/trait.SeedableRng.html#method.from_entropy diff --git a/src/guide-seeding.md b/src/guide-seeding.md index 8e27655..6d82237 100644 --- a/src/guide-seeding.md +++ b/src/guide-seeding.md @@ -141,6 +141,6 @@ function such as Argon2 must be used. [`SeedableRng::from_rng`]: ../rand/rand_core/trait.SeedableRng.html#method.from_rng [`SeedableRng::seed_from_u64`]: ../rand/rand_core/trait.SeedableRng.html#method.seed_from_u64 [`SeedableRng::from_entropy`]: ../rand/rand_core/trait.SeedableRng.html#method.from_entropy -[`XorShiftRng`]: ../rand/rand_xorshift/struct.XorShiftRng.html +[`XorShiftRng`]: https://docs.rs/rand_xorshift/latest/rand_xorshift/struct.XorShiftRng.html [`ChaCha8Rng`]: ../rand/rand_chacha/struct.ChaCha8Rng.html [`rand_seeder`]: https://github.com/rust-random/seeder/ diff --git a/src/guide-seq.md b/src/guide-seq.md index 2abcce7..d35df16 100644 --- a/src/guide-seq.md +++ b/src/guide-seq.md @@ -1,16 +1,92 @@ # Sequences Rand implements a few common random operations on sequences via the -[`IteratorRandom`] and [`SliceRandom`] traits: +[`IteratorRandom`] and [`SliceRandom`] traits. -- `choose` one element uniformly from the sequence -- `choose_multiple` elements uniformly without replacement -- `choose_weighted` — choose an element non-uniformly by use of a defined - weight from a slice (also see the [`WeightedIndex`] distribution) -- `shuffle` a slice -- `partial_shuffle` a slice, effectively extracting `amount` elements in - random order +## Generating indices +To sample: + +- a single index within a given range, use [`Rng::gen_range`] +- multiple distinct indices from `0..length`, use [`index::sample`] +- multiple distinct indices from `0..length` with weights, use [`index::sample_weighted`] + +## Shuffling + +To shuffle a slice: + +- [`SliceRandom::shuffle`]: fully shuffle a slice +- [`SliceRandom::partial_shuffle`]: partial shuffle; useful to extract + `amount` random elements in random order + +## Sampling + +The following provide a convenient way of sampling a value from a slice or iterator: + +- [`SliceRandom::choose`]: sample one element from a slice (by ref) +- [`SliceRandom::choose_mut`]: sample one element from a slice (by ref mut) +- [`SliceRandom::choose_multiple`]: sample multiple distinct elements from a slice (returns iterator of references to elements) +- [`IteratorRandom::choose`]: sample one element from an iterator (by value) +- [`IteratorRandom::choose_stable`]: sample one element from an iterator (by value), where RNG calls are unaffected by the iterator's [`size_hint`] +- [`IteratorRandom::choose_multiple_fill`]: sample multiple elements, placing into a buffer +- [`IteratorRandom::choose_multiple`]: sample multiple elements, returning a [`Vec`] + +Note that operating on an iterator is often less efficient than operating on a +slice. + +## Weighted sampling + +For example, weighted sampling could be used to model the colour of a marble +sampled from a bucket containing 5 green, 15 red and 80 blue marbles. + +### With replacement + +Sampling *with replacement* implies that any sampled values (marbles) are +replaced (thus, the probability of sampling each variant is not affected by the +action of sampling). + +This is implemented by the following distributions: + +- [`WeightedIndex`] has fast setup and `O(log N)` sampling +- [`WeightedAliasIndex`] has slow setup and `O(1)` sampling, thus *may* be + faster with a large number of samples + +For convenience, you may use: + +- [`SliceRandom::choose_weighted`] +- [`SliceRandom::choose_weighted_mut`] + +#### Without replacement + +Sampling *without replacement* implies that the action of sampling modifies the +distribution. Since the [`Distribution`] trait is built around the idea of +immutable distributions, we offer the following: + +- [`SliceRandom::choose_multiple_weighted`]: sample `amount` distinct values + from a slice with weights +- [`index::sample_weighted`]: sample `amount` distinct indices from a range with + weights +- Implement yourself: see the section in [Random processes](guide-process.html#sampling-without-replacement) + +[`Distribution`]: ../rand/rand/distributions/trait.Distribution.html [`IteratorRandom`]: ../rand/rand/seq/trait.IteratorRandom.html [`SliceRandom`]: ../rand/rand/seq/trait.SliceRandom.html [`WeightedIndex`]: ../rand/rand/distributions/struct.WeightedIndex.html +[`WeightedAliasIndex`]: ../rand/rand_distr/weighted_alias/struct.WeightedAliasIndex.html +[`SliceRandom::choose`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose +[`SliceRandom::choose_mut`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose_mut +[`SliceRandom::choose_multiple`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose_multiple +[`IteratorRandom::choose`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose +[`IteratorRandom::choose_stable`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose_stable +[`IteratorRandom::choose_multiple`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose_multiple +[`IteratorRandom::choose_multiple_fill`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose_multiple_fill +[`SliceRandom::choose_weighted`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose_weighted +[`SliceRandom::choose_weighted_mut`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose_weighted_mut +[`SliceRandom::choose_multiple_weighted`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.choose_multiple_weighted +[`SliceRandom::shuffle`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.shuffle +[`SliceRandom::partial_shuffle`]: ../rand/rand/seq/trait.SliceRandom.html#tymethod.partial_shuffle +[`Rng::gen_range`]: ../rand/rand/trait.Rng.html#method.gen_range +[`index::sample`]: ../rand/rand/seq/index/fn.sample.html +[`index::sample_weighted`]: ../rand/rand/seq/index/fn.sample_weighted.html +[`size_hint`]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.size_hint +[`Vec`]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html diff --git a/src/guide-values.md b/src/guide-values.md index 2cf7506..29d9282 100644 --- a/src/guide-values.md +++ b/src/guide-values.md @@ -67,6 +67,6 @@ if rand::random() { [`Rng::gen_ratio`]: ../rand/rand/trait.Rng.html#method.gen_ratio [`Rng::fill`]: ../rand/rand/trait.Rng.html#method.fill [`Rng::try_fill`]: ../rand/rand/trait.Rng.html#method.try_fill -[`random`]: ../rand/rand/fn.random.htm +[`random`]: ../rand/rand/fn.random.html [`thread_rng`]: ../rand/rand/fn.thread_rng.html [`Standard`]: ../rand/rand/distributions/struct.Standard.html diff --git a/src/portability.md b/src/portability.md index 3954395..eaa560b 100644 --- a/src/portability.md +++ b/src/portability.md @@ -14,7 +14,7 @@ documented as being unportable (e.g. `StdRng`, `SmallRng`). ## Crate versions -We try to follow [semver rules](https://docs.npmjs.com/misc/semver) regarding +We try to follow [semver rules](https://semver.org/) regarding API-breaking changes and `MAJOR.MINOR.PATCH` versions: - New *patch* versions should not include API-breaking changes or major new @@ -36,9 +36,9 @@ When given fixed inputs, We expect all pseudo-random algorithms to test the value-stability of their output, where possible: -- PRNGs should be compared with a reference vector ([example](https://github.com/rust-random/rngs/blob/master/rand_xoshiro/src/xoshiro256starstar.rs#L113)) +- PRNGs should be compared with a reference vector ([example](https://github.com/rust-random/rngs/blob/master/rand_xoshiro/src/xoshiro256starstar.rs#L115)) - Other algorithms should include their own test vectors within a - `value_stability` test or similar ([example](https://github.com/rust-random/rand/blob/master/src/distributions/bernoulli.rs#L168)) + `value_stability` test or similar ([example](https://github.com/rust-random/rand/blob/master/src/distributions/bernoulli.rs#L203)) ## Limitations diff --git a/src/update-0.6.md b/src/update-0.6.md index 0e5dc3b..a74810d 100644 --- a/src/update-0.6.md +++ b/src/update-0.6.md @@ -59,7 +59,7 @@ feature flag still exists to avoid breakage, but no longer does anything. [`SmallRng`]: ../rand/rand/rngs/struct.SmallRng.html [`Pcg32`]: ../rand/rand_pcg/type.Pcg32.html [`Pcg64Mcg`]: ../rand/rand_pcg/type.Pcg64Mcg.html -[`Rng`]: ../rand/trait.Rng.html +[`Rng`]: ../rand/rand/trait.Rng.html [`IteratorRandom`]: ../rand/rand/seq/trait.IteratorRandom.html [`SliceRandom`]: ../rand/rand/seq/trait.SliceRandom.html [`WeightedChoice`]: https://docs.rs/rand/0.5/rand/distributions/struct.WeightedChoice.html diff --git a/src/update-0.7.md b/src/update-0.7.md index 9fb5987..ebb9060 100644 --- a/src/update-0.7.md +++ b/src/update-0.7.md @@ -51,9 +51,9 @@ These have seen less change than in the previous release, but noteworthy is: For the most widely used distributions ([`Standard`] and [`Uniform`]), there have been no significant changes. But for *most* of the rest... -- We added a new crate, [rand_distr], to house the all distributions +- We added a new crate, [`rand_distr`], to house the all distributions (including re-exporting those still within [`rand::distributions`]). If you - previously used [`rand::distributions::Normal`], now you use + previously used `rand::distributions::Normal`, now you use [`rand_distr::Normal`]. - Constructors for many distributions changed in order to return a `Result` instead of panicking on error. @@ -88,7 +88,7 @@ architectures. [`StdRng`]: ../rand/rand/rngs/struct.StdRng.html [`ThreadRng`]: ../rand/rand/rngs/struct.ThreadRng.html [`Pcg64`]: ../rand/rand_pcg/type.Pcg64.html -[`rand::distributions::weighted::alias_method::WeightedIndex`]: ../rand/rand/distributions/alias_method/struct.WeightedIndex.html +[`rand::distributions::weighted::alias_method::WeightedIndex`]: https://docs.rs/rand/0.7/rand/distributions/weighted/alias_method/struct.WeightedIndex.html [getrandom]: https://github.com/rust-random/getrandom [`FromEntropy`]: https://docs.rs/rand/0.6.0/rand/trait.FromEntropy.html [`SeedableRng`]: https://rust-random.github.io/rand/rand/trait.SeedableRng.html @@ -96,7 +96,7 @@ architectures. [`Standard`]: ../rand/rand/distributions/struct.Standard.html [`Uniform`]: ../rand/rand/distributions/struct.Uniform.html [`rand::distributions`]: ../rand/rand/distributions/index.html -[`rand::distributions::Normal`]: ../rand/rand/distributions/struct.Normal.html +[`rand_distr`]: ../rand/rand_distr/index.html [`rand_distr::Normal`]: ../rand/rand_distr/struct.Normal.html [`NonZeroU*`]: https://doc.rust-lang.org/std/num/index.html [`rand_distr::Pert`]: ../rand/rand_distr/struct.Pert.html @@ -104,3 +104,4 @@ architectures. [`rand_distr::UnitBall`]: ../rand/rand_distr/struct.UnitBall.html [`rand_distr::UnitDisc`]: ../rand/rand_distr/struct.UnitDisc.html [`rand_distr::UnitSphere`]: ../rand/rand_distr/struct.UnitSphere.html +[`OsRng`]: ../rand/rand_core/struct.OsRng.html diff --git a/src/update-0.8.md b/src/update-0.8.md index e452288..27f6595 100644 --- a/src/update-0.8.md +++ b/src/update-0.8.md @@ -137,13 +137,13 @@ Several smaller changes occurred to rand distributions: .collect(); ``` - The alternative implementation of [`WeightedIndex`] employing the alias - method was moved from `rand` to [`rand_distr::WeightedAliasIndex`]. The + method was moved from `rand` to [`rand_distr::weighted_alias::WeightedAliasIndex`]. The alias method is faster for large sizes, but it suffers from a slow initialization, making it less generally useful. In `rand_distr` v0.4, more changes occurred (since v0.2): -- [`rand_distr::WeightedAliasIndex`] was added (moved from the `rand` crate) +- [`rand_distr::weighted_alias::WeightedAliasIndex`] was added (moved from the `rand` crate) - [`rand_distr::InverseGaussian`] and [`rand_distr::NormalInverseGaussian`] were added - The [`Geometric`] and [`Hypergeometric`] distributions are now supported. @@ -211,7 +211,7 @@ enforce our rules regarding value-breaking changes (see [Portability] section). [`Rng::try_fill`]: ../rand/rand/trait.Rng.html#method.try_fill [`SmallRng`]: ../rand/rand/rngs/struct.SmallRng.html [`StdRng`]: ../rand/rand/rngs/struct.StdRng.html -[`StepRng`]: ../rand/rand/rngs/struct.StepRng.html +[`StepRng`]: ../rand/rand/rngs/mock/struct.StepRng.html [`ThreadRng`]: ../rand/rand/rngs/struct.ThreadRng.html [`ReseedingRng`]: ../rand/rand/rngs/adapter/struct.ReseedingRng.html [`Standard`]: ../rand/rand/distributions/struct.Standard.html @@ -220,12 +220,12 @@ enforce our rules regarding value-breaking changes (see [Portability] section). [`UniformSampler::sample_single_inclusive`]: ../rand/rand/distributions/uniform/trait.UniformSampler.html#method.sample_single_inclusive [`Alphanumeric`]: ../rand/rand/distributions/struct.Alphanumeric.html [`WeightedIndex`]: ../rand/rand/distributions/struct.WeightedIndex.html -[`rand::rngs::adpater`]: ../rand/rand/rngs/adapter/index.html +[`rand::rngs::adapter`]: ../rand/rand/rngs/adapter/index.html [`rand::seq::index::sample_weighted`]: ../rand/rand/seq/index/fn.sample_weighted.html [`SliceRandom::choose_multiple_weighted`]: ../rand/rand/seq/trait.SliceRandom.html#method.choose_multiple_weighted [`IteratorRandom::choose`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose [`IteratorRandom::choose_stable`]: ../rand/rand/seq/trait.IteratorRandom.html#method.choose_stable -[`rand_distr::WeightedAliasIndex`]: ../rand/rand_distr/struct.WeightedAliasIndex.html +[`rand_distr::weighted_alias::WeightedAliasIndex`]: ../rand/rand_distr/weighted_alias/struct.WeightedAliasIndex.html [`rand_distr::InverseGaussian`]: ../rand/rand_distr/struct.InverseGaussian.html [`rand_distr::NormalInverseGaussian`]: ../rand/rand_distr/struct.NormalInverseGaussian.html [`rand_distr::Dirichlet`]: ../rand/rand_distr/struct.Dirichlet.html From 98ba32e16308000b00525c445793c5001e92aa27 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 22 Feb 2022 18:46:49 +0000 Subject: [PATCH 6/7] Tweaks by vks Co-authored-by: Vinzent Steinberg --- src/guide-dist.md | 2 +- src/guide-seq.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guide-dist.md b/src/guide-dist.md index 767ab82..c31cd17 100644 --- a/src/guide-dist.md +++ b/src/guide-dist.md @@ -124,7 +124,7 @@ The [`rand`] crate provides only two non-uniform distributions: - The [`WeightedIndex`] distribution may be used to sample from a sequence of weighted values. See the [Sequences] section. -Many more distributions are provided by the [`rand_distr`] crate. +Many more non-uniform distributions are provided by the [`rand_distr`] crate. ### Integers diff --git a/src/guide-seq.md b/src/guide-seq.md index d35df16..c53f699 100644 --- a/src/guide-seq.md +++ b/src/guide-seq.md @@ -56,7 +56,7 @@ For convenience, you may use: - [`SliceRandom::choose_weighted`] - [`SliceRandom::choose_weighted_mut`] -#### Without replacement +### Without replacement Sampling *without replacement* implies that the action of sampling modifies the distribution. Since the [`Distribution`] trait is built around the idea of From 56ed9ab4216c2fb68dd39539a530e0a544debd2e Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 23 Feb 2022 10:31:18 +0000 Subject: [PATCH 7/7] Clarify error-handling behaviour of OsRng and ThreadRng --- src/guide-err.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/guide-err.md b/src/guide-err.md index bf7c26d..90b4fea 100644 --- a/src/guide-err.md +++ b/src/guide-err.md @@ -19,10 +19,15 @@ reduce to calls to [`RngCore`]'s "infallible" methods. Since most RNGs cannot fail anyway this is usually not a problem, but the few generators which can may be forced to fail in this case: -- [`OsRng`] is a wrapper over [`getrandom`]. "In general, on supported - platforms, failure is highly unlikely, though not impossible." +- [`OsRng`] is a wrapper over [`getrandom`]. From the latter's documentation: + "In general, on supported platforms, failure is highly unlikely, though not + impossible." [`OsRng`] will forward errors through + [`RngCore::try_fill_bytes`] while other methods panic on error. - [`thread_rng`] seeds itself via [`OsRng`] on first use and periodically - thereafter, thus can potentially fail, though unlikely + thereafter, thus can potentially fail, though unlikely. If initial seeding + fails, a panic will result. If a failure happens during reseeding (less + likely) then the RNG continues without reseeding; a log message (warning) + is emitted if logging is enabled. [`Rng::try_fill`]: ../rand/rand/trait.Rng.html#method.try_fill [`RngCore::try_fill_bytes`]: ../rand/rand_core/trait.RngCore.html#tymethod.try_fill_bytes