Merge branch 'master' into dirichlet-small-alpha
This commit is contained in:
commit
c44ac16e40
37
.github/workflows/gh-pages.yml
vendored
37
.github/workflows/gh-pages.yml
vendored
@ -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
|
||||
|
94
.github/workflows/test.yml
vendored
94
.github/workflows/test.yml
vendored
@ -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
|
||||
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -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
707
Cargo.lock.msrv
Normal 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"
|
40
Cargo.toml
40
Cargo.toml
@ -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
|
30
README.md
30
README.md
@ -5,7 +5,7 @@
|
||||
[](https://rust-random.github.io/book/)
|
||||
[](https://rust-random.github.io/rand/rand)
|
||||
[](https://docs.rs/rand)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
[](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
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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
111
benches/seq_choose.rs
Normal 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
50
benches/shuffle.rs
Normal 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]
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
82
examples/rayon-monte-carlo.rs
Normal file
82
examples/rayon-monte-carlo.rs
Normal 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)
|
||||
);
|
||||
}
|
@ -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 }
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
[](https://rust-random.github.io/book/)
|
||||
[](https://rust-random.github.io/rand/rand_chacha)
|
||||
[](https://docs.rs/rand_chacha)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
|
||||
A cryptographically secure random number generator that uses the ChaCha
|
||||
algorithm.
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))))]
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -5,7 +5,7 @@
|
||||
[](https://rust-random.github.io/book/)
|
||||
[](https://rust-random.github.io/rand/rand_core)
|
||||
[](https://docs.rs/rand_core)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
[](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
|
||||
|
@ -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 {
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
||||
|
@ -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
|
||||
///
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -5,7 +5,7 @@
|
||||
[](https://rust-random.github.io/book/)
|
||||
[](https://rust-random.github.io/rand/rand_distr)
|
||||
[](https://docs.rs/rand_distr)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
|
||||
Implements a full suite of random number distribution sampling routines.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
|
@ -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]
|
||||
|
@ -5,7 +5,7 @@
|
||||
[](https://rust-random.github.io/book/)
|
||||
[](https://rust-random.github.io/rand/rand_pcg)
|
||||
[](https://docs.rs/rand_pcg)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
[](https://github.com/rust-random/rand#rust-version-requirements)
|
||||
|
||||
Implements a selection of PCG random number generators.
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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
@ -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);
|
||||
|
@ -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`.
|
||||
|
36
src/lib.rs
36
src/lib.rs
@ -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]
|
||||
|
42
src/rng.rs
42
src/rng.rs
@ -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::*;
|
||||
|
@ -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,
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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
160
src/seq/coin_flipper.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
src/seq/increasing_uniform.rs
Normal file
108
src/seq/increasing_uniform.rs
Normal 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)
|
||||
}
|
113
src/seq/index.rs
113
src/seq/index.rs
@ -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
|
||||
|
387
src/seq/mod.rs
387
src/seq/mod.rs
@ -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
|
||||
// [Fisher–Yates 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)];
|
||||
|
Loading…
x
Reference in New Issue
Block a user