Merge branch 'master' into dirichlet-small-alpha

This commit is contained in:
warren 2023-02-27 19:22:54 -05:00
commit c44ac16e40
79 changed files with 2603 additions and 1162 deletions

View File

@ -1,5 +1,10 @@
name: gh-pages
permissions:
contents: read
pages: write
id-token: write
on:
push:
branches:
@ -9,23 +14,33 @@ jobs:
deploy:
name: GH-pages documentation
runs-on: ubuntu-latest
environment:
name: github-pages
url: https://rust-random.github.io/rand/
steps:
- uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: doc (rand)
uses: dtolnay/rust-toolchain@nightly
- name: Build docs
env:
RUSTDOCFLAGS: --cfg doc_cfg
# --all builds all crates, but with default features for other crates (okay in this case)
run: |
cargo doc --all --features nightly,serde1,getrandom,small_rng
cp utils/redirect.html target/doc/index.html
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/doc
path: './target/doc'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

View File

@ -6,24 +6,23 @@ on:
pull_request:
branches: [ master, '0.[0-9]+' ]
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
check-doc:
name: Check doc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
uses: dtolnay/rust-toolchain@nightly
- run: cargo install cargo-deadlinks
- name: doc (rand)
env:
RUSTDOCFLAGS: --cfg doc_cfg
# --all builds all crates, but with default features for other crates (okay in this case)
run: cargo deadlinks --ignore-fragments -- --all --features nightly,serde1,getrandom,small_rng,min_const_gen
run: cargo deadlinks --ignore-fragments -- --all --features nightly,serde1,getrandom,small_rng
test:
runs-on: ${{ matrix.os }}
@ -47,7 +46,8 @@ jobs:
# Test both windows-gnu and windows-msvc; use beta rust on one
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
toolchain: 1.36.0 # MSRV
variant: MSRV
toolchain: 1.56.0
- os: ubuntu-latest
deps: sudo apt-get update ; sudo apt install gcc-multilib
target: i686-unknown-linux-gnu
@ -58,18 +58,22 @@ jobs:
variant: minimal_versions
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: MSRV
if: ${{ matrix.variant == 'MSRV' }}
run: cp Cargo.lock.msrv Cargo.lock
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
profile: minimal
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}
override: true
- run: ${{ matrix.deps }}
- name: Maybe minimal versions
if: ${{ matrix.variant == 'minimal_versions' }}
run: cargo generate-lockfile -Z minimal-versions
run: |
cargo generate-lockfile -Z minimal-versions
# Overrides for dependencies with incorrect requirements (may need periodic updating)
cargo update -p regex --precise 1.5.1
- name: Maybe nightly
if: ${{ matrix.toolchain == 'nightly' }}
run: |
@ -77,21 +81,15 @@ jobs:
cargo test --target ${{ matrix.target }} --all-features
cargo test --target ${{ matrix.target }} --benches --features=nightly
cargo test --target ${{ matrix.target }} --manifest-path rand_distr/Cargo.toml --benches
cargo test --target ${{ matrix.target }} --lib --tests --no-default-features --features min_const_gen
cargo test --target ${{ matrix.target }} --lib --tests --no-default-features
- name: Test rand
run: |
cargo test --target ${{ matrix.target }} --lib --tests --no-default-features
cargo build --target ${{ matrix.target }} --no-default-features --features alloc,getrandom,small_rng
cargo test --target ${{ matrix.target }} --lib --tests --no-default-features --features=alloc,getrandom,small_rng
cargo test --target ${{ matrix.target }} --examples
- name: Test rand (all stable features, non-MSRV)
if: ${{ matrix.toolchain != '1.36.0' }}
- name: Test rand (all stable features)
run: |
cargo test --target ${{ matrix.target }} --features=serde1,log,small_rng,min_const_gen
- name: Test rand (all stable features, MSRV)
if: ${{ matrix.toolchain == '1.36.0' }}
run: |
# const generics are not stable on 1.36.0
cargo test --target ${{ matrix.target }} --features=serde1,log,small_rng
- name: Test rand_core
run: |
@ -119,16 +117,14 @@ jobs:
toolchain: stable
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
profile: minimal
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}
override: true
- name: Cache cargo plugins
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: ~/.cargo/bin/
key: ${{ runner.os }}-cargo-plugins
@ -147,12 +143,12 @@ jobs:
test-miri:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
run: |
MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
rustup default "$MIRI_NIGHTLY"
rustup component add miri
rustup toolchain install nightly --component miri
rustup override set nightly
cargo miri setup
- name: Test rand
run: |
cargo miri test --no-default-features --lib --tests
@ -167,41 +163,33 @@ jobs:
test-no-std:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@nightly
with:
profile: minimal
toolchain: nightly
target: thumbv6m-none-eabi
override: true
- name: Build top-level only
run: cargo build --target=thumbv6m-none-eabi --no-default-features
test-avr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-01-07 # Pinned compiler version due to https://github.com/rust-lang/compiler-builtins/issues/400
components: rust-src
override: true
- name: Build top-level only
run: cargo build -Z build-std=core --target=avr-unknown-gnu-atmega328 --no-default-features
# Disabled due to lack of known working compiler versions (not older than our MSRV)
# test-avr:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - name: Install toolchain
# uses: dtolnay/rust-toolchain@nightly
# with:
# components: rust-src
# - name: Build top-level only
# run: cargo build -Z build-std=core --target=avr-unknown-gnu-atmega328 --no-default-features
test-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@nightly
with:
profile: minimal
toolchain: nightly
target: aarch64-apple-ios
override: true
- name: Build top-level only
run: cargo build --target=aarch64-apple-ios

View File

@ -8,12 +8,24 @@ A [separate changelog is kept for rand_core](rand_core/CHANGELOG.md).
You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful.
## [0.9.0] - unreleased
### Distributions
- `{Uniform, UniformSampler}::{new, new_inclusive}` return a `Result` (instead of potentially panicking) (#1229)
- `Uniform` implements `TryFrom` instead of `From` for ranges (#1229)
### Other
- Simpler and faster implementation of Floyd's F2 (#1277). This
changes some outputs from `rand::seq::index::sample` and
`rand::seq::SliceRandom::choose_multiple`.
## [0.8.5] - 2021-08-20
### Fixes
- Fix build on non-32/64-bit architectures (#1144)
- Fix "min_const_gen" feature for `no_std` (#1173)
- Check `libc::pthread_atfork` return value with panic on error (#1178)
- More robust reseeding in case `ReseedingRng` is used from a fork handler (#1178)
- Fix nightly: remove unused `slice_partition_at_index` feature (#1215)
- Fix nightly + `simd_support`: update `packed_simd` (#1216)
### Rngs
- `StdRng`: Switch from HC128 to ChaCha12 on emscripten (#1142).

707
Cargo.lock.msrv Normal file
View File

@ -0,0 +1,707 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "average"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843ec791d3f24503bbf72bbd5e49a3ab4dbb4bcd0a8ef6b0c908efa73caa27b1"
dependencies = [
"easy-cast",
"float-ord",
"num-traits",
]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]]
name = "easy-cast"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd102ee8c418348759919b83b81cdbdc933ffe29740b903df448b4bafaa348e"
dependencies = [
"libm",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "float-ord"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]]
name = "libm"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "num_cpus"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.9.0"
dependencies = [
"bincode",
"criterion",
"libc",
"log",
"rand_chacha",
"rand_core",
"rand_pcg",
"rayon",
"serde",
]
[[package]]
name = "rand_chacha"
version = "0.4.0"
dependencies = [
"ppv-lite86",
"rand_core",
"serde",
"serde_json",
]
[[package]]
name = "rand_core"
version = "0.7.0"
dependencies = [
"getrandom",
"serde",
]
[[package]]
name = "rand_distr"
version = "0.5.0"
dependencies = [
"average",
"num-traits",
"rand",
"rand_pcg",
"serde",
"special",
]
[[package]]
name = "rand_pcg"
version = "0.4.0"
dependencies = [
"bincode",
"rand_core",
"serde",
]
[[package]]
name = "rayon"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "special"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a65e074159b75dcf173a4733ab2188baac24967b5c8ec9ed87ae15fcbc7636"
dependencies = [
"libc",
]
[[package]]
name = "syn"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -1,6 +1,6 @@
[package]
name = "rand"
version = "0.8.5"
version = "0.9.0"
authors = ["The Rand Project Developers", "The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@ -13,7 +13,8 @@ Random number generators and other randomness functionality.
keywords = ["random", "rng"]
categories = ["algorithms", "no-std"]
autobenches = true
edition = "2018"
edition = "2021"
rust-version = "1.56"
include = ["src/", "LICENSE-*", "README.md", "CHANGELOG.md", "COPYRIGHT"]
[package.metadata.docs.rs]
@ -28,7 +29,7 @@ features = ["small_rng", "serde1"]
[features]
# Meta-features:
default = ["std", "std_rng"]
nightly = [] # enables performance optimizations requiring nightly rust
nightly = [] # some additions requiring nightly Rust
serde1 = ["serde", "rand_core/serde1"]
# Option (enabled by default): without "std" rand uses libcore; this option
@ -41,8 +42,8 @@ alloc = ["rand_core/alloc"]
# Option: use getrandom package for seeding
getrandom = ["rand_core/getrandom"]
# Option (requires nightly): experimental SIMD support
simd_support = ["packed_simd"]
# Option (requires nightly Rust): experimental SIMD support
simd_support = []
# Option (enabled by default): enable StdRng
std_rng = ["rand_chacha"]
@ -50,10 +51,6 @@ std_rng = ["rand_chacha"]
# Option: enable SmallRng
small_rng = []
# Option: for rustc ≥ 1.51, enable generating random arrays of any size
# using min-const-generics
min_const_gen = []
[workspace]
members = [
"rand_core",
@ -63,23 +60,28 @@ members = [
]
[dependencies]
rand_core = { path = "rand_core", version = "0.6.0" }
rand_core = { path = "rand_core", version = "0.7.0" }
log = { version = "0.4.4", optional = true }
serde = { version = "1.0.103", features = ["derive"], optional = true }
rand_chacha = { path = "rand_chacha", version = "0.3.0", default-features = false, optional = true }
[dependencies.packed_simd]
# NOTE: so far no version works reliably due to dependence on unstable features
package = "packed_simd_2"
version = "0.3.6"
optional = true
features = ["into_bits"]
rand_chacha = { path = "rand_chacha", version = "0.4.0", default-features = false, optional = true }
[target.'cfg(unix)'.dependencies]
# Used for fork protection (reseeding.rs)
libc = { version = "0.2.22", optional = true, default-features = false }
[dev-dependencies]
rand_pcg = { path = "rand_pcg", version = "0.3.0" }
rand_pcg = { path = "rand_pcg", version = "0.4.0" }
# Only to test serde1
bincode = "1.2.1"
rayon = "1.5.3"
criterion = { version = "0.4" }
[[bench]]
name = "seq_choose"
path = "benches/seq_choose.rs"
harness = false
[[bench]]
name = "shuffle"
path = "benches/shuffle.rs"
harness = false

View File

@ -5,7 +5,7 @@
[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand)
[![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
A Rust library for random number generation, featuring:
@ -51,7 +51,7 @@ Add this to your `Cargo.toml`:
```toml
[dependencies]
rand = "0.8.4"
rand = "0.8.5"
```
To get started using Rand, see [The Book](https://rust-random.github.io/book).
@ -97,15 +97,8 @@ issue tracker with the keyword `yank` *should* uncover the motivation.
### Rust version requirements
Since version 0.8, Rand requires **Rustc version 1.36 or greater**.
Rand 0.7 requires Rustc 1.32 or greater while versions 0.5 require Rustc 1.22 or
greater, and 0.4 and 0.3 (since approx. June 2017) require Rustc version 1.15 or
greater. Subsets of the Rand code may work with older Rust versions, but this is
not supported.
Continuous Integration (CI) will always test the minimum supported Rustc version
(the MSRV). The current policy is that this can be updated in any
Rand release if required, but the change must be noted in the changelog.
The Minimum Supported Rust Version (MSRV) is `rustc >= 1.56.0`.
Older releases may work (depending on feature configuration) but are untested.
## Crate Features
@ -125,11 +118,9 @@ Optionally, the following dependencies can be enabled:
Additionally, these features configure Rand:
- `small_rng` enables inclusion of the `SmallRng` PRNG
- `nightly` enables some optimizations requiring nightly Rust
- `nightly` includes some additions requiring nightly Rust
- `simd_support` (experimental) enables sampling of SIMD values
(uniformly random SIMD integers and floats), requiring nightly Rust
- `min_const_gen` enables generating random arrays of
any size using min-const-generics, requiring Rust ≥ 1.51.
Note that nightly features are not stable and therefore not all library and
compiler versions will be compatible. This is especially true of Rand's
@ -143,10 +134,13 @@ unavailable.
### WASM support
The WASM target `wasm32-unknown-unknown` is not *automatically* supported by
`rand` or `getrandom`. To solve this, either use a different target such as
`wasm32-wasi` or add a direct dependency on `getrandom` with the `js` feature
(if the target supports JavaScript). See
Seeding entropy from OS on WASM target `wasm32-unknown-unknown` is not
*automatically* supported by `rand` or `getrandom`. If you are fine with
seeding the generator manually, you can disable the `getrandom` feature
and use the methods on the `SeedableRng` trait. To enable seeding from OS,
either use a different target such as `wasm32-wasi` or add a direct
dependency on `getrandom` with the `js` feature (if the target supports
JavaScript). See
[getrandom#WebAssembly support](https://docs.rs/getrandom/latest/getrandom/#webassembly-support).
# License

View File

@ -131,36 +131,36 @@ macro_rules! distr {
}
// uniform
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));
distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7));
distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43));
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100).unwrap());
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000).unwrap());
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000).unwrap());
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123).unwrap());
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789).unwrap());
distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7).unwrap());
distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43).unwrap());
#[cfg(target_pointer_width = "64")]
distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8));
distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057));
distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8).unwrap());
distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057).unwrap());
distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));
distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319).unwrap());
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319).unwrap());
const LARGE_SEC: u64 = u64::max_value() / 1000;
distr_duration!(distr_uniform_duration_largest,
Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))
Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999)).unwrap()
);
distr_duration!(distr_uniform_duration_large,
Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2))
Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2)).unwrap()
);
distr_duration!(distr_uniform_duration_one,
Uniform::new(Duration::new(0, 0), Duration::new(1, 0))
Uniform::new(Duration::new(0, 0), Duration::new(1, 0)).unwrap()
);
distr_duration!(distr_uniform_duration_variety,
Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954))
Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954)).unwrap()
);
distr_duration!(distr_uniform_duration_edge,
Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1))
Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1)).unwrap()
);
// standard
@ -272,7 +272,7 @@ macro_rules! uniform_sample {
let high = black_box($high);
b.iter(|| {
for _ in 0..10 {
let dist = UniformInt::<$type>::new(low, high);
let dist = UniformInt::<$type>::new(low, high).unwrap();
for _ in 0..$count {
black_box(dist.sample(&mut rng));
}
@ -291,7 +291,7 @@ macro_rules! uniform_inclusive {
let high = black_box($high);
b.iter(|| {
for _ in 0..10 {
let dist = UniformInt::<$type>::new_inclusive(low, high);
let dist = UniformInt::<$type>::new_inclusive(low, high).unwrap();
for _ in 0..$count {
black_box(dist.sample(&mut rng));
}
@ -311,7 +311,7 @@ macro_rules! uniform_single {
let high = black_box($high);
b.iter(|| {
for _ in 0..(10 * $count) {
black_box(UniformInt::<$type>::sample_single(low, high, &mut rng));
black_box(UniformInt::<$type>::sample_single(low, high, &mut rng).unwrap());
}
});
}

View File

@ -13,9 +13,9 @@ extern crate test;
use test::Bencher;
use core::mem::size_of;
use rand::prelude::*;
use rand::seq::*;
use core::mem::size_of;
// We force use of 32-bit RNG since seq code is optimised for use with 32-bit
// generators on all platforms.
@ -74,76 +74,6 @@ seq_slice_choose_multiple!(seq_slice_choose_multiple_950_of_1000, 950, 1000);
seq_slice_choose_multiple!(seq_slice_choose_multiple_10_of_100, 10, 100);
seq_slice_choose_multiple!(seq_slice_choose_multiple_90_of_100, 90, 100);
#[bench]
fn seq_iter_choose_from_1000(b: &mut Bencher) {
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
let x: &mut [usize] = &mut [1; 1000];
for (i, r) in x.iter_mut().enumerate() {
*r = i;
}
b.iter(|| {
let mut s = 0;
for _ in 0..RAND_BENCH_N {
s += x.iter().choose(&mut rng).unwrap();
}
s
});
b.bytes = size_of::<usize>() as u64 * crate::RAND_BENCH_N;
}
#[derive(Clone)]
struct UnhintedIterator<I: Iterator + Clone> {
iter: I,
}
impl<I: Iterator + Clone> Iterator for UnhintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[derive(Clone)]
struct WindowHintedIterator<I: ExactSizeIterator + Iterator + Clone> {
iter: I,
window_size: usize,
}
impl<I: ExactSizeIterator + Iterator + Clone> Iterator for WindowHintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(core::cmp::min(self.iter.len(), self.window_size), None)
}
}
#[bench]
fn seq_iter_unhinted_choose_from_1000(b: &mut Bencher) {
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
let x: &[usize] = &[1; 1000];
b.iter(|| {
UnhintedIterator { iter: x.iter() }
.choose(&mut rng)
.unwrap()
})
}
#[bench]
fn seq_iter_window_hinted_choose_from_1000(b: &mut Bencher) {
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();
let x: &[usize] = &[1; 1000];
b.iter(|| {
WindowHintedIterator {
iter: x.iter(),
window_size: 7,
}
.choose(&mut rng)
})
}
#[bench]
fn seq_iter_choose_multiple_10_of_100(b: &mut Bencher) {
let mut rng = SmallRng::from_rng(thread_rng()).unwrap();

111
benches/seq_choose.rs Normal file
View File

@ -0,0 +1,111 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::prelude::*;
use rand::SeedableRng;
criterion_group!(
name = benches;
config = Criterion::default();
targets = bench
);
criterion_main!(benches);
pub fn bench(c: &mut Criterion) {
bench_rng::<rand_chacha::ChaCha20Rng>(c, "ChaCha20");
bench_rng::<rand_pcg::Pcg32>(c, "Pcg32");
bench_rng::<rand_pcg::Pcg64>(c, "Pcg64");
}
fn bench_rng<Rng: RngCore + SeedableRng>(c: &mut Criterion, rng_name: &'static str) {
for length in [1, 2, 3, 10, 100, 1000].map(|x| black_box(x)) {
c.bench_function(
format!("choose_size-hinted_from_{length}_{rng_name}").as_str(),
|b| {
let mut rng = Rng::seed_from_u64(123);
b.iter(|| choose_size_hinted(length, &mut rng))
},
);
c.bench_function(
format!("choose_stable_from_{length}_{rng_name}").as_str(),
|b| {
let mut rng = Rng::seed_from_u64(123);
b.iter(|| choose_stable(length, &mut rng))
},
);
c.bench_function(
format!("choose_unhinted_from_{length}_{rng_name}").as_str(),
|b| {
let mut rng = Rng::seed_from_u64(123);
b.iter(|| choose_unhinted(length, &mut rng))
},
);
c.bench_function(
format!("choose_windowed_from_{length}_{rng_name}").as_str(),
|b| {
let mut rng = Rng::seed_from_u64(123);
b.iter(|| choose_windowed(length, 7, &mut rng))
},
);
}
}
fn choose_size_hinted<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose(rng)
}
fn choose_stable<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose_stable(rng)
}
fn choose_unhinted<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = UnhintedIterator { iter: (0..max) };
iterator.choose(rng)
}
fn choose_windowed<R: Rng>(max: usize, window_size: usize, rng: &mut R) -> Option<usize> {
let iterator = WindowHintedIterator {
iter: (0..max),
window_size,
};
iterator.choose(rng)
}
#[derive(Clone)]
struct UnhintedIterator<I: Iterator + Clone> {
iter: I,
}
impl<I: Iterator + Clone> Iterator for UnhintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[derive(Clone)]
struct WindowHintedIterator<I: ExactSizeIterator + Iterator + Clone> {
iter: I,
window_size: usize,
}
impl<I: ExactSizeIterator + Iterator + Clone> Iterator for WindowHintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(core::cmp::min(self.iter.len(), self.window_size), None)
}
}

50
benches/shuffle.rs Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::prelude::*;
use rand::SeedableRng;
criterion_group!(
name = benches;
config = Criterion::default();
targets = bench
);
criterion_main!(benches);
pub fn bench(c: &mut Criterion) {
bench_rng::<rand_chacha::ChaCha12Rng>(c, "ChaCha12");
bench_rng::<rand_pcg::Pcg32>(c, "Pcg32");
bench_rng::<rand_pcg::Pcg64>(c, "Pcg64");
}
fn bench_rng<Rng: RngCore + SeedableRng>(c: &mut Criterion, rng_name: &'static str) {
for length in [1, 2, 3, 10, 100, 1000, 10000].map(|x| black_box(x)) {
c.bench_function(format!("shuffle_{length}_{rng_name}").as_str(), |b| {
let mut rng = Rng::seed_from_u64(123);
let mut vec: Vec<usize> = (0..length).collect();
b.iter(|| {
vec.shuffle(&mut rng);
vec[0]
})
});
if length >= 10 {
c.bench_function(
format!("partial_shuffle_{length}_{rng_name}").as_str(),
|b| {
let mut rng = Rng::seed_from_u64(123);
let mut vec: Vec<usize> = (0..length).collect();
b.iter(|| {
vec.partial_shuffle(&mut rng, length / 2);
vec[0]
})
},
);
}
}
}

View File

@ -29,7 +29,7 @@
use rand::distributions::{Distribution, Uniform};
fn main() {
let range = Uniform::new(-1.0f64, 1.0);
let range = Uniform::new(-1.0f64, 1.0).unwrap();
let mut rng = rand::thread_rng();
let total = 1_000_000;

View File

@ -80,7 +80,7 @@ fn main() {
let num_simulations = 10000;
let mut rng = rand::thread_rng();
let random_door = Uniform::new(0u32, 3);
let random_door = Uniform::new(0u32, 3).unwrap();
let (mut switch_wins, mut switch_losses) = (0, 0);
let (mut keep_wins, mut keep_losses) = (0, 0);

View File

@ -0,0 +1,82 @@
// Copyright 2018 Developers of the Rand project.
// Copyright 2013-2018 The Rust Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! # Monte Carlo estimation of π with a chosen seed and rayon for parallelism
//!
//! Imagine that we have a square with sides of length 2 and a unit circle
//! (radius = 1), both centered at the origin. The areas are:
//!
//! ```text
//! area of circle = πr² = π * r * r = π
//! area of square = 2² = 4
//! ```
//!
//! The circle is entirely within the square, so if we sample many points
//! randomly from the square, roughly π / 4 of them should be inside the circle.
//!
//! We can use the above fact to estimate the value of π: pick many points in
//! the square at random, calculate the fraction that fall within the circle,
//! and multiply this fraction by 4.
//!
//! Note on determinism:
//! It's slightly tricky to build a parallel simulation using Rayon
//! which is both efficient *and* reproducible.
//!
//! Rayon's ParallelIterator api does not guarantee that the work will be
//! batched into identical batches on every run, so we can't simply use
//! map_init to construct one RNG per Rayon batch.
//!
//! Instead, we do our own batching, so that a Rayon work item becomes a
//! batch. Then we can fix our rng stream to the batched work item.
//! Batching amortizes the cost of constructing the Rng from a fixed seed
//! over BATCH_SIZE trials. Manually batching also turns out to be faster
//! for the nondeterministic version of this program as well.
#![cfg(all(feature = "std", feature = "std_rng"))]
use rand::distributions::{Distribution, Uniform};
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
use rayon::prelude::*;
static SEED: u64 = 0;
static BATCH_SIZE: u64 = 10_000;
static BATCHES: u64 = 1000;
fn main() {
let range = Uniform::new(-1.0f64, 1.0).unwrap();
let in_circle = (0..BATCHES)
.into_par_iter()
.map(|i| {
let mut rng = ChaCha8Rng::seed_from_u64(SEED);
// We chose ChaCha because it's fast, has suitable statistical properties for simulation,
// and because it supports this set_stream() api, which lets us choose a different stream
// per work item. ChaCha supports 2^64 independent streams.
rng.set_stream(i);
let mut count = 0;
for _ in 0..BATCH_SIZE {
let a = range.sample(&mut rng);
let b = range.sample(&mut rng);
if a * a + b * b <= 1.0 {
count += 1;
}
}
count
})
.sum::<usize>();
// assert this is deterministic
assert_eq!(in_circle, 7852263);
// prints something close to 3.14159...
println!(
"π is approximately {}",
4. * (in_circle as f64) / ((BATCH_SIZE * BATCHES) as f64)
);
}

View File

@ -1,6 +1,6 @@
[package]
name = "rand_chacha"
version = "0.3.1"
version = "0.4.0"
authors = ["The Rand Project Developers", "The Rust Project Developers", "The CryptoCorrosion Contributors"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@ -12,10 +12,11 @@ ChaCha random number generator
"""
keywords = ["random", "rng", "chacha"]
categories = ["algorithms", "no-std"]
edition = "2018"
edition = "2021"
rust-version = "1.56"
[dependencies]
rand_core = { path = "../rand_core", version = "0.6.0" }
rand_core = { path = "../rand_core", version = "0.7.0" }
ppv-lite86 = { version = "0.2.14", default-features = false, features = ["simd"] }
serde = { version = "1.0", features = ["derive"], optional = true }

View File

@ -5,7 +5,7 @@
[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_chacha)
[![API](https://docs.rs/rand_chacha/badge.svg)](https://docs.rs/rand_chacha)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
A cryptographically secure random number generator that uses the ChaCha
algorithm.

View File

@ -13,7 +13,7 @@
use self::core::fmt;
use crate::guts::ChaCha;
use rand_core::block::{BlockRng, BlockRngCore};
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, Error, RngCore, SeedableRng};
#[cfg(feature = "serde1")] use serde::{Serialize, Deserialize, Serializer, Deserializer};
@ -99,7 +99,7 @@ macro_rules! chacha_impl {
}
}
impl CryptoRng for $ChaChaXCore {}
impl CryptoBlockRng for $ChaChaXCore {}
/// A cryptographically secure random number generator that uses the ChaCha algorithm.
///
@ -626,12 +626,12 @@ mod test {
#[test]
fn test_trait_objects() {
use rand_core::CryptoRngCore;
use rand_core::CryptoRng;
let rng = &mut ChaChaRng::from_seed(Default::default()) as &mut dyn CryptoRngCore;
let r1 = rng.next_u64();
let rng: &mut dyn RngCore = rng.as_rngcore();
let r2 = rng.next_u64();
assert_ne!(r1, r2);
let mut rng1 = ChaChaRng::from_seed(Default::default());
let rng2 = &mut rng1.clone() as &mut dyn CryptoRng;
for _ in 0..1000 {
assert_eq!(rng1.next_u64(), rng2.next_u64());
}
}
}

View File

@ -13,6 +13,7 @@
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://rust-random.github.io/rand/"
)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

View File

@ -4,9 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.4] - 2021-08-20
### Fixed
## [0.6.4] - 2022-09-15
- Fix unsoundness in `<BlockRng64 as RngCore>::next_u32` (#1160)
- Reduce use of `unsafe` and improve gen_bytes performance (#1180)
- Add `CryptoRngCore` trait (#1187, #1230)
## [0.6.3] - 2021-06-15
### Changed

View File

@ -1,6 +1,6 @@
[package]
name = "rand_core"
version = "0.6.4"
version = "0.7.0"
authors = ["The Rand Project Developers", "The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@ -12,7 +12,8 @@ Core random number generator traits and tools for implementation.
"""
keywords = ["random", "rng"]
categories = ["algorithms", "no-std"]
edition = "2018"
edition = "2021"
rust-version = "1.56"
[package.metadata.docs.rs]
# To build locally:

View File

@ -5,7 +5,7 @@
[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_core)
[![API](https://docs.rs/rand_core/badge.svg)](https://docs.rs/rand_core)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
Core traits and error types of the [rand] library, plus tools for implementing
RNGs.
@ -43,7 +43,7 @@ The traits and error types are also available via `rand`.
The current version is:
```
rand_core = "0.6.0"
rand_core = "0.6.4"
```
Rand libs have inter-dependencies and make use of the

View File

@ -43,7 +43,7 @@
//! }
//! }
//!
//! // optionally, also implement CryptoRng for MyRngCore
//! // optionally, also implement CryptoBlockRng for MyRngCore
//!
//! // Final RNG.
//! let mut rng = BlockRng::<MyRngCore>::seed_from_u64(0);
@ -54,7 +54,7 @@
//! [`fill_bytes`]: RngCore::fill_bytes
use crate::impls::{fill_via_u32_chunks, fill_via_u64_chunks};
use crate::{CryptoRng, Error, RngCore, SeedableRng};
use crate::{Error, CryptoRng, RngCore, SeedableRng};
use core::convert::AsRef;
use core::fmt;
#[cfg(feature = "serde1")]
@ -77,6 +77,12 @@ pub trait BlockRngCore {
fn generate(&mut self, results: &mut Self::Results);
}
/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// See [`CryptoRng`][crate::CryptoRng] docs for more information.
pub trait CryptoBlockRng: BlockRngCore { }
/// A wrapper type implementing [`RngCore`] for some type implementing
/// [`BlockRngCore`] with `u32` array buffer; i.e. this can be used to implement
/// a full RNG from just a `generate` function.
@ -178,10 +184,7 @@ impl<R: BlockRngCore> BlockRng<R> {
}
}
impl<R: BlockRngCore<Item = u32>> RngCore for BlockRng<R>
where
<R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]>,
{
impl<R: BlockRngCore<Item = u32>> RngCore for BlockRng<R> {
#[inline]
fn next_u32(&mut self) -> u32 {
if self.index >= self.results.as_ref().len() {
@ -226,7 +229,7 @@ where
self.generate_and_set(0);
}
let (consumed_u32, filled_u8) =
fill_via_u32_chunks(&self.results.as_ref()[self.index..], &mut dest[read_len..]);
fill_via_u32_chunks(&mut self.results.as_mut()[self.index..], &mut dest[read_len..]);
self.index += consumed_u32;
read_len += filled_u8;
@ -259,6 +262,8 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> {
}
}
impl<R: CryptoBlockRng + BlockRngCore<Item = u32>> CryptoRng for BlockRng<R> {}
/// A wrapper type implementing [`RngCore`] for some type implementing
/// [`BlockRngCore`] with `u64` array buffer; i.e. this can be used to implement
/// a full RNG from just a `generate` function.
@ -346,10 +351,7 @@ impl<R: BlockRngCore> BlockRng64<R> {
}
}
impl<R: BlockRngCore<Item = u64>> RngCore for BlockRng64<R>
where
<R as BlockRngCore>::Results: AsRef<[u64]> + AsMut<[u64]>,
{
impl<R: BlockRngCore<Item = u64>> RngCore for BlockRng64<R> {
#[inline]
fn next_u32(&mut self) -> u32 {
let mut index = self.index - self.half_used as usize;
@ -387,13 +389,13 @@ where
let mut read_len = 0;
self.half_used = false;
while read_len < dest.len() {
if self.index as usize >= self.results.as_ref().len() {
if self.index >= self.results.as_ref().len() {
self.core.generate(&mut self.results);
self.index = 0;
}
let (consumed_u64, filled_u8) = fill_via_u64_chunks(
&self.results.as_ref()[self.index as usize..],
&mut self.results.as_mut()[self.index..],
&mut dest[read_len..],
);
@ -428,7 +430,7 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
}
}
impl<R: BlockRngCore + CryptoRng> CryptoRng for BlockRng<R> {}
impl<R: CryptoBlockRng + BlockRngCore<Item = u64>> CryptoRng for BlockRng64<R> {}
#[cfg(test)]
mod test {

View File

@ -53,16 +53,14 @@ pub fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
}
trait Observable: Copy {
type Bytes: AsRef<[u8]>;
fn to_le_bytes(self) -> Self::Bytes;
fn to_le(self) -> Self;
// Contract: observing self is memory-safe (implies no uninitialised padding)
fn as_byte_slice(x: &[Self]) -> &[u8];
}
impl Observable for u32 {
type Bytes = [u8; 4];
fn to_le_bytes(self) -> Self::Bytes {
self.to_le_bytes()
fn to_le(self) -> Self {
self.to_le()
}
fn as_byte_slice(x: &[Self]) -> &[u8] {
let ptr = x.as_ptr() as *const u8;
@ -71,9 +69,8 @@ impl Observable for u32 {
}
}
impl Observable for u64 {
type Bytes = [u8; 8];
fn to_le_bytes(self) -> Self::Bytes {
self.to_le_bytes()
fn to_le(self) -> Self {
self.to_le()
}
fn as_byte_slice(x: &[Self]) -> &[u8] {
let ptr = x.as_ptr() as *const u8;
@ -82,28 +79,27 @@ impl Observable for u64 {
}
}
fn fill_via_chunks<T: Observable>(src: &[T], dest: &mut [u8]) -> (usize, usize) {
/// Fill dest from src
///
/// Returns `(n, byte_len)`. `src[..n]` is consumed (and possibly mutated),
/// `dest[..byte_len]` is filled. `src[n..]` and `dest[byte_len..]` are left
/// unaltered.
fn fill_via_chunks<T: Observable>(src: &mut [T], dest: &mut [u8]) -> (usize, usize) {
let size = core::mem::size_of::<T>();
let byte_len = min(src.len() * size, dest.len());
let num_chunks = (byte_len + size - 1) / size;
if cfg!(target_endian = "little") {
// On LE we can do a simple copy, which is 25-50% faster:
dest[..byte_len].copy_from_slice(&T::as_byte_slice(&src[..num_chunks])[..byte_len]);
} else {
// This code is valid on all arches, but slower than the above:
let mut i = 0;
let mut iter = dest[..byte_len].chunks_exact_mut(size);
for chunk in &mut iter {
chunk.copy_from_slice(src[i].to_le_bytes().as_ref());
i += 1;
}
let chunk = iter.into_remainder();
if !chunk.is_empty() {
chunk.copy_from_slice(&src[i].to_le_bytes().as_ref()[..chunk.len()]);
// Byte-swap for portability of results. This must happen before copying
// since the size of dest is not guaranteed to be a multiple of T or to be
// sufficiently aligned.
if cfg!(target_endian = "big") {
for x in &mut src[..num_chunks] {
*x = x.to_le();
}
}
dest[..byte_len].copy_from_slice(&T::as_byte_slice(&src[..num_chunks])[..byte_len]);
(num_chunks, byte_len)
}
@ -112,6 +108,9 @@ fn fill_via_chunks<T: Observable>(src: &[T], dest: &mut [u8]) -> (usize, usize)
///
/// The return values are `(consumed_u32, filled_u8)`.
///
/// On big-endian systems, endianness of `src[..consumed_u32]` values is
/// swapped. No other adjustments to `src` are made.
///
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
/// the length of `dest`.
/// `consumed_u32` is the number of words consumed from `src`, which is the same
@ -137,7 +136,7 @@ fn fill_via_chunks<T: Observable>(src: &[T], dest: &mut [u8]) -> (usize, usize)
/// }
/// }
/// ```
pub fn fill_via_u32_chunks(src: &[u32], dest: &mut [u8]) -> (usize, usize) {
pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks(src, dest)
}
@ -145,13 +144,17 @@ pub fn fill_via_u32_chunks(src: &[u32], dest: &mut [u8]) -> (usize, usize) {
/// based RNG.
///
/// The return values are `(consumed_u64, filled_u8)`.
///
/// On big-endian systems, endianness of `src[..consumed_u64]` values is
/// swapped. No other adjustments to `src` are made.
///
/// `filled_u8` is the number of filled bytes in `dest`, which may be less than
/// the length of `dest`.
/// `consumed_u64` is the number of words consumed from `src`, which is the same
/// as `filled_u8 / 8` rounded up.
///
/// See `fill_via_u32_chunks` for an example.
pub fn fill_via_u64_chunks(src: &[u64], dest: &mut [u8]) -> (usize, usize) {
pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks(src, dest)
}
@ -175,33 +178,41 @@ mod test {
#[test]
fn test_fill_via_u32_chunks() {
let src = [1, 2, 3];
let src_orig = [1, 2, 3];
let mut src = src_orig;
let mut dst = [0u8; 11];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (3, 11));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 11));
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0]);
let mut src = src_orig;
let mut dst = [0u8; 13];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (3, 12));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (3, 12));
assert_eq!(dst, [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0]);
let mut src = src_orig;
let mut dst = [0u8; 5];
assert_eq!(fill_via_u32_chunks(&src, &mut dst), (2, 5));
assert_eq!(fill_via_u32_chunks(&mut src, &mut dst), (2, 5));
assert_eq!(dst, [1, 0, 0, 0, 2]);
}
#[test]
fn test_fill_via_u64_chunks() {
let src = [1, 2];
let src_orig = [1, 2];
let mut src = src_orig;
let mut dst = [0u8; 11];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (2, 11));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 11));
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0]);
let mut src = src_orig;
let mut dst = [0u8; 17];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (2, 16));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (2, 16));
assert_eq!(dst, [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]);
let mut src = src_orig;
let mut dst = [0u8; 5];
assert_eq!(fill_via_u64_chunks(&src, &mut dst), (1, 5));
assert_eq!(fill_via_u64_chunks(&mut src, &mut dst), (1, 5));
assert_eq!(dst, [1, 0, 0, 0, 0]);
}
}

View File

@ -41,8 +41,8 @@
use core::convert::AsMut;
use core::default::Default;
#[cfg(feature = "std")] extern crate std;
#[cfg(feature = "alloc")] extern crate alloc;
#[cfg(feature = "std")] extern crate std;
#[cfg(feature = "alloc")] use alloc::boxed::Box;
pub use error::Error;
@ -182,10 +182,17 @@ pub trait RngCore {
/// `fill_bytes` may be implemented with
/// `self.try_fill_bytes(dest).unwrap()` or more specific error handling.
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error>;
/// Convert an [`RngCore`] to a [`RngReadAdapter`].
#[cfg(feature = "std")]
fn read_adapter(&mut self) -> RngReadAdapter<'_, Self>
where Self: Sized {
RngReadAdapter { inner: self }
}
}
/// A marker trait used to indicate that an [`RngCore`] or [`BlockRngCore`]
/// implementation is supposed to be cryptographically secure.
/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// *Cryptographically secure generators*, also known as *CSPRNGs*, should
/// satisfy an additional properties over other generators: given the first
@ -206,36 +213,7 @@ pub trait RngCore {
/// weaknesses such as seeding from a weak entropy source or leaking state.
///
/// [`BlockRngCore`]: block::BlockRngCore
pub trait CryptoRng {}
/// An extension trait that is automatically implemented for any type
/// implementing [`RngCore`] and [`CryptoRng`].
///
/// It may be used as a trait object, and supports upcasting to [`RngCore`] via
/// the [`CryptoRngCore::as_rngcore`] method.
///
/// # Example
///
/// ```
/// use rand_core::CryptoRngCore;
///
/// #[allow(unused)]
/// fn make_token(rng: &mut dyn CryptoRngCore) -> [u8; 32] {
/// let mut buf = [0u8; 32];
/// rng.fill_bytes(&mut buf);
/// buf
/// }
/// ```
pub trait CryptoRngCore: RngCore {
/// Upcast to an [`RngCore`] trait object.
fn as_rngcore(&mut self) -> &mut dyn RngCore;
}
impl<T: CryptoRng + RngCore> CryptoRngCore for T {
fn as_rngcore(&mut self) -> &mut dyn RngCore {
self
}
}
pub trait CryptoRng: RngCore {}
/// A random number generator that can be explicitly seeded.
///
@ -469,14 +447,37 @@ impl<R: RngCore + ?Sized> RngCore for Box<R> {
}
}
/// Adapter that enables reading through a [`io::Read`](std::io::Read) from a [`RngCore`].
///
/// # Examples
///
/// ```no_run
/// # use std::{io, io::Read};
/// # use std::fs::File;
/// # use rand_core::{OsRng, RngCore};
///
/// io::copy(&mut OsRng.read_adapter().take(100), &mut File::create("/tmp/random.bytes").unwrap()).unwrap();
/// ```
#[cfg(feature = "std")]
impl std::io::Read for dyn RngCore {
pub struct RngReadAdapter<'a, R: RngCore + ?Sized> {
inner: &'a mut R,
}
#[cfg(feature = "std")]
impl<R: RngCore + ?Sized> std::io::Read for RngReadAdapter<'_, R> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
self.try_fill_bytes(buf)?;
self.inner.try_fill_bytes(buf)?;
Ok(buf.len())
}
}
#[cfg(feature = "std")]
impl<R: RngCore + ?Sized> std::fmt::Debug for RngReadAdapter<'_, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ReadAdapter").finish()
}
}
// Implement `CryptoRng` for references to a `CryptoRng`.
impl<'a, R: CryptoRng + ?Sized> CryptoRng for &'a mut R {}

View File

@ -19,8 +19,9 @@ use getrandom::getrandom;
/// The implementation is provided by the [getrandom] crate. Refer to
/// [getrandom] documentation for details.
///
/// This struct is only available when specifying the crate feature `getrandom`
/// or `std`. When using the `rand` lib, it is also available as `rand::rngs::OsRng`.
/// This struct is available as `rand_core::OsRng` and as `rand::rngs::OsRng`.
/// In both cases, this requires the crate feature `getrandom` or `std`
/// (enabled by default in `rand` but not in `rand_core`).
///
/// # Blocking and error handling
///

View File

@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.5.0] - unreleased
- Remove unused fields from `Gamma`, `NormalInverseGaussian` and `Zipf` distributions (#1184)
This breaks serialization compatibility with older versions.
- Upgrade Rand
- Fix Knuth's method so `Poisson` doesn't return -1.0 for small lambda
- Fix `Poisson` distribution instantiation so it return an error if lambda is infinite
## [0.4.3] - 2021-12-30
- Fix `no_std` build (#1208)
## [0.4.2] - 2021-09-18
- New `Zeta` and `Zipf` distributions (#1136)
- New `SkewNormal` distribution (#1149)

View File

@ -1,6 +1,6 @@
[package]
name = "rand_distr"
version = "0.4.2"
version = "0.5.0"
authors = ["The Rand Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@ -12,7 +12,8 @@ Sampling from random number distributions
"""
keywords = ["random", "rng", "distribution", "probability"]
categories = ["algorithms", "no-std"]
edition = "2018"
edition = "2021"
rust-version = "1.56"
include = ["src/", "LICENSE-*", "README.md", "CHANGELOG.md", "COPYRIGHT"]
[features]
@ -23,14 +24,14 @@ std_math = ["num-traits/std"]
serde1 = ["serde", "rand/serde1"]
[dependencies]
rand = { path = "..", version = "0.8.0", default-features = false }
rand = { path = "..", version = "0.9.0", default-features = false }
num-traits = { version = "0.2", default-features = false, features = ["libm"] }
serde = { version = "1.0.103", features = ["derive"], optional = true }
[dev-dependencies]
rand_pcg = { version = "0.3.0", path = "../rand_pcg" }
rand_pcg = { version = "0.4.0", path = "../rand_pcg" }
# For inline examples
rand = { path = "..", version = "0.8.0", default-features = false, features = ["std_rng", "std", "small_rng"] }
rand = { path = "..", version = "0.9.0", default-features = false, features = ["std_rng", "std", "small_rng"] }
# Histogram implementation for testing uniformity
average = { version = "0.13", features = [ "std" ] }
# Special functions for testing distributions

View File

@ -5,7 +5,7 @@
[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_distr)
[![API](https://docs.rs/rand_distr/badge.svg)](https://docs.rs/rand_distr)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
Implements a full suite of random number distribution sampling routines.

View File

@ -4,7 +4,8 @@ version = "0.0.0"
authors = ["The Rand Project Developers"]
license = "MIT OR Apache-2.0"
description = "Criterion benchmarks of the rand_distr crate"
edition = "2018"
edition = "2021"
rust-version = "1.56"
publish = false
[workspace]
@ -19,4 +20,4 @@ rand_pcg = { path = "../../rand_pcg/" }
[[bench]]
name = "distributions"
path = "src/distributions.rs"
harness = false
harness = false

View File

@ -13,6 +13,8 @@ use crate::{Distribution, Uniform};
use rand::Rng;
use core::fmt;
use core::cmp::Ordering;
#[allow(unused_imports)]
use num_traits::Float;
/// The binomial distribution `Binomial(n, p)`.
///
@ -28,7 +30,7 @@ use core::cmp::Ordering;
/// let v = bin.sample(&mut rand::thread_rng());
/// println!("{} is from a binomial distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Binomial {
/// Number of trials.
@ -161,8 +163,8 @@ impl Distribution<u64> for Binomial {
// return value
let mut y: i64;
let gen_u = Uniform::new(0., p4);
let gen_v = Uniform::new(0., 1.);
let gen_u = Uniform::new(0., p4).unwrap();
let gen_v = Uniform::new(0., 1.).unwrap();
loop {
// Step 1: Generate `u` for selecting the region. If region 1 is
@ -345,4 +347,9 @@ mod test {
fn test_binomial_invalid_lambda_neg() {
Binomial::new(20, -10.0).unwrap();
}
#[test]
fn binomial_distributions_can_be_compared() {
assert_eq!(Binomial::new(1, 1.0), Binomial::new(1, 1.0));
}
}

View File

@ -31,7 +31,7 @@ use core::fmt;
/// let v = cau.sample(&mut rand::thread_rng());
/// println!("{} is from a Cauchy(2, 5) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Cauchy<F>
where F: Float + FloatConst, Standard: Distribution<F>
@ -164,4 +164,9 @@ mod test {
assert_almost_eq!(*a, *b, 1e-5);
}
}
#[test]
fn cauchy_distributions_can_be_compared() {
assert_eq!(Cauchy::new(1.0, 2.0), Cauchy::new(1.0, 2.0));
}
}

View File

@ -32,7 +32,7 @@ use alloc::{boxed::Box, vec, vec::Vec};
/// println!("{:?} is from a Dirichlet([1.0, 2.0, 3.0]) distribution", samples);
/// ```
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Dirichlet<F>
where
@ -292,4 +292,9 @@ mod test {
fn test_dirichlet_invalid_alpha() {
Dirichlet::new_with_size(0.0f64, 2).unwrap();
}
#[test]
fn dirichlet_distributions_can_be_compared() {
assert_eq!(Dirichlet::new(&[1.0, 2.0]), Dirichlet::new(&[1.0, 2.0]));
}
}

View File

@ -91,7 +91,7 @@ impl Distribution<f64> for Exp1 {
/// let v = exp.sample(&mut rand::thread_rng());
/// println!("{} is from a Exp(2) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Exp<F>
where F: Float, Exp1: Distribution<F>
@ -178,4 +178,9 @@ mod test {
fn test_exp_invalid_lambda_nan() {
Exp::new(f64::nan()).unwrap();
}
#[test]
fn exponential_distributions_can_be_compared() {
assert_eq!(Exp::new(1.0), Exp::new(1.0));
}
}

View File

@ -27,7 +27,7 @@ use rand::Rng;
/// let val: f64 = thread_rng().sample(Frechet::new(0.0, 1.0, 1.0).unwrap());
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Frechet<F>
where
@ -182,4 +182,9 @@ mod tests {
.zip(&probabilities)
.all(|(p_hat, p)| (p_hat - p).abs() < 0.003))
}
#[test]
fn frechet_distributions_can_be_compared() {
assert_eq!(Frechet::new(1.0, 2.0, 3.0), Frechet::new(1.0, 2.0, 3.0));
}
}

View File

@ -54,7 +54,7 @@ use serde::{Serialize, Deserialize};
/// Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3
/// (September 2000), 363-372.
/// DOI:[10.1145/358407.358414](https://doi.acm.org/10.1145/358407.358414)
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Gamma<F>
where
@ -91,7 +91,7 @@ impl fmt::Display for Error {
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl std::error::Error for Error {}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
enum GammaRepr<F>
where
@ -119,7 +119,7 @@ where
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
struct GammaSmallShape<F>
where
@ -135,7 +135,7 @@ where
///
/// See `Gamma` for sampling from a Gamma distribution with general
/// shape parameters.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
struct GammaLargeShape<F>
where
@ -280,7 +280,7 @@ where
/// let v = chi.sample(&mut rand::thread_rng());
/// println!("{} is from a χ²(11) distribution", v)
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct ChiSquared<F>
where
@ -314,7 +314,7 @@ impl fmt::Display for ChiSquaredError {
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl std::error::Error for ChiSquaredError {}
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
enum ChiSquaredRepr<F>
where
@ -385,7 +385,7 @@ where
/// let v = f.sample(&mut rand::thread_rng());
/// println!("{} is from an F(2, 32) distribution", v)
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct FisherF<F>
where
@ -472,7 +472,7 @@ where
/// let v = t.sample(&mut rand::thread_rng());
/// println!("{} is from a t(11) distribution", v)
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct StudentT<F>
where
@ -522,7 +522,7 @@ where
/// Generating beta variates with nonintegral shape parameters.
/// Communications of the ACM 21, 317-322.
/// https://doi.org/10.1145/359460.359482
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
enum BetaAlgorithm<N> {
BB(BB<N>),
@ -530,7 +530,7 @@ enum BetaAlgorithm<N> {
}
/// Algorithm BB for `min(alpha, beta) > 1`.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
struct BB<N> {
alpha: N,
@ -539,12 +539,11 @@ struct BB<N> {
}
/// Algorithm BC for `min(alpha, beta) <= 1`.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
struct BC<N> {
alpha: N,
beta: N,
delta: N,
kappa1: N,
kappa2: N,
}
@ -560,7 +559,7 @@ struct BC<N> {
/// let v = beta.sample(&mut rand::thread_rng());
/// println!("{} is from a Beta(2, 5) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Beta<F>
where
@ -646,7 +645,7 @@ where
Ok(Beta {
a, b, switched_params,
algorithm: BetaAlgorithm::BC(BC {
alpha, beta, delta, kappa1, kappa2,
alpha, beta, kappa1, kappa2,
})
})
}
@ -811,4 +810,29 @@ mod test {
assert!(!beta.sample(&mut rng).is_nan(), "failed at i={}", i);
}
}
#[test]
fn gamma_distributions_can_be_compared() {
assert_eq!(Gamma::new(1.0, 2.0), Gamma::new(1.0, 2.0));
}
#[test]
fn beta_distributions_can_be_compared() {
assert_eq!(Beta::new(1.0, 2.0), Beta::new(1.0, 2.0));
}
#[test]
fn chi_squared_distributions_can_be_compared() {
assert_eq!(ChiSquared::new(1.0), ChiSquared::new(1.0));
}
#[test]
fn fisher_f_distributions_can_be_compared() {
assert_eq!(FisherF::new(1.0, 2.0), FisherF::new(1.0, 2.0));
}
#[test]
fn student_t_distributions_can_be_compared() {
assert_eq!(StudentT::new(1.0), StudentT::new(1.0));
}
}

View File

@ -3,6 +3,8 @@
use crate::Distribution;
use rand::Rng;
use core::fmt;
#[allow(unused_imports)]
use num_traits::Float;
/// The geometric distribution `Geometric(p)` bounded to `[0, u64::MAX]`.
///
@ -25,7 +27,7 @@ use core::fmt;
/// let v = geo.sample(&mut rand::thread_rng());
/// println!("{} is from a Geometric(0.25) distribution", v);
/// ```
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Geometric
{
@ -233,4 +235,9 @@ mod test {
results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>() / results.len() as f64;
assert!((variance - expected_variance).abs() < expected_variance / 10.0);
}
#[test]
fn geometric_distributions_can_be_compared() {
assert_eq!(Geometric::new(1.0), Geometric::new(1.0));
}
}

View File

@ -27,7 +27,7 @@ use rand::Rng;
/// let val: f64 = thread_rng().sample(Gumbel::new(0.0, 1.0).unwrap());
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Gumbel<F>
where
@ -152,4 +152,9 @@ mod tests {
.zip(&probabilities)
.all(|(p_hat, p)| (p_hat - p).abs() < 0.003))
}
#[test]
fn gumbel_distributions_can_be_compared() {
assert_eq!(Gumbel::new(1.0, 2.0), Gumbel::new(1.0, 2.0));
}
}

View File

@ -4,8 +4,10 @@ use crate::Distribution;
use rand::Rng;
use rand::distributions::uniform::Uniform;
use core::fmt;
#[allow(unused_imports)]
use num_traits::Float;
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
enum SamplingMethod {
InverseTransform{ initial_p: f64, initial_x: i64 },
@ -43,7 +45,7 @@ enum SamplingMethod {
/// let v = hypergeo.sample(&mut rand::thread_rng());
/// println!("{} is from a hypergeometric distribution", v);
/// ```
#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Hypergeometric {
n1: u64,
@ -249,7 +251,7 @@ impl Distribution<u64> for Hypergeometric {
x
},
RejectionAcceptance { m, a, lambda_l, lambda_r, x_l, x_r, p1, p2, p3 } => {
let distr_region_select = Uniform::new(0.0, p3);
let distr_region_select = Uniform::new(0.0, p3).unwrap();
loop {
let (y, v) = loop {
let u = distr_region_select.sample(rng);
@ -417,4 +419,9 @@ mod test {
test_hypergeometric_mean_and_variance(10100, 10000, 1000, &mut rng);
test_hypergeometric_mean_and_variance(100100, 100, 10000, &mut rng);
}
#[test]
fn hypergeometric_distributions_can_be_compared() {
assert_eq!(Hypergeometric::new(1, 2, 3), Hypergeometric::new(1, 2, 3));
}
}

View File

@ -26,7 +26,7 @@ impl fmt::Display for Error {
impl std::error::Error for Error {}
/// The [inverse Gaussian distribution](https://en.wikipedia.org/wiki/Inverse_Gaussian_distribution)
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct InverseGaussian<F>
where
@ -109,4 +109,9 @@ mod tests {
assert!(InverseGaussian::new(1.0, -1.0).is_err());
assert!(InverseGaussian::new(1.0, 1.0).is_ok());
}
#[test]
fn inverse_gaussian_distributions_can_be_compared() {
assert_eq!(InverseGaussian::new(1.0, 2.0), InverseGaussian::new(1.0, 2.0));
}
}

View File

@ -11,6 +11,7 @@
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://rust-random.github.io/rand/"
)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![allow(

View File

@ -112,7 +112,7 @@ impl Distribution<f64> for StandardNormal {
/// ```
///
/// [`StandardNormal`]: crate::StandardNormal
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Normal<F>
where F: Float, StandardNormal: Distribution<F>
@ -227,7 +227,7 @@ where F: Float, StandardNormal: Distribution<F>
/// let v = log_normal.sample(&mut rand::thread_rng());
/// println!("{} is from an ln N(2, 9) distribution", v)
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct LogNormal<F>
where F: Float, StandardNormal: Distribution<F>
@ -368,4 +368,14 @@ mod tests {
assert!(LogNormal::from_mean_cv(0.0, 1.0).is_err());
assert!(LogNormal::from_mean_cv(1.0, -1.0).is_err());
}
#[test]
fn normal_distributions_can_be_compared() {
assert_eq!(Normal::new(1.0, 2.0), Normal::new(1.0, 2.0));
}
#[test]
fn log_normal_distributions_can_be_compared() {
assert_eq!(LogNormal::new(1.0, 2.0), LogNormal::new(1.0, 2.0));
}
}

View File

@ -26,7 +26,7 @@ impl fmt::Display for Error {
impl std::error::Error for Error {}
/// The [normal-inverse Gaussian distribution](https://en.wikipedia.org/wiki/Normal-inverse_Gaussian_distribution)
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct NormalInverseGaussian<F>
where
@ -34,7 +34,6 @@ where
StandardNormal: Distribution<F>,
Standard: Distribution<F>,
{
alpha: F,
beta: F,
inverse_gaussian: InverseGaussian<F>,
}
@ -63,7 +62,6 @@ where
let inverse_gaussian = InverseGaussian::new(mu, F::one()).unwrap();
Ok(Self {
alpha,
beta,
inverse_gaussian,
})
@ -104,4 +102,9 @@ mod tests {
assert!(NormalInverseGaussian::new(1.0, 2.0).is_err());
assert!(NormalInverseGaussian::new(2.0, 1.0).is_ok());
}
#[test]
fn normal_inverse_gaussian_distributions_can_be_compared() {
assert_eq!(NormalInverseGaussian::new(1.0, 2.0), NormalInverseGaussian::new(1.0, 2.0));
}
}

View File

@ -23,7 +23,7 @@ use core::fmt;
/// let val: f64 = thread_rng().sample(Pareto::new(1., 2.).unwrap());
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Pareto<F>
where F: Float, OpenClosed01: Distribution<F>
@ -131,4 +131,9 @@ mod tests {
105.8826669383772,
]);
}
#[test]
fn pareto_distributions_can_be_compared() {
assert_eq!(Pareto::new(1.0, 2.0), Pareto::new(1.0, 2.0));
}
}

View File

@ -30,7 +30,7 @@ use core::fmt;
/// ```
///
/// [`Triangular`]: crate::Triangular
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Pert<F>
where
@ -146,4 +146,9 @@ mod test {
assert!(Pert::new(min, max, mode).is_err());
}
}
#[test]
fn pert_distributions_can_be_compared() {
assert_eq!(Pert::new(1.0, 3.0, 2.0), Pert::new(1.0, 3.0, 2.0));
}
}

View File

@ -28,7 +28,7 @@ use core::fmt;
/// let v = poi.sample(&mut rand::thread_rng());
/// println!("{} is from a Poisson(2) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Poisson<F>
where F: Float + FloatConst, Standard: Distribution<F>
@ -44,14 +44,17 @@ where F: Float + FloatConst, Standard: Distribution<F>
/// Error type returned from `Poisson::new`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Error {
/// `lambda <= 0` or `nan`.
/// `lambda <= 0`
ShapeTooSmall,
/// `lambda = ∞` or `lambda = nan`
NonFinite,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Error::ShapeTooSmall => "lambda is not positive in Poisson distribution",
Error::NonFinite => "lambda is infinite or nan in Poisson distribution",
})
}
}
@ -66,6 +69,9 @@ where F: Float + FloatConst, Standard: Distribution<F>
/// Construct a new `Poisson` with the given shape parameter
/// `lambda`.
pub fn new(lambda: F) -> Result<Poisson<F>, Error> {
if !lambda.is_finite() {
return Err(Error::NonFinite);
}
if !(lambda > F::zero()) {
return Err(Error::ShapeTooSmall);
}
@ -89,8 +95,8 @@ where F: Float + FloatConst, Standard: Distribution<F>
// for low expected values use the Knuth method
if self.lambda < F::from(12.0).unwrap() {
let mut result = F::zero();
let mut p = F::one();
let mut result = F::one();
let mut p = rng.gen::<F>();
while p > self.exp_lambda {
p = p*rng.gen::<F>();
result = result + F::one();
@ -161,10 +167,15 @@ mod test {
#[test]
fn test_poisson_avg() {
test_poisson_avg_gen::<f64>(10.0, 0.5);
test_poisson_avg_gen::<f64>(15.0, 0.5);
test_poisson_avg_gen::<f32>(10.0, 0.5);
test_poisson_avg_gen::<f32>(15.0, 0.5);
test_poisson_avg_gen::<f64>(10.0, 0.1);
test_poisson_avg_gen::<f64>(15.0, 0.1);
test_poisson_avg_gen::<f32>(10.0, 0.1);
test_poisson_avg_gen::<f32>(15.0, 0.1);
//Small lambda will use Knuth's method with exp_lambda == 1.0
test_poisson_avg_gen::<f32>(0.00000000000000005, 0.1);
test_poisson_avg_gen::<f64>(0.00000000000000005, 0.1);
}
#[test]
@ -173,9 +184,20 @@ mod test {
Poisson::new(0.0).unwrap();
}
#[test]
#[should_panic]
fn test_poisson_invalid_lambda_infinity() {
Poisson::new(f64::INFINITY).unwrap();
}
#[test]
#[should_panic]
fn test_poisson_invalid_lambda_neg() {
Poisson::new(-10.0).unwrap();
}
#[test]
fn poisson_distributions_can_be_compared() {
assert_eq!(Poisson::new(1.0), Poisson::new(1.0));
}
}

View File

@ -40,7 +40,7 @@ use rand::Rng;
/// [skew normal distribution]: https://en.wikipedia.org/wiki/Skew_normal_distribution
/// [`Normal`]: struct.Normal.html
/// [A Method to Simulate the Skew Normal Distribution]: https://dx.doi.org/10.4236/am.2014.513201
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct SkewNormal<F>
where
@ -253,4 +253,9 @@ mod tests {
assert!(value.is_nan());
}
}
#[test]
fn skew_normal_distributions_can_be_compared() {
assert_eq!(SkewNormal::new(1.0, 2.0, 3.0), SkewNormal::new(1.0, 2.0, 3.0));
}
}

View File

@ -31,7 +31,7 @@ use core::fmt;
/// ```
///
/// [`Pert`]: crate::Pert
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Triangular<F>
where F: Float, Standard: Distribution<F>
@ -130,4 +130,9 @@ mod test {
assert!(Triangular::new(min, max, mode).is_err());
}
}
#[test]
fn triangular_distributions_can_be_compared() {
assert_eq!(Triangular::new(1.0, 3.0, 2.0), Triangular::new(1.0, 3.0, 2.0));
}
}

View File

@ -31,7 +31,7 @@ pub struct UnitBall;
impl<F: Float + SampleUniform> Distribution<[F; 3]> for UnitBall {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [F; 3] {
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap());
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap()).unwrap();
let mut x1;
let mut x2;
let mut x3;

View File

@ -35,7 +35,7 @@ pub struct UnitCircle;
impl<F: Float + SampleUniform> Distribution<[F; 2]> for UnitCircle {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [F; 2] {
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap());
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap()).unwrap();
let mut x1;
let mut x2;
let mut sum;

View File

@ -30,7 +30,7 @@ pub struct UnitDisc;
impl<F: Float + SampleUniform> Distribution<[F; 2]> for UnitDisc {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [F; 2] {
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap());
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap()).unwrap();
let mut x1;
let mut x2;
loop {

View File

@ -34,7 +34,7 @@ pub struct UnitSphere;
impl<F: Float + SampleUniform> Distribution<[F; 3]> for UnitSphere {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> [F; 3] {
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap());
let uniform = Uniform::new(F::from(-1.).unwrap(), F::from(1.).unwrap()).unwrap();
loop {
let (x1, x2) = (uniform.sample(rng), uniform.sample(rng));
let sum = x1 * x1 + x2 * x2;

View File

@ -11,6 +11,7 @@
use crate::ziggurat_tables;
use rand::distributions::hidden_export::IntoFloat;
use rand::Rng;
use num_traits::Float;
/// Calculates ln(gamma(x)) (natural logarithm of the gamma
/// function) using the Lanczos approximation.
@ -25,7 +26,7 @@ use rand::Rng;
/// `Ag(z)` is an infinite series with coefficients that can be calculated
/// ahead of time - we use just the first 6 terms, which is good enough
/// for most purposes.
pub(crate) fn log_gamma<F: num_traits::Float>(x: F) -> F {
pub(crate) fn log_gamma<F: Float>(x: F) -> F {
// precalculated 6 coefficients for the first 6 terms of the series
let coefficients: [F; 6] = [
F::from(76.18009172947146).unwrap(),

View File

@ -23,7 +23,7 @@ use core::fmt;
/// let val: f64 = thread_rng().sample(Weibull::new(1., 10.).unwrap());
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Weibull<F>
where F: Float, OpenClosed01: Distribution<F>
@ -129,4 +129,9 @@ mod tests {
7.877212340241561,
]);
}
#[test]
fn weibull_distributions_can_be_compared() {
assert_eq!(Weibull::new(1.0, 2.0), Weibull::new(1.0, 2.0));
}
}

View File

@ -221,8 +221,8 @@ impl<W: AliasableWeight> WeightedAliasIndex<W> {
// Prepare distributions for sampling. Creating them beforehand improves
// sampling performance.
let uniform_index = Uniform::new(0, n);
let uniform_within_weight_sum = Uniform::new(W::ZERO, weight_sum);
let uniform_index = Uniform::new(0, n).unwrap();
let uniform_within_weight_sum = Uniform::new(W::ZERO, weight_sum).unwrap();
Ok(Self {
aliases: aliases.aliases,
@ -458,7 +458,7 @@ mod test {
let random_weight_distribution = Uniform::new_inclusive(
W::ZERO,
W::MAX / W::try_from_u32_lossy(NUM_WEIGHTS).unwrap(),
);
).unwrap();
for _ in 0..NUM_WEIGHTS {
weights.push(rng.sample(&random_weight_distribution));
}

View File

@ -46,7 +46,7 @@ use core::fmt;
///
/// [zeta distribution]: https://en.wikipedia.org/wiki/Zeta_distribution
/// [Non-Uniform Random Variate Generation]: https://doi.org/10.1007/978-1-4613-8643-8
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Zeta<F>
where F: Float, Standard: Distribution<F>, OpenClosed01: Distribution<F>
{
@ -142,10 +142,9 @@ where F: Float, Standard: Distribution<F>, OpenClosed01: Distribution<F>
/// due to Jason Crease[1].
///
/// [1]: https://jasoncrease.medium.com/rejection-sampling-the-zipf-distribution-6b359792cffa
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Zipf<F>
where F: Float, Standard: Distribution<F> {
n: F,
s: F,
t: F,
q: F,
@ -202,7 +201,7 @@ where F: Float, Standard: Distribution<F> {
};
debug_assert!(t > F::zero());
Ok(Zipf {
n, s, t, q
s, t, q
})
}
@ -371,4 +370,14 @@ mod tests {
1.0, 2.0, 3.0, 2.0
]);
}
#[test]
fn zipf_distributions_can_be_compared() {
assert_eq!(Zipf::new(1, 2.0), Zipf::new(1, 2.0));
}
#[test]
fn zeta_distributions_can_be_compared() {
assert_eq!(Zeta::new(1.0), Zeta::new(1.0));
}
}

View File

@ -89,8 +89,8 @@ fn normal() {
.fold(core::f64::NEG_INFINITY, |a, &b| a.max(b))
);
for (&d, &e) in diff.iter().zip(expected_error.iter()) {
// Difference larger than 3 standard deviations or cutoff
let tol = (3. * e).max(1e-4);
// Difference larger than 4 standard deviations or cutoff
let tol = (4. * e).max(1e-4);
assert!(d <= tol, "Difference = {} * tol", d / tol);
}
}
@ -172,8 +172,8 @@ fn skew_normal() {
.fold(core::f64::NEG_INFINITY, |a, &b| a.max(b))
);
for (&d, &e) in diff.iter().zip(expected_error.iter()) {
// Difference larger than 3 standard deviations or cutoff
let tol = (3. * e).max(1e-4);
// Difference larger than 4 standard deviations or cutoff
let tol = (4. * e).max(1e-4);
assert!(d <= tol, "Difference = {} * tol", d / tol);
}
}

View File

@ -64,7 +64,7 @@ fn test_samples<F: Debug + ApproxEq, D: Distribution<F>>(
}
#[test]
fn binominal_stability() {
fn binomial_stability() {
// We have multiple code paths: np < 10, p > 0.5
test_samples(353, Binomial::new(2, 0.7).unwrap(), &[1, 1, 2, 1]);
test_samples(353, Binomial::new(20, 0.3).unwrap(), &[7, 7, 5, 7]);

View File

@ -1,6 +1,6 @@
[package]
name = "rand_pcg"
version = "0.3.1"
version = "0.4.0"
authors = ["The Rand Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@ -12,13 +12,14 @@ Selected PCG random number generators
"""
keywords = ["random", "rng", "pcg"]
categories = ["algorithms", "no-std"]
edition = "2018"
edition = "2021"
rust-version = "1.56"
[features]
serde1 = ["serde"]
[dependencies]
rand_core = { path = "../rand_core", version = "0.6.0" }
rand_core = { path = "../rand_core", version = "0.7.0" }
serde = { version = "1", features = ["derive"], optional = true }
[dev-dependencies]

View File

@ -5,7 +5,7 @@
[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/)
[![API](https://img.shields.io/badge/api-master-yellow.svg)](https://rust-random.github.io/rand/rand_pcg)
[![API](https://docs.rs/rand_pcg/badge.svg)](https://docs.rs/rand_pcg)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)](https://github.com/rust-random/rand#rust-version-requirements)
Implements a selection of PCG random number generators.

View File

@ -33,6 +33,7 @@
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://rust-random.github.io/rand/"
)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![no_std]

View File

@ -19,7 +19,7 @@ where_single_line = true
# struct_field_align_threshold = 20
# Compatibility:
edition = "2018" # we require compatibility back to 1.32.0
edition = "2021"
# Misc:
inline_attribute_width = 80

View File

@ -14,6 +14,7 @@ use core::{fmt, u64};
#[cfg(feature = "serde1")]
use serde::{Serialize, Deserialize};
/// The Bernoulli distribution.
///
/// This is a special case of the Binomial distribution where `n = 1`.
@ -33,7 +34,7 @@ use serde::{Serialize, Deserialize};
/// This `Bernoulli` distribution uses 64 bits from the RNG (a `u64`),
/// so only probabilities that are multiples of 2<sup>-64</sup> can be
/// represented.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct Bernoulli {
/// Probability of success, relative to the maximal integer.
@ -147,10 +148,10 @@ mod test {
use crate::Rng;
#[test]
#[cfg(feature="serde1")]
#[cfg(feature = "serde1")]
fn test_serializing_deserializing_bernoulli() {
let coin_flip = Bernoulli::new(0.5).unwrap();
let de_coin_flip : Bernoulli = bincode::deserialize(&bincode::serialize(&coin_flip).unwrap()).unwrap();
let de_coin_flip: Bernoulli = bincode::deserialize(&bincode::serialize(&coin_flip).unwrap()).unwrap();
assert_eq!(coin_flip.p_int, de_coin_flip.p_int);
}
@ -211,4 +212,9 @@ mod test {
true, false, false, true, false, false, true, true, true, true
]);
}
#[test]
fn bernoulli_distributions_can_be_compared() {
assert_eq!(Bernoulli::new(1.0), Bernoulli::new(1.0));
}
}

View File

@ -64,7 +64,7 @@ pub trait Distribution<T> {
/// .collect();
///
/// // Dice-rolling:
/// let die_range = Uniform::new_inclusive(1, 6);
/// let die_range = Uniform::new_inclusive(1, 6).unwrap();
/// let mut roll_die = die_range.sample_iter(&mut rng);
/// while roll_die.next().unwrap() != 6 {
/// println!("Not a 6; rolling again!");
@ -93,7 +93,7 @@ pub trait Distribution<T> {
///
/// let mut rng = thread_rng();
///
/// let die = Uniform::new_inclusive(1, 6);
/// let die = Uniform::new_inclusive(1, 6).unwrap();
/// let even_number = die.map(|num| num % 2 == 0);
/// while !even_number.sample(&mut rng) {
/// println!("Still odd; rolling again!");
@ -112,7 +112,7 @@ pub trait Distribution<T> {
}
}
impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D {
impl<'a, T, D: Distribution<T> + ?Sized> Distribution<T> for &'a D {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T {
(*self).sample(rng)
}
@ -227,7 +227,7 @@ mod tests {
#[test]
fn test_distributions_map() {
let dist = Uniform::new_inclusive(0, 5).map(|val| val + 15);
let dist = Uniform::new_inclusive(0, 5).unwrap().map(|val| val + 15);
let mut rng = crate::test::rng(212);
let val = dist.sample(&mut rng);
@ -240,6 +240,7 @@ mod tests {
rng: &mut R,
) -> impl Iterator<Item = i32> + '_ {
Uniform::new_inclusive(1, 6)
.unwrap()
.sample_iter(rng)
.filter(|x| *x != 5)
.take(10)

View File

@ -8,11 +8,11 @@
//! Basic floating-point number distributions
use crate::distributions::utils::FloatSIMDUtils;
use crate::distributions::utils::{IntAsSIMD, FloatAsSIMD, FloatSIMDUtils};
use crate::distributions::{Distribution, Standard};
use crate::Rng;
use core::mem;
#[cfg(feature = "simd_support")] use packed_simd::*;
#[cfg(feature = "simd_support")] use core::simd::*;
#[cfg(feature = "serde1")]
use serde::{Serialize, Deserialize};
@ -99,7 +99,7 @@ macro_rules! float_impls {
// The exponent is encoded using an offset-binary representation
let exponent_bits: $u_scalar =
(($exponent_bias + exponent) as $u_scalar) << $fraction_bits;
$ty::from_bits(self | exponent_bits)
$ty::from_bits(self | $uty::splat(exponent_bits))
}
}
@ -108,13 +108,13 @@ macro_rules! float_impls {
// Multiply-based method; 24/53 random bits; [0, 1) interval.
// We use the most significant bits because for simple RNGs
// those are usually more random.
let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
let float_size = mem::size_of::<$f_scalar>() as $u_scalar * 8;
let precision = $fraction_bits + 1;
let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
let value: $uty = rng.gen();
let value = value >> (float_size - precision);
scale * $ty::cast_from_int(value)
let value = value >> $uty::splat(float_size - precision);
$ty::splat(scale) * $ty::cast_from_int(value)
}
}
@ -123,14 +123,14 @@ macro_rules! float_impls {
// Multiply-based method; 24/53 random bits; (0, 1] interval.
// We use the most significant bits because for simple RNGs
// those are usually more random.
let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
let float_size = mem::size_of::<$f_scalar>() as $u_scalar * 8;
let precision = $fraction_bits + 1;
let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
let value: $uty = rng.gen();
let value = value >> (float_size - precision);
let value = value >> $uty::splat(float_size - precision);
// Add 1 to shift up; will not overflow because of right-shift:
scale * $ty::cast_from_int(value + 1)
$ty::splat(scale) * $ty::cast_from_int(value + $uty::splat(1))
}
}
@ -140,11 +140,11 @@ macro_rules! float_impls {
// We use the most significant bits because for simple RNGs
// those are usually more random.
use core::$f_scalar::EPSILON;
let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
let float_size = mem::size_of::<$f_scalar>() as $u_scalar * 8;
let value: $uty = rng.gen();
let fraction = value >> (float_size - $fraction_bits);
fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0)
let fraction = value >> $uty::splat(float_size - $fraction_bits);
fraction.into_float_with_exponent(0) - $ty::splat(1.0 - EPSILON / 2.0)
}
}
}
@ -169,10 +169,10 @@ float_impls! { f64x4, u64x4, f64, u64, 52, 1023 }
#[cfg(feature = "simd_support")]
float_impls! { f64x8, u64x8, f64, u64, 52, 1023 }
#[cfg(test)]
mod tests {
use super::*;
use crate::distributions::utils::FloatAsSIMD;
use crate::rngs::mock::StepRng;
const EPSILON32: f32 = ::core::f32::EPSILON;
@ -182,29 +182,31 @@ mod tests {
($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
#[test]
fn $fnn() {
let two = $ty::splat(2.0);
// Standard
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.gen::<$ty>(), $ZERO);
let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
assert_eq!(one.gen::<$ty>(), $EPSILON / two);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
assert_eq!(max.gen::<$ty>(), $ty::splat(1.0) - $EPSILON / two);
// OpenClosed01
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0);
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), $ZERO + $EPSILON / two);
let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + $ty::splat(1.0));
// Open01
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
assert_eq!(zeros.sample::<$ty, _>(Open01), $ZERO + $EPSILON / two);
let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0);
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / two * $ty::splat(3.0));
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
assert_eq!(max.sample::<$ty, _>(Open01), $ty::splat(1.0) - $EPSILON / two);
}
};
}
@ -222,29 +224,31 @@ mod tests {
($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
#[test]
fn $fnn() {
let two = $ty::splat(2.0);
// Standard
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.gen::<$ty>(), $ZERO);
let mut one = StepRng::new(1 << 11, 0);
assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
assert_eq!(one.gen::<$ty>(), $EPSILON / two);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
assert_eq!(max.gen::<$ty>(), $ty::splat(1.0) - $EPSILON / two);
// OpenClosed01
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0);
assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), $ZERO + $EPSILON / two);
let mut one = StepRng::new(1 << 11, 0);
assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + $ty::splat(1.0));
// Open01
let mut zeros = StepRng::new(0, 0);
assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
assert_eq!(zeros.sample::<$ty, _>(Open01), $ZERO + $EPSILON / two);
let mut one = StepRng::new(1 << 12, 0);
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / two * $ty::splat(3.0));
let mut max = StepRng::new(!0, 0);
assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
assert_eq!(max.sample::<$ty, _>(Open01), $ty::splat(1.0) - $EPSILON / two);
}
};
}
@ -296,16 +300,16 @@ mod tests {
// non-SIMD types; we assume this pattern continues across all
// SIMD types.
test_samples(&Standard, f32x2::new(0.0, 0.0), &[
f32x2::new(0.0035963655, 0.7346052),
f32x2::new(0.09778172, 0.20298547),
f32x2::new(0.34296435, 0.81664366),
test_samples(&Standard, f32x2::from([0.0, 0.0]), &[
f32x2::from([0.0035963655, 0.7346052]),
f32x2::from([0.09778172, 0.20298547]),
f32x2::from([0.34296435, 0.81664366]),
]);
test_samples(&Standard, f64x2::new(0.0, 0.0), &[
f64x2::new(0.7346051961657583, 0.20298547462974248),
f64x2::new(0.8166436635290655, 0.7423708925400552),
f64x2::new(0.16387782224016323, 0.9087068770169618),
test_samples(&Standard, f64x2::from([0.0, 0.0]), &[
f64x2::from([0.7346051961657583, 0.20298547462974248]),
f64x2::from([0.8166436635290655, 0.7423708925400552]),
f64x2::from([0.16387782224016323, 0.9087068770169618]),
]);
}
}

View File

@ -11,12 +11,17 @@
use crate::distributions::{Distribution, Standard};
use crate::Rng;
#[cfg(all(target_arch = "x86", feature = "simd_support"))]
use core::arch::x86::__m512i;
#[cfg(target_arch = "x86")]
use core::arch::x86::{__m128i, __m256i};
#[cfg(all(target_arch = "x86_64", feature = "simd_support"))]
use core::arch::x86_64::__m512i;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::{__m128i, __m256i};
use core::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize,
NonZeroU128};
#[cfg(feature = "simd_support")] use packed_simd::*;
#[cfg(feature = "simd_support")] use core::simd::*;
use core::mem;
impl Distribution<u8> for Standard {
#[inline]
@ -109,53 +114,54 @@ impl_nzint!(NonZeroU64, NonZeroU64::new);
impl_nzint!(NonZeroU128, NonZeroU128::new);
impl_nzint!(NonZeroUsize, NonZeroUsize::new);
#[cfg(feature = "simd_support")]
macro_rules! simd_impl {
($(($intrinsic:ident, $vec:ty),)+) => {$(
macro_rules! x86_intrinsic_impl {
($($intrinsic:ident),+) => {$(
/// Available only on x86/64 platforms
impl Distribution<$intrinsic> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $intrinsic {
$intrinsic::from_bits(rng.gen::<$vec>())
// On proper hardware, this should compile to SIMD instructions
// Verified on x86 Haswell with __m128i, __m256i
let mut buf = [0_u8; mem::size_of::<$intrinsic>()];
rng.fill_bytes(&mut buf);
// x86 is little endian so no need for conversion
// SAFETY: we know [u8; N] and $intrinsic have the same size
unsafe { mem::transmute_copy(&buf) }
}
}
)+};
($bits:expr,) => {};
($bits:expr, $ty:ty, $($ty_more:ty,)*) => {
simd_impl!($bits, $($ty_more,)*);
impl Distribution<$ty> for Standard {
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
let mut vec: $ty = Default::default();
unsafe {
let ptr = &mut vec;
let b_ptr = &mut *(ptr as *mut $ty as *mut [u8; $bits/8]);
rng.fill_bytes(b_ptr);
}
vec.to_le()
}
}
};
}
#[cfg(feature = "simd_support")]
simd_impl!(16, u8x2, i8x2,);
macro_rules! simd_impl {
($($ty:ty),+) => {$(
/// Requires nightly Rust and the [`simd_support`] feature
///
/// [`simd_support`]: https://github.com/rust-random/rand#crate-features
impl<const LANES: usize> Distribution<Simd<$ty, LANES>> for Standard
where
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Simd<$ty, LANES> {
let mut vec = Simd::default();
rng.fill(vec.as_mut_array().as_mut_slice());
vec
}
}
)+};
}
#[cfg(feature = "simd_support")]
simd_impl!(32, u8x4, i8x4, u16x2, i16x2,);
#[cfg(feature = "simd_support")]
simd_impl!(64, u8x8, i8x8, u16x4, i16x4, u32x2, i32x2,);
#[cfg(feature = "simd_support")]
simd_impl!(128, u8x16, i8x16, u16x8, i16x8, u32x4, i32x4, u64x2, i64x2,);
#[cfg(feature = "simd_support")]
simd_impl!(256, u8x32, i8x32, u16x16, i16x16, u32x8, i32x8, u64x4, i64x4,);
#[cfg(feature = "simd_support")]
simd_impl!(512, u8x64, i8x64, u16x32, i16x32, u32x16, i32x16, u64x8, i64x8,);
simd_impl!(u8, i8, u16, i16, u32, i32, u64, i64, usize, isize);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
x86_intrinsic_impl!(__m128i, __m256i);
#[cfg(all(
feature = "simd_support",
any(target_arch = "x86", target_arch = "x86_64")
any(target_arch = "x86", target_arch = "x86_64"),
feature = "simd_support"
))]
simd_impl!((__m128i, u8x16), (__m256i, u8x32),);
x86_intrinsic_impl!(__m512i);
#[cfg(test)]
mod tests {
@ -221,24 +227,19 @@ mod tests {
{
// We only test a sub-set of types here and make assumptions about the rest.
test_samples(u8x2::default(), &[
u8x2::new(9, 126),
u8x2::new(247, 167),
u8x2::new(111, 149),
]);
test_samples(u8x4::default(), &[
u8x4::new(9, 126, 87, 132),
u8x4::new(247, 167, 123, 153),
u8x4::new(111, 149, 73, 120),
u8x4::from([9, 126, 87, 132]),
u8x4::from([247, 167, 123, 153]),
u8x4::from([111, 149, 73, 120]),
]);
test_samples(u8x8::default(), &[
u8x8::new(9, 126, 87, 132, 247, 167, 123, 153),
u8x8::new(111, 149, 73, 120, 68, 171, 98, 223),
u8x8::new(24, 121, 1, 50, 13, 46, 164, 20),
u8x8::from([9, 126, 87, 132, 247, 167, 123, 153]),
u8x8::from([111, 149, 73, 120, 68, 171, 98, 223]),
u8x8::from([24, 121, 1, 50, 13, 46, 164, 20]),
]);
test_samples(i64x8::default(), &[
i64x8::new(
i64x8::from([
-7387126082252079607,
-2350127744969763473,
1487364411147516184,
@ -247,8 +248,8 @@ mod tests {
6022086574635100741,
-5080089175222015595,
-4066367846667249123,
),
i64x8::new(
]),
i64x8::from([
9180885022207963908,
3095981199532211089,
6586075293021332726,
@ -257,8 +258,8 @@ mod tests {
5287129228749947252,
444726432079249540,
-1587028029513790706,
),
i64x8::new(
]),
i64x8::from([
6075236523189346388,
1351763722368165432,
-6192309979959753740,
@ -267,7 +268,7 @@ mod tests {
7522501477800909500,
-1837258847956201231,
-586926753024886735,
),
]),
]);
}
}

View File

@ -149,18 +149,22 @@ use crate::Rng;
/// * `bool`: Generates `false` or `true`, each with probability 0.5.
/// * Floating point types (`f32` and `f64`): Uniformly distributed in the
/// half-open range `[0, 1)`. See notes below.
/// * Wrapping integers (`Wrapping<T>`), besides the type identical to their
/// * Wrapping integers ([`Wrapping<T>`]), besides the type identical to their
/// normal integer variants.
/// * Non-zero integers ([`NonZeroU8`]), which are like their normal integer
/// variants but cannot produce zero.
/// * SIMD types like x86's [`__m128i`], `std::simd`'s [`u32x4`]/[`f32x4`]/
/// [`mask32x4`] (requires [`simd_support`]), where each lane is distributed
/// like their scalar `Standard` variants. See the list of `Standard`
/// implementations for more.
///
/// The `Standard` distribution also supports generation of the following
/// compound types where all component types are supported:
///
/// * Tuples (up to 12 elements): each element is generated sequentially.
/// * Arrays (up to 32 elements): each element is generated sequentially;
/// * Arrays: each element is generated sequentially;
/// see also [`Rng::fill`] which supports arbitrary array length for integer
/// types and tends to be faster for `u32` and smaller types.
/// When using `rustc` ≥ 1.51, enable the `min_const_gen` feature to support
/// arrays larger than 32 elements.
/// and float types and tends to be faster for `u32` and smaller types.
/// Note that [`Rng::fill`] and `Standard`'s array support are *not* equivalent:
/// the former is optimised for integer types (using fewer RNG calls for
/// element types smaller than the RNG word size), while the latter supports
@ -213,6 +217,13 @@ use crate::Rng;
/// CPUs all methods have approximately equal performance).
///
/// [`Uniform`]: uniform::Uniform
/// [`Wrapping<T>`]: std::num::Wrapping
/// [`NonZeroU8`]: std::num::NonZeroU8
/// [`__m128i`]: https://doc.rust-lang.org/core/arch/x86/struct.__m128i.html
/// [`u32x4`]: std::simd::u32x4
/// [`f32x4`]: std::simd::f32x4
/// [`mask32x4`]: std::simd::mask32x4
/// [`simd_support`]: https://github.com/rust-random/rand#crate-features
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct Standard;

View File

@ -20,8 +20,9 @@ use crate::Rng;
#[cfg(feature = "serde1")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "min_const_gen")]
use core::mem::{self, MaybeUninit};
#[cfg(feature = "simd_support")]
use core::simd::*;
// ----- Sampling distributions -----
@ -80,9 +81,9 @@ impl Distribution<char> for Standard {
// reserved for surrogates. This is the size of that gap.
const GAP_SIZE: u32 = 0xDFFF - 0xD800 + 1;
// Uniform::new(0, 0x11_0000 - GAP_SIZE) can also be used but it
// Uniform::new(0, 0x11_0000 - GAP_SIZE) can also be used, but it
// seemed slower.
let range = Uniform::new(GAP_SIZE, 0x11_0000);
let range = Uniform::new(GAP_SIZE, 0x11_0000).unwrap();
let mut n = range.sample(rng);
if n <= 0xDFFF {
@ -145,6 +146,51 @@ impl Distribution<bool> for Standard {
}
}
/// Requires nightly Rust and the [`simd_support`] feature
///
/// Note that on some hardware like x86/64 mask operations like [`_mm_blendv_epi8`]
/// only care about a single bit. This means that you could use uniform random bits
/// directly:
///
/// ```ignore
/// // this may be faster...
/// let x = unsafe { _mm_blendv_epi8(a.into(), b.into(), rng.gen::<__m128i>()) };
///
/// // ...than this
/// let x = rng.gen::<mask8x16>().select(b, a);
/// ```
///
/// Since most bits are unused you could also generate only as many bits as you need, i.e.:
/// ```
/// #![feature(portable_simd)]
/// use std::simd::*;
/// use rand::prelude::*;
/// let mut rng = thread_rng();
///
/// let x = u16x8::splat(rng.gen::<u8>() as u16);
/// let mask = u16x8::splat(1) << u16x8::from([0, 1, 2, 3, 4, 5, 6, 7]);
/// let rand_mask = (x & mask).simd_eq(mask);
/// ```
///
/// [`_mm_blendv_epi8`]: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_blendv_epi8&ig_expand=514/
/// [`simd_support`]: https://github.com/rust-random/rand#crate-features
#[cfg(feature = "simd_support")]
impl<T, const LANES: usize> Distribution<Mask<T, LANES>> for Standard
where
T: MaskElement + Default,
LaneCount<LANES>: SupportedLaneCount,
Standard: Distribution<Simd<T, LANES>>,
Simd<T, LANES>: SimdPartialOrd<Mask = Mask<T, LANES>>,
{
#[inline]
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Mask<T, LANES> {
// `MaskElement` must be a signed integer, so this is equivalent
// to the scalar `i32 < 0` method
let var = rng.gen::<Simd<T, LANES>>();
var.simd_lt(Simd::default())
}
}
macro_rules! tuple_impl {
// use variables to indicate the arity of the tuple
($($tyvar:ident),* ) => {
@ -189,8 +235,6 @@ tuple_impl! {A, B, C, D, E, F, G, H, I, J}
tuple_impl! {A, B, C, D, E, F, G, H, I, J, K}
tuple_impl! {A, B, C, D, E, F, G, H, I, J, K, L}
#[cfg(feature = "min_const_gen")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "min_const_gen")))]
impl<T, const N: usize> Distribution<[T; N]> for Standard
where Standard: Distribution<T>
{
@ -206,30 +250,6 @@ where Standard: Distribution<T>
}
}
#[cfg(not(feature = "min_const_gen"))]
macro_rules! array_impl {
// recursive, given at least one type parameter:
{$n:expr, $t:ident, $($ts:ident,)*} => {
array_impl!{($n - 1), $($ts,)*}
impl<T> Distribution<[T; $n]> for Standard where Standard: Distribution<T> {
#[inline]
fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] {
[_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*]
}
}
};
// empty case:
{$n:expr,} => {
impl<T> Distribution<[T; $n]> for Standard {
fn sample<R: Rng + ?Sized>(&self, _rng: &mut R) -> [T; $n] { [] }
}
};
}
#[cfg(not(feature = "min_const_gen"))]
array_impl! {32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,}
impl<T> Distribution<Option<T>> for Standard
where Standard: Distribution<T>
{

View File

@ -75,7 +75,7 @@ impl<'a, T> Slice<'a, T> {
0 => Err(EmptySlice),
len => Ok(Self {
slice,
range: Uniform::new(0, len),
range: Uniform::new(0, len).unwrap(),
}),
}
}

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
//! Math helper functions
#[cfg(feature = "simd_support")] use packed_simd::*;
#[cfg(feature = "simd_support")] use core::simd::*;
pub(crate) trait WideningMultiply<RHS = Self> {
@ -31,7 +31,7 @@ macro_rules! wmul_impl {
};
// simd bulk implementation
($(($ty:ident, $wide:ident),)+, $shift:expr) => {
($(($ty:ident, $wide:ty),)+, $shift:expr) => {
$(
impl WideningMultiply for $ty {
type Output = ($ty, $ty);
@ -45,7 +45,7 @@ macro_rules! wmul_impl {
let y: $wide = self.cast();
let x: $wide = x.cast();
let tmp = y * x;
let hi: $ty = (tmp >> $shift).cast();
let hi: $ty = (tmp >> Simd::splat($shift)).cast();
let lo: $ty = tmp.cast();
(hi, lo)
}
@ -99,19 +99,20 @@ macro_rules! wmul_impl_large {
#[inline(always)]
fn wmul(self, b: $ty) -> Self::Output {
// needs wrapping multiplication
const LOWER_MASK: $scalar = !0 >> $half;
let mut low = (self & LOWER_MASK) * (b & LOWER_MASK);
let mut t = low >> $half;
low &= LOWER_MASK;
t += (self >> $half) * (b & LOWER_MASK);
low += (t & LOWER_MASK) << $half;
let mut high = t >> $half;
t = low >> $half;
low &= LOWER_MASK;
t += (b >> $half) * (self & LOWER_MASK);
low += (t & LOWER_MASK) << $half;
high += t >> $half;
high += (self >> $half) * (b >> $half);
let lower_mask = <$ty>::splat(!0 >> $half);
let half = <$ty>::splat($half);
let mut low = (self & lower_mask) * (b & lower_mask);
let mut t = low >> half;
low &= lower_mask;
t += (self >> half) * (b & lower_mask);
low += (t & lower_mask) << half;
let mut high = t >> half;
t = low >> half;
low &= lower_mask;
t += (b >> half) * (self & lower_mask);
low += (t & lower_mask) << half;
high += t >> half;
high += (self >> half) * (b >> half);
(high, low)
}
@ -148,11 +149,11 @@ mod simd_wmul {
#[cfg(target_arch = "x86_64")] use core::arch::x86_64::*;
wmul_impl! {
(u8x2, u16x2),
(u8x4, u16x4),
(u8x8, u16x8),
(u8x16, u16x16),
(u8x32, u16x32),,
(u8x32, u16x32),
(u8x64, Simd<u16, 64>),,
8
}
@ -162,21 +163,21 @@ mod simd_wmul {
wmul_impl! { (u16x8, u32x8),, 16 }
#[cfg(not(target_feature = "avx2"))]
wmul_impl! { (u16x16, u32x16),, 16 }
#[cfg(not(target_feature = "avx512bw"))]
wmul_impl! { (u16x32, Simd<u32, 32>),, 16 }
// 16-bit lane widths allow use of the x86 `mulhi` instructions, which
// means `wmul` can be implemented with only two instructions.
#[allow(unused_macros)]
macro_rules! wmul_impl_16 {
($ty:ident, $intrinsic:ident, $mulhi:ident, $mullo:ident) => {
($ty:ident, $mulhi:ident, $mullo:ident) => {
impl WideningMultiply for $ty {
type Output = ($ty, $ty);
#[inline(always)]
fn wmul(self, x: $ty) -> Self::Output {
let b = $intrinsic::from_bits(x);
let a = $intrinsic::from_bits(self);
let hi = $ty::from_bits(unsafe { $mulhi(a, b) });
let lo = $ty::from_bits(unsafe { $mullo(a, b) });
let hi = unsafe { $mulhi(self.into(), x.into()) }.into();
let lo = unsafe { $mullo(self.into(), x.into()) }.into();
(hi, lo)
}
}
@ -184,23 +185,20 @@ mod simd_wmul {
}
#[cfg(target_feature = "sse2")]
wmul_impl_16! { u16x8, __m128i, _mm_mulhi_epu16, _mm_mullo_epi16 }
wmul_impl_16! { u16x8, _mm_mulhi_epu16, _mm_mullo_epi16 }
#[cfg(target_feature = "avx2")]
wmul_impl_16! { u16x16, __m256i, _mm256_mulhi_epu16, _mm256_mullo_epi16 }
// FIXME: there are no `__m512i` types in stdsimd yet, so `wmul::<u16x32>`
// cannot use the same implementation.
wmul_impl_16! { u16x16, _mm256_mulhi_epu16, _mm256_mullo_epi16 }
#[cfg(target_feature = "avx512bw")]
wmul_impl_16! { u16x32, _mm512_mulhi_epu16, _mm512_mullo_epi16 }
wmul_impl! {
(u32x2, u64x2),
(u32x4, u64x4),
(u32x8, u64x8),,
(u32x8, u64x8),
(u32x16, Simd<u64, 16>),,
32
}
// TODO: optimize, this seems to seriously slow things down
wmul_impl_large! { (u8x64,) u8, 4 }
wmul_impl_large! { (u16x32,) u16, 8 }
wmul_impl_large! { (u32x16,) u32, 16 }
wmul_impl_large! { (u64x2, u64x4, u64x8,) u64, 32 }
}
@ -229,6 +227,10 @@ pub(crate) trait FloatSIMDUtils {
// value, not by retaining the binary representation.
type UInt;
fn cast_from_int(i: Self::UInt) -> Self;
type Scalar;
fn replace(self, index: usize, new_value: Self::Scalar) -> Self;
fn extract(self, index: usize) -> Self::Scalar;
}
/// Implement functions available in std builds but missing from core primitives
@ -243,26 +245,23 @@ pub(crate) trait Float: Sized {
/// Implement functions on f32/f64 to give them APIs similar to SIMD types
pub(crate) trait FloatAsSIMD: Sized {
#[inline(always)]
fn lanes() -> usize {
1
}
const LANES: usize = 1;
#[inline(always)]
fn splat(scalar: Self) -> Self {
scalar
}
}
pub(crate) trait IntAsSIMD: Sized {
#[inline(always)]
fn extract(self, index: usize) -> Self {
debug_assert_eq!(index, 0);
self
}
#[inline(always)]
fn replace(self, index: usize, new_value: Self) -> Self {
debug_assert_eq!(index, 0);
new_value
fn splat(scalar: Self) -> Self {
scalar
}
}
impl IntAsSIMD for u32 {}
impl IntAsSIMD for u64 {}
pub(crate) trait BoolAsSIMD: Sized {
fn any(self) -> bool;
fn all(self) -> bool;
@ -308,6 +307,7 @@ macro_rules! scalar_float_impl {
impl FloatSIMDUtils for $ty {
type Mask = bool;
type Scalar = $ty;
type UInt = $uty;
#[inline(always)]
@ -350,6 +350,18 @@ macro_rules! scalar_float_impl {
fn cast_from_int(i: Self::UInt) -> Self {
i as $ty
}
#[inline]
fn replace(self, index: usize, new_value: Self::Scalar) -> Self {
debug_assert_eq!(index, 0);
new_value
}
#[inline]
fn extract(self, index: usize) -> Self::Scalar {
debug_assert_eq!(index, 0);
self
}
}
impl FloatAsSIMD for $ty {}
@ -362,42 +374,42 @@ scalar_float_impl!(f64, u64);
#[cfg(feature = "simd_support")]
macro_rules! simd_impl {
($ty:ident, $f_scalar:ident, $mty:ident, $uty:ident) => {
impl FloatSIMDUtils for $ty {
type Mask = $mty;
type UInt = $uty;
($fty:ident, $uty:ident) => {
impl<const LANES: usize> FloatSIMDUtils for Simd<$fty, LANES>
where LaneCount<LANES>: SupportedLaneCount
{
type Mask = Mask<<$fty as SimdElement>::Mask, LANES>;
type Scalar = $fty;
type UInt = Simd<$uty, LANES>;
#[inline(always)]
fn all_lt(self, other: Self) -> bool {
self.lt(other).all()
self.simd_lt(other).all()
}
#[inline(always)]
fn all_le(self, other: Self) -> bool {
self.le(other).all()
self.simd_le(other).all()
}
#[inline(always)]
fn all_finite(self) -> bool {
self.finite_mask().all()
self.is_finite().all()
}
#[inline(always)]
fn finite_mask(self) -> Self::Mask {
// This can possibly be done faster by checking bit patterns
let neg_inf = $ty::splat(::core::$f_scalar::NEG_INFINITY);
let pos_inf = $ty::splat(::core::$f_scalar::INFINITY);
self.gt(neg_inf) & self.lt(pos_inf)
self.is_finite()
}
#[inline(always)]
fn gt_mask(self, other: Self) -> Self::Mask {
self.gt(other)
self.simd_gt(other)
}
#[inline(always)]
fn ge_mask(self, other: Self) -> Self::Mask {
self.ge(other)
self.simd_ge(other)
}
#[inline(always)]
@ -406,24 +418,32 @@ macro_rules! simd_impl {
// true, and 0 for false. Adding that to the binary
// representation of a float means subtracting one from
// the binary representation, resulting in the next lower
// value representable by $ty. This works even when the
// value representable by $fty. This works even when the
// current value is infinity.
debug_assert!(mask.any(), "At least one lane must be set");
<$ty>::from_bits(<$uty>::from_bits(self) + <$uty>::from_bits(mask))
Self::from_bits(self.to_bits() + mask.to_int().cast())
}
#[inline]
fn cast_from_int(i: Self::UInt) -> Self {
i.cast()
}
#[inline]
fn replace(mut self, index: usize, new_value: Self::Scalar) -> Self {
self.as_mut_array()[index] = new_value;
self
}
#[inline]
fn extract(self, index: usize) -> Self::Scalar {
self.as_array()[index]
}
}
};
}
#[cfg(feature="simd_support")] simd_impl! { f32x2, f32, m32x2, u32x2 }
#[cfg(feature="simd_support")] simd_impl! { f32x4, f32, m32x4, u32x4 }
#[cfg(feature="simd_support")] simd_impl! { f32x8, f32, m32x8, u32x8 }
#[cfg(feature="simd_support")] simd_impl! { f32x16, f32, m32x16, u32x16 }
#[cfg(feature="simd_support")] simd_impl! { f64x2, f64, m64x2, u64x2 }
#[cfg(feature="simd_support")] simd_impl! { f64x4, f64, m64x4, u64x4 }
#[cfg(feature="simd_support")] simd_impl! { f64x8, f64, m64x8, u64x8 }
#[cfg(feature = "simd_support")]
simd_impl!(f32, u32);
#[cfg(feature = "simd_support")]
simd_impl!(f64, u64);

View File

@ -25,8 +25,10 @@ use serde::{Serialize, Deserialize};
/// Sampling a `WeightedIndex` distribution returns the index of a randomly
/// selected element from the iterator used when the `WeightedIndex` was
/// created. The chance of a given element being picked is proportional to the
/// value of the element. The weights can use any type `X` for which an
/// implementation of [`Uniform<X>`] exists.
/// weight of the element. The weights can use any type `X` for which an
/// implementation of [`Uniform<X>`] exists. The implementation guarantees that
/// elements with zero weight are never picked, even when the weights are
/// floating point numbers.
///
/// # Performance
///
@ -42,8 +44,8 @@ use serde::{Serialize, Deserialize};
/// weights of type `X`, where `N` is the number of weights. However, since
/// `Vec` doesn't guarantee a particular growth strategy, additional memory
/// might be allocated but not used. Since the `WeightedIndex` object also
/// contains, this might cause additional allocations, though for primitive
/// types, [`Uniform<X>`] doesn't allocate any memory.
/// contains an instance of `X::Sampler`, this might cause additional allocations,
/// though for primitive types, [`Uniform<X>`] doesn't allocate any memory.
///
/// Sampling from `WeightedIndex` will result in a single call to
/// `Uniform<X>::sample` (method of the [`Distribution`] trait), which typically
@ -65,7 +67,7 @@ use serde::{Serialize, Deserialize};
/// println!("{}", choices[dist.sample(&mut rng)]);
/// }
///
/// let items = [('a', 0), ('b', 3), ('c', 7)];
/// let items = [('a', 0.0), ('b', 3.0), ('c', 7.0)];
/// let dist2 = WeightedIndex::new(items.iter().map(|item| item.1)).unwrap();
/// for _ in 0..100 {
/// // 0% chance to print 'a', 30% chance to print 'b', 70% chance to print 'c'
@ -75,7 +77,7 @@ use serde::{Serialize, Deserialize};
///
/// [`Uniform<X>`]: crate::distributions::Uniform
/// [`RngCore`]: crate::RngCore
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub struct WeightedIndex<X: SampleUniform + PartialOrd> {
@ -121,7 +123,7 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> {
if total_weight == zero {
return Err(WeightedError::AllWeightsZero);
}
let distr = X::Sampler::new(zero, total_weight.clone());
let distr = X::Sampler::new(zero, total_weight.clone()).unwrap();
Ok(WeightedIndex {
cumulative_weights: weights,
@ -139,6 +141,10 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> {
/// allocation internally.
///
/// In case of error, `self` is not modified.
///
/// Note: Updating floating-point weights may cause slight inaccuracies in the total weight.
/// This method may not return `WeightedError::AllWeightsZero` when all weights
/// are zero if using floating-point weights.
pub fn update_weights(&mut self, new_weights: &[(usize, &X)]) -> Result<(), WeightedError>
where X: for<'a> ::core::ops::AddAssign<&'a X>
+ for<'a> ::core::ops::SubAssign<&'a X>
@ -214,7 +220,7 @@ impl<X: SampleUniform + PartialOrd> WeightedIndex<X> {
}
self.total_weight = total_weight;
self.weight_distribution = X::Sampler::new(zero, self.total_weight.clone());
self.weight_distribution = X::Sampler::new(zero, self.total_weight.clone()).unwrap();
Ok(())
}
@ -224,18 +230,9 @@ impl<X> Distribution<usize> for WeightedIndex<X>
where X: SampleUniform + PartialOrd
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> usize {
use ::core::cmp::Ordering;
let chosen_weight = self.weight_distribution.sample(rng);
// Find the first item which has a weight *higher* than the chosen weight.
self.cumulative_weights
.binary_search_by(|w| {
if *w <= chosen_weight {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_err()
self.cumulative_weights.partition_point(|w| w <= &chosen_weight)
}
}
@ -418,6 +415,11 @@ mod test {
2, 2, 1, 3, 2, 1, 3, 3, 2, 1,
]);
}
#[test]
fn weighted_index_distributions_can_be_compared() {
assert_eq!(WeightedIndex::new(&[1, 2]), WeightedIndex::new(&[1, 2]));
}
}
/// Error type returned from `WeightedIndex::new`.

View File

@ -49,8 +49,7 @@
#![deny(missing_debug_implementations)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
#![no_std]
#![cfg_attr(feature = "simd_support", feature(stdsimd))]
#![cfg_attr(feature = "nightly", feature(slice_partition_at_index))]
#![cfg_attr(feature = "simd_support", feature(stdsimd, portable_simd))]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![allow(
clippy::float_cmp,
@ -111,36 +110,10 @@ use crate::distributions::{Distribution, Standard};
/// Generates a random value using the thread-local random number generator.
///
/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for
/// documentation of the entropy source and [`Standard`] for documentation of
/// distributions and type-specific generation.
/// This function is simply a shortcut for `thread_rng().gen()`:
///
/// # Provided implementations
///
/// The following types have provided implementations that
/// generate values with the following ranges and distributions:
///
/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed
/// over all values of the type.
/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all
/// code points in the range `0...0x10_FFFF`, except for the range
/// `0xD800...0xDFFF` (the surrogate code points). This includes
/// unassigned/reserved code points.
/// * `bool`: Generates `false` or `true`, each with probability 0.5.
/// * Floating point types (`f32` and `f64`): Uniformly distributed in the
/// half-open range `[0, 1)`. See notes below.
/// * Wrapping integers (`Wrapping<T>`), besides the type identical to their
/// normal integer variants.
///
/// Also supported is the generation of the following
/// compound types where all component types are supported:
///
/// * Tuples (up to 12 elements): each element is generated sequentially.
/// * Arrays (up to 32 elements): each element is generated sequentially;
/// see also [`Rng::fill`] which supports arbitrary array length for integer
/// types and tends to be faster for `u32` and smaller types.
/// * `Option<T>` first generates a `bool`, and if true generates and returns
/// `Some(value)` where `value: T`, otherwise returning `None`.
/// - See [`ThreadRng`] for documentation of the generator and security
/// - See [`Standard`] for documentation of supported types and distributions
///
/// # Examples
///
@ -178,6 +151,7 @@ use crate::distributions::{Distribution, Standard};
/// ```
///
/// [`Standard`]: distributions::Standard
/// [`ThreadRng`]: rngs::ThreadRng
#[cfg(all(feature = "std", feature = "std_rng"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
#[inline]

View File

@ -53,7 +53,7 @@ use core::{mem, slice};
/// # let v = foo(&mut thread_rng());
/// ```
pub trait Rng: RngCore {
/// Return a random value supporting the [`Standard`] distribution.
/// Return a random value via the [`Standard`] distribution.
///
/// # Example
///
@ -68,11 +68,9 @@ pub trait Rng: RngCore {
///
/// # Arrays and tuples
///
/// The `rng.gen()` method is able to generate arrays (up to 32 elements)
/// The `rng.gen()` method is able to generate arrays
/// and tuples (up to 12 elements), so long as all element types can be
/// generated.
/// When using `rustc` ≥ 1.51, enable the `min_const_gen` feature to support
/// arrays larger than 32 elements.
///
/// For arrays of integers, especially for those with small element types
/// (< 64 bit), it will likely be faster to instead use [`Rng::fill`].
@ -132,7 +130,7 @@ pub trait Rng: RngCore {
R: SampleRange<T>
{
assert!(!range.is_empty(), "cannot sample empty range");
range.sample_single(self)
range.sample_single(self).unwrap()
}
/// Sample a new value, using the given distribution.
@ -144,10 +142,10 @@ pub trait Rng: RngCore {
/// use rand::distributions::Uniform;
///
/// let mut rng = thread_rng();
/// let x = rng.sample(Uniform::new(10u32, 15));
/// let x = rng.sample(Uniform::new(10u32, 15).unwrap());
/// // Type annotation requires two types, the type and distribution; the
/// // distribution can be inferred.
/// let y = rng.sample::<u16, _>(Uniform::new(10, 15));
/// let y = rng.sample::<u16, _>(Uniform::new(10, 15).unwrap());
/// ```
fn sample<T, D: Distribution<T>>(&mut self, distr: D) -> T {
distr.sample(self)
@ -183,7 +181,7 @@ pub trait Rng: RngCore {
/// .collect::<Vec<(f64, bool)>>());
///
/// // Dice-rolling:
/// let die_range = Uniform::new_inclusive(1, 6);
/// let die_range = Uniform::new_inclusive(1, 6).unwrap();
/// let mut roll_die = (&mut rng).sample_iter(die_range);
/// while roll_die.next().unwrap() != 6 {
/// println!("Not a 6; rolling again!");
@ -392,8 +390,6 @@ macro_rules! impl_fill {
impl_fill!(u16, u32, u64, usize, u128,);
impl_fill!(i8, i16, i32, i64, isize, i128,);
#[cfg_attr(doc_cfg, doc(cfg(feature = "min_const_gen")))]
#[cfg(feature = "min_const_gen")]
impl<T, const N: usize> Fill for [T; N]
where [T]: Fill
{
@ -402,32 +398,6 @@ where [T]: Fill
}
}
#[cfg(not(feature = "min_const_gen"))]
macro_rules! impl_fill_arrays {
($n:expr,) => {};
($n:expr, $N:ident) => {
impl<T> Fill for [T; $n] where [T]: Fill {
fn try_fill<R: Rng + ?Sized>(&mut self, rng: &mut R) -> Result<(), Error> {
self[..].try_fill(rng)
}
}
};
($n:expr, $N:ident, $($NN:ident,)*) => {
impl_fill_arrays!($n, $N);
impl_fill_arrays!($n - 1, $($NN,)*);
};
(!div $n:expr,) => {};
(!div $n:expr, $N:ident, $($NN:ident,)*) => {
impl_fill_arrays!($n, $N);
impl_fill_arrays!(!div $n / 2, $($NN,)*);
};
}
#[cfg(not(feature = "min_const_gen"))]
#[rustfmt::skip]
impl_fill_arrays!(32, N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,);
#[cfg(not(feature = "min_const_gen"))]
impl_fill_arrays!(!div 4096, N,N,N,N,N,N,N,);
#[cfg(test)]
mod test {
use super::*;

View File

@ -12,7 +12,7 @@
use core::mem::size_of;
use rand_core::block::{BlockRng, BlockRngCore};
use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, Error, RngCore, SeedableRng};
/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the
@ -113,7 +113,6 @@ where
impl<R, Rsdr: RngCore> RngCore for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng,
<R as BlockRngCore>::Results: AsRef<[u32]> + AsMut<[u32]>,
{
#[inline(always)]
fn next_u32(&mut self) -> u32 {
@ -148,8 +147,8 @@ where
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
where
R: BlockRngCore + SeedableRng + CryptoRng,
Rsdr: RngCore + CryptoRng,
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
Rsdr: CryptoRng,
{
}
@ -209,8 +208,8 @@ where
ReseedingCore {
inner: rng,
reseeder,
threshold: threshold as i64,
bytes_until_reseed: threshold as i64,
threshold,
bytes_until_reseed: threshold,
fork_counter: 0,
}
}
@ -277,10 +276,10 @@ where
}
}
impl<R, Rsdr> CryptoRng for ReseedingCore<R, Rsdr>
impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng + CryptoRng,
Rsdr: RngCore + CryptoRng,
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
Rsdr: CryptoRng,
{
}

View File

@ -114,4 +114,9 @@ impl SeedableRng for SmallRng {
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
Rng::from_rng(rng).map(SmallRng)
}
#[inline(always)]
fn seed_from_u64(state: u64) -> Self {
SmallRng(Rng::seed_from_u64(state))
}
}

View File

@ -11,6 +11,7 @@
use core::cell::UnsafeCell;
use std::rc::Rc;
use std::thread_local;
use std::fmt;
use super::std::Core;
use crate::rngs::adapter::ReseedingRng;
@ -39,31 +40,43 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;
/// A reference to the thread-local generator
///
/// This type is a reference to a lazily-initialized thread-local generator.
/// An instance can be obtained via [`thread_rng`] or via `ThreadRng::default()`.
/// This handle is safe to use everywhere (including thread-local destructors),
/// though it is recommended not to use inside a fork handler.
/// The handle cannot be passed between threads (is not `Send` or `Sync`).
///
/// `ThreadRng` uses the same PRNG as [`StdRng`] for security and performance
/// and is automatically seeded from [`OsRng`].
/// `ThreadRng` uses the same CSPRNG as [`StdRng`], ChaCha12. As with
/// [`StdRng`], the algorithm may be changed, subject to reasonable expectations
/// of security and performance.
///
/// Unlike `StdRng`, `ThreadRng` uses the [`ReseedingRng`] wrapper to reseed
/// the PRNG from fresh entropy every 64 kiB of random data as well as after a
/// fork on Unix (though not quite immediately; see documentation of
/// [`ReseedingRng`]).
/// Note that the reseeding is done as an extra precaution against side-channel
/// attacks and mis-use (e.g. if somehow weak entropy were supplied initially).
/// The PRNG algorithms used are assumed to be secure.
/// `ThreadRng` is automatically seeded from [`OsRng`] with periodic reseeding
/// (every 64 kiB, as well as "soon" after a fork on Unix — see [`ReseedingRng`]
/// documentation for details).
///
/// Security must be considered relative to a threat model and validation
/// requirements. `ThreadRng` attempts to meet basic security considerations
/// for producing unpredictable random numbers: use a CSPRNG, use a
/// recommended platform-specific seed ([`OsRng`]), and avoid
/// leaking internal secrets e.g. via [`Debug`] implementation or serialization.
/// Memory is not zeroized on drop.
///
/// [`ReseedingRng`]: crate::rngs::adapter::ReseedingRng
/// [`StdRng`]: crate::rngs::StdRng
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct ThreadRng {
// Rc is explicitly !Send and !Sync
rng: Rc<UnsafeCell<ReseedingRng<Core, OsRng>>>,
}
/// Debug implementation does not leak internal state
impl fmt::Debug for ThreadRng {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "ThreadRng {{ .. }}")
}
}
thread_local!(
// We require Rc<..> to avoid premature freeing when thread_rng is used
// within thread-local destructors. See #968.
@ -77,13 +90,23 @@ thread_local!(
}
);
/// Retrieve the lazily-initialized thread-local random number generator,
/// seeded by the system. Intended to be used in method chaining style,
/// e.g. `thread_rng().gen::<i32>()`, or cached locally, e.g.
/// `let mut rng = thread_rng();`. Invoked by the `Default` trait, making
/// `ThreadRng::default()` equivalent.
/// Access the thread-local generator
///
/// For more information see [`ThreadRng`].
/// Returns a reference to the local [`ThreadRng`], initializing the generator
/// on the first call on each thread.
///
/// Example usage:
/// ```
/// use rand::Rng;
///
/// # fn main() {
/// // rand::random() may be used instead of rand::thread_rng().gen():
/// println!("A random boolean: {}", rand::random::<bool>());
///
/// let mut rng = rand::thread_rng();
/// println!("A simulated die roll: {}", rng.gen_range(1..=6));
/// # }
/// ```
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng"))))]
pub fn thread_rng() -> ThreadRng {
let rng = THREAD_RNG_KEY.with(|t| t.clone());
@ -92,7 +115,7 @@ pub fn thread_rng() -> ThreadRng {
impl Default for ThreadRng {
fn default() -> ThreadRng {
crate::prelude::thread_rng()
thread_rng()
}
}
@ -140,4 +163,11 @@ mod test {
r.gen::<i32>();
assert_eq!(r.gen_range(0..1), 0);
}
#[test]
fn test_debug_output() {
// We don't care about the exact output here, but it must not include
// private CSPRNG state or the cache stored by BlockRng!
assert_eq!(std::format!("{:?}", crate::thread_rng()), "ThreadRng { .. }");
}
}

160
src/seq/coin_flipper.rs Normal file
View File

@ -0,0 +1,160 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::RngCore;
pub(crate) struct CoinFlipper<R: RngCore> {
pub rng: R,
chunk: u32, //TODO(opt): this should depend on RNG word size
chunk_remaining: u32,
}
impl<R: RngCore> CoinFlipper<R> {
pub fn new(rng: R) -> Self {
Self {
rng,
chunk: 0,
chunk_remaining: 0,
}
}
#[inline]
/// Returns true with a probability of 1 / d
/// Uses an expected two bits of randomness
/// Panics if d == 0
pub fn gen_ratio_one_over(&mut self, d: usize) -> bool {
debug_assert_ne!(d, 0);
// This uses the same logic as `gen_ratio` but is optimized for the case that
// the starting numerator is one (which it always is for `Sequence::Choose()`)
// In this case (but not `gen_ratio`), this way of calculating c is always accurate
let c = (usize::BITS - 1 - d.leading_zeros()).min(32);
if self.flip_c_heads(c) {
let numerator = 1 << c;
return self.gen_ratio(numerator, d);
} else {
return false;
}
}
#[inline]
/// Returns true with a probability of n / d
/// Uses an expected two bits of randomness
fn gen_ratio(&mut self, mut n: usize, d: usize) -> bool {
// Explanation:
// We are trying to return true with a probability of n / d
// If n >= d, we can just return true
// Otherwise there are two possibilities 2n < d and 2n >= d
// In either case we flip a coin.
// If 2n < d
// If it comes up tails, return false
// If it comes up heads, double n and start again
// This is fair because (0.5 * 0) + (0.5 * 2n / d) = n / d and 2n is less than d
// (if 2n was greater than d we would effectively round it down to 1
// by returning true)
// If 2n >= d
// If it comes up tails, set n to 2n - d and start again
// If it comes up heads, return true
// This is fair because (0.5 * 1) + (0.5 * (2n - d) / d) = n / d
// Note that if 2n = d and the coin comes up tails, n will be set to 0
// before restarting which is equivalent to returning false.
// As a performance optimization we can flip multiple coins at once
// This is efficient because we can use the `lzcnt` intrinsic
// We can check up to 32 flips at once but we only receive one bit of information
// - all heads or at least one tail.
// Let c be the number of coins to flip. 1 <= c <= 32
// If 2n < d, n * 2^c < d
// If the result is all heads, then set n to n * 2^c
// If there was at least one tail, return false
// If 2n >= d, the order of results matters so we flip one coin at a time so c = 1
// Ideally, c will be as high as possible within these constraints
while n < d {
// Find a good value for c by counting leading zeros
// This will either give the highest possible c, or 1 less than that
let c = n
.leading_zeros()
.saturating_sub(d.leading_zeros() + 1)
.clamp(1, 32);
if self.flip_c_heads(c) {
// All heads
// Set n to n * 2^c
// If 2n >= d, the while loop will exit and we will return `true`
// If n * 2^c > `usize::MAX` we always return `true` anyway
n = n.saturating_mul(2_usize.pow(c));
} else {
//At least one tail
if c == 1 {
// Calculate 2n - d.
// We need to use wrapping as 2n might be greater than `usize::MAX`
let next_n = n.wrapping_add(n).wrapping_sub(d);
if next_n == 0 || next_n > n {
// This will happen if 2n < d
return false;
}
n = next_n;
} else {
// c > 1 so 2n < d so we can return false
return false;
}
}
}
true
}
/// If the next `c` bits of randomness all represent heads, consume them, return true
/// Otherwise return false and consume the number of heads plus one.
/// Generates new bits of randomness when necessary (in 32 bit chunks)
/// Has a 1 in 2 to the `c` chance of returning true
/// `c` must be less than or equal to 32
fn flip_c_heads(&mut self, mut c: u32) -> bool {
debug_assert!(c <= 32);
// Note that zeros on the left of the chunk represent heads.
// It needs to be this way round because zeros are filled in when left shifting
loop {
let zeros = self.chunk.leading_zeros();
if zeros < c {
// The happy path - we found a 1 and can return false
// Note that because a 1 bit was detected,
// We cannot have run out of random bits so we don't need to check
// First consume all of the bits read
// Using shl seems to give worse performance for size-hinted iterators
self.chunk = self.chunk.wrapping_shl(zeros + 1);
self.chunk_remaining = self.chunk_remaining.saturating_sub(zeros + 1);
return false;
} else {
// The number of zeros is larger than `c`
// There are two possibilities
if let Some(new_remaining) = self.chunk_remaining.checked_sub(c) {
// Those zeroes were all part of our random chunk,
// throw away `c` bits of randomness and return true
self.chunk_remaining = new_remaining;
self.chunk <<= c;
return true;
} else {
// Some of those zeroes were part of the random chunk
// and some were part of the space behind it
// We need to take into account only the zeroes that were random
c -= self.chunk_remaining;
// Generate a new chunk
self.chunk = self.rng.next_u32();
self.chunk_remaining = 32;
// Go back to start of loop
}
}
}
}
}

View File

@ -0,0 +1,108 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{Rng, RngCore};
/// Similar to a Uniform distribution,
/// but after returning a number in the range [0,n], n is increased by 1.
pub(crate) struct IncreasingUniform<R: RngCore> {
pub rng: R,
n: u32,
// Chunk is a random number in [0, (n + 1) * (n + 2) *..* (n + chunk_remaining) )
chunk: u32,
chunk_remaining: u8,
}
impl<R: RngCore> IncreasingUniform<R> {
/// Create a dice roller.
/// The next item returned will be a random number in the range [0,n]
pub fn new(rng: R, n: u32) -> Self {
// If n = 0, the first number returned will always be 0
// so we don't need to generate a random number
let chunk_remaining = if n == 0 { 1 } else { 0 };
Self {
rng,
n,
chunk: 0,
chunk_remaining,
}
}
/// Returns a number in [0,n] and increments n by 1.
/// Generates new random bits as needed
/// Panics if `n >= u32::MAX`
#[inline]
pub fn next_index(&mut self) -> usize {
let next_n = self.n + 1;
// There's room for further optimisation here:
// gen_range uses rejection sampling (or other method; see #1196) to avoid bias.
// When the initial sample is biased for range 0..bound
// it may still be viable to use for a smaller bound
// (especially if small biases are considered acceptable).
let next_chunk_remaining = self.chunk_remaining.checked_sub(1).unwrap_or_else(|| {
// If the chunk is empty, generate a new chunk
let (bound, remaining) = calculate_bound_u32(next_n);
// bound = (n + 1) * (n + 2) *..* (n + remaining)
self.chunk = self.rng.gen_range(0..bound);
// Chunk is a random number in
// [0, (n + 1) * (n + 2) *..* (n + remaining) )
remaining - 1
});
let result = if next_chunk_remaining == 0 {
// `chunk` is a random number in the range [0..n+1)
// Because `chunk_remaining` is about to be set to zero
// we do not need to clear the chunk here
self.chunk as usize
} else {
// `chunk` is a random number in a range that is a multiple of n+1
// so r will be a random number in [0..n+1)
let r = self.chunk % next_n;
self.chunk /= next_n;
r as usize
};
self.chunk_remaining = next_chunk_remaining;
self.n = next_n;
result
}
}
#[inline]
/// Calculates `bound`, `count` such that bound (m)*(m+1)*..*(m + remaining - 1)
fn calculate_bound_u32(m: u32) -> (u32, u8) {
debug_assert!(m > 0);
#[inline]
const fn inner(m: u32) -> (u32, u8) {
let mut product = m;
let mut current = m + 1;
loop {
if let Some(p) = u32::checked_mul(product, current) {
product = p;
current += 1;
} else {
// Count has a maximum value of 13 for when min is 1 or 2
let count = (current - m) as u8;
return (product, count);
}
}
}
const RESULT2: (u32, u8) = inner(2);
if m == 2 {
// Making this value a constant instead of recalculating it
// gives a significant (~50%) performance boost for small shuffles
return RESULT2;
}
inner(m)
}

View File

@ -238,7 +238,7 @@ where R: Rng + ?Sized {
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 j = usize::from(length >= 500_000);
let amount_fp = amount as f32;
let m4 = C[0][j] * amount_fp;
// Short-cut: when amount < 12, floyd's is always faster
@ -249,7 +249,7 @@ where R: Rng + ?Sized {
}
} else {
const C: [f32; 2] = [270.0, 330.0 / 9.0];
let j = if length < 500_000 { 0 } else { 1 };
let j = usize::from(length >= 500_000);
if (length as f32) < C[j] * (amount as f32) {
sample_inplace(rng, length, amount)
} else {
@ -267,9 +267,7 @@ where R: Rng + ?Sized {
/// sometimes be useful to have the indices themselves so this is provided as
/// an alternative.
///
/// This implementation uses `O(length + amount)` space and `O(length)` time
/// if the "nightly" feature is enabled, or `O(length)` space and
/// `O(length + amount * log length)` time otherwise.
/// This implementation uses `O(length + amount)` space and `O(length)` time.
///
/// Panics if `amount > length`.
#[cfg(feature = "std")]
@ -300,9 +298,7 @@ where
///
/// This implementation uses the algorithm described by Efraimidis and Spirakis
/// in this paper: https://doi.org/10.1016/j.ipl.2005.11.003
/// It uses `O(length + amount)` space and `O(length)` time if the
/// "nightly" feature is enabled, or `O(length)` space and `O(length
/// + amount * log length)` time otherwise.
/// It uses `O(length + amount)` space and `O(length)` time.
///
/// Panics if `amount > length`.
#[cfg(feature = "std")]
@ -347,63 +343,33 @@ where
}
impl<N> Eq for Element<N> {}
#[cfg(feature = "nightly")]
{
let mut candidates = Vec::with_capacity(length.as_usize());
let mut index = N::zero();
while index < length {
let weight = weight(index.as_usize()).into();
if !(weight >= 0.) {
return Err(WeightedError::InvalidWeight);
}
let key = rng.gen::<f64>().powf(1.0 / weight);
candidates.push(Element { index, key });
index += N::one();
let mut candidates = Vec::with_capacity(length.as_usize());
let mut index = N::zero();
while index < length {
let weight = weight(index.as_usize()).into();
if !(weight >= 0.) {
return Err(WeightedError::InvalidWeight);
}
// Partially sort the array to find the `amount` elements with the greatest
// keys. Do this by using `select_nth_unstable` to put the elements with
// the *smallest* keys at the beginning of the list in `O(n)` time, which
// provides equivalent information about the elements with the *greatest* keys.
let (_, mid, greater)
= candidates.select_nth_unstable(length.as_usize() - amount.as_usize());
let key = rng.gen::<f64>().powf(1.0 / weight);
candidates.push(Element { index, key });
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
result.push(mid.index);
for element in greater {
result.push(element.index);
}
Ok(IndexVec::from(result))
index += N::one();
}
#[cfg(not(feature = "nightly"))]
{
use alloc::collections::BinaryHeap;
// Partially sort the array to find the `amount` elements with the greatest
// keys. Do this by using `select_nth_unstable` to put the elements with
// the *smallest* keys at the beginning of the list in `O(n)` time, which
// provides equivalent information about the elements with the *greatest* keys.
let (_, mid, greater)
= candidates.select_nth_unstable(length.as_usize() - amount.as_usize());
// Partially sort the array such that the `amount` elements with the largest
// keys are first using a binary max heap.
let mut candidates = BinaryHeap::with_capacity(length.as_usize());
let mut index = N::zero();
while index < length {
let weight = weight(index.as_usize()).into();
if !(weight >= 0.) {
return Err(WeightedError::InvalidWeight);
}
let key = rng.gen::<f64>().powf(1.0 / weight);
candidates.push(Element { index, key });
index += N::one();
}
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
while result.len() < amount.as_usize() {
result.push(candidates.pop().unwrap().index);
}
Ok(IndexVec::from(result))
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
result.push(mid.index);
for element in greater {
result.push(element.index);
}
Ok(IndexVec::from(result))
}
/// Randomly sample exactly `amount` indices from `0..length`, using Floyd's
@ -414,33 +380,18 @@ where
/// This implementation uses `O(amount)` memory and `O(amount^2)` time.
fn sample_floyd<R>(rng: &mut R, length: u32, amount: u32) -> IndexVec
where R: Rng + ?Sized {
// For small amount we use Floyd's fully-shuffled variant. For larger
// amounts this is slow due to Vec::insert performance, so we shuffle
// afterwards. Benchmarks show little overhead from extra logic.
let floyd_shuffle = amount < 50;
// Note that the values returned by `rng.gen_range()` can be
// inferred from the returned vector by working backwards from
// the last entry. This bijection proves the algorithm fair.
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);
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;
if let Some(pos) = indices.iter().position(|&x| x == t) {
indices[pos] = j;
}
indices.push(t);
}
if !floyd_shuffle {
// Reimplement SliceRandom::shuffle with smaller indices
for i in (1..amount).rev() {
// invariant: elements with index > i have been locked in place.
indices.swap(i as usize, rng.gen_range(0..=i) as usize);
}
}
IndexVec::from(indices)
}
@ -528,7 +479,7 @@ where
let mut cache = HashSet::with_capacity(amount.as_usize());
#[cfg(not(feature = "std"))]
let mut cache = BTreeSet::new();
let distr = Uniform::new(X::zero(), length);
let distr = Uniform::new(X::zero(), length).unwrap();
let mut indices = Vec::with_capacity(amount.as_usize());
for _ in 0..amount.as_usize() {
let mut pos = distr.sample(rng);
@ -662,8 +613,8 @@ mod test {
);
};
do_test(10, 6, &[8, 0, 3, 5, 9, 6]); // floyd
do_test(25, 10, &[18, 15, 14, 9, 0, 13, 5, 24]); // floyd
do_test(10, 6, &[8, 3, 5, 9, 0, 6]); // floyd
do_test(25, 10, &[18, 14, 9, 15, 0, 13, 5, 24]); // floyd
do_test(300, 8, &[30, 283, 150, 1, 73, 13, 285, 35]); // floyd
do_test(300, 80, &[31, 289, 248, 154, 5, 78, 19, 286]); // inplace
do_test(300, 180, &[31, 289, 248, 154, 5, 78, 19, 286]); // inplace

View File

@ -1,4 +1,4 @@
// Copyright 2018 Developers of the Rand project.
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
@ -24,20 +24,28 @@
//! `usize` indices are sampled as a `u32` where possible (also providing a
//! small performance boost in some cases).
mod coin_flipper;
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub mod index;
#[cfg(feature = "alloc")] use core::ops::Index;
mod increasing_uniform;
#[cfg(feature = "alloc")] use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::ops::Index;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use crate::distributions::uniform::{SampleBorrow, SampleUniform};
#[cfg(feature = "alloc")] use crate::distributions::WeightedError;
#[cfg(feature = "alloc")]
use crate::distributions::WeightedError;
use crate::Rng;
use self::coin_flipper::CoinFlipper;
use self::increasing_uniform::IncreasingUniform;
/// Extension trait on slices, providing random mutation and sampling methods.
///
/// This trait is implemented on all `[T]` slice types, providing several
@ -77,14 +85,16 @@ pub trait SliceRandom {
/// assert_eq!(choices[..0].choose(&mut rng), None);
/// ```
fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item>
where R: Rng + ?Sized;
where
R: Rng + ?Sized;
/// Returns a mutable reference to one random element of the slice, or
/// `None` if the slice is empty.
///
/// For slices, complexity is `O(1)`.
fn choose_mut<R>(&mut self, rng: &mut R) -> Option<&mut Self::Item>
where R: Rng + ?Sized;
where
R: Rng + ?Sized;
/// Chooses `amount` elements from the slice at random, without repetition,
/// and in random order. The returned iterator is appropriate both for
@ -113,7 +123,8 @@ pub trait SliceRandom {
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn choose_multiple<R>(&self, rng: &mut R, amount: usize) -> SliceChooseIter<Self, Self::Item>
where R: Rng + ?Sized;
where
R: Rng + ?Sized;
/// Similar to [`choose`], but where the likelihood of each outcome may be
/// specified.
@ -123,21 +134,25 @@ pub trait SliceRandom {
/// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`.
///
/// For slices of length `n`, complexity is `O(n)`.
/// See also [`choose_weighted_mut`], [`distributions::weighted`].
/// For more information about the underlying algorithm,
/// see [`distributions::WeightedIndex`].
///
/// See also [`choose_weighted_mut`].
///
/// # Example
///
/// ```
/// use rand::prelude::*;
///
/// let choices = [('a', 2), ('b', 1), ('c', 1)];
/// let choices = [('a', 2), ('b', 1), ('c', 1), ('d', 0)];
/// let mut rng = thread_rng();
/// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c'
/// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c',
/// // and 'd' will never be printed
/// println!("{:?}", choices.choose_weighted(&mut rng, |item| item.1).unwrap().0);
/// ```
/// [`choose`]: SliceRandom::choose
/// [`choose_weighted_mut`]: SliceRandom::choose_weighted_mut
/// [`distributions::weighted`]: crate::distributions::weighted
/// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn choose_weighted<R, F, B, X>(
@ -161,11 +176,14 @@ pub trait SliceRandom {
/// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`.
///
/// For slices of length `n`, complexity is `O(n)`.
/// See also [`choose_weighted`], [`distributions::weighted`].
/// For more information about the underlying algorithm,
/// see [`distributions::WeightedIndex`].
///
/// See also [`choose_weighted`].
///
/// [`choose_mut`]: SliceRandom::choose_mut
/// [`choose_weighted`]: SliceRandom::choose_weighted
/// [`distributions::weighted`]: crate::distributions::weighted
/// [`distributions::WeightedIndex`]: crate::distributions::WeightedIndex
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn choose_weighted_mut<R, F, B, X>(
@ -192,10 +210,9 @@ pub trait SliceRandom {
/// If all of the weights are equal, even if they are all zero, each element has
/// an equal likelihood of being selected.
///
/// The complexity of this method depends on the feature `partition_at_index`.
/// If the feature is enabled, then for slices of length `n`, the complexity
/// is `O(n)` space and `O(n)` time. Otherwise, the complexity is `O(n)` space and
/// `O(n * log amount)` time.
/// This implementation uses `O(length + amount)` space and `O(length)` time
/// if the "nightly" feature is enabled, or `O(length)` space and
/// `O(length + amount * log length)` time otherwise.
///
/// # Example
///
@ -228,6 +245,7 @@ pub trait SliceRandom {
/// Shuffle a mutable slice in place.
///
/// For slices of length `n`, complexity is `O(n)`.
/// The resulting permutation is picked uniformly from the set of all possible permutations.
///
/// # Example
///
@ -242,7 +260,8 @@ pub trait SliceRandom {
/// println!("Shuffled: {:?}", y);
/// ```
fn shuffle<R>(&mut self, rng: &mut R)
where R: Rng + ?Sized;
where
R: Rng + ?Sized;
/// Shuffle a slice in place, but exit early.
///
@ -264,7 +283,8 @@ pub trait SliceRandom {
fn partial_shuffle<R>(
&mut self, rng: &mut R, amount: usize,
) -> (&mut [Self::Item], &mut [Self::Item])
where R: Rng + ?Sized;
where
R: Rng + ?Sized;
}
/// Extension trait on iterators, providing random sampling methods.
@ -302,26 +322,30 @@ pub trait IteratorRandom: Iterator + Sized {
/// `choose` returning different elements. If you want consistent results
/// and RNG usage consider using [`IteratorRandom::choose_stable`].
fn choose<R>(mut self, rng: &mut R) -> Option<Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
let (mut lower, mut upper) = self.size_hint();
let mut consumed = 0;
let mut result = None;
// Handling for this condition outside the loop allows the optimizer to eliminate the loop
// when the Iterator is an ExactSizeIterator. This has a large performance impact on e.g.
// seq_iter_choose_from_1000.
if upper == Some(lower) {
return if lower == 0 {
None
} else {
self.nth(gen_index(rng, lower))
return match lower {
0 => None,
1 => self.next(),
_ => self.nth(gen_index(rng, lower)),
};
}
let mut coin_flipper = coin_flipper::CoinFlipper::new(rng);
let mut consumed = 0;
// Continue until the iterator is exhausted
loop {
if lower > 1 {
let ix = gen_index(rng, lower + consumed);
let ix = gen_index(coin_flipper.rng, lower + consumed);
let skip = if ix < lower {
result = self.nth(ix);
lower - (ix + 1)
@ -341,7 +365,7 @@ pub trait IteratorRandom: Iterator + Sized {
return result;
}
consumed += 1;
if gen_index(rng, consumed) == 0 {
if coin_flipper.gen_ratio_one_over(consumed) {
result = elem;
}
}
@ -371,9 +395,12 @@ pub trait IteratorRandom: Iterator + Sized {
///
/// [`choose`]: IteratorRandom::choose
fn choose_stable<R>(mut self, rng: &mut R) -> Option<Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
let mut consumed = 0;
let mut result = None;
let mut coin_flipper = CoinFlipper::new(rng);
loop {
// Currently the only way to skip elements is `nth()`. So we need to
@ -385,7 +412,7 @@ pub trait IteratorRandom: Iterator + Sized {
let (lower, _) = self.size_hint();
if lower >= 2 {
let highest_selected = (0..lower)
.filter(|ix| gen_index(rng, consumed+ix+1) == 0)
.filter(|ix| coin_flipper.gen_ratio_one_over(consumed + ix + 1))
.last();
consumed += lower;
@ -400,10 +427,10 @@ pub trait IteratorRandom: Iterator + Sized {
let elem = self.nth(next);
if elem.is_none() {
return result
return result;
}
if gen_index(rng, consumed+1) == 0 {
if coin_flipper.gen_ratio_one_over(consumed + 1) {
result = elem;
}
consumed += 1;
@ -424,7 +451,9 @@ pub trait IteratorRandom: Iterator + Sized {
/// Complexity is `O(n)` where `n` is the length of the iterator.
/// For slices, prefer [`SliceRandom::choose_multiple`].
fn choose_multiple_fill<R>(mut self, rng: &mut R, buf: &mut [Self::Item]) -> usize
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
let amount = buf.len();
let mut len = 0;
while len < amount {
@ -464,7 +493,9 @@ pub trait IteratorRandom: Iterator + Sized {
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn choose_multiple<R>(mut self, rng: &mut R, amount: usize) -> Vec<Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
let mut reservoir = Vec::with_capacity(amount);
reservoir.extend(self.by_ref().take(amount));
@ -488,12 +519,13 @@ pub trait IteratorRandom: Iterator + Sized {
}
}
impl<T> SliceRandom for [T] {
type Item = T;
fn choose<R>(&self, rng: &mut R) -> Option<&Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
if self.is_empty() {
None
} else {
@ -502,7 +534,9 @@ impl<T> SliceRandom for [T] {
}
fn choose_mut<R>(&mut self, rng: &mut R) -> Option<&mut Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
if self.is_empty() {
None
} else {
@ -513,7 +547,9 @@ impl<T> SliceRandom for [T] {
#[cfg(feature = "alloc")]
fn choose_multiple<R>(&self, rng: &mut R, amount: usize) -> SliceChooseIter<Self, Self::Item>
where R: Rng + ?Sized {
where
R: Rng + ?Sized,
{
let amount = ::core::cmp::min(amount, self.len());
SliceChooseIter {
slice: self,
@ -584,37 +620,52 @@ impl<T> SliceRandom for [T] {
}
fn shuffle<R>(&mut self, rng: &mut R)
where R: Rng + ?Sized {
for i in (1..self.len()).rev() {
// invariant: elements with index > i have been locked in place.
self.swap(i, gen_index(rng, i + 1));
where
R: Rng + ?Sized,
{
if self.len() <= 1 {
// There is no need to shuffle an empty or single element slice
return;
}
self.partial_shuffle(rng, self.len());
}
fn partial_shuffle<R>(
&mut self, rng: &mut R, amount: usize,
) -> (&mut [Self::Item], &mut [Self::Item])
where R: Rng + ?Sized {
// This applies Durstenfeld's algorithm for the
where
R: Rng + ?Sized,
{
let m = self.len().saturating_sub(amount);
// The algorithm below is based on Durstenfeld's algorithm for the
// [FisherYates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm)
// for an unbiased permutation, but exits early after choosing `amount`
// elements.
let len = self.len();
let end = if amount >= len { 0 } else { len - amount };
for i in (end..len).rev() {
// invariant: elements with index > i have been locked in place.
self.swap(i, gen_index(rng, i + 1));
// for an unbiased permutation.
// It ensures that the last `amount` elements of the slice
// are randomly selected from the whole slice.
//`IncreasingUniform::next_index()` is faster than `gen_index`
//but only works for 32 bit integers
//So we must use the slow method if the slice is longer than that.
if self.len() < (u32::MAX as usize) {
let mut chooser = IncreasingUniform::new(rng, m as u32);
for i in m..self.len() {
let index = chooser.next_index();
self.swap(i, index);
}
} else {
for i in m..self.len() {
let index = gen_index(rng, i + 1);
self.swap(i, index);
}
}
let r = self.split_at_mut(end);
let r = self.split_at_mut(m);
(r.1, r.0)
}
}
impl<I> IteratorRandom for I where I: Iterator + Sized {}
/// An iterator over multiple slice elements.
///
/// This struct is created by
@ -634,7 +685,7 @@ impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> Iterator for SliceCho
fn next(&mut self) -> Option<Self::Item> {
// TODO: investigate using SliceIndex::get_unchecked when stable
self.indices.next().map(|i| &self.slice[i as usize])
self.indices.next().map(|i| &self.slice[i])
}
fn size_hint(&self) -> (usize, Option<usize>) {
@ -651,12 +702,12 @@ impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> ExactSizeIterator
}
}
// Sample a number uniformly between 0 and `ubound`. Uses 32-bit sampling where
// possible, primarily in order to produce the same output on 32-bit and 64-bit
// platforms.
#[inline]
fn gen_index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize {
if ubound <= (core::u32::MAX as usize) {
rng.gen_range(0..ubound as u32) as usize
} else {
@ -664,12 +715,13 @@ fn gen_index<R: Rng + ?Sized>(rng: &mut R, ubound: usize) -> usize {
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "alloc")] use crate::Rng;
#[cfg(all(feature = "alloc", not(feature = "std")))] use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use crate::Rng;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec::Vec;
#[test]
fn test_slice_choose() {
@ -718,7 +770,7 @@ mod test {
.choose_multiple(&mut r, 8)
.cloned()
.collect::<Vec<char>>(),
&['d', 'm', 'b', 'n', 'c', 'k', 'h', 'e']
&['d', 'm', 'n', 'k', 'h', 'e', 'b', 'c']
);
#[cfg(feature = "alloc")]
@ -728,11 +780,11 @@ mod test {
let mut r = crate::test::rng(414);
nums.shuffle(&mut r);
assert_eq!(nums, [9, 5, 3, 10, 7, 12, 8, 11, 6, 4, 0, 2, 1]);
assert_eq!(nums, [5, 11, 0, 8, 7, 12, 6, 4, 9, 3, 1, 2, 10]);
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let res = nums.partial_shuffle(&mut r, 6);
assert_eq!(res.0, &mut [7, 4, 8, 6, 9, 3]);
assert_eq!(res.1, &mut [0, 1, 2, 12, 11, 5, 10]);
assert_eq!(res.0, &mut [7, 12, 6, 8, 1, 9]);
assert_eq!(res.1, &mut [0, 11, 2, 3, 4, 5, 10]);
}
#[derive(Clone)]
@ -830,28 +882,40 @@ mod test {
#[cfg(feature = "alloc")]
test_iter(r, (0..9).collect::<Vec<_>>().into_iter());
test_iter(r, UnhintedIterator { iter: 0..9 });
test_iter(r, ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
});
test_iter(r, ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
});
test_iter(r, WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
});
test_iter(r, WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
});
test_iter(
r,
ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
},
);
test_iter(
r,
ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
},
);
test_iter(
r,
WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
},
);
test_iter(
r,
WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
},
);
assert_eq!((0..0).choose(r), None);
assert_eq!(UnhintedIterator { iter: 0..0 }.choose(r), None);
@ -884,28 +948,40 @@ mod test {
#[cfg(feature = "alloc")]
test_iter(r, (0..9).collect::<Vec<_>>().into_iter());
test_iter(r, UnhintedIterator { iter: 0..9 });
test_iter(r, ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
});
test_iter(r, ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
});
test_iter(r, WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
});
test_iter(r, WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
});
test_iter(
r,
ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
},
);
test_iter(
r,
ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
},
);
test_iter(
r,
WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
},
);
test_iter(
r,
WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
},
);
assert_eq!((0..0).choose(r), None);
assert_eq!(UnhintedIterator { iter: 0..0 }.choose(r), None);
@ -925,33 +1001,48 @@ mod test {
}
let reference = test_iter(0..9);
assert_eq!(test_iter([0, 1, 2, 3, 4, 5, 6, 7, 8].iter().cloned()), reference);
assert_eq!(
test_iter([0, 1, 2, 3, 4, 5, 6, 7, 8].iter().cloned()),
reference
);
#[cfg(feature = "alloc")]
assert_eq!(test_iter((0..9).collect::<Vec<_>>().into_iter()), reference);
assert_eq!(test_iter(UnhintedIterator { iter: 0..9 }), reference);
assert_eq!(test_iter(ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
}), reference);
assert_eq!(test_iter(ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
}), reference);
assert_eq!(test_iter(WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
}), reference);
assert_eq!(test_iter(WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
}), reference);
assert_eq!(
test_iter(ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: false,
}),
reference
);
assert_eq!(
test_iter(ChunkHintedIterator {
iter: 0..9,
chunk_size: 4,
chunk_remaining: 4,
hint_total_size: true,
}),
reference
);
assert_eq!(
test_iter(WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: false,
}),
reference
);
assert_eq!(
test_iter(WindowHintedIterator {
iter: 0..9,
window_size: 2,
hint_total_size: true,
}),
reference
);
}
#[test]
@ -1122,7 +1213,7 @@ mod test {
assert_eq!(choose([].iter().cloned()), None);
assert_eq!(choose(0..100), Some(33));
assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(40));
assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(27));
assert_eq!(
choose(ChunkHintedIterator {
iter: 0..100,
@ -1167,8 +1258,8 @@ mod test {
}
assert_eq!(choose([].iter().cloned()), None);
assert_eq!(choose(0..100), Some(40));
assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(40));
assert_eq!(choose(0..100), Some(27));
assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(27));
assert_eq!(
choose(ChunkHintedIterator {
iter: 0..100,
@ -1176,7 +1267,7 @@ mod test {
chunk_remaining: 32,
hint_total_size: false,
}),
Some(40)
Some(27)
);
assert_eq!(
choose(ChunkHintedIterator {
@ -1185,7 +1276,7 @@ mod test {
chunk_remaining: 32,
hint_total_size: true,
}),
Some(40)
Some(27)
);
assert_eq!(
choose(WindowHintedIterator {
@ -1193,7 +1284,7 @@ mod test {
window_size: 32,
hint_total_size: false,
}),
Some(40)
Some(27)
);
assert_eq!(
choose(WindowHintedIterator {
@ -1201,7 +1292,7 @@ mod test {
window_size: 32,
hint_total_size: true,
}),
Some(40)
Some(27)
);
}
@ -1253,9 +1344,13 @@ mod test {
// Case 2: All of the weights are 0
let choices = [('a', 0), ('b', 0), ('c', 0)];
assert_eq!(choices
.choose_multiple_weighted(&mut rng, 2, |item| item.1)
.unwrap().count(), 2);
assert_eq!(
choices
.choose_multiple_weighted(&mut rng, 2, |item| item.1)
.unwrap()
.count(),
2
);
// Case 3: Negative weights
let choices = [('a', -1), ('b', 1), ('c', 1)];
@ -1268,9 +1363,13 @@ mod test {
// Case 4: Empty list
let choices = [];
assert_eq!(choices
.choose_multiple_weighted(&mut rng, 0, |_: &()| 0)
.unwrap().count(), 0);
assert_eq!(
choices
.choose_multiple_weighted(&mut rng, 0, |_: &()| 0)
.unwrap()
.count(),
0
);
// Case 5: NaN weights
let choices = [('a', core::f64::NAN), ('b', 1.0), ('c', 1.0)];