diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 3e0dd6b4f5b..00000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: dhardy diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 093433dffb3..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Bug report -about: Something doesn't work as expected -title: '' -labels: X-bug -assignees: '' - ---- - -## Summary - -A clear and concise description of what the bug is. - -What behaviour is expected, and why? - -## Code sample - -```rust -// Code demonstrating the problem -``` diff --git a/.github/ISSUE_TEMPLATE/compile-issue.md b/.github/ISSUE_TEMPLATE/compile-issue.md deleted file mode 100644 index 8a8354bd5d3..00000000000 --- a/.github/ISSUE_TEMPLATE/compile-issue.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Compile issue -about: Report / ask about a compilation issue -title: '' -labels: '' -assignees: '' - ---- - -# Common issues - -**Problem**: `rand_hc::Hc128Rng: rand_core::SeedableRng` (or other RNG) - -**Quick solution**: `cargo update` - -**Details**: This happens when multiple versions of the `rand_core` crate are in use. Check your `Cargo.lock` file for all versions of `rand_core`. Note that some versions (0.2.2 and 0.3.1) are compatibility shims and are not a problem by themselves. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 90c57c87bcd..00000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - ---- - -## Background - -**What is your motivation?** - -**What type of application is this?** (E.g. cryptography, game, numerical simulation) - -## Feature request - -
diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md deleted file mode 100644 index a3e76cabdc9..00000000000 --- a/.github/ISSUE_TEMPLATE/other.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Other -about: empty template -title: '' -labels: '' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/suggest-a-change.md b/.github/ISSUE_TEMPLATE/suggest-a-change.md deleted file mode 100644 index e260055561b..00000000000 --- a/.github/ISSUE_TEMPLATE/suggest-a-change.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Suggest a change -about: Describe this issue template's purpose here. -title: 'CHANGE: ' -labels: '' -assignees: '' - ---- - -A change request is considered a (small) Request-For-Comment, and requires a concrete proposal and motivation. - -## Summary - -How does this affect the API / end-user? (Include API-breaking changes, value-breaking changes and API additions.) - -## Details - -What changes does this require internally? - -## Motivation - -What is the motivation for this change? - -Since every change has a cost (even if just API churn or extra code size), every change must have sufficient motivation. This is arguably the most important part of the RFC. - -## Alternatives - -Which alternatives might be considered, and why or why not? diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 02ac88f0673..00000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,7 +0,0 @@ -- [ ] Added a `CHANGELOG.md` entry - -# Summary - -# Motivation - -# Details diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index b42e381cf7f..00000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: 2 -updates: -- package-ecosystem: cargo - directory: "/" - versioning-strategy: lockfile-only - allow: - - dependency-type: "all" - schedule: - interval: weekly - groups: - all-deps: - patterns: - - "*" -- package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: weekly diff --git a/.github/workflows/benches.yml b/.github/workflows/benches.yml deleted file mode 100644 index 58205efb024..00000000000 --- a/.github/workflows/benches.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Benches - -on: - push: - branches: [ master ] - paths-ignore: - - "**.md" - - "examples/**" - pull_request: - branches: [ master ] - paths-ignore: - - "**.md" - - "examples/**" - -defaults: - run: - working-directory: ./benches - -jobs: - clippy-fmt: - name: "Benches: Check Clippy and rustfmt" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: clippy, rustfmt - - name: Rustfmt - run: cargo fmt -- --check - - name: Clippy - run: cargo clippy --all-targets -- -D warnings - benches: - name: "Benches: Test" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly - - name: Test - run: RUSTFLAGS=-Dwarnings cargo test --benches diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 7bd7960d14c..00000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Publish to crates.io and create GitHub release -on: - push: - tags: ['[0-9].[0-9].*', '[0-9].[1-9][0-9].*'] - -jobs: - # Source: https://crates.io/docs/trusted-publishing - publish: - runs-on: ubuntu-latest - environment: release - permissions: - id-token: write - steps: - - uses: actions/checkout@v6 - - uses: rust-lang/crates-io-auth-action@v1 - id: auth - - run: cargo publish - env: - CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index b6b5b0985cf..00000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,187 +0,0 @@ -name: Main tests - -on: - push: - branches: [ master, '0.[0-9]+' ] - paths-ignore: - - "**.md" - - "benches/**" - pull_request: - branches: [ master, '0.[0-9]+' ] - paths-ignore: - - "**.md" - - "benches/**" - schedule: - - cron: '0 0 * * SUN' - -permissions: - contents: read # to fetch code (actions/checkout) - -jobs: - lockfile: - name: Lockfile is up-to-date - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable - - run: cargo check --workspace --locked - - clippy: - name: Clippy - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.93 - components: clippy - - run: cargo clippy --workspace --lib --bins --tests -- -D warnings - - fmt: - name: rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - run: cargo fmt --all -- --check - - check-doc: - name: Check doc - runs-on: ubuntu-latest - env: - RUSTDOCFLAGS: "-Dwarnings --cfg docsrs -Zunstable-options --generate-link-to-definition" - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - - name: rand - run: cargo doc --all-features --no-deps - - test: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - toolchain: stable - - os: macos-latest - target: x86_64-apple-darwin - toolchain: stable - # TODO: also aarch64 / M1 - - os: windows-latest - target: x86_64-pc-windows-gnu - toolchain: stable - - os: windows-latest - target: x86_64-pc-windows-msvc - toolchain: beta - # Test both windows-gnu and windows-msvc; use beta rust on one - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - variant: MSRV - toolchain: 1.85.0 - - os: ubuntu-latest - deps: sudo apt-get update ; sudo apt install gcc-multilib - target: i686-unknown-linux-gnu - toolchain: nightly - - os: ubuntu-latest - target: x86_64-unknown-linux-gnu - toolchain: nightly - variant: minimal_versions - - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - uses: dtolnay/rust-toolchain@master - with: - target: ${{ matrix.target }} - toolchain: ${{ matrix.toolchain }} - - run: ${{ matrix.deps }} - - name: Maybe minimal versions - if: ${{ matrix.variant == 'minimal_versions' }} - run: | - cargo generate-lockfile -Z minimal-versions - - name: Maybe nightly - if: ${{ matrix.toolchain == 'nightly' }} - run: | - cargo test --target ${{ matrix.target }} - cargo test --target ${{ matrix.target }} --all-features - 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,sys_rng,unbiased - cargo test --target ${{ matrix.target }} --lib --tests --no-default-features --features=alloc,sys_rng - cargo test --target ${{ matrix.target }} --examples - - name: Test rand (all stable features) - run: | - cargo test --target ${{ matrix.target }} --features=serde,log - - test-cross: - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-latest - target: powerpc-unknown-linux-gnu - toolchain: stable - - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - uses: dtolnay/rust-toolchain@master - with: - target: ${{ matrix.target }} - toolchain: ${{ matrix.toolchain }} - - name: Cache cargo plugins - uses: actions/cache@v5 - with: - path: ~/.cargo/bin/ - key: ${{ runner.os }}-cargo-plugins - - name: Install cross - run: cargo install cross || true - - name: Test - run: | - # all stable features: - cross test --no-fail-fast --target ${{ matrix.target }} --features=serde,log - cross test --no-fail-fast --target ${{ matrix.target }} --examples - - test-miri: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - run: | - 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 - cargo miri test --features=log - - test-no-std: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - with: - target: thumbv6m-none-eabi - - name: Build top-level only - run: cargo build --target=thumbv6m-none-eabi --no-default-features - - test-ios: - runs-on: macos-latest - steps: - - uses: actions/checkout@v6 - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - with: - target: aarch64-apple-ios - - name: Build top-level only - run: cargo build --target=aarch64-apple-ios diff --git a/.gitignore b/.gitignore index e65aa1774db..a9d37c560c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ target -rand_wasm_bindgen_test*.[tj]s -rand_wasm_bindgen_test*.wasm -/benches/Cargo.lock +Cargo.lock diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000000..2c5dbd6b783 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +language: rust +rust: + - 1.0.0 + - stable + - beta + - nightly +sudo: false +before_script: + - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH +script: + - cargo build --verbose + - cargo test --verbose + - cargo doc --no-deps +after_success: + - travis-cargo --only nightly doc-upload +env: + global: + secure: "BdDntVHSompN+Qxz5Rz45VI4ZqhD72r6aPl166FADlnkIwS6N6FLWdqs51O7G5CpoMXEDvyYrjmRMZe/GYLIG9cmqmn/wUrWPO+PauGiIuG/D2dmfuUNvSTRcIe7UQLXrfP3yyfZPgqsH6pSnNEVopquQKy3KjzqepgriOJtbyY=" + + + +notifications: + email: + on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7c32e4ef145..00000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,1209 +0,0 @@ -# Changelog -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). - -A [separate changelog is kept for rand_core](https://github.com/rust-random/core/blob/master/CHANGELOG.md). - -You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful. - -## [0.10.0] - 2026-02-08 - -### Changes -- The dependency on `rand_chacha` has been replaced with a dependency on `chacha20`. This changes the implementation behind `StdRng`, but the output remains the same. There may be some API breakage when using the ChaCha-types directly as these are now the ones in `chacha20` instead of `rand_chacha` ([#1642]). -- Rename fns `IndexedRandom::choose_multiple` -> `sample`, `choose_multiple_array` -> `sample_array`, `choose_multiple_weighted` -> `sample_weighted`, struct `SliceChooseIter` -> `IndexedSamples` and fns `IteratorRandom::choose_multiple` -> `sample`, `choose_multiple_fill` -> `sample_fill` ([#1632]) -- Use Edition 2024 and MSRV 1.85 ([#1653]) -- Let `Fill` be implemented for element types, not sliceable types ([#1652]) -- Fix `OsError::raw_os_error` on UEFI targets by returning `Option` ([#1665]) -- Replace fn `TryRngCore::read_adapter(..) -> RngReadAdapter` with simpler struct `RngReader` ([#1669]) -- Remove fns `SeedableRng::from_os_rng`, `try_from_os_rng` ([#1674]) -- Remove `Clone` support for `StdRng`, `ReseedingRng` ([#1677]) -- Use `postcard` instead of `bincode` to test the serde feature ([#1693]) -- Avoid excessive allocation in `IteratorRandom::sample` when `amount` is much larger than iterator size ([#1695]) -- Rename `os_rng` -> `sys_rng`, `OsRng` -> `SysRng`, `OsError` -> `SysError` ([#1697]) -- Rename `Rng` -> `RngExt` as upstream `rand_core` has renamed `RngCore` -> `Rng` ([#1717]) - -### Additions -- Add fns `IndexedRandom::choose_iter`, `choose_weighted_iter` ([#1632]) -- Pub export `Xoshiro128PlusPlus`, `Xoshiro256PlusPlus` prngs ([#1649]) -- Pub export `ChaCha8Rng`, `ChaCha12Rng`, `ChaCha20Rng` behind `chacha` feature ([#1659]) -- Fn `rand::make_rng() -> R where R: SeedableRng` ([#1734]) - -### Removals -- Removed `ReseedingRng` ([#1722]) -- Removed unused feature "nightly" ([#1732]) -- Removed feature `small_rng` ([#1732]) - -[#1632]: https://github.com/rust-random/rand/pull/1632 -[#1642]: https://github.com/rust-random/rand/pull/1642 -[#1649]: https://github.com/rust-random/rand/pull/1649 -[#1652]: https://github.com/rust-random/rand/pull/1652 -[#1653]: https://github.com/rust-random/rand/pull/1653 -[#1659]: https://github.com/rust-random/rand/pull/1659 -[#1665]: https://github.com/rust-random/rand/pull/1665 -[#1669]: https://github.com/rust-random/rand/pull/1669 -[#1674]: https://github.com/rust-random/rand/pull/1674 -[#1677]: https://github.com/rust-random/rand/pull/1677 -[#1693]: https://github.com/rust-random/rand/pull/1693 -[#1695]: https://github.com/rust-random/rand/pull/1695 -[#1697]: https://github.com/rust-random/rand/pull/1697 -[#1717]: https://github.com/rust-random/rand/pull/1717 -[#1722]: https://github.com/rust-random/rand/pull/1722 -[#1732]: https://github.com/rust-random/rand/pull/1732 -[#1734]: https://github.com/rust-random/rand/pull/1734 - -## [0.9.2] - 2025-07-20 - -### Deprecated -- Deprecate `rand::rngs::mock` module and `StepRng` generator ([#1634]) - -### Additions -- Enable `WeightedIndex` (de)serialization ([#1646]) - -## [0.9.1] - 2025-04-17 - -### Security and unsafe -- Revise "not a crypto library" policy again ([#1565]) -- Remove `zerocopy` dependency from `rand` ([#1579]) - -### Fixes -- Fix feature `simd_support` for recent nightly rust ([#1586]) - -### Changes -- Allow `fn rand::seq::index::sample_weighted` and `fn IndexedRandom::choose_multiple_weighted` to return fewer than `amount` results ([#1623]), reverting an undocumented change ([#1382]) to the previous release. - -### Additions -- Add `rand::distr::Alphabetic` distribution. ([#1587]) -- Re-export `rand_core` ([#1604]) - -[#1565]: https://github.com/rust-random/rand/pull/1565 -[#1579]: https://github.com/rust-random/rand/pull/1579 -[#1586]: https://github.com/rust-random/rand/pull/1586 -[#1587]: https://github.com/rust-random/rand/pull/1587 -[#1604]: https://github.com/rust-random/rand/pull/1604 -[#1623]: https://github.com/rust-random/rand/pull/1623 -[#1634]: https://github.com/rust-random/rand/pull/1634 -[#1646]: https://github.com/rust-random/rand/pull/1646 - -## [0.9.0] - 2025-01-27 - -### Security and unsafe -- Policy: "rand is not a crypto library" ([#1514]) -- Remove fork-protection from `ReseedingRng` and `ThreadRng`. Instead, it is recommended to call `ThreadRng::reseed` on fork. ([#1379]) -- Use `zerocopy` to replace some `unsafe` code ([#1349], [#1393], [#1446], [#1502]) - -### Dependencies -- Bump the MSRV to 1.63.0 ([#1207], [#1246], [#1269], [#1341], [#1416], [#1536]); note that 1.60.0 may work for dependents when using `--ignore-rust-version` -- Update to `rand_core` v0.9.0 ([#1558]) - -### Features -- Support `std` feature without `getrandom` or `rand_chacha` ([#1354]) -- Enable feature `small_rng` by default ([#1455]) -- Remove implicit feature `rand_chacha`; use `std_rng` instead. ([#1473]) -- Rename feature `serde1` to `serde` ([#1477]) -- Rename feature `getrandom` to `os_rng` ([#1537]) -- Add feature `thread_rng` ([#1547]) - -### API changes: rand_core traits -- Add fn `RngCore::read_adapter` implementing `std::io::Read` ([#1267]) -- Add trait `CryptoBlockRng: BlockRngCore`; make `trait CryptoRng: RngCore` ([#1273]) -- Add traits `TryRngCore`, `TryCryptoRng` ([#1424], [#1499]) -- Rename `fn SeedableRng::from_rng` -> `try_from_rng` and add infallible variant `fn from_rng` ([#1424]) -- Rename `fn SeedableRng::from_entropy` -> `from_os_rng` and add fallible variant `fn try_from_os_rng` ([#1424]) -- Add bounds `Clone` and `AsRef` to associated type `SeedableRng::Seed` ([#1491]) - -### API changes: Rng trait and top-level fns -- Rename fn `rand::thread_rng()` to `rand::rng()` and remove from the prelude ([#1506]) -- Remove fn `rand::random()` from the prelude ([#1506]) -- Add top-level fns `random_iter`, `random_range`, `random_bool`, `random_ratio`, `fill` ([#1488]) -- Re-introduce fn `Rng::gen_iter` as `random_iter` ([#1305], [#1500]) -- Rename fn `Rng::gen` to `random` to avoid conflict with the new `gen` keyword in Rust 2024 ([#1438]) -- Rename fns `Rng::gen_range` to `random_range`, `gen_bool` to `random_bool`, `gen_ratio` to `random_ratio` ([#1505]) -- Annotate panicking methods with `#[track_caller]` ([#1442], [#1447]) - -### API changes: RNGs -- Fix `::Seed` size to 256 bits ([#1455]) -- Remove first parameter (`rng`) of `ReseedingRng::new` ([#1533]) - -### API changes: Sequences -- Split trait `SliceRandom` into `IndexedRandom`, `IndexedMutRandom`, `SliceRandom` ([#1382]) -- Add `IndexedRandom::choose_multiple_array`, `index::sample_array` ([#1453], [#1469]) - -### API changes: Distributions: renames -- Rename module `rand::distributions` to `rand::distr` ([#1470]) -- Rename distribution `Standard` to `StandardUniform` ([#1526]) -- Move `distr::Slice` -> `distr::slice::Choose`, `distr::EmptySlice` -> `distr::slice::Empty` ([#1548]) -- Rename trait `distr::DistString` -> `distr::SampleString` ([#1548]) -- Rename `distr::DistIter` -> `distr::Iter`, `distr::DistMap` -> `distr::Map` ([#1548]) - -### API changes: Distributions -- Relax `Sized` bound on `Distribution for &D` ([#1278]) -- Remove impl of `Distribution>` for `StandardUniform` ([#1526]) -- Let distribution `StandardUniform` support all `NonZero*` types ([#1332]) -- Fns `{Uniform, UniformSampler}::{new, new_inclusive}` return a `Result` (instead of potentially panicking) ([#1229]) -- Distribution `Uniform` implements `TryFrom` instead of `From` for ranges ([#1229]) -- Add `UniformUsize` ([#1487]) -- Remove support for generating `isize` and `usize` values with `StandardUniform`, `Uniform` (except via `UniformUsize`) and `Fill` and usage as a `WeightedAliasIndex` weight ([#1487]) -- Add impl `DistString` for distributions `Slice` and `Uniform` ([#1315]) -- Add fn `Slice::num_choices` ([#1402]) -- Add fn `p()` for distribution `Bernoulli` to access probability ([#1481]) - -### API changes: Weighted distributions -- Add `pub` module `rand::distr::weighted`, moving `WeightedIndex` there ([#1548]) -- Add trait `weighted::Weight`, allowing `WeightedIndex` to trap overflow ([#1353]) -- Add fns `weight, weights, total_weight` to distribution `WeightedIndex` ([#1420]) -- Rename enum `WeightedError` to `weighted::Error`, revising variants ([#1382]) and mark as `#[non_exhaustive]` ([#1480]) - -### API changes: SIMD -- Switch to `std::simd`, expand SIMD & docs ([#1239]) - -### Reproducibility-breaking changes -- Make `ReseedingRng::reseed` discard remaining data from the last block generated ([#1379]) -- Change fn `SmallRng::seed_from_u64` implementation ([#1203]) -- Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` ([#1462]) -- Fix portability of distribution `Slice` ([#1469]) -- Make `Uniform` for `usize` portable via `UniformUsize` ([#1487]) -- Fix `IndexdRandom::choose_multiple_weighted` for very small seeds and optimize for large input length / low memory ([#1530]) - -### Reproducibility-breaking optimisations -- Optimize fn `sample_floyd`, affecting output of `rand::seq::index::sample` and `rand::seq::SliceRandom::choose_multiple` ([#1277]) -- New, faster algorithms for `IteratorRandom::choose` and `choose_stable` ([#1268]) -- New, faster algorithms for `SliceRandom::shuffle` and `partial_shuffle` ([#1272]) -- Optimize distribution `Uniform`: use Canon's method (single sampling) / Lemire's method (distribution sampling) for faster sampling (breaks value stability; [#1287]) -- Optimize fn `sample_single_inclusive` for floats (+~20% perf) ([#1289]) - -### Other optimisations -- Improve `SmallRng` initialization performance ([#1482]) -- Optimise SIMD widening multiply ([#1247]) - -### Other -- Add `Cargo.lock.msrv` file ([#1275]) -- Reformat with `rustfmt` and enforce ([#1448]) -- Apply Clippy suggestions and enforce ([#1448], [#1474]) -- Move all benchmarks to new `benches` crate ([#1329], [#1439]) and migrate to Criterion ([#1490]) - -### Documentation -- Improve `ThreadRng` related docs ([#1257]) -- Docs: enable experimental `--generate-link-to-definition` feature ([#1327]) -- Better doc of crate features, use `doc_auto_cfg` ([#1411], [#1450]) - -[#1203]: https://github.com/rust-random/rand/pull/1203 -[#1207]: https://github.com/rust-random/rand/pull/1207 -[#1229]: https://github.com/rust-random/rand/pull/1229 -[#1239]: https://github.com/rust-random/rand/pull/1239 -[#1246]: https://github.com/rust-random/rand/pull/1246 -[#1247]: https://github.com/rust-random/rand/pull/1247 -[#1257]: https://github.com/rust-random/rand/pull/1257 -[#1267]: https://github.com/rust-random/rand/pull/1267 -[#1268]: https://github.com/rust-random/rand/pull/1268 -[#1269]: https://github.com/rust-random/rand/pull/1269 -[#1272]: https://github.com/rust-random/rand/pull/1272 -[#1273]: https://github.com/rust-random/rand/pull/1273 -[#1275]: https://github.com/rust-random/rand/pull/1275 -[#1277]: https://github.com/rust-random/rand/pull/1277 -[#1278]: https://github.com/rust-random/rand/pull/1278 -[#1287]: https://github.com/rust-random/rand/pull/1287 -[#1289]: https://github.com/rust-random/rand/pull/1289 -[#1305]: https://github.com/rust-random/rand/pull/1305 -[#1315]: https://github.com/rust-random/rand/pull/1315 -[#1327]: https://github.com/rust-random/rand/pull/1327 -[#1329]: https://github.com/rust-random/rand/pull/1329 -[#1332]: https://github.com/rust-random/rand/pull/1332 -[#1341]: https://github.com/rust-random/rand/pull/1341 -[#1349]: https://github.com/rust-random/rand/pull/1349 -[#1353]: https://github.com/rust-random/rand/pull/1353 -[#1354]: https://github.com/rust-random/rand/pull/1354 -[#1379]: https://github.com/rust-random/rand/pull/1379 -[#1382]: https://github.com/rust-random/rand/pull/1382 -[#1393]: https://github.com/rust-random/rand/pull/1393 -[#1402]: https://github.com/rust-random/rand/pull/1402 -[#1411]: https://github.com/rust-random/rand/pull/1411 -[#1416]: https://github.com/rust-random/rand/pull/1416 -[#1420]: https://github.com/rust-random/rand/pull/1420 -[#1424]: https://github.com/rust-random/rand/pull/1424 -[#1438]: https://github.com/rust-random/rand/pull/1438 -[#1439]: https://github.com/rust-random/rand/pull/1439 -[#1442]: https://github.com/rust-random/rand/pull/1442 -[#1446]: https://github.com/rust-random/rand/pull/1446 -[#1447]: https://github.com/rust-random/rand/pull/1447 -[#1448]: https://github.com/rust-random/rand/pull/1448 -[#1450]: https://github.com/rust-random/rand/pull/1450 -[#1453]: https://github.com/rust-random/rand/pull/1453 -[#1455]: https://github.com/rust-random/rand/pull/1455 -[#1462]: https://github.com/rust-random/rand/pull/1462 -[#1469]: https://github.com/rust-random/rand/pull/1469 -[#1470]: https://github.com/rust-random/rand/pull/1470 -[#1473]: https://github.com/rust-random/rand/pull/1473 -[#1474]: https://github.com/rust-random/rand/pull/1474 -[#1477]: https://github.com/rust-random/rand/pull/1477 -[#1480]: https://github.com/rust-random/rand/pull/1480 -[#1481]: https://github.com/rust-random/rand/pull/1481 -[#1482]: https://github.com/rust-random/rand/pull/1482 -[#1487]: https://github.com/rust-random/rand/pull/1487 -[#1488]: https://github.com/rust-random/rand/pull/1488 -[#1490]: https://github.com/rust-random/rand/pull/1490 -[#1491]: https://github.com/rust-random/rand/pull/1491 -[#1499]: https://github.com/rust-random/rand/pull/1499 -[#1500]: https://github.com/rust-random/rand/pull/1500 -[#1502]: https://github.com/rust-random/rand/pull/1502 -[#1505]: https://github.com/rust-random/rand/pull/1505 -[#1506]: https://github.com/rust-random/rand/pull/1506 -[#1514]: https://github.com/rust-random/rand/pull/1514 -[#1526]: https://github.com/rust-random/rand/pull/1526 -[#1530]: https://github.com/rust-random/rand/pull/1530 -[#1533]: https://github.com/rust-random/rand/pull/1533 -[#1536]: https://github.com/rust-random/rand/pull/1536 -[#1537]: https://github.com/rust-random/rand/pull/1537 -[#1547]: https://github.com/rust-random/rand/pull/1547 -[#1548]: https://github.com/rust-random/rand/pull/1548 -[#1558]: https://github.com/rust-random/rand/pull/1558 - -## [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]). - We now use ChaCha12 on all platforms. - -### Documentation -- Added docs about rand's use of const generics ([#1150]) -- Better random chars example ([#1157]) - -[#1142]: https://github.com/rust-random/rand/pull/1142 -[#1144]: https://github.com/rust-random/rand/pull/1144 -[#1150]: https://github.com/rust-random/rand/pull/1150 -[#1157]: https://github.com/rust-random/rand/pull/1157 -[#1173]: https://github.com/rust-random/rand/pull/1173 -[#1178]: https://github.com/rust-random/rand/pull/1178 -[#1215]: https://github.com/rust-random/rand/pull/1215 -[#1216]: https://github.com/rust-random/rand/pull/1216 - -## [0.8.4] - 2021-06-15 - -### Additions -- Use const-generics to support arrays of all sizes ([#1104]) -- Implement `Clone` and `Copy` for `Alphanumeric` ([#1126]) -- Add `Distribution::map` to derive a distribution using a closure ([#1129]) -- Add `Slice` distribution ([#1107]) -- Add `DistString` trait with impls for `Standard` and `Alphanumeric` ([#1133]) - -### Other -- Reorder asserts in `Uniform` float distributions for easier debugging of non-finite arguments ([#1094], [#1108]) -- Add range overflow check in `Uniform` float distributions ([#1108]) -- Deprecate `rngs::adapter::ReadRng` ([#1130]) - -## [0.8.3] - 2021-01-25 - -### Fixes -- Fix `no-std` + `alloc` build by gating `choose_multiple_weighted` on `std` ([#1088]) - -## [0.8.2] - 2021-01-12 - -### Fixes -- Fix panic in `UniformInt::sample_single_inclusive` and `Rng::gen_range` when providing a full integer range (eg `0..=MAX`) ([#1087]) - -## [0.8.1] - 2020-12-31 - -### Other -- Enable all stable features in the playground ([#1081]) -## [0.8.0] - 2020-12-18 - -### Platform support -- The minimum supported Rust version is now 1.36 ([#1011]) -- `getrandom` updated to v0.2 ([#1041]) -- Remove `wasm-bindgen` and `stdweb` feature flags. For details of WASM support, see the [getrandom documentation](https://docs.rs/getrandom/latest). ([#948]) -- `ReadRng::next_u32` and `next_u64` now use little-Endian conversion instead of native-Endian, affecting results on Big-Endian platforms ([#1061]) -- The `nightly` feature no longer implies the `simd_support` feature ([#1048]) -- Fix `simd_support` feature to work on current nightlies ([#1056]) - -### Rngs -- `ThreadRng` is no longer `Copy` to enable safe usage within thread-local destructors ([#1035]) -- `gen_range(a, b)` was replaced with `gen_range(a..b)`. `gen_range(a..=b)` is also supported. Note that `a` and `b` can no longer be references or SIMD types. ([#744], [#1003]) -- Replace `AsByteSliceMut` with `Fill` and add support for `[bool], [char], [f32], [f64]` ([#940]) -- Restrict `rand::rngs::adapter` to `std` ([#1027]; see also [#928]) -- `StdRng`: add new `std_rng` feature flag (enabled by default, but might need to be used if disabling default crate features) ([#948]) -- `StdRng`: Switch from ChaCha20 to ChaCha12 for better performance ([#1028]) -- `SmallRng`: Replace PCG algorithm with xoshiro{128,256}++ ([#1038]) - -### Sequences -- Add `IteratorRandom::choose_stable` as an alternative to `choose` which does not depend on size hints ([#1057]) -- Improve accuracy and performance of `IteratorRandom::choose` ([#1059]) -- Implement `IntoIterator` for `IndexVec`, replacing the `into_iter` method ([#1007]) -- Add value stability tests for `seq` module ([#933]) - -### Misc -- Support `PartialEq` and `Eq` for `StdRng`, `SmallRng` and `StepRng` ([#979]) -- Added a `serde1` feature and added Serialize/Deserialize to `UniformInt` and `WeightedIndex` ([#974]) -- Drop some unsafe code ([#962], [#963], [#1011]) -- Reduce packaged crate size ([#983]) -- Migrate to GitHub Actions from Travis+AppVeyor ([#1073]) - -### Distributions -- `Alphanumeric` samples bytes instead of chars ([#935]) -- `Uniform` now supports `char`, enabling `rng.gen_range('A'..='Z')` ([#1068]) -- Add `UniformSampler::sample_single_inclusive` ([#1003]) - -#### Weighted sampling -- Implement weighted sampling without replacement ([#976], [#1013]) -- `rand::distributions::alias_method::WeightedIndex` was moved to `rand_distr::WeightedAliasIndex`. The simpler alternative `rand::distribution::WeightedIndex` remains. ([#945]) -- Improve treatment of rounding errors in `WeightedIndex::update_weights` ([#956]) -- `WeightedIndex`: return error on NaN instead of panic ([#1005]) - -### Documentation -- Document types supported by `random` ([#994]) -- Document notes on password generation ([#995]) -- Note that `SmallRng` may not be the best choice for performance and in some other cases ([#1038]) -- Use `doc(cfg)` to annotate feature-gated items ([#1019]) -- Adjust README ([#1065]) - -[#744]: https://github.com/rust-random/rand/pull/744 -[#933]: https://github.com/rust-random/rand/pull/933 -[#935]: https://github.com/rust-random/rand/pull/935 -[#940]: https://github.com/rust-random/rand/pull/940 -[#945]: https://github.com/rust-random/rand/pull/945 -[#948]: https://github.com/rust-random/rand/pull/948 -[#956]: https://github.com/rust-random/rand/pull/956 -[#962]: https://github.com/rust-random/rand/pull/962 -[#963]: https://github.com/rust-random/rand/pull/963 -[#974]: https://github.com/rust-random/rand/pull/974 -[#976]: https://github.com/rust-random/rand/pull/976 -[#979]: https://github.com/rust-random/rand/pull/979 -[#983]: https://github.com/rust-random/rand/pull/983 -[#994]: https://github.com/rust-random/rand/pull/994 -[#995]: https://github.com/rust-random/rand/pull/995 -[#1003]: https://github.com/rust-random/rand/pull/1003 -[#1005]: https://github.com/rust-random/rand/pull/1005 -[#1007]: https://github.com/rust-random/rand/pull/1007 -[#1011]: https://github.com/rust-random/rand/pull/1011 -[#1013]: https://github.com/rust-random/rand/pull/1013 -[#1019]: https://github.com/rust-random/rand/pull/1019 -[#1027]: https://github.com/rust-random/rand/pull/1027 -[#1028]: https://github.com/rust-random/rand/pull/1028 -[#1035]: https://github.com/rust-random/rand/pull/1035 -[#1038]: https://github.com/rust-random/rand/pull/1038 -[#1041]: https://github.com/rust-random/rand/pull/1041 -[#1048]: https://github.com/rust-random/rand/pull/1048 -[#1056]: https://github.com/rust-random/rand/pull/1056 -[#1057]: https://github.com/rust-random/rand/pull/1057 -[#1059]: https://github.com/rust-random/rand/pull/1059 -[#1061]: https://github.com/rust-random/rand/pull/1061 -[#1065]: https://github.com/rust-random/rand/pull/1065 -[#1068]: https://github.com/rust-random/rand/pull/1068 -[#1073]: https://github.com/rust-random/rand/pull/1073 - -## [0.7.3] - 2020-01-10 - -### Fixes -- The `Bernoulli` distribution constructors now reports an error on NaN and on `denominator == 0`. ([#925]) -- Use `std::sync::Once` to register fork handler, avoiding possible atomicity violation ([#928]) -- Fix documentation on the precision of generated floating-point values - -### Changes -- Unix: make libc dependency optional; only use fork protection with std feature ([#928]) - -### Additions -- Implement `std::error::Error` for `BernoulliError` ([#919]) - -## [0.7.2] - 2019-09-16 - -### Fixes -- Fix dependency on `rand_core` 0.5.1 ([#890]) - -### Additions -- Unit tests for value stability of distributions added ([#888]) - -## [0.7.1] - 2019-09-13 - -### Yanked -This release was yanked since it depends on `rand_core::OsRng` added in 0.5.1 but specifies a dependency on version 0.5.0 ([#890]), causing broken builds when updating from `rand 0.7.0` without also updating `rand_core`. - -### Fixes -- Fix `no_std` behaviour, appropriately enable c2-chacha's `std` feature ([#844]) -- `alloc` feature in `no_std` is available since Rust 1.36 ([#856]) -- Fix or squelch issues from Clippy lints ([#840]) - -### Additions -- Add a `no_std` target to CI to continuously evaluate `no_std` status ([#844]) -- `WeightedIndex`: allow adjusting a sub-set of weights ([#866]) - -## [0.7.0] - 2019-06-28 - -### Fixes -- Fix incorrect pointer usages revealed by Miri testing ([#780], [#781]) -- Fix (tiny!) bias in `Uniform` for 8- and 16-bit ints ([#809]) - -### Crate -- Bumped MSRV (min supported Rust version) to 1.32.0 -- Updated to Rust Edition 2018 ([#823], [#824]) -- Removed dependence on `rand_xorshift`, `rand_isaac`, `rand_jitter` crates ([#759], [#765]) -- Remove dependency on `winapi` ([#724]) -- Removed all `build.rs` files ([#824]) -- Removed code already deprecated in version 0.6 ([#757]) -- Removed the serde1 feature (It's still available for backwards compatibility, but it does not do anything. [#830]) -- Many documentation changes - -### rand_core -- Updated to `rand_core` 0.5.0 -- `Error` type redesigned with new API ([#800]) -- Move `from_entropy` method to `SeedableRng` and remove `FromEntropy` ([#800]) -- `SeedableRng::from_rng` is now expected to be value-stable ([#815]) - -### Standard RNGs -- OS interface moved from `rand_os` to new `getrandom` crate ([#765], [getrandom](https://github.com/rust-random/getrandom)) -- Use ChaCha for `StdRng` and `ThreadRng` ([#792]) -- Feature-gate `SmallRng` ([#792]) -- `ThreadRng` now supports `Copy` ([#758]) -- Deprecated `EntropyRng` ([#765]) -- Enable fork protection of `ReseedingRng` without `std` ([#724]) - -### Distributions -- Many distributions have been moved to `rand_distr` ([#761]) -- `Bernoulli::new` constructor now returns a `Result` ([#803]) -- `Distribution::sample_iter` adjusted for more flexibility ([#758]) -- Added `distributions::weighted::alias_method::WeightedIndex` for `O(1)` sampling ([#692]) -- Support sampling `NonZeroU*` types with the `Standard` distribution ([#728]) -- Optimised `Binomial` distribution sampling ([#735], [#740], [#752]) -- Optimised SIMD float sampling ([#739]) - -### Sequences -- Make results portable across 32- and 64-bit by using `u32` samples for `usize` where possible ([#809]) - -[#692]: https://github.com/rust-random/rand/pull/692 -[#724]: https://github.com/rust-random/rand/pull/724 -[#728]: https://github.com/rust-random/rand/pull/728 -[#735]: https://github.com/rust-random/rand/pull/735 -[#739]: https://github.com/rust-random/rand/pull/739 -[#740]: https://github.com/rust-random/rand/pull/740 -[#752]: https://github.com/rust-random/rand/pull/752 -[#757]: https://github.com/rust-random/rand/pull/757 -[#758]: https://github.com/rust-random/rand/pull/758 -[#759]: https://github.com/rust-random/rand/pull/759 -[#761]: https://github.com/rust-random/rand/pull/761 -[#765]: https://github.com/rust-random/rand/pull/765 -[#780]: https://github.com/rust-random/rand/pull/780 -[#781]: https://github.com/rust-random/rand/pull/781 -[#792]: https://github.com/rust-random/rand/pull/792 -[#800]: https://github.com/rust-random/rand/pull/800 -[#803]: https://github.com/rust-random/rand/pull/803 -[#809]: https://github.com/rust-random/rand/pull/809 -[#815]: https://github.com/rust-random/rand/pull/815 -[#823]: https://github.com/rust-random/rand/pull/823 -[#824]: https://github.com/rust-random/rand/pull/824 -[#830]: https://github.com/rust-random/rand/pull/830 -[#840]: https://github.com/rust-random/rand/pull/840 -[#844]: https://github.com/rust-random/rand/pull/844 -[#856]: https://github.com/rust-random/rand/pull/856 -[#866]: https://github.com/rust-random/rand/pull/866 -[#888]: https://github.com/rust-random/rand/pull/888 -[#890]: https://github.com/rust-random/rand/pull/890 -[#919]: https://github.com/rust-random/rand/pull/919 -[#925]: https://github.com/rust-random/rand/pull/925 -[#928]: https://github.com/rust-random/rand/pull/928 - -## [0.6.5] - 2019-01-28 - -### Crates -- Update `rand_core` to 0.4 ([#703]) -- Move `JitterRng` to its own crate ([#685]) -- Add a wasm-bindgen test crate ([#696]) - -### Platforms -- Fuchsia: Replaced fuchsia-zircon with fuchsia-cprng - -### Doc -- Use RFC 1946 for doc links ([#691]) -- Fix some doc links and notes ([#711]) - -## [0.6.4] - 2019-01-08 - -### Fixes -- Move wasm-bindgen shims to correct crate ([#686]) -- Make `wasm32-unknown-unknown` compile but fail at run-time if missing bindings ([#686]) - -## [0.6.3] - 2019-01-04 - -### Fixes -- Make the `std` feature require the optional `rand_os` dependency ([#675]) -- Re-export the optional WASM dependencies of `rand_os` from `rand` to avoid breakage ([#674]) - -## [0.6.2] - 2019-01-04 - -### Additions -- Add `Default` for `ThreadRng` ([#657]) -- Move `rngs::OsRng` to `rand_os` sub-crate; clean up code; use as dependency ([#643]) -- Add `rand_xoshiro` sub-crate, plus benchmarks ([#642], [#668]) - -### Fixes -- Fix bias in `UniformInt::sample_single` ([#662]) -- Use `autocfg` instead of `rustc_version` for rustc version detection ([#664]) -- Disable `i128` and `u128` if the `target_os` is `emscripten` ([#671]: work-around Emscripten limitation) -- CI fixes ([#660], [#671]) - -### Optimisations -- Optimise memory usage of `UnitCircle` and `UnitSphereSurface` distributions (no PR) - -## [0.6.1] - 2018-11-22 -- Support sampling `Duration` also for `no_std` (only since Rust 1.25) ([#649]) -- Disable default features of `libc` ([#647]) - -## [0.6.0] - 2018-11-14 - -### Project organisation -- Rand has moved from [rust-lang-nursery](https://github.com/rust-lang-nursery/rand) to [rust-random](https://github.com/rust-random/rand)! ([#578]) -- Created [The Rust Random Book](https://rust-random.github.io/book/) ([source](https://github.com/rust-random/book)) -- Update copyright and licence notices ([#591], [#611]) -- Migrate policy documentation from the wiki ([#544]) - -### Platforms -- Add fork protection on Unix ([#466]) -- Added support for wasm-bindgen ([#541], [#559], [#562], [#600]) -- Enable `OsRng` for powerpc64, sparc and sparc64 ([#609]) -- Use `syscall` from `libc` on Linux instead of redefining it ([#629]) - -### RNGs -- Switch `SmallRng` to use PCG ([#623]) -- Implement `Pcg32` and `Pcg64Mcg` generators ([#632]) -- Move ISAAC RNGs to a dedicated crate ([#551]) -- Move Xorshift RNG to its own crate ([#557]) -- Move ChaCha and HC128 RNGs to dedicated crates ([#607], [#636]) -- Remove usage of `Rc` from `ThreadRng` ([#615]) - -### Sampling and distributions -- Implement `Rng.gen_ratio()` and `Bernoulli::new_ratio()` ([#491]) -- Make `Uniform` strictly respect `f32` / `f64` high/low bounds ([#477]) -- Allow `gen_range` and `Uniform` to work on non-`Copy` types ([#506]) -- `Uniform` supports inclusive ranges: `Uniform::from(a..=b)`. This is automatically enabled for Rust >= 1.27. ([#566]) -- Implement `TrustedLen` and `FusedIterator` for `DistIter` ([#620]) - -#### New distributions -- Add the `Dirichlet` distribution ([#485]) -- Added sampling from the unit sphere and circle ([#567]) -- Implement the triangular distribution ([#575]) -- Implement the Weibull distribution ([#576]) -- Implement the Beta distribution ([#574]) - -#### Optimisations -- Optimise `Bernoulli::new` ([#500]) -- Optimise `char` sampling ([#519]) -- Optimise sampling of `std::time::Duration` ([#583]) - -### Sequences -- Redesign the `seq` module ([#483], [#515]) -- Add `WeightedIndex` and `choose_weighted` ([#518], [#547]) -- Optimised and changed return type of the `sample_indices` function ([#479]) -- Use `Iterator::size_hint()` to speed up `IteratorRandom::choose` ([#593]) - -### SIMD -- Support for generating SIMD types ([#523], [#542], [#561], [#630]) - -### Other -- Revise CI scripts ([#632], [#635]) -- Remove functionality already deprecated in 0.5 ([#499]) -- Support for `i128` and `u128` is automatically enabled for Rust >= 1.26. This renders the `i128_support` feature obsolete. It still exists for backwards compatibility but does not have any effect. This breaks programs using Rand with `i128_support` on nightlies older than Rust 1.26. ([#571]) - -[#466]: https://github.com/rust-random/rand/pull/466 -[#477]: https://github.com/rust-random/rand/pull/477 -[#479]: https://github.com/rust-random/rand/pull/479 -[#483]: https://github.com/rust-random/rand/pull/483 -[#485]: https://github.com/rust-random/rand/pull/485 -[#491]: https://github.com/rust-random/rand/pull/491 -[#499]: https://github.com/rust-random/rand/pull/499 -[#500]: https://github.com/rust-random/rand/pull/500 -[#506]: https://github.com/rust-random/rand/pull/506 -[#515]: https://github.com/rust-random/rand/pull/515 -[#518]: https://github.com/rust-random/rand/pull/518 -[#519]: https://github.com/rust-random/rand/pull/519 -[#523]: https://github.com/rust-random/rand/pull/523 -[#541]: https://github.com/rust-random/rand/pull/541 -[#542]: https://github.com/rust-random/rand/pull/542 -[#544]: https://github.com/rust-random/rand/pull/544 -[#547]: https://github.com/rust-random/rand/pull/547 -[#551]: https://github.com/rust-random/rand/pull/551 -[#557]: https://github.com/rust-random/rand/pull/557 -[#559]: https://github.com/rust-random/rand/pull/559 -[#561]: https://github.com/rust-random/rand/pull/561 -[#562]: https://github.com/rust-random/rand/pull/562 -[#566]: https://github.com/rust-random/rand/pull/566 -[#567]: https://github.com/rust-random/rand/pull/567 -[#571]: https://github.com/rust-random/rand/pull/571 -[#574]: https://github.com/rust-random/rand/pull/574 -[#575]: https://github.com/rust-random/rand/pull/575 -[#576]: https://github.com/rust-random/rand/pull/576 -[#578]: https://github.com/rust-random/rand/pull/578 -[#583]: https://github.com/rust-random/rand/pull/583 -[#591]: https://github.com/rust-random/rand/pull/591 -[#593]: https://github.com/rust-random/rand/pull/593 -[#600]: https://github.com/rust-random/rand/pull/600 -[#607]: https://github.com/rust-random/rand/pull/607 -[#609]: https://github.com/rust-random/rand/pull/609 -[#611]: https://github.com/rust-random/rand/pull/611 -[#615]: https://github.com/rust-random/rand/pull/615 -[#620]: https://github.com/rust-random/rand/pull/620 -[#623]: https://github.com/rust-random/rand/pull/623 -[#629]: https://github.com/rust-random/rand/pull/629 -[#630]: https://github.com/rust-random/rand/pull/630 -[#632]: https://github.com/rust-random/rand/pull/632 -[#635]: https://github.com/rust-random/rand/pull/635 -[#636]: https://github.com/rust-random/rand/pull/636 -[#642]: https://github.com/rust-random/rand/pull/642 -[#643]: https://github.com/rust-random/rand/pull/643 -[#647]: https://github.com/rust-random/rand/pull/647 -[#649]: https://github.com/rust-random/rand/pull/649 -[#657]: https://github.com/rust-random/rand/pull/657 -[#660]: https://github.com/rust-random/rand/pull/660 -[#662]: https://github.com/rust-random/rand/pull/662 -[#664]: https://github.com/rust-random/rand/pull/664 -[#668]: https://github.com/rust-random/rand/pull/668 -[#671]: https://github.com/rust-random/rand/pull/671 -[#674]: https://github.com/rust-random/rand/pull/674 -[#675]: https://github.com/rust-random/rand/pull/675 -[#685]: https://github.com/rust-random/rand/pull/685 -[#686]: https://github.com/rust-random/rand/pull/686 -[#691]: https://github.com/rust-random/rand/pull/691 -[#696]: https://github.com/rust-random/rand/pull/696 -[#703]: https://github.com/rust-random/rand/pull/703 -[#711]: https://github.com/rust-random/rand/pull/711 - -## [0.5.5] - 2018-08-07 - -### Documentation -- Fix links in documentation ([#582]) - -## [0.5.4] - 2018-07-11 - -### Platform support -- Make `OsRng` work via WASM/stdweb for WebWorkers - -## [0.5.3] - 2018-06-26 - -### Platform support -- OpenBSD, Bitrig: fix compilation (broken in 0.5.1) ([#530]) - -## [0.5.2] - 2018-06-18 - -### Platform support -- Hide `OsRng` and `JitterRng` on unsupported platforms ([#512]; fixes [#503]) - -## [0.5.1] - 2018-06-08 - -### New distributions -- Added Cauchy distribution. ([#474], [#486]) -- Added Pareto distribution. ([#495]) - -### Platform support and `OsRng` -- Remove blanket Unix implementation. ([#484]) -- Remove Wasm unimplemented stub. ([#484]) -- Dragonfly BSD: read from `/dev/random`. ([#484]) -- Bitrig: use `getentropy` like OpenBSD. ([#484]) -- Solaris: (untested) use `getrandom` if available, otherwise `/dev/random`. ([#484]) -- Emscripten, `stdweb`: split the read up in chunks. ([#484]) -- Emscripten, Haiku: don't do an extra blocking read from `/dev/random`. ([#484]) -- Linux, NetBSD, Solaris: read in blocking mode on first use in `fill_bytes`. ([#484]) -- Fuchsia, CloudABI: fix compilation (broken in Rand 0.5). ([#484]) - -## [0.5.0] - 2018-05-21 - -### Crate features and organisation -- Minimum Rust version update: 1.22.0. ([#239]) -- Create a separate `rand_core` crate. ([#288]) -- Deprecate `rand_derive`. ([#256]) -- Add `prelude` (and module reorganisation). ([#435]) -- Add `log` feature. Logging is now available in `JitterRng`, `OsRng`, `EntropyRng` and `ReseedingRng`. ([#246]) -- Add `serde1` feature for some PRNGs. ([#189]) -- `stdweb` feature for `OsRng` support on WASM via stdweb. ([#272], [#336]) - -### `Rng` trait -- Split `Rng` into `RngCore` and `Rng` extension trait. `next_u32`, `next_u64` and `fill_bytes` are now part of `RngCore`. ([#265]) -- Add `Rng::sample`. ([#256]) -- Deprecate `Rng::gen_weighted_bool`. ([#308]) -- Add `Rng::gen_bool`. ([#308]) -- Remove `Rng::next_f32` and `Rng::next_f64`. ([#273]) -- Add optimized `Rng::fill` and `Rng::try_fill` methods. ([#247]) -- Deprecate `Rng::gen_iter`. ([#286]) -- Deprecate `Rng::gen_ascii_chars`. ([#279]) - -### `rand_core` crate -- `rand` now depends on new `rand_core` crate ([#288]) -- `RngCore` and `SeedableRng` are now part of `rand_core`. ([#288]) -- Add modules to help implementing RNGs `impl` and `le`. ([#209], [#228]) -- Add `Error` and `ErrorKind`. ([#225]) -- Add `CryptoRng` marker trait. ([#273]) -- Add `BlockRngCore` trait. ([#281]) -- Add `BlockRng` and `BlockRng64` wrappers to help implementations. ([#281], [#325]) -- Revise the `SeedableRng` trait. ([#233]) -- Remove default implementations for `RngCore::next_u64` and `RngCore::fill_bytes`. ([#288]) -- Add `RngCore::try_fill_bytes`. ([#225]) - -### Other traits and types -- Add `FromEntropy` trait. ([#233], [#375]) -- Add `SmallRng` wrapper. ([#296]) -- Rewrite `ReseedingRng` to only work with `BlockRngCore` (substantial performance improvement). ([#281]) -- Deprecate `weak_rng`. Use `SmallRng` instead. ([#296]) -- Deprecate `AsciiGenerator`. ([#279]) - -### Random number generators -- Switch `StdRng` and `thread_rng` to HC-128. ([#277]) -- `StdRng` must now be created with `from_entropy` instead of `new` -- Change `thread_rng` reseeding threshold to 32 MiB. ([#277]) -- PRNGs no longer implement `Copy`. ([#209]) -- `Debug` implementations no longer show internals. ([#209]) -- Implement `Clone` for `ReseedingRng`, `JitterRng`, OsRng`. ([#383], [#384]) -- Implement serialization for `XorShiftRng`, `IsaacRng` and `Isaac64Rng` under the `serde1` feature. ([#189]) -- Implement `BlockRngCore` for `ChaChaCore` and `Hc128Core`. ([#281]) -- All PRNGs are now portable across big- and little-endian architectures. ([#209]) -- `Isaac64Rng::next_u32` no longer throws away half the results. ([#209]) -- Add `IsaacRng::new_from_u64` and `Isaac64Rng::new_from_u64`. ([#209]) -- Add the HC-128 CSPRNG `Hc128Rng`. ([#210]) -- Change ChaCha20 to have 64-bit counter and 64-bit stream. ([#349]) -- Changes to `JitterRng` to get its size down from 2112 to 24 bytes. ([#251]) -- Various performance improvements to all PRNGs. - -### Platform support and `OsRng` -- Add support for CloudABI. ([#224]) -- Remove support for NaCl. ([#225]) -- WASM support for `OsRng` via stdweb, behind the `stdweb` feature. ([#272], [#336]) -- Use `getrandom` on more platforms for Linux, and on Android. ([#338]) -- Use the `SecRandomCopyBytes` interface on macOS. ([#322]) -- On systems that do not have a syscall interface, only keep a single file descriptor open for `OsRng`. ([#239]) -- On Unix, first try a single read from `/dev/random`, then `/dev/urandom`. ([#338]) -- Better error handling and reporting in `OsRng` (using new error type). ([#225]) -- `OsRng` now uses non-blocking when available. ([#225]) -- Add `EntropyRng`, which provides `OsRng`, but has `JitterRng` as a fallback. ([#235]) - -### Distributions -- New `Distribution` trait. ([#256]) -- Add `Distribution::sample_iter` and `Rng::::sample_iter`. ([#361]) -- Deprecate `Rand`, `Sample` and `IndependentSample` traits. ([#256]) -- Add a `Standard` distribution (replaces most `Rand` implementations). ([#256]) -- Add `Binomial` and `Poisson` distributions. ([#96]) -- Add `Bernoulli` dsitribution. ([#411]) -- Add `Alphanumeric` distribution. ([#279]) -- Remove `Closed01` distribution, add `OpenClosed01`. ([#274], [#420]) -- Rework `Range` type, making it possible to implement it for user types. ([#274]) -- Rename `Range` to `Uniform`. ([#395]) -- Add `Uniform::new_inclusive` for inclusive ranges. ([#274]) -- Use widening multiply method for much faster integer range reduction. ([#274]) -- `Standard` distribution for `char` uses `Uniform` internally. ([#274]) -- `Standard` distribution for `bool` uses sign test. ([#274]) -- Implement `Standard` distribution for `Wrapping`. ([#436]) -- Implement `Uniform` distribution for `Duration`. ([#427]) - -[#96]: https://github.com/rust-random/rand/pull/96 -[#189]: https://github.com/rust-random/rand/pull/189 -[#209]: https://github.com/rust-random/rand/pull/209 -[#210]: https://github.com/rust-random/rand/pull/210 -[#224]: https://github.com/rust-random/rand/pull/224 -[#225]: https://github.com/rust-random/rand/pull/225 -[#228]: https://github.com/rust-random/rand/pull/228 -[#233]: https://github.com/rust-random/rand/pull/233 -[#235]: https://github.com/rust-random/rand/pull/235 -[#239]: https://github.com/rust-random/rand/pull/239 -[#246]: https://github.com/rust-random/rand/pull/246 -[#247]: https://github.com/rust-random/rand/pull/247 -[#251]: https://github.com/rust-random/rand/pull/251 -[#256]: https://github.com/rust-random/rand/pull/256 -[#265]: https://github.com/rust-random/rand/pull/265 -[#272]: https://github.com/rust-random/rand/pull/272 -[#273]: https://github.com/rust-random/rand/pull/273 -[#274]: https://github.com/rust-random/rand/pull/274 -[#277]: https://github.com/rust-random/rand/pull/277 -[#279]: https://github.com/rust-random/rand/pull/279 -[#281]: https://github.com/rust-random/rand/pull/281 -[#286]: https://github.com/rust-random/rand/pull/286 -[#288]: https://github.com/rust-random/rand/pull/288 -[#296]: https://github.com/rust-random/rand/pull/296 -[#308]: https://github.com/rust-random/rand/pull/308 -[#322]: https://github.com/rust-random/rand/pull/322 -[#325]: https://github.com/rust-random/rand/pull/325 -[#336]: https://github.com/rust-random/rand/pull/336 -[#338]: https://github.com/rust-random/rand/pull/338 -[#349]: https://github.com/rust-random/rand/pull/349 -[#361]: https://github.com/rust-random/rand/pull/361 -[#375]: https://github.com/rust-random/rand/pull/375 -[#383]: https://github.com/rust-random/rand/pull/383 -[#384]: https://github.com/rust-random/rand/pull/384 -[#395]: https://github.com/rust-random/rand/pull/395 -[#411]: https://github.com/rust-random/rand/pull/411 -[#420]: https://github.com/rust-random/rand/pull/420 -[#427]: https://github.com/rust-random/rand/pull/427 -[#435]: https://github.com/rust-random/rand/pull/435 -[#436]: https://github.com/rust-random/rand/pull/436 -[#474]: https://github.com/rust-random/rand/pull/474 -[#484]: https://github.com/rust-random/rand/pull/484 -[#486]: https://github.com/rust-random/rand/pull/486 -[#495]: https://github.com/rust-random/rand/pull/495 -[#503]: https://github.com/rust-random/rand/pull/503 -[#512]: https://github.com/rust-random/rand/pull/512 -[#530]: https://github.com/rust-random/rand/pull/530 -[#582]: https://github.com/rust-random/rand/pull/582 -## [0.4.6] - 2019-01-26 - -### Platforms -- Fuchsia: Replaced fuchsia-zircon with fuchsia-cprng - -## [0.4.5] - 2019-01-09 - -### Fixed -- Remove dependency on default features of `rand_core` ([#689]) - -## [0.4.4] - 2019-01-08 - -Version yanked due to semver-breaking change ([#688]). - -### Added -- SGX support - -## [0.4.3] - 2018-08-16 - -### Fixed -- Use correct syscall number for PowerPC ([#589]) - -## [0.4.2] - 2018-01-06 - -### Changed -- Use `winapi` on Windows -- Update for Fuchsia OS -- Remove dev-dependency on `log` - -## [0.4.1] - 2017-12-17 - -### Added -- `no_std` support - -## [0.4.0-pre.0] - 2017-12-11 - -### Added -- `JitterRng` added as a high-quality alternative entropy source using the system timer -- new `seq` module with `sample_iter`, `sample_slice`, etc. -- WASM support via dummy implementations (fail at run-time) -- Additional benchmarks, covering generators and new seq code - -### Changed -- `thread_rng` uses `JitterRng` if seeding from system time fails (slower but more secure than previous method) - -### Deprecated -- `sample` function deprecated (replaced by `sample_iter`) - -[#589]: https://github.com/rust-random/rand/pull/589 -[#688]: https://github.com/rust-random/rand/pull/688 -[#689]: https://github.com/rust-random/rand/pull/689 - -## [0.3.22] - 2018-02-05 -Code replaced with a compatibility layer over rand 0.4. - -## [0.3.20] - 2018-01-06 -### Changed -- Remove dev-dependency on `log` -- Update `fuchsia-zircon` dependency to 0.3.2 - - -## [0.3.19] - 2017-12-27 -### Changed -- Require `log <= 0.3.8` for dev builds -- Update `fuchsia-zircon` dependency to 0.3 -- Fix broken links in docs (to unblock compiler docs testing CI) - - -## [0.3.18] - 2017-11-06 -### Changed -- `thread_rng` is seeded from the system time if `OsRng` fails -- `weak_rng` now uses `thread_rng` internally - - -## [0.3.17] - 2017-10-07 -### Changed - - Fuchsia: Magenta was renamed Zircon - -## [0.3.16] - 2017-07-27 -### Added -- Implement Debug for mote non-public types -- implement `Rand` for (i|u)i128 -- Support for Fuchsia - -### Changed -- Add inline attribute to SampleRange::construct_range. - This improves the benchmark for sample in 11% and for shuffle in 16%. -- Use `RtlGenRandom` instead of `CryptGenRandom` - - -## [0.3.15] - 2016-11-26 -### Added -- Add `Rng` trait method `choose_mut` -- Redox support - -### Changed -- Use `arc4rand` for `OsRng` on FreeBSD. -- Use `arc4random(3)` for `OsRng` on OpenBSD. - -### Fixed -- Fix filling buffers 4 GiB or larger with `OsRng::fill_bytes` on Windows - - -## [0.3.14] - 2016-02-13 -### Fixed -- Inline definitions from winapi/advapi32, which decreases build times - - -## [0.3.13] - 2016-01-09 -### Fixed -- Compatible with Rust 1.7.0-nightly (needed some extra type annotations) - - -## [0.3.12] - 2015-11-09 -### Changed -- Replaced the methods in `next_f32` and `next_f64` with the technique described - Saito & Matsumoto at MCQMC'08. The new method should exhibit a slightly more - uniform distribution. -- Depend on libc 0.2 - -### Fixed -- Fix iterator protocol issue in `rand::sample` - - -## [0.3.11] - 2015-08-31 -### Added -- Implement `Rand` for arrays with n <= 32 - - -## [0.3.10] - 2015-08-17 -### Added -- Support for NaCl platforms - -### Changed -- Allow `Rng` to be `?Sized`, impl for `&mut R` and `Box` where `R: ?Sized + Rng` - - -## [0.3.9] - 2015-06-18 -### Changed -- Use `winapi` for Windows API things - -### Fixed -- Fixed test on stable/nightly -- Fix `getrandom` syscall number for aarch64-unknown-linux-gnu - - -## [0.3.8] - 2015-04-23 -### Changed -- `log` is a dev dependency - -### Fixed -- Fix race condition of atomics in `is_getrandom_available` - - -## [0.3.7] - 2015-04-03 -### Fixed -- Derive Copy/Clone changes - - -## [0.3.6] - 2015-04-02 -### Changed -- Move to stable Rust! - - -## [0.3.5] - 2015-04-01 -### Fixed -- Compatible with Rust master - - -## [0.3.4] - 2015-03-31 -### Added -- Implement Clone for `Weighted` - -### Fixed -- Compatible with Rust master - - -## [0.3.3] - 2015-03-26 -### Fixed -- Fix compile on Windows - - -## [0.3.2] - 2015-03-26 - - -## [0.3.1] - 2015-03-26 -### Fixed -- Fix compile on Windows - - -## [0.3.0] - 2015-03-25 -### Changed -- Update to use log version 0.3.x - - -## [0.2.1] - 2015-03-22 -### Fixed -- Compatible with Rust master -- Fixed iOS compilation - - -## [0.2.0] - 2015-03-06 -### Fixed -- Compatible with Rust master (move from `old_io` to `std::io`) - - -## [0.1.4] - 2015-03-04 -### Fixed -- Compatible with Rust master (use wrapping ops) - - -## [0.1.3] - 2015-02-20 -### Fixed -- Compatible with Rust master - -### Removed -- Removed Copy implementations from RNGs - - -## [0.1.2] - 2015-02-03 -### Added -- Imported functionality from `std::rand`, including: - - `StdRng`, `SeedableRng`, `TreadRng`, `weak_rng()` - - `ReaderRng`: A wrapper around any Reader to treat it as an RNG. -- Imported documentation from `std::rand` -- Imported tests from `std::rand` - - -## 0.1.1 - 2015-02-03 -### Added -- Migrate to a cargo-compatible directory structure. - -### Fixed -- Do not use entropy during `gen_weighted_bool(1)` - - -## Rust 0.12.0 - 2014-10-09 -### Added -- Impl Rand for tuples of arity 11 and 12 -- Include ChaCha pseudorandom generator -- Add `next_f64` and `next_f32` to Rng -- Implement Clone for PRNGs - -### Changed -- Rename `TaskRng` to `ThreadRng` and `task_rng` to `thread_rng` (since a - runtime is removed from Rust). - -### Fixed -- Improved performance of ISAAC and ISAAC64 by 30% and 12 % respectively, by - informing the optimiser that indexing is never out-of-bounds. - -### Removed -- Removed the Deprecated `choose_option` - - -## Rust 0.11.0 - 2014-07-02 -### Added -- document when to use `OSRng` in cryptographic context, and explain why we use `/dev/urandom` instead of `/dev/random` -- `Rng::gen_iter()` which will return an infinite stream of random values -- `Rng::gen_ascii_chars()` which will return an infinite stream of random ascii characters - -### Changed -- Now only depends on libcore! -- Remove `Rng.choose()`, rename `Rng.choose_option()` to `.choose()` -- Rename OSRng to OsRng -- The WeightedChoice structure is no longer built with a `Vec>`, - but rather a `&mut [Weighted]`. This means that the WeightedChoice - structure now has a lifetime associated with it. -- The `sample` method on `Rng` has been moved to a top-level function in the - `rand` module due to its dependence on `Vec`. - -### Removed -- `Rng::gen_vec()` was removed. Previous behavior can be regained with - `rng.gen_iter().take(n).collect()` -- `Rng::gen_ascii_str()` was removed. Previous behavior can be regained with - `rng.gen_ascii_chars().take(n).collect()` -- {IsaacRng, Isaac64Rng, XorShiftRng}::new() have all been removed. These all - relied on being able to use an OSRng for seeding, but this is no longer - available in librand (where these types are defined). To retain the same - functionality, these types now implement the `Rand` trait so they can be - generated with a random seed from another random number generator. This allows - the stdlib to use an OSRng to create seeded instances of these RNGs. -- Rand implementations for `Box` and `@T` were removed. These seemed to be - pretty rare in the codebase, and it allows for librand to not depend on - liballoc. Additionally, other pointer types like Rc and Arc were not - supported. -- Remove a slew of old deprecated functions - - -## [Rust 0.10] - 2014-04-03 -### Changed -- replace `Rng.shuffle's` functionality with `.shuffle_mut` -- bubble up IO errors when creating an OSRng - -### Fixed -- Use `fill()` instead of `read()` -- Rewrite OsRng in Rust for windows - -## 0.10-pre - 2014-03-02 -### Added -- Separate `rand` out of the standard library - -[Unreleased]: https://github.com/rust-random/rand/compare/0.10.0...HEAD -[0.10.0]: https://github.com/rust-random/rand/compare/0.9.2...0.10.0 -[0.9.2]: https://github.com/rust-random/rand/compare/0.9.1...0.9.2 -[0.9.1]: https://github.com/rust-random/rand/compare/0.9.0...0.9.1 -[0.9.0]: https://github.com/rust-random/rand/compare/0.8.5...0.9.0 -[0.8.5]: https://github.com/rust-random/rand/compare/0.8.4...0.8.5 -[0.8.4]: https://github.com/rust-random/rand/compare/0.8.3...0.8.4 -[0.8.3]: https://github.com/rust-random/rand/compare/0.8.2...0.8.3 -[0.8.2]: https://github.com/rust-random/rand/compare/0.8.1...0.8.2 -[0.8.1]: https://github.com/rust-random/rand/compare/0.8.0...0.8.1 -[0.8.0]: https://github.com/rust-random/rand/compare/0.7.3...0.8.0 -[0.7.3]: https://github.com/rust-random/rand/compare/0.7.2...0.7.3 -[0.7.2]: https://github.com/rust-random/rand/compare/0.7.1...0.7.2 -[0.7.1]: https://github.com/rust-random/rand/compare/0.7.0...0.7.1 -[0.7.0]: https://github.com/rust-random/rand/compare/0.6.5...0.7.0 -[0.6.5]: https://github.com/rust-random/rand/compare/0.6.4...0.6.5 -[0.6.4]: https://github.com/rust-random/rand/compare/0.6.3...0.6.4 -[0.6.3]: https://github.com/rust-random/rand/compare/0.6.2...0.6.3 -[0.6.2]: https://github.com/rust-random/rand/compare/0.6.1...0.6.2 -[0.6.1]: https://github.com/rust-random/rand/compare/0.6.0...0.6.1 -[0.6.0]: https://github.com/rust-random/rand/compare/0.5.5...0.6.0 -[0.5.5]: https://github.com/rust-random/rand/compare/0.5.4...0.5.5 -[0.5.4]: https://github.com/rust-random/rand/compare/0.5.3...0.5.4 -[0.5.3]: https://github.com/rust-random/rand/compare/0.5.2...0.5.3 -[0.5.2]: https://github.com/rust-random/rand/compare/0.5.1...0.5.2 -[0.5.1]: https://github.com/rust-random/rand/compare/0.5.0...0.5.1 -[0.5.0]: https://github.com/rust-random/rand/compare/0.4.6...0.5.0 -[0.4.6]: https://github.com/rust-random/rand/compare/0.4.5...0.4.6 -[0.4.5]: https://github.com/rust-random/rand/compare/0.4.4...0.4.5 -[0.4.4]: https://github.com/rust-random/rand/compare/0.4.3...0.4.4 -[0.4.3]: https://github.com/rust-random/rand/compare/0.4.2...0.4.3 -[0.4.2]: https://github.com/rust-random/rand/compare/0.4.1...0.4.2 -[0.4.1]: https://github.com/rust-random/rand/compare/0.4.0-pre.0...0.4.1 -[0.4.0-pre.0]: https://github.com/rust-random/rand/compare/0.3.22...0.4.0-pre.0 -[0.3.22]: https://github.com/rust-random/rand/compare/0.3.20...0.3.22 -[0.3.20]: https://github.com/rust-random/rand/compare/0.3.19...0.3.20 -[0.3.19]: https://github.com/rust-random/rand/compare/0.3.18...0.3.19 -[0.3.18]: https://github.com/rust-random/rand/compare/0.3.17...0.3.18 -[0.3.17]: https://github.com/rust-random/rand/compare/0.3.16...0.3.17 -[0.3.16]: https://github.com/rust-random/rand/compare/0.3.15...0.3.16 -[0.3.15]: https://github.com/rust-random/rand/compare/0.3.14...0.3.15 -[0.3.14]: https://github.com/rust-random/rand/compare/0.3.13...0.3.14 -[0.3.13]: https://github.com/rust-random/rand/compare/0.3.12...0.3.13 -[0.3.12]: https://github.com/rust-random/rand/compare/0.3.11...0.3.12 -[0.3.11]: https://github.com/rust-random/rand/compare/0.3.10...0.3.11 -[0.3.10]: https://github.com/rust-random/rand/compare/0.3.9...0.3.10 -[0.3.9]: https://github.com/rust-random/rand/compare/0.3.8...0.3.9 -[0.3.8]: https://github.com/rust-random/rand/compare/0.3.7...0.3.8 -[0.3.7]: https://github.com/rust-random/rand/compare/0.3.6...0.3.7 -[0.3.6]: https://github.com/rust-random/rand/compare/0.3.5...0.3.6 -[0.3.5]: https://github.com/rust-random/rand/compare/0.3.4...0.3.5 -[0.3.4]: https://github.com/rust-random/rand/compare/0.3.3...0.3.4 -[0.3.3]: https://github.com/rust-random/rand/compare/0.3.2...0.3.3 -[0.3.2]: https://github.com/rust-random/rand/compare/0.3.1...0.3.2 -[0.3.1]: https://github.com/rust-random/rand/compare/0.3.0...0.3.1 -[0.3.0]: https://github.com/rust-random/rand/compare/0.2.1...0.3.0 -[0.2.1]: https://github.com/rust-random/rand/compare/0.2.0...0.2.1 -[0.2.0]: https://github.com/rust-random/rand/compare/0.1.4...0.2.0 -[0.1.3]: https://github.com/rust-random/rand/compare/0.1.3...0.1.4 -[0.1.3]: https://github.com/rust-random/rand/compare/0.1.2...0.1.3 -[0.1.2]: https://github.com/rust-random/rand/compare/0.1.1...0.1.2 diff --git a/COPYRIGHT b/COPYRIGHT deleted file mode 100644 index 468d907caf9..00000000000 --- a/COPYRIGHT +++ /dev/null @@ -1,12 +0,0 @@ -Copyrights in the Rand project are retained by their contributors. No -copyright assignment is required to contribute to the Rand project. - -For full authorship information, see the version control history. - -Except as otherwise noted (below and/or in individual files), Rand is -licensed under the Apache License, Version 2.0 or - or the MIT license - or , at your option. - -The Rand project includes code from the Rust project -published under these same licenses. diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 14c2e83cf81..00000000000 --- a/Cargo.lock +++ /dev/null @@ -1,528 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core", -] - -[[package]] -name = "cobs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" -dependencies = [ - "thiserror", -] - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embedded-io" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" - -[[package]] -name = "embedded-io" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "rand_core", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.182" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "postcard" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" -dependencies = [ - "cobs", - "embedded-io 0.4.0", - "embedded-io 0.6.1", - "serde", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.10.0" -dependencies = [ - "chacha20", - "getrandom", - "log", - "postcard", - "rand_core", - "rand_pcg", - "rayon", - "serde", - "serde_json", -] - -[[package]] -name = "rand_core" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" - -[[package]] -name = "rand_pcg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a36dd10a879f5c16e363eed24471b321b114ce3112c2d5d8df06545fcf48152" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen 0.46.0", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 30810babf66..755b6f1f33d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,76 +1,21 @@ [package] + name = "rand" -version = "0.10.0" -authors = ["The Rand Project Developers", "The Rust Project Developers"] -license = "MIT OR Apache-2.0" +version = "0.3.15" +authors = ["The Rust Project Developers"] +license = "MIT/Apache-2.0" readme = "README.md" -repository = "https://github.com/rust-random/rand" -documentation = "https://docs.rs/rand" -homepage = "https://rust-random.github.io/book" +repository = "https://github.com/rust-lang/rand" +documentation = "https://doc.rust-lang.org/rand" +homepage = "https://github.com/rust-lang/rand" description = """ Random number generators and other randomness functionality. """ keywords = ["random", "rng"] -categories = ["algorithms", "no-std"] -autobenches = true -edition = "2024" -rust-version = "1.85" -include = ["src/", "LICENSE-*", "README.md", "CHANGELOG.md", "COPYRIGHT"] - -[package.metadata.docs.rs] -# To build locally: -# RUSTDOCFLAGS="--cfg docsrs -Zunstable-options --generate-link-to-definition" cargo +nightly doc --all --all-features --no-deps --open -all-features = true -rustdoc-args = ["--generate-link-to-definition"] - -[package.metadata.playground] -features = ["serde"] - -[features] -# Meta-features: -default = ["std", "std_rng", "sys_rng", "thread_rng"] -serde = ["dep:serde"] - -# Option (enabled by default): without "std" rand uses libcore; this option -# enables functionality expected to be available on a standard platform. -std = ["alloc", "getrandom?/std"] - -# Option: "alloc" enables support for Vec and Box when not using "std" -alloc = [] - -# Option: enable SysRng -sys_rng = ["dep:getrandom", "getrandom/sys_rng"] - -# Option (requires nightly Rust): experimental SIMD support -simd_support = [] - -# Option (enabled by default): enable StdRng -std_rng = ["dep:chacha20"] - -# Option: enable ThreadRng and rng() -thread_rng = ["std", "std_rng", "sys_rng"] - -# Option: enable rand::rngs::ChaCha*Rng -chacha = ["dep:chacha20"] - -# Option: use unbiased sampling for algorithms supporting this option: Uniform distribution. -# By default, bias affecting no more than one in 2^48 samples is accepted. -# Note: enabling this option is expected to affect reproducibility of results. -unbiased = [] - -# Option: enable logging -log = ["dep:log"] +categories = ["algorithms"] [dependencies] -rand_core = { version = "0.10.0", default-features = false } -log = { version = "0.4.4", optional = true } -serde = { version = "1.0.103", features = ["derive"], optional = true } -chacha20 = { version = "0.10.0", default-features = false, features = ["rng"], optional = true } -getrandom = { version = "0.4.0", optional = true } +libc = "0.2" [dev-dependencies] -rand_pcg = "0.10" -# Only to test serde -postcard = {version = "1.1.3", default-features = false, features = ["alloc"]} -rayon = "1.7" -serde_json = "1.0.140" +log = "0.3.0" diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 494ad3bfdfe..16fe87b06e8 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -1,6 +1,6 @@ Apache License Version 2.0, January 2004 - https://www.apache.org/licenses/ + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -174,3 +174,28 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index d93b5baf341..39d4bdb5acd 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,3 @@ -Copyright 2018 Developers of the Rand project Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any diff --git a/README.md b/README.md index 0fb98fbb0c6..52615849630 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,52 @@ -# Rand +rand +==== -[![Test Status](https://github.com/rust-random/rand/actions/workflows/test.yml/badge.svg?event=push)](https://github.com/rust-random/rand/actions) -[![Crate](https://img.shields.io/crates/v/rand.svg)](https://crates.io/crates/rand) -[![Book](https://img.shields.io/badge/book-master-yellow.svg)](https://rust-random.github.io/book/) -[![API](https://docs.rs/rand/badge.svg)](https://docs.rs/rand) +A Rust library for random number generators and other randomness functionality. -Rand is a set of crates supporting (pseudo-)random generators: +[![Build Status](https://travis-ci.org/rust-lang-nursery/rand.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/rand) +[![Build status](https://ci.appveyor.com/api/projects/status/rm5c9o33k3jhchbw?svg=true)](https://ci.appveyor.com/project/alexcrichton/rand) -- Built over a standard RNG trait: [`rand_core::RngCore`](https://docs.rs/rand_core/latest/rand_core/trait.RngCore.html) -- With fast implementations of both [strong](https://rust-random.github.io/book/guide-rngs.html#cryptographically-secure-pseudo-random-number-generators-csprngs) and - [small](https://rust-random.github.io/book/guide-rngs.html#basic-pseudo-random-number-generators-prngs) generators: [`rand::rngs`](https://docs.rs/rand/latest/rand/rngs/index.html), and more RNGs: [`chacha20`](https://docs.rs/chacha20), [`rand_xoshiro`](https://docs.rs/rand_xoshiro/), [`rand_pcg`](https://docs.rs/rand_pcg/), [`rand_sfc`](https://docs.rs/rand_sfc/), [`rand_seeder`](https://docs.rs/rand_seeder/), [rngs repo](https://github.com/rust-random/rngs/) -- [`rand::rng`](https://docs.rs/rand/latest/rand/fn.rng.html) is an asymptotically-fast, automatically-seeded and reasonably strong generator available on all `std` targets -- Direct support for seeding generators from the [getrandom] crate +[Documentation](https://doc.rust-lang.org/rand) -With broad support for random value generation and random processes: +## Usage -- [`StandardUniform`](https://docs.rs/rand/latest/rand/distr/struct.StandardUniform.html) random value sampling, - [`Uniform`](https://docs.rs/rand/latest/rand/distr/struct.Uniform.html)-ranged value sampling - and [more](https://docs.rs/rand/latest/rand/distr/index.html) -- Samplers for a large number of non-uniform random number distributions via our own - [`rand_distr`](https://docs.rs/rand_distr) and via - the [`statrs`](https://docs.rs/statrs) -- Random processes (mostly choose and shuffle) via [`rand::seq`](https://docs.rs/rand/latest/rand/seq/index.html) traits +Add this to your `Cargo.toml`: -All with: +```toml +[dependencies] +rand = "0.3" +``` -- [Portably reproducible output](https://rust-random.github.io/book/portability.html) -- `#[no_std]` compatibility (partial) -- *Many* performance optimisations thanks to contributions from the wide - user-base +and this to your crate root: -Rand **is not**: +```rust +extern crate rand; +``` -- Small (LoC). Most low-level crates are small, but the higher-level `rand` - and `rand_distr` each contain a lot of functionality. -- Simple (implementation). We have a strong focus on correctness, speed and flexibility, but - not simplicity. If you prefer a small-and-simple library, there are - alternatives including [fastrand](https://crates.io/crates/fastrand) - and [oorandom](https://crates.io/crates/oorandom). -- Primarily a cryptographic library. `rand` does provide some generators which - aim to support unpredictable value generation under certain constraints; - see [SECURITY.md](https://github.com/rust-random/rand/blob/master/SECURITY.md) for details. - Users are expected to determine for themselves - whether `rand`'s functionality meets their own security requirements. +## Examples -Documentation: +There is built-in support for a random number generator (RNG) associated with each thread stored in thread-local storage. This RNG can be accessed via thread_rng, or used implicitly via random. This RNG is normally randomly seeded from an operating-system source of randomness, e.g. /dev/urandom on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data. -- [The Rust Rand Book](https://rust-random.github.io/book) -- [API reference (docs.rs)](https://docs.rs/rand) +```rust +let tuple = rand::random::<(f64, char)>(); +println!("{:?}", tuple) +``` +```rust +use rand::Rng; -## Versions +let mut rng = rand::thread_rng(); +if rng.gen() { // random bool + println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +} +``` -Rand is *mature* (suitable for general usage, with infrequent breaking releases -which minimise breakage) but not yet at 1.0. Current `MAJOR.MINOR` versions are: +It is also possible to use other RNG types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. -- Version 0.10 was released in February 2026. +```rust +use rand::{Rng, ChaChaRng}; -See the [CHANGELOG](https://github.com/rust-random/rand/blob/master/CHANGELOG.md) or [Upgrade Guide](https://rust-random.github.io/book/update.html) for more details. +let mut rng = rand::ChaChaRng::new_unseeded(); +println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +``` -## Crate Features - -Rand is built with these features enabled by default: - -- `std` enables functionality dependent on the `std` lib -- `alloc` (implied by `std`) enables functionality requiring an allocator; a - significant portion of sequence and distribution functionality requires this -- `sys_rng` enables `rand::rngs::SysRng` (uses the [getrandom] crate) -- `std_rng` enables `rand::rngs::StdRng` (uses the [chacha20] crate) -- `thread_rng` (implies `std`, `std_rng`, `sys_rng`) enables `rand::rngs::ThreadRng` and `rand::rng()` - -Optionally, the following dependencies can be enabled: - -- `chacha` enables `rand::rngs::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}` (uses the [chacha20] crate) -- `log` enables logging (uses the [log] crate) - -Additionally, these features configure Rand: - -- `simd_support` (experimental) enables sampling of SIMD values (uniformly - random SIMD integers and floats). Since `std::simd` is not yet stable this - feature requires nightly Rust and may cause build failures. -- `unbiased` use unbiased sampling for algorithms supporting this option: Uniform distribution. - - (By default, bias affecting no more than one in 2^48 samples is accepted.) - - Note: enabling this option is expected to affect reproducibility of results. - -## Portability - -### Reproducibility - -Achieving reproducible results requires not only deterministic algorithms with fixed inputs but also a commitment to stability of algorithms and some platform-specific considerations. A subset of `rand` does aim to support reproducibility; read more about this in the book: [Portability](https://rust-random.github.io/book/portability.html). - -### WebAssembly support - -The [WASI](https://github.com/WebAssembly/WASI/tree/main) and Emscripten -targets are directly supported. The `wasm32-unknown-unknown` target is not -*automatically* supported. To enable support for this target, refer to the -[`getrandom` documentation for WebAssembly](https://docs.rs/getrandom/latest/getrandom/#webassembly-support). -Alternatively, the `sys_rng` feature may be disabled. - -# License - -Rand is distributed under the terms of both the MIT license and the -Apache License (Version 2.0). - -See [LICENSE-APACHE](https://github.com/rust-random/rand/blob/master/LICENSE-APACHE) and [LICENSE-MIT](https://github.com/rust-random/rand/blob/master/LICENSE-MIT), and -[COPYRIGHT](https://github.com/rust-random/rand/blob/master/COPYRIGHT) for details. - -[getrandom]: https://crates.io/crates/getrandom -[chacha20]: https://crates.io/crates/chacha20 -[log]: https://crates.io/crates/log diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 3acf543cfbe..00000000000 --- a/SECURITY.md +++ /dev/null @@ -1,88 +0,0 @@ -# Security Policy - -## Disclaimer - -Rand is a community project and cannot provide legally-binding guarantees of -security. - -## Security premises - -### Marker traits - -Rand provides the marker traits `CryptoRng`, `TryCryptoRng` and -`CryptoBlockRng`. Generators (RNGs) implementing one of these traits which are -used according to these additional constraints: - -- The generator may be constructed using `std::default::Default` where the - generator supports this trait. Note that generators should *only* support - `Default` where the `default()` instance is appropriately seeded: for - example `SysRng` has no state and thus has a trivial `default()` instance - while `ThreadRng::default()` returns a handle to a thread-local instance - seeded using `SysRng`. -- The generator may be constructed using `rand_core::SeedableRng` in any of - the following ways where the generator supports this trait: - - - Via `SeedableRng::from_seed` using a cryptographically secure seed value - - Via `SeedableRng::from_rng` or `try_from_rng` using a cryptographically - secure source `rng` such as `SysRng` or `ThreadRng`. -- The state (memory) of the generator and its seed value (or source `rng`) are - not exposed - -are expected to provide the following: - -- An attacker cannot predict the output with more accuracy than what would be - expected through pure chance since each possible output value of any method - under the above traits which generates output bytes (including - `RngCore::next_u32`, `RngCore::next_u64`, `RngCore::fill_bytes`, - `TryRngCore::try_next_u32`, `TryRngCore::try_next_u64`, - `TryRngCore::try_fill_bytes` and `BlockRngCore::generate`) should be equally - likely -- Knowledge of prior outputs from the generator does not aid an attacker in - predicting future outputs - -### Specific generators - -`SysRng` is a stateless "generator" implemented via [getrandom]. As such, it has -no possible state to leak and cannot be improperly seeded. - -`StdRng` is a `CryptoRng` and `SeedableRng` using a pseudo-random algorithm -selected for good security and performance qualities. Since it does not offer -reproducibility of output, its algorithm may be changed in any release version. - -`ChaCha12Rng` and `ChaCha20Rng` are selected pseudo-random generators -distributed by the `rand` project which meet the requirements of the `CryptoRng` -trait and implement `SeedableRng` with a commitment to reproducibility of -results. - -`ThreadRng` is a conveniently-packaged generator over `StdRng` offering -automatic seeding from `SysRng`, periodic reseeding and thread locality. -This random source is intended to offer a good compromise between cryptographic -security, fast generation with reasonably low memory and initialization cost -overheads, and robustness against misuse. - -[getrandom]: https://crates.io/crates/getrandom - -### Distributions - -Methods of the `Rng` trait, functionality of the `rand::seq` module and -implementators of the `Distribution` trait are expected, while using a -cryptographically secure `CryptoRng` instance meeting the above constraints, -to not introduce significant bias to their operation beyond what would be -expected of the operation. Note that the usage of 'significant' here permits -some bias, as noted for example in the documentation of the `Uniform` -distribution. - -## Supported Versions - -We aim to provide security fixes in the form of a new patch version for the -latest release version of `rand` and its dependencies `rand_core` and -`chacha20`, as well as for prior major and minor releases which were, at some -time during the previous 12 months, the latest release version. - -## Reporting a Vulnerability - -If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. - -Please disclose it at [security advisory](https://github.com/rust-random/rand/security/advisories/new). - -This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..4a6104291e1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,17 @@ +environment: + matrix: + - TARGET: x86_64-pc-windows-msvc + - TARGET: i686-pc-windows-msvc + - TARGET: i686-pc-windows-gnu +install: + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" + - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - SET PATH=%PATH%;C:\MinGW\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --verbose --target %TARGET% diff --git a/benches/Cargo.toml b/benches/Cargo.toml deleted file mode 100644 index 20975d5a202..00000000000 --- a/benches/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "benches" -version = "0.1.0" -edition = "2024" -publish = false - -[features] -# Option (requires nightly Rust): experimental SIMD support -simd_support = ["rand/simd_support"] - -[workspace] - -[dev-dependencies] -rand = { path = ".." } -rand_pcg = "0.10" -chacha20 = { version = "0.10.0", default-features = false, features = ["rng"] } -criterion = "0.5" -criterion-cycles-per-byte = "0.6" - -[[bench]] -name = "array" -harness = false - -[[bench]] -name = "bool" -harness = false - -[[bench]] -name = "generators" -harness = false - -[[bench]] -name = "seq_choose" -harness = false - -[[bench]] -name = "shuffle" -harness = false - -[[bench]] -name = "simd" -harness = false - -[[bench]] -name = "standard" -harness = false - -[[bench]] -name = "uniform" -harness = false - -[[bench]] -name = "uniform_float" -harness = false - -[[bench]] -name = "weighted" -harness = false diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 00000000000..5fa92bdbea0 --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,97 @@ +#![feature(test)] + +extern crate test; +extern crate rand; + +const RAND_BENCH_N: u64 = 1000; + +mod distributions; + +use std::mem::size_of; +use test::{black_box, Bencher}; +use rand::{XorShiftRng, StdRng, IsaacRng, Isaac64Rng, Rng}; +use rand::{OsRng, sample, weak_rng}; + +#[bench] +fn rand_xorshift(b: &mut Bencher) { + let mut rng: XorShiftRng = OsRng::new().unwrap().gen(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_isaac(b: &mut Bencher) { + let mut rng: IsaacRng = OsRng::new().unwrap().gen(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_isaac64(b: &mut Bencher) { + let mut rng: Isaac64Rng = OsRng::new().unwrap().gen(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_std(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_f32(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.next_f32()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_f64(b: &mut Bencher) { + let mut rng = StdRng::new().unwrap(); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.next_f64()); + } + }); + b.bytes = size_of::() as u64 * RAND_BENCH_N; +} + +#[bench] +fn rand_shuffle_100(b: &mut Bencher) { + let mut rng = weak_rng(); + let x : &mut [usize] = &mut [1; 100]; + b.iter(|| { + rng.shuffle(x); + }) +} + +#[bench] +fn rand_sample_10_of_100(b: &mut Bencher) { + let mut rng = weak_rng(); + let x : &[usize] = &[1; 100]; + b.iter(|| { + sample(&mut rng, x, 10); + }) +} diff --git a/benches/benches/array.rs b/benches/benches/array.rs deleted file mode 100644 index 862fcec95fa..00000000000 --- a/benches/benches/array.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating/filling arrays and iterators of output - -use criterion::{Criterion, criterion_group, criterion_main}; -use rand::distr::StandardUniform; -use rand::prelude::*; -use rand_pcg::Pcg64Mcg; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - let mut g = c.benchmark_group("random_1kb"); - g.throughput(criterion::Throughput::Bytes(1024)); - - g.bench_function("u16_iter_repeat", |b| { - use core::iter; - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: Vec = iter::repeat(()).map(|()| rng.random()).take(512).collect(); - v - }); - }); - - g.bench_function("u16_sample_iter", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: Vec = StandardUniform.sample_iter(&mut rng).take(512).collect(); - v - }); - }); - - g.bench_function("u16_gen_array", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: [u16; 512] = rng.random(); - v - }); - }); - - g.bench_function("u16_fill", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - let mut buf = [0u16; 512]; - b.iter(|| { - rng.fill(&mut buf[..]); - buf - }); - }); - - g.bench_function("u64_iter_repeat", |b| { - use core::iter; - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: Vec = iter::repeat(()).map(|()| rng.random()).take(128).collect(); - v - }); - }); - - g.bench_function("u64_sample_iter", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: Vec = StandardUniform.sample_iter(&mut rng).take(128).collect(); - v - }); - }); - - g.bench_function("u64_gen_array", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| { - let v: [u64; 128] = rng.random(); - v - }); - }); - - g.bench_function("u64_fill", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - let mut buf = [0u64; 128]; - b.iter(|| { - rng.fill(&mut buf[..]); - buf - }); - }); -} diff --git a/benches/benches/bool.rs b/benches/benches/bool.rs deleted file mode 100644 index ff15ec59b43..00000000000 --- a/benches/benches/bool.rs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating/filling arrays and iterators of output - -use criterion::{Criterion, criterion_group, criterion_main}; -use rand::distr::Bernoulli; -use rand::prelude::*; -use rand_pcg::Pcg32; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - let mut g = c.benchmark_group("random_bool"); - g.sample_size(1000); - g.warm_up_time(core::time::Duration::from_millis(500)); - g.measurement_time(core::time::Duration::from_millis(1000)); - - g.bench_function("standard", |b| { - let mut rng: Pcg32 = rand::make_rng(); - b.iter(|| rng.sample::(rand::distr::StandardUniform)) - }); - - g.bench_function("const", |b| { - let mut rng: Pcg32 = rand::make_rng(); - b.iter(|| rng.random_bool(0.18)) - }); - - g.bench_function("var", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let p = rng.random(); - b.iter(|| rng.random_bool(p)) - }); - - g.bench_function("ratio_const", |b| { - let mut rng: Pcg32 = rand::make_rng(); - b.iter(|| rng.random_ratio(2, 3)) - }); - - g.bench_function("ratio_var", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let d = rng.random_range(1..=100); - let n = rng.random_range(0..=d); - b.iter(|| rng.random_ratio(n, d)); - }); - - g.bench_function("bernoulli_const", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let d = Bernoulli::new(0.18).unwrap(); - b.iter(|| rng.sample(d)) - }); - - g.bench_function("bernoulli_var", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let p = rng.random(); - let d = Bernoulli::new(p).unwrap(); - b.iter(|| rng.sample(d)) - }); -} diff --git a/benches/benches/generators.rs b/benches/benches/generators.rs deleted file mode 100644 index 74a05abd092..00000000000 --- a/benches/benches/generators.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use chacha20::rand_core::UnwrapErr; -use chacha20::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}; -use core::time::Duration; -use criterion::measurement::WallTime; -use criterion::{BenchmarkGroup, Criterion, black_box, criterion_group, criterion_main}; -use rand::prelude::*; -use rand::rngs::SysRng; -use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg}; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = random_bytes, random_u32, random_u64, init_gen, init_from_u64, init_from_seed -); -criterion_main!(benches); - -pub fn random_bytes(c: &mut Criterion) { - let mut g = c.benchmark_group("random_bytes"); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - g.throughput(criterion::Throughput::Bytes(1024)); - - fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { - g.bench_function(name, |b| { - let mut buf = [0u8; 1024]; - b.iter(|| { - rng.fill_bytes(&mut buf); - black_box(buf); - }); - }); - } - - bench(&mut g, "pcg32", rand::make_rng::()); - bench(&mut g, "pcg64", rand::make_rng::()); - bench(&mut g, "pcg64mcg", rand::make_rng::()); - bench(&mut g, "pcg64dxsm", rand::make_rng::()); - bench(&mut g, "chacha8", rand::make_rng::()); - bench(&mut g, "chacha12", rand::make_rng::()); - bench(&mut g, "chacha20", rand::make_rng::()); - bench(&mut g, "std", rand::make_rng::()); - bench(&mut g, "small", rand::make_rng::()); - bench(&mut g, "os", UnwrapErr(SysRng)); - bench(&mut g, "thread", rand::rng()); - - g.finish() -} - -pub fn random_u32(c: &mut Criterion) { - let mut g = c.benchmark_group("random_u32"); - g.sample_size(1000); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - g.throughput(criterion::Throughput::Bytes(4)); - - fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { - g.bench_function(name, |b| { - b.iter(|| rng.random::()); - }); - } - - bench(&mut g, "pcg32", rand::make_rng::()); - bench(&mut g, "pcg64", rand::make_rng::()); - bench(&mut g, "pcg64mcg", rand::make_rng::()); - bench(&mut g, "pcg64dxsm", rand::make_rng::()); - bench(&mut g, "chacha8", rand::make_rng::()); - bench(&mut g, "chacha12", rand::make_rng::()); - bench(&mut g, "chacha20", rand::make_rng::()); - bench(&mut g, "std", rand::make_rng::()); - bench(&mut g, "small", rand::make_rng::()); - bench(&mut g, "os", UnwrapErr(SysRng)); - bench(&mut g, "thread", rand::rng()); - - g.finish() -} - -pub fn random_u64(c: &mut Criterion) { - let mut g = c.benchmark_group("random_u64"); - g.sample_size(1000); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - g.throughput(criterion::Throughput::Bytes(8)); - - fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { - g.bench_function(name, |b| { - b.iter(|| rng.random::()); - }); - } - - bench(&mut g, "pcg32", rand::make_rng::()); - bench(&mut g, "pcg64", rand::make_rng::()); - bench(&mut g, "pcg64mcg", rand::make_rng::()); - bench(&mut g, "pcg64dxsm", rand::make_rng::()); - bench(&mut g, "chacha8", rand::make_rng::()); - bench(&mut g, "chacha12", rand::make_rng::()); - bench(&mut g, "chacha20", rand::make_rng::()); - bench(&mut g, "std", rand::make_rng::()); - bench(&mut g, "small", rand::make_rng::()); - bench(&mut g, "os", UnwrapErr(SysRng)); - bench(&mut g, "thread", rand::rng()); - - g.finish() -} - -pub fn init_gen(c: &mut Criterion) { - let mut g = c.benchmark_group("init_gen"); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - - fn bench(g: &mut BenchmarkGroup, name: &str) { - g.bench_function(name, |b| { - let mut rng: Pcg32 = rand::make_rng(); - b.iter(|| R::from_rng(&mut rng)); - }); - } - - bench::(&mut g, "pcg32"); - bench::(&mut g, "pcg64"); - bench::(&mut g, "pcg64mcg"); - bench::(&mut g, "pcg64dxsm"); - bench::(&mut g, "chacha8"); - bench::(&mut g, "chacha12"); - bench::(&mut g, "chacha20"); - bench::(&mut g, "std"); - bench::(&mut g, "small"); - - g.finish() -} - -pub fn init_from_u64(c: &mut Criterion) { - let mut g = c.benchmark_group("init_from_u64"); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - - fn bench(g: &mut BenchmarkGroup, name: &str) { - g.bench_function(name, |b| { - let mut rng: Pcg32 = rand::make_rng(); - let seed = rng.random(); - b.iter(|| R::seed_from_u64(black_box(seed))); - }); - } - - bench::(&mut g, "pcg32"); - bench::(&mut g, "pcg64"); - bench::(&mut g, "pcg64mcg"); - bench::(&mut g, "pcg64dxsm"); - bench::(&mut g, "chacha8"); - bench::(&mut g, "chacha12"); - bench::(&mut g, "chacha20"); - bench::(&mut g, "std"); - bench::(&mut g, "small"); - - g.finish() -} - -pub fn init_from_seed(c: &mut Criterion) { - let mut g = c.benchmark_group("init_from_seed"); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - - fn bench(g: &mut BenchmarkGroup, name: &str) - where - rand::distr::StandardUniform: Distribution<::Seed>, - { - g.bench_function(name, |b| { - let mut rng: Pcg32 = rand::make_rng(); - let seed = rng.random(); - b.iter(|| R::from_seed(black_box(seed.clone()))); - }); - } - - bench::(&mut g, "pcg32"); - bench::(&mut g, "pcg64"); - bench::(&mut g, "pcg64mcg"); - bench::(&mut g, "pcg64dxsm"); - bench::(&mut g, "chacha8"); - bench::(&mut g, "chacha12"); - bench::(&mut g, "chacha20"); - bench::(&mut g, "std"); - bench::(&mut g, "small"); - - g.finish() -} diff --git a/benches/benches/seq_choose.rs b/benches/benches/seq_choose.rs deleted file mode 100644 index 92a0bdd0019..00000000000 --- a/benches/benches/seq_choose.rs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use criterion::{Criterion, black_box, criterion_group, criterion_main}; -use rand::SeedableRng; -use rand::prelude::*; -use rand_pcg::Pcg32; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - c.bench_function("seq_slice_choose_1_of_100", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 100]; - rng.fill(&mut buf); - let x = black_box(&mut buf); - - b.iter(|| x.choose(&mut rng).unwrap()); - }); - - let lens = [(1, 1000), (950, 1000), (10, 100), (90, 100)]; - for (amount, len) in lens { - let name = format!("seq_slice_sample_{amount}_of_{len}"); - c.bench_function(name.as_str(), |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 1000]; - rng.fill(&mut buf); - let x = black_box(&buf[..len]); - - let mut results_buf = [0i32; 950]; - let y = black_box(&mut results_buf[..amount]); - let amount = black_box(amount); - - b.iter(|| { - // Collect full result to prevent unwanted shortcuts getting - // first element (in case sample_indices returns an iterator). - for (slot, sample) in y.iter_mut().zip(x.sample(&mut rng, amount)) { - *slot = *sample; - } - y[amount - 1] - }) - }); - } - - let lens = [(1, 1000), (950, 1000), (10, 100), (90, 100)]; - for (amount, len) in lens { - let name = format!("seq_slice_sample_weighted_{amount}_of_{len}"); - c.bench_function(name.as_str(), |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 1000]; - rng.fill(&mut buf); - let x = black_box(&buf[..len]); - - let mut results_buf = [0i32; 950]; - let y = black_box(&mut results_buf[..amount]); - let amount = black_box(amount); - - b.iter(|| { - // Collect full result to prevent unwanted shortcuts getting - // first element (in case sample_indices returns an iterator). - let samples_iter = x.sample_weighted(&mut rng, amount, |_| 1.0).unwrap(); - for (slot, sample) in y.iter_mut().zip(samples_iter) { - *slot = *sample; - } - y[amount - 1] - }) - }); - } - - c.bench_function("seq_iter_sample_10_of_100", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 100]; - rng.fill(&mut buf); - let x = black_box(&buf); - b.iter(|| x.iter().cloned().sample(&mut rng, 10)) - }); - - c.bench_function("seq_iter_sample_fill_10_of_100", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 100]; - rng.fill(&mut buf); - let x = black_box(&buf); - let mut buf = [0; 10]; - b.iter(|| x.iter().cloned().sample_fill(&mut rng, &mut buf)) - }); - - bench_rng::(c, "ChaCha20"); - bench_rng::(c, "Pcg32"); - bench_rng::(c, "Pcg64"); -} - -fn bench_rng(c: &mut Criterion, rng_name: &'static str) { - for length in [1, 2, 3, 10, 100, 1000].map(black_box) { - let name = format!("choose_size-hinted_from_{length}_{rng_name}"); - c.bench_function(name.as_str(), |b| { - let mut rng = R::seed_from_u64(123); - b.iter(|| choose_size_hinted(length, &mut rng)) - }); - - let name = format!("choose_stable_from_{length}_{rng_name}"); - c.bench_function(name.as_str(), |b| { - let mut rng = R::seed_from_u64(123); - b.iter(|| choose_stable(length, &mut rng)) - }); - - let name = format!("choose_unhinted_from_{length}_{rng_name}"); - c.bench_function(name.as_str(), |b| { - let mut rng = R::seed_from_u64(123); - b.iter(|| choose_unhinted(length, &mut rng)) - }); - - let name = format!("choose_windowed_from_{length}_{rng_name}"); - c.bench_function(name.as_str(), |b| { - let mut rng = R::seed_from_u64(123); - b.iter(|| choose_windowed(length, 7, &mut rng)) - }); - } -} - -fn choose_size_hinted(max: usize, rng: &mut R) -> Option { - let iterator = 0..max; - iterator.choose(rng) -} - -fn choose_stable(max: usize, rng: &mut R) -> Option { - let iterator = 0..max; - iterator.choose_stable(rng) -} - -fn choose_unhinted(max: usize, rng: &mut R) -> Option { - let iterator = UnhintedIterator { iter: (0..max) }; - iterator.choose(rng) -} - -fn choose_windowed(max: usize, window_size: usize, rng: &mut R) -> Option { - let iterator = WindowHintedIterator { - iter: (0..max), - window_size, - }; - iterator.choose(rng) -} - -#[derive(Clone)] -struct UnhintedIterator { - iter: I, -} -impl Iterator for UnhintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } -} - -#[derive(Clone)] -struct WindowHintedIterator { - iter: I, - window_size: usize, -} -impl Iterator for WindowHintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - (core::cmp::min(self.iter.len(), self.window_size), None) - } -} diff --git a/benches/benches/shuffle.rs b/benches/benches/shuffle.rs deleted file mode 100644 index 8ace45a5a18..00000000000 --- a/benches/benches/shuffle.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use criterion::{Criterion, black_box, criterion_group, criterion_main}; -use rand::SeedableRng; -use rand::prelude::*; -use rand_pcg::Pcg32; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - c.bench_function("seq_shuffle_100", |b| { - let mut rng: Pcg32 = rand::make_rng(); - let mut buf = [0i32; 100]; - rng.fill(&mut buf); - let x = black_box(&mut buf); - b.iter(|| { - x.shuffle(&mut rng); - x[0] - }) - }); - - bench_rng::(c, "ChaCha12"); - bench_rng::(c, "Pcg32"); - bench_rng::(c, "Pcg64"); -} - -fn bench_rng(c: &mut Criterion, rng_name: &'static str) { - for length in [1, 2, 3, 10, 100, 1000, 10000].map(black_box) { - c.bench_function(format!("shuffle_{length}_{rng_name}").as_str(), |b| { - let mut rng = R::seed_from_u64(123); - let mut vec: Vec = (0..length).collect(); - b.iter(|| { - vec.shuffle(&mut rng); - vec[0] - }) - }); - - if length >= 10 { - let name = format!("partial_shuffle_{length}_{rng_name}"); - c.bench_function(name.as_str(), |b| { - let mut rng = R::seed_from_u64(123); - let mut vec: Vec = (0..length).collect(); - b.iter(|| { - vec.partial_shuffle(&mut rng, length / 2); - vec[0] - }) - }); - } - } -} diff --git a/benches/benches/simd.rs b/benches/benches/simd.rs deleted file mode 100644 index b5515daf616..00000000000 --- a/benches/benches/simd.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating SIMD / wide types - -#![cfg_attr(feature = "simd_support", feature(portable_simd))] - -use criterion::{Criterion, criterion_group, criterion_main}; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = simd -); -criterion_main!(benches); - -#[cfg(not(feature = "simd_support"))] -pub fn simd(_: &mut Criterion) {} - -#[cfg(feature = "simd_support")] -pub fn simd(c: &mut Criterion) { - use rand::prelude::*; - use rand_pcg::Pcg64Mcg; - - let mut g = c.benchmark_group("random_simd"); - - g.bench_function("u128", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("m128i", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("m256i", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("m512i", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("u64x2", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("u32x4", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("u32x8", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("u16x8", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); - - g.bench_function("u8x16", |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - b.iter(|| rng.random::()); - }); -} diff --git a/benches/benches/standard.rs b/benches/benches/standard.rs deleted file mode 100644 index a58f7049b70..00000000000 --- a/benches/benches/standard.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::time::Duration; -use criterion::measurement::WallTime; -use criterion::{BenchmarkGroup, Criterion, criterion_group, criterion_main}; -use rand::distr::{Alphabetic, Alphanumeric, Open01, OpenClosed01, StandardUniform}; -use rand::prelude::*; -use rand_pcg::Pcg64Mcg; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -fn bench_ty(g: &mut BenchmarkGroup, name: &str) -where - D: Distribution + Default, -{ - g.throughput(criterion::Throughput::Bytes(core::mem::size_of::() as u64)); - g.bench_function(name, |b| { - let mut rng: Pcg64Mcg = rand::make_rng(); - - b.iter(|| rng.sample::(D::default())); - }); -} - -pub fn bench(c: &mut Criterion) { - let mut g = c.benchmark_group("StandardUniform"); - g.sample_size(1000); - g.warm_up_time(Duration::from_millis(500)); - g.measurement_time(Duration::from_millis(1000)); - - macro_rules! do_ty { - ($t:ty) => { - bench_ty::<$t, StandardUniform>(&mut g, stringify!($t)); - }; - ($t:ty, $($tt:ty),*) => { - do_ty!($t); - do_ty!($($tt),*); - }; - } - - do_ty!(i8, i16, i32, i64, i128); - do_ty!(f32, f64); - do_ty!(char); - - bench_ty::(&mut g, "Alphabetic"); - bench_ty::(&mut g, "Alphanumeric"); - - bench_ty::(&mut g, "Open01/f32"); - bench_ty::(&mut g, "Open01/f64"); - bench_ty::(&mut g, "OpenClosed01/f32"); - bench_ty::(&mut g, "OpenClosed01/f64"); - - g.finish(); -} diff --git a/benches/benches/uniform.rs b/benches/benches/uniform.rs deleted file mode 100644 index 30f1e2919d3..00000000000 --- a/benches/benches/uniform.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implement benchmarks for uniform distributions over integer types - -#![cfg_attr(feature = "simd_support", feature(portable_simd))] - -use chacha20::ChaCha8Rng; -use core::time::Duration; -use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; -use rand::distr::uniform::{SampleRange, Uniform}; -use rand::prelude::*; -use rand_pcg::{Pcg32, Pcg64}; -#[cfg(feature = "simd_support")] -use std::simd::{Simd, num::SimdUint}; - -const WARM_UP_TIME: Duration = Duration::from_millis(1000); -const MEASUREMENT_TIME: Duration = Duration::from_secs(3); -const SAMPLE_SIZE: usize = 100_000; -const N_RESAMPLES: usize = 10_000; - -macro_rules! sample { - (@range $T:ty, $U:ty, 1, $rng:ident) => {{ - assert_eq!(<$T>::BITS, <$U>::BITS); - let bits = (<$T>::BITS / 2); - let mask = (1 as $U).wrapping_neg() >> bits; - let x = $rng.random::<$U>(); - ((x >> bits) * (x & mask)) as $T - }}; - - (@range $T:ty, $U:ty, $len:tt, $rng:ident) => {{ - let bits = (<$T>::BITS / 2); - let mask = Simd::splat((1 as $U).wrapping_neg() >> bits); - let bits = Simd::splat(bits as $U); - let x = $rng.random::>(); - ((x >> bits) * (x & mask)).cast() - }}; - - (@MIN $T:ty, 1) => { - <$T>::MIN - }; - - (@MIN $T:ty, $len:tt) => { - Simd::<$T, $len>::splat(<$T>::MIN) - }; - - (@wrapping_add $lhs:expr, $rhs:expr, 1) => { - $lhs.wrapping_add($rhs) - }; - - (@wrapping_add $lhs:expr, $rhs:expr, $len:tt) => { - ($lhs + $rhs) - }; - - ($R:ty, $T:ty, $U:ty, $len:tt, $g:expr) => { - $g.bench_function(BenchmarkId::new(stringify!($R), "single"), |b| { - let mut rng: $R = rand::make_rng(); - let range = sample!(@range $T, $U, $len, rng); - let low = sample!(@MIN $T, $len); - let high = sample!(@wrapping_add low, range, $len); - - b.iter(|| (low..=high).sample_single(&mut rng)); - }); - - $g.bench_function(BenchmarkId::new(stringify!($R), "distr"), |b| { - let mut rng: $R = rand::make_rng(); - let range = sample!(@range $T, $U, $len, rng); - let low = sample!(@MIN $T, $len); - let high = sample!(@wrapping_add low, range, $len); - let dist = Uniform::new_inclusive(low, high).unwrap(); - - b.iter(|| dist.sample(&mut rng)); - }); - }; - - // Entrypoint: - // $T is the output type (integer) - // $U is the unsigned version of the output type - // $len is the width for SIMD or 1 for non-SIMD - ($c:expr, $T:ty, $U:ty, $len:tt) => {{ - let mut g = $c.benchmark_group(concat!("sample_", stringify!($T), "x", stringify!($len))); - g.sample_size(SAMPLE_SIZE); - g.warm_up_time(WARM_UP_TIME); - g.measurement_time(MEASUREMENT_TIME); - g.nresamples(N_RESAMPLES); - sample!(SmallRng, $T, $U, $len, g); - sample!(ChaCha8Rng, $T, $U, $len, g); - sample!(Pcg32, $T, $U, $len, g); - sample!(Pcg64, $T, $U, $len, g); - g.finish(); - }}; -} - -fn sample(c: &mut Criterion) { - sample!(c, i8, u8, 1); - sample!(c, i16, u16, 1); - sample!(c, i32, u32, 1); - sample!(c, i64, u64, 1); - sample!(c, i128, u128, 1); - #[cfg(feature = "simd_support")] - sample!(c, u8, u8, 8); - #[cfg(feature = "simd_support")] - sample!(c, u8, u8, 16); - #[cfg(feature = "simd_support")] - sample!(c, u8, u8, 32); - #[cfg(feature = "simd_support")] - sample!(c, u8, u8, 64); - #[cfg(feature = "simd_support")] - sample!(c, i16, u16, 8); - #[cfg(feature = "simd_support")] - sample!(c, i16, u16, 16); - #[cfg(feature = "simd_support")] - sample!(c, i16, u16, 32); -} - -criterion_group! { - name = benches; - config = Criterion::default(); - targets = sample -} -criterion_main!(benches); diff --git a/benches/benches/uniform_float.rs b/benches/benches/uniform_float.rs deleted file mode 100644 index 3aede4b3a16..00000000000 --- a/benches/benches/uniform_float.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implement benchmarks for uniform distributions over FP types -//! -//! Sampling methods compared: -//! -//! - sample: current method: (x12 - 1.0) * (b - a) + a - -use chacha20::ChaCha8Rng; -use core::time::Duration; -use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; -use rand::distr::uniform::{SampleUniform, Uniform, UniformSampler}; -use rand::prelude::*; -use rand_pcg::{Pcg32, Pcg64}; - -const WARM_UP_TIME: Duration = Duration::from_millis(1000); -const MEASUREMENT_TIME: Duration = Duration::from_secs(3); -const SAMPLE_SIZE: usize = 100_000; -const N_RESAMPLES: usize = 10_000; - -macro_rules! single_random { - ($R:ty, $T:ty, $g:expr) => { - $g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| { - let mut rng: $R = rand::make_rng(); - let (mut low, mut high); - loop { - low = <$T>::from_bits(rng.random()); - high = <$T>::from_bits(rng.random()); - if (low < high) && (high - low).is_normal() { - break; - } - } - - b.iter(|| <$T as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng)); - }); - }; - - ($c:expr, $T:ty) => {{ - let mut g = $c.benchmark_group("uniform_single"); - g.sample_size(SAMPLE_SIZE); - g.warm_up_time(WARM_UP_TIME); - g.measurement_time(MEASUREMENT_TIME); - g.nresamples(N_RESAMPLES); - single_random!(SmallRng, $T, g); - single_random!(ChaCha8Rng, $T, g); - single_random!(Pcg32, $T, g); - single_random!(Pcg64, $T, g); - g.finish(); - }}; -} - -fn single_random(c: &mut Criterion) { - single_random!(c, f32); - single_random!(c, f64); -} - -macro_rules! distr_random { - ($R:ty, $T:ty, $g:expr) => { - $g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| { - let mut rng: $R = rand::make_rng(); - let dist = loop { - let low = <$T>::from_bits(rng.random()); - let high = <$T>::from_bits(rng.random()); - if let Ok(dist) = Uniform::<$T>::new_inclusive(low, high) { - break dist; - } - }; - - b.iter(|| dist.sample(&mut rng)); - }); - }; - - ($c:expr, $T:ty) => {{ - let mut g = $c.benchmark_group("uniform_distribution"); - g.sample_size(SAMPLE_SIZE); - g.warm_up_time(WARM_UP_TIME); - g.measurement_time(MEASUREMENT_TIME); - g.nresamples(N_RESAMPLES); - distr_random!(SmallRng, $T, g); - distr_random!(ChaCha8Rng, $T, g); - distr_random!(Pcg32, $T, g); - distr_random!(Pcg64, $T, g); - g.finish(); - }}; -} - -fn distr_random(c: &mut Criterion) { - distr_random!(c, f32); - distr_random!(c, f64); -} - -criterion_group! { - name = benches; - config = Criterion::default(); - targets = single_random, distr_random -} -criterion_main!(benches); diff --git a/benches/benches/weighted.rs b/benches/benches/weighted.rs deleted file mode 100644 index e5ba371224c..00000000000 --- a/benches/benches/weighted.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use criterion::{Criterion, black_box, criterion_group, criterion_main}; -use rand::distr::weighted::WeightedIndex; -use rand::prelude::*; -use rand::seq::index::sample_weighted; - -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - c.bench_function("weighted_index_creation", |b| { - let mut rng = rand::rng(); - let weights = black_box([1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]); - b.iter(|| { - let distr = WeightedIndex::new(weights.to_vec()).unwrap(); - rng.sample(distr) - }) - }); - - c.bench_function("weighted_index_modification", |b| { - let mut rng = rand::rng(); - let weights = black_box([1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]); - let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); - b.iter(|| { - distr.update_weights(&[(2, &4), (5, &1)]).unwrap(); - rng.sample(&distr) - }) - }); - - let lens = [ - (1, 1000, "1k"), - (10, 1000, "1k"), - (100, 1000, "1k"), - (100, 1_000_000, "1M"), - (200, 1_000_000, "1M"), - (400, 1_000_000, "1M"), - (600, 1_000_000, "1M"), - (1000, 1_000_000, "1M"), - ]; - for (amount, length, len_name) in lens { - let name = format!("weighted_sample_indices_{amount}_of_{len_name}"); - c.bench_function(name.as_str(), |b| { - let length = black_box(length); - let amount = black_box(amount); - let mut rng: SmallRng = rand::make_rng(); - b.iter(|| sample_weighted(&mut rng, length, |idx| (1 + (idx % 100)) as u32, amount)) - }); - } -} diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs new file mode 100644 index 00000000000..152615d7ba3 --- /dev/null +++ b/benches/distributions/exponential.rs @@ -0,0 +1,18 @@ +use std::mem::size_of; +use test::Bencher; +use rand; +use rand::distributions::exponential::Exp; +use rand::distributions::Sample; + +#[bench] +fn rand_exp(b: &mut Bencher) { + let mut rng = rand::weak_rng(); + let mut exp = Exp::new(2.71828 * 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + exp.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs new file mode 100644 index 00000000000..bf3fd367a9b --- /dev/null +++ b/benches/distributions/gamma.rs @@ -0,0 +1,31 @@ +use std::mem::size_of; +use test::Bencher; +use rand; +use rand::distributions::IndependentSample; +use rand::distributions::gamma::Gamma; + +#[bench] +fn bench_gamma_large_shape(b: &mut Bencher) { + let gamma = Gamma::new(10., 1.0); + let mut rng = rand::weak_rng(); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + gamma.ind_sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} + +#[bench] +fn bench_gamma_small_shape(b: &mut Bencher) { + let gamma = Gamma::new(0.1, 1.0); + let mut rng = rand::weak_rng(); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + gamma.ind_sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} diff --git a/benches/distributions/mod.rs b/benches/distributions/mod.rs new file mode 100644 index 00000000000..49f6bd9c06c --- /dev/null +++ b/benches/distributions/mod.rs @@ -0,0 +1,3 @@ +mod exponential; +mod normal; +mod gamma; diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs new file mode 100644 index 00000000000..1c858b19b39 --- /dev/null +++ b/benches/distributions/normal.rs @@ -0,0 +1,18 @@ +use std::mem::size_of; +use test::Bencher; +use rand; +use rand::distributions::Sample; +use rand::distributions::normal::Normal; + +#[bench] +fn rand_normal(b: &mut Bencher) { + let mut rng = rand::weak_rng(); + let mut normal = Normal::new(-2.71828, 3.14159); + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + normal.sample(&mut rng); + } + }); + b.bytes = size_of::() as u64 * ::RAND_BENCH_N; +} diff --git a/benches/rustfmt.toml b/benches/rustfmt.toml deleted file mode 100644 index b64fd7ad0e6..00000000000 --- a/benches/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -max_width = 120 -fn_call_width = 108 diff --git a/clippy.toml b/clippy.toml deleted file mode 100644 index 14793c52048..00000000000 --- a/clippy.toml +++ /dev/null @@ -1,2 +0,0 @@ -# Don't warn about these identifiers when using clippy::doc_markdown. -doc-valid-idents = ["ChaCha", "ChaCha12", "SplitMix64", "ZiB", ".."] diff --git a/examples/monte-carlo.rs b/examples/monte-carlo.rs deleted file mode 100644 index d5b898f17f0..00000000000 --- a/examples/monte-carlo.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2018 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Monte Carlo estimation of Ο€ -//! -//! 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. -use rand::distr::{Distribution, Uniform}; - -fn main() { - let range = Uniform::new(-1.0f64, 1.0).unwrap(); - let mut rng = rand::rng(); - - let total = 1_000_000; - let mut in_circle = 0; - - for _ in 0..total { - let a = range.sample(&mut rng); - let b = range.sample(&mut rng); - if a * a + b * b <= 1.0 { - in_circle += 1; - } - } - - // prints something close to 3.14159... - println!( - "Ο€ is approximately {}", - 4. * (in_circle as f64) / (total as f64) - ); -} diff --git a/examples/monty-hall.rs b/examples/monty-hall.rs deleted file mode 100644 index 5e0d808aa15..00000000000 --- a/examples/monty-hall.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2018 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! ## Monty Hall Problem -//! -//! This is a simulation of the [Monty Hall Problem][]: -//! -//! > Suppose you're on a game show, and you're given the choice of three doors: -//! > Behind one door is a car; behind the others, goats. You pick a door, say -//! > No. 1, and the host, who knows what's behind the doors, opens another -//! > door, say No. 3, which has a goat. He then says to you, "Do you want to -//! > pick door No. 2?" Is it to your advantage to switch your choice? -//! -//! The rather unintuitive answer is that you will have a 2/3 chance of winning -//! if you switch and a 1/3 chance of winning if you don't, so it's better to -//! switch. -//! -//! This program will simulate the game show and with large enough simulation -//! steps it will indeed confirm that it is better to switch. -//! -//! [Monty Hall Problem]: https://en.wikipedia.org/wiki/Monty_Hall_problem - -use rand::RngExt; -use rand::distr::{Distribution, Uniform}; - -struct SimulationResult { - win: bool, - switch: bool, -} - -// Run a single simulation of the Monty Hall problem. -fn simulate(random_door: &Uniform, rng: &mut R) -> SimulationResult { - let car = random_door.sample(rng); - - // This is our initial choice - let mut choice = random_door.sample(rng); - - // The game host opens a door - let open = game_host_open(car, choice, rng); - - // Shall we switch? - let switch = rng.random(); - if switch { - choice = switch_door(choice, open); - } - - SimulationResult { - win: choice == car, - switch, - } -} - -// Returns the door the game host opens given our choice and knowledge of -// where the car is. The game host will never open the door with the car. -fn game_host_open(car: u32, choice: u32, rng: &mut R) -> u32 { - use rand::seq::IndexedRandom; - *free_doors(&[car, choice]).choose(rng).unwrap() -} - -// Returns the door we switch to, given our current choice and -// the open door. There will only be one valid door. -fn switch_door(choice: u32, open: u32) -> u32 { - free_doors(&[choice, open])[0] -} - -fn free_doors(blocked: &[u32]) -> Vec { - (0..3).filter(|x| !blocked.contains(x)).collect() -} - -fn main() { - // The estimation will be more accurate with more simulations - let num_simulations = 10000; - - let mut rng = rand::rng(); - 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); - - println!("Running {} simulations...", num_simulations); - for _ in 0..num_simulations { - let result = simulate(&random_door, &mut rng); - - match (result.win, result.switch) { - (true, true) => switch_wins += 1, - (true, false) => keep_wins += 1, - (false, true) => switch_losses += 1, - (false, false) => keep_losses += 1, - } - } - - let total_switches = switch_wins + switch_losses; - let total_keeps = keep_wins + keep_losses; - - println!( - "Switched door {} times with {} wins and {} losses", - total_switches, switch_wins, switch_losses - ); - - println!( - "Kept our choice {} times with {} wins and {} losses", - total_keeps, keep_wins, keep_losses - ); - - // With a large number of simulations, the values should converge to - // 0.667 and 0.333 respectively. - println!( - "Estimated chance to win if we switch: {}", - switch_wins as f32 / total_switches as f32 - ); - println!( - "Estimated chance to win if we don't: {}", - keep_wins as f32 / total_keeps as f32 - ); -} diff --git a/examples/print-next.rs b/examples/print-next.rs deleted file mode 100644 index 204bfb4b4ad..00000000000 --- a/examples/print-next.rs +++ /dev/null @@ -1,7 +0,0 @@ -use rand::Rng; - -fn main() { - let mut rng = rand::rng(); - println!("Next u32: {0:>#18X} = {0:>20}", rng.next_u32()); - println!("Next u64: {0:>#18X} = {0:>20}", rng.next_u64()); -} diff --git a/examples/rayon-monte-carlo.rs b/examples/rayon-monte-carlo.rs deleted file mode 100644 index f1d1ba47a12..00000000000 --- a/examples/rayon-monte-carlo.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2018 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , 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. - -use chacha20::ChaCha8Rng; -use rand::distr::{Distribution, Uniform}; -use rand_core::SeedableRng; -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::(); - - // 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) - ); -} diff --git a/rand_macros/Cargo.toml b/rand_macros/Cargo.toml new file mode 100644 index 00000000000..143bfb356ab --- /dev/null +++ b/rand_macros/Cargo.toml @@ -0,0 +1,20 @@ +[package] + +name = "rand_macros" +version = "0.1.10" +authors = ["The Rust Project Developers"] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-lang/rand" +documentation = "http://doc.rust-lang.org/rand" +homepage = "https://github.com/rust-lang/rand" +description = """ +`#[derive]`-like functionality for the `rand::Rand` trait. +""" + +[lib] +name = "rand_macros" +plugin = true + +[dev-dependencies] +rand = { path = "..", version = "0.3" } diff --git a/rand_macros/README.md b/rand_macros/README.md new file mode 100644 index 00000000000..7e7ea8b9ca6 --- /dev/null +++ b/rand_macros/README.md @@ -0,0 +1,23 @@ +`#[derive]`-like functionality for the `rand::Rand` trait. + +## Example + +```rust +#![feature(plugin)] + +#![plugin(rand_macros)] + +extern crate rand; + +#[derive_Rand] +struct Foo { + x: u8, + y: isize +} + +#[derive_Rand] +enum Bar { + X(char), + Y(f64) +} +``` diff --git a/rand_macros/src/lib.rs b/rand_macros/src/lib.rs new file mode 100644 index 00000000000..9bf9b0e91e5 --- /dev/null +++ b/rand_macros/src/lib.rs @@ -0,0 +1,183 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_private, plugin_registrar)] + +extern crate syntax; +extern crate syntax_ext; +extern crate rustc_plugin; + +use syntax::ast::{MetaItem, Expr}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax_ext::deriving::generic::*; +use syntax_ext::deriving::generic::ty::*; +use syntax::parse::token; +use syntax::ptr::P; +use rustc_plugin::Registry; + +#[plugin_registrar] +pub fn plugin_registrar(reg: &mut Registry) { + reg.register_syntax_extension(token::intern("derive_Rand"), + base::MultiDecorator(Box::new(expand_deriving_rand))); +} + + + + +pub fn expand_deriving_rand(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) { + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: Path::new(vec!("rand", "Rand")), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "rand", + is_unsafe: false, + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("R", + vec!( Path::new(vec!("rand", "Rng")) ))) + }, + explicit_self: None, + args: vec!( + Ptr(Box::new(Literal(Path::new_local("R"))), + Borrowed(None, ast::MutMutable)) + ), + ret_ty: Self_, + attributes: Vec::new(), + combine_substructure: combine_substructure(Box::new(|a, b, c| { + rand_substructure(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, &item, &mut |i| push(i)) +} + +fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { + let rng = if substr.nonself_args.len() == 1 { + &substr.nonself_args[0] + } else { + cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`") + }; + let rand_ident = vec!( + cx.ident_of("rand"), + cx.ident_of("Rand"), + cx.ident_of("rand") + ); + let rand_call = |cx: &mut ExtCtxt, span| { + cx.expr_call_global(span, + rand_ident.clone(), + vec!(rng.clone())) + }; + + return match *substr.fields { + StaticStruct(_, ref summary) => { + let path = cx.path_ident(trait_span, substr.type_ident); + rand_thing(cx, trait_span, path, summary, rand_call) + } + StaticEnum(_, ref variants) => { + if variants.is_empty() { + cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants"); + // let compilation continue + return cx.expr_usize(trait_span, 0); + } + + let variant_count = cx.expr_usize(trait_span, variants.len()); + + let rand_name = cx.path_all(trait_span, + true, + rand_ident.clone(), + Vec::new(), + Vec::new(), + Vec::new()); + let rand_name = cx.expr_path(rand_name); + + // ::rand::Rand::rand(rng) + let rv_call = cx.expr_call(trait_span, + rand_name, + vec!(rng.clone())); + + // need to specify the usize-ness of the random number + let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize")); + let value_ident = cx.ident_of("__value"); + let let_statement = cx.stmt_let_typed(trait_span, + false, + value_ident, + usize_ty, + rv_call); + + // rand() % variants.len() + let value_ref = cx.expr_ident(trait_span, value_ident); + let rand_variant = cx.expr_binary(trait_span, + ast::BiRem, + value_ref, + variant_count); + + let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| { + let i_expr = cx.expr_usize(v_span, i); + let pat = cx.pat_lit(v_span, i_expr); + + let path = cx.path(v_span, vec![substr.type_ident, ident]); + let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp)); + cx.arm(v_span, vec!( pat ), thing) + }).collect:: >(); + + // _ => {} at the end. Should never occur + arms.push(cx.arm_unreachable(trait_span)); + + let match_expr = cx.expr_match(trait_span, rand_variant, arms); + + let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr)); + cx.expr_block(block) + } + _ => cx.bug("Non-static method in `derive(Rand)`") + }; + + fn rand_thing(cx: &mut ExtCtxt, + trait_span: Span, + ctor_path: ast::Path, + summary: &StaticFields, + mut rand_call: F) + -> P where + F: FnMut(&mut ExtCtxt, Span) -> P, + { + let path = cx.expr_path(ctor_path.clone()); + match *summary { + Unnamed(ref fields) => { + if fields.is_empty() { + path + } else { + let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect(); + cx.expr_call(trait_span, path, exprs) + } + } + Named(ref fields) => { + let rand_fields = fields.iter().map(|&(ident, span)| { + let e = rand_call(cx, span); + cx.field_imm(span, ident, e) + }).collect(); + cx.expr_struct(trait_span, ctor_path, rand_fields) + } + } + } +} diff --git a/rand_macros/tests/rand_macros.rs b/rand_macros/tests/rand_macros.rs new file mode 100644 index 00000000000..8cd3c8b9ae6 --- /dev/null +++ b/rand_macros/tests/rand_macros.rs @@ -0,0 +1,30 @@ +#![allow(dead_code)] +#![feature(plugin, custom_derive)] +#![plugin(rand_macros)] + +extern crate rand; + +use rand::Rng; + +#[derive_Rand] +struct Foo { + x: u8, + y: isize +} + +#[derive_Rand] +enum Bar { + X(char), + Y(f64) +} + +#[test] +fn smoke() { + let mut rng = rand::XorShiftRng::new_unseeded(); + + // check nothing horrible happens internally: + for _ in 0..100 { + let _: Foo = rng.gen(); + let _: Bar = rng.gen(); + } +} diff --git a/src/chacha.rs b/src/chacha.rs new file mode 100644 index 00000000000..1acec5e9bf5 --- /dev/null +++ b/src/chacha.rs @@ -0,0 +1,318 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ChaCha random number generator. + +use std::num::Wrapping as w; +use {Rng, SeedableRng, Rand, w32}; + +const KEY_WORDS : usize = 8; // 8 words for the 256-bit key +const STATE_WORDS : usize = 16; +const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing + +/// A random number generator that uses the ChaCha20 algorithm [1]. +/// +/// The ChaCha algorithm is widely accepted as suitable for +/// cryptographic purposes, but this implementation has not been +/// verified as such. Prefer a generator like `OsRng` that defers to +/// the operating system for cases that need high security. +/// +/// [1]: D. J. Bernstein, [*ChaCha, a variant of +/// Salsa20*](http://cr.yp.to/chacha.html) +#[derive(Copy, Clone, Debug)] +pub struct ChaChaRng { + buffer: [w32; STATE_WORDS], // Internal buffer of output + state: [w32; STATE_WORDS], // Initial state + index: usize, // Index into state +} + +static EMPTY: ChaChaRng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS +}; + + +macro_rules! quarter_round{ + ($a: expr, $b: expr, $c: expr, $d: expr) => {{ + $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); + $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12)); + $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8)); + $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7)); + }} +} + +macro_rules! double_round{ + ($x: expr) => {{ + // Column round + quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]); + quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]); + quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]); + quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]); + // Diagonal round + quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]); + quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]); + quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]); + quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]); + }} +} + +#[inline] +fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { + *output = *input; + + for _ in 0..CHACHA_ROUNDS / 2 { + double_round!(output); + } + + for i in 0..STATE_WORDS { + output[i] = output[i] + input[i]; + } +} + +impl ChaChaRng { + + /// Create an ChaCha random number generator using the default + /// fixed key of 8 zero words. + /// + /// # Examples + /// + /// ```rust + /// use rand::{Rng, ChaChaRng}; + /// + /// let mut ra = ChaChaRng::new_unseeded(); + /// println!("{:?}", ra.next_u32()); + /// println!("{:?}", ra.next_u32()); + /// ``` + /// + /// Since this equivalent to a RNG with a fixed seed, repeated executions + /// of an unseeded RNG will produce the same result. This code sample will + /// consistently produce: + /// + /// - 2917185654 + /// - 2419978656 + pub fn new_unseeded() -> ChaChaRng { + let mut rng = EMPTY; + rng.init(&[0; KEY_WORDS]); + rng + } + + /// Sets the internal 128-bit ChaCha counter to + /// a user-provided value. This permits jumping + /// arbitrarily ahead (or backwards) in the pseudorandom stream. + /// + /// Since the nonce words are used to extend the counter to 128 bits, + /// users wishing to obtain the conventional ChaCha pseudorandom stream + /// associated with a particular nonce can call this function with + /// arguments `0, desired_nonce`. + /// + /// # Examples + /// + /// ```rust + /// use rand::{Rng, ChaChaRng}; + /// + /// let mut ra = ChaChaRng::new_unseeded(); + /// ra.set_counter(0u64, 1234567890u64); + /// println!("{:?}", ra.next_u32()); + /// println!("{:?}", ra.next_u32()); + /// ``` + pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { + self.state[12] = w((counter_low >> 0) as u32); + self.state[13] = w((counter_low >> 32) as u32); + self.state[14] = w((counter_high >> 0) as u32); + self.state[15] = w((counter_high >> 32) as u32); + self.index = STATE_WORDS; // force recomputation + } + + /// Initializes `self.state` with the appropriate key and constants + /// + /// We deviate slightly from the ChaCha specification regarding + /// the nonce, which is used to extend the counter to 128 bits. + /// This is provably as strong as the original cipher, though, + /// since any distinguishing attack on our variant also works + /// against ChaCha with a chosen-nonce. See the XSalsa20 [1] + /// security proof for a more involved example of this. + /// + /// The modified word layout is: + /// ```text + /// constant constant constant constant + /// key key key key + /// key key key key + /// counter counter counter counter + /// ``` + /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 + /// nonce.*](http://cr.yp.to/papers.html#xsalsa) + fn init(&mut self, key: &[u32; KEY_WORDS]) { + self.state[0] = w(0x61707865); + self.state[1] = w(0x3320646E); + self.state[2] = w(0x79622D32); + self.state[3] = w(0x6B206574); + + for i in 0..KEY_WORDS { + self.state[4+i] = w(key[i]); + } + + self.state[12] = w(0); + self.state[13] = w(0); + self.state[14] = w(0); + self.state[15] = w(0); + + self.index = STATE_WORDS; + } + + /// Refill the internal output buffer (`self.buffer`) + fn update(&mut self) { + core(&mut self.buffer, &self.state); + self.index = 0; + // update 128-bit counter + self.state[12] = self.state[12] + w(1); + if self.state[12] != w(0) { return }; + self.state[13] = self.state[13] + w(1); + if self.state[13] != w(0) { return }; + self.state[14] = self.state[14] + w(1); + if self.state[14] != w(0) { return }; + self.state[15] = self.state[15] + w(1); + } +} + +impl Rng for ChaChaRng { + #[inline] + fn next_u32(&mut self) -> u32 { + if self.index == STATE_WORDS { + self.update(); + } + + let value = self.buffer[self.index % STATE_WORDS]; + self.index += 1; + value.0 + } +} + +impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { + + fn reseed(&mut self, seed: &'a [u32]) { + // reset state + self.init(&[0u32; KEY_WORDS]); + // set key in place + let key = &mut self.state[4 .. 4+KEY_WORDS]; + for (k, s) in key.iter_mut().zip(seed.iter()) { + *k = w(*s); + } + } + + /// Create a ChaCha generator from a seed, + /// obtained from a variable-length u32 array. + /// Only up to 8 words are used; if less than 8 + /// words are used, the remaining are set to zero. + fn from_seed(seed: &'a [u32]) -> ChaChaRng { + let mut rng = EMPTY; + rng.reseed(seed); + rng + } +} + +impl Rand for ChaChaRng { + fn rand(other: &mut R) -> ChaChaRng { + let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; + for word in key.iter_mut() { + *word = other.gen(); + } + SeedableRng::from_seed(&key[..]) + } +} + + +#[cfg(test)] +mod test { + use {Rng, SeedableRng}; + use super::ChaChaRng; + + #[test] + fn test_rng_rand_seeded() { + let s = ::test::rng().gen_iter::().take(8).collect::>(); + let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); + let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_seeded() { + let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + let mut rb: ChaChaRng = SeedableRng::from_seed(seed); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_reseed() { + let s = ::test::rng().gen_iter::().take(8).collect::>(); + let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed(&s); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + + #[test] + fn test_rng_true_values() { + // Test vectors 1 and 2 from + // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 + let seed : &[_] = &[0u32; 8]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + + let v = (0..16).map(|_| ra.next_u32()).collect::>(); + assert_eq!(v, + vec!(0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, + 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, + 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, + 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2)); + + let v = (0..16).map(|_| ra.next_u32()).collect::>(); + assert_eq!(v, + vec!(0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, + 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, + 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, + 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b)); + + + let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + + // Store the 17*i-th 32-bit word, + // i.e., the i-th word of the i-th 16-word block + let mut v : Vec = Vec::new(); + for _ in 0..16 { + v.push(ra.next_u32()); + for _ in 0..16 { + ra.next_u32(); + } + } + + assert_eq!(v, + vec!(0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, + 0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384, + 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, + 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4)); + } + + #[test] + fn test_rng_clone() { + let seed : &[_] = &[0u32; 8]; + let mut rng: ChaChaRng = SeedableRng::from_seed(seed); + let mut clone = rng.clone(); + for _ in 0..16 { + assert_eq!(rng.next_u64(), clone.next_u64()); + } + } +} diff --git a/src/distr/bernoulli.rs b/src/distr/bernoulli.rs deleted file mode 100644 index 1fe88f2c7de..00000000000 --- a/src/distr/bernoulli.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Bernoulli distribution `Bernoulli(p)`. - -use crate::distr::Distribution; -use crate::{Rng, RngExt}; -use core::fmt; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// The [Bernoulli distribution](https://en.wikipedia.org/wiki/Bernoulli_distribution) `Bernoulli(p)`. -/// -/// This distribution describes a single boolean random variable, which is true -/// with probability `p` and false with probability `1 - p`. -/// It is a special case of the Binomial distribution with `n = 1`. -/// -/// # Plot -/// -/// The following plot shows the Bernoulli distribution with `p = 0.1`, -/// `p = 0.5`, and `p = 0.9`. -/// -/// ![Bernoulli distribution](https://raw.githubusercontent.com/rust-random/charts/main/charts/bernoulli.svg) -/// -/// # Example -/// -/// ```rust -/// use rand::distr::{Bernoulli, Distribution}; -/// -/// let d = Bernoulli::new(0.3).unwrap(); -/// let v = d.sample(&mut rand::rng()); -/// println!("{} is from a Bernoulli distribution", v); -/// ``` -/// -/// # Precision -/// -/// This `Bernoulli` distribution uses 64 bits from the RNG (a `u64`), -/// so only probabilities that are multiples of 2-64 can be -/// represented. -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Bernoulli { - /// Probability of success, relative to the maximal integer. - p_int: u64, -} - -// To sample from the Bernoulli distribution we use a method that compares a -// random `u64` value `v < (p * 2^64)`. -// -// If `p == 1.0`, the integer `v` to compare against can not represented as a -// `u64`. We manually set it to `u64::MAX` instead (2^64 - 1 instead of 2^64). -// Note that value of `p < 1.0` can never result in `u64::MAX`, because an -// `f64` only has 53 bits of precision, and the next largest value of `p` will -// result in `2^64 - 2048`. -// -// Also there is a 100% theoretical concern: if someone consistently wants to -// generate `true` using the Bernoulli distribution (i.e. by using a probability -// of `1.0`), just using `u64::MAX` is not enough. On average it would return -// false once every 2^64 iterations. Some people apparently care about this -// case. -// -// That is why we special-case `u64::MAX` to always return `true`, without using -// the RNG, and pay the performance price for all uses that *are* reasonable. -// Luckily, if `new()` and `sample` are close, the compiler can optimize out the -// extra check. -const ALWAYS_TRUE: u64 = u64::MAX; - -// This is just `2.0.powi(64)`, but written this way because it is not available -// in `no_std` mode. -const SCALE: f64 = 2.0 * (1u64 << 63) as f64; - -/// Error type returned from [`Bernoulli::new`]. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum BernoulliError { - /// `p < 0` or `p > 1`. - InvalidProbability, -} - -impl fmt::Display for BernoulliError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - BernoulliError::InvalidProbability => "p is outside [0, 1] in Bernoulli distribution", - }) - } -} - -impl core::error::Error for BernoulliError {} - -impl Bernoulli { - /// Construct a new `Bernoulli` with the given probability of success `p`. - /// - /// # Precision - /// - /// For `p = 1.0`, the resulting distribution will always generate true. - /// For `p = 0.0`, the resulting distribution will always generate false. - /// - /// This method is accurate for any input `p` in the range `[0, 1]` which is - /// a multiple of 2-64. (Note that not all multiples of - /// 2-64 in `[0, 1]` can be represented as a `f64`.) - #[inline] - pub fn new(p: f64) -> Result { - if !(0.0..1.0).contains(&p) { - if p == 1.0 { - return Ok(Bernoulli { p_int: ALWAYS_TRUE }); - } - return Err(BernoulliError::InvalidProbability); - } - Ok(Bernoulli { - p_int: (p * SCALE) as u64, - }) - } - - /// Construct a new `Bernoulli` with the probability of success of - /// `numerator`-in-`denominator`. I.e. `new_ratio(2, 3)` will return - /// a `Bernoulli` with a 2-in-3 chance, or about 67%, of returning `true`. - /// - /// return `true`. If `numerator == 0` it will always return `false`. - /// For `numerator > denominator` and `denominator == 0`, this returns an - /// error. Otherwise, for `numerator == denominator`, samples are always - /// true; for `numerator == 0` samples are always false. - #[inline] - pub fn from_ratio(numerator: u32, denominator: u32) -> Result { - if numerator > denominator || denominator == 0 { - return Err(BernoulliError::InvalidProbability); - } - if numerator == denominator { - return Ok(Bernoulli { p_int: ALWAYS_TRUE }); - } - let p_int = ((f64::from(numerator) / f64::from(denominator)) * SCALE) as u64; - Ok(Bernoulli { p_int }) - } - - #[inline] - /// Returns the probability (`p`) of the distribution. - /// - /// This value may differ slightly from the input due to loss of precision. - pub fn p(&self) -> f64 { - if self.p_int == ALWAYS_TRUE { - 1.0 - } else { - (self.p_int as f64) / SCALE - } - } -} - -impl Distribution for Bernoulli { - #[inline] - fn sample(&self, rng: &mut R) -> bool { - // Make sure to always return true for p = 1.0. - if self.p_int == ALWAYS_TRUE { - return true; - } - let v: u64 = rng.random(); - v < self.p_int - } -} - -#[cfg(test)] -mod test { - use super::Bernoulli; - use crate::RngExt; - use crate::distr::Distribution; - - #[test] - #[cfg(feature = "serde")] - fn test_serializing_deserializing_bernoulli() { - let coin_flip = Bernoulli::new(0.5).unwrap(); - let de_coin_flip: Bernoulli = - postcard::from_bytes(&postcard::to_allocvec(&coin_flip).unwrap()).unwrap(); - - assert_eq!(coin_flip.p_int, de_coin_flip.p_int); - } - - #[test] - fn test_trivial() { - // We prefer to be explicit here. - #![allow(clippy::bool_assert_comparison)] - - let mut r = crate::test::rng(1); - let always_false = Bernoulli::new(0.0).unwrap(); - let always_true = Bernoulli::new(1.0).unwrap(); - for _ in 0..5 { - assert_eq!(r.sample::(&always_false), false); - assert_eq!(r.sample::(&always_true), true); - assert_eq!(Distribution::::sample(&always_false, &mut r), false); - assert_eq!(Distribution::::sample(&always_true, &mut r), true); - } - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_average() { - const P: f64 = 0.3; - const NUM: u32 = 3; - const DENOM: u32 = 10; - let d1 = Bernoulli::new(P).unwrap(); - let d2 = Bernoulli::from_ratio(NUM, DENOM).unwrap(); - const N: u32 = 100_000; - - let mut sum1: u32 = 0; - let mut sum2: u32 = 0; - let mut rng = crate::test::rng(2); - for _ in 0..N { - if d1.sample(&mut rng) { - sum1 += 1; - } - if d2.sample(&mut rng) { - sum2 += 1; - } - } - let avg1 = (sum1 as f64) / (N as f64); - assert!((avg1 - P).abs() < 5e-3); - - let avg2 = (sum2 as f64) / (N as f64); - assert!((avg2 - (NUM as f64) / (DENOM as f64)).abs() < 5e-3); - } - - #[test] - fn value_stability() { - let mut rng = crate::test::rng(3); - let distr = Bernoulli::new(0.4532).unwrap(); - let mut buf = [false; 10]; - for x in &mut buf { - *x = rng.sample(distr); - } - assert_eq!( - buf, - [ - 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)); - } -} diff --git a/src/distr/distribution.rs b/src/distr/distribution.rs deleted file mode 100644 index 1a83d5f1586..00000000000 --- a/src/distr/distribution.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Distribution trait and associates - -use crate::Rng; -#[cfg(feature = "alloc")] -use alloc::string::String; -use core::iter; - -#[cfg(doc)] -use crate::RngExt; - -/// Types (distributions) that can be used to create a random instance of `T`. -/// -/// It is possible to sample from a distribution through both the -/// `Distribution` and [`RngExt`] traits, via `distr.sample(&mut rng)` and -/// `rng.sample(distr)`. They also both offer the [`sample_iter`] method, which -/// produces an iterator that samples from the distribution. -/// -/// All implementations are expected to be immutable; this has the significant -/// advantage of not needing to consider thread safety, and for most -/// distributions efficient state-less sampling algorithms are available. -/// -/// Implementations are typically expected to be portable with reproducible -/// results when used with a PRNG with fixed seed; see the -/// [portability chapter](https://rust-random.github.io/book/portability.html) -/// of The Rust Rand Book. In some cases this does not apply, e.g. the `usize` -/// type requires different sampling on 32-bit and 64-bit machines. -/// -/// [`sample_iter`]: Distribution::sample_iter -pub trait Distribution { - /// Generate a random value of `T`, using `rng` as the source of randomness. - fn sample(&self, rng: &mut R) -> T; - - /// Create an iterator that generates random values of `T`, using `rng` as - /// the source of randomness. - /// - /// Note that this function takes `self` by value. This works since - /// `Distribution` is impl'd for `&D` where `D: Distribution`, - /// however borrowing is not automatic hence `distr.sample_iter(...)` may - /// need to be replaced with `(&distr).sample_iter(...)` to borrow or - /// `(&*distr).sample_iter(...)` to reborrow an existing reference. - /// - /// # Example - /// - /// ``` - /// use rand::distr::{Distribution, Alphanumeric, Uniform, StandardUniform}; - /// - /// let mut rng = rand::rng(); - /// - /// // Vec of 16 x f32: - /// let v: Vec = StandardUniform.sample_iter(&mut rng).take(16).collect(); - /// - /// // String: - /// let s: String = Alphanumeric - /// .sample_iter(&mut rng) - /// .take(7) - /// .map(char::from) - /// .collect(); - /// - /// // Dice-rolling: - /// 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!"); - /// } - /// ``` - fn sample_iter(self, rng: R) -> Iter - where - R: Rng, - Self: Sized, - { - Iter { - distr: self, - rng, - phantom: core::marker::PhantomData, - } - } - - /// Map sampled values to type `S` - /// - /// # Example - /// - /// ``` - /// use rand::distr::{Distribution, Uniform}; - /// - /// let die = Uniform::new_inclusive(1, 6).unwrap(); - /// let even_number = die.map(|num| num % 2 == 0); - /// while !even_number.sample(&mut rand::rng()) { - /// println!("Still odd; rolling again!"); - /// } - /// ``` - fn map(self, func: F) -> Map - where - F: Fn(T) -> S, - Self: Sized, - { - Map { - distr: self, - func, - phantom: core::marker::PhantomData, - } - } -} - -impl + ?Sized> Distribution for &D { - fn sample(&self, rng: &mut R) -> T { - (*self).sample(rng) - } -} - -/// An iterator over a [`Distribution`] -/// -/// This iterator yields random values of type `T` with distribution `D` -/// from a random generator of type `R`. -/// -/// Construct this `struct` using [`Distribution::sample_iter`] or -/// [`RngExt::sample_iter`]. It is also used by [`RngExt::random_iter`] and -/// [`crate::random_iter`]. -#[derive(Debug)] -pub struct Iter { - distr: D, - rng: R, - phantom: core::marker::PhantomData, -} - -impl Iterator for Iter -where - D: Distribution, - R: Rng, -{ - type Item = T; - - #[inline(always)] - fn next(&mut self) -> Option { - // Here, self.rng may be a reference, but we must take &mut anyway. - // Even if sample could take an R: Rng by value, we would need to do this - // since Rng is not copyable and we cannot enforce that this is "reborrowable". - Some(self.distr.sample(&mut self.rng)) - } - - fn size_hint(&self) -> (usize, Option) { - (usize::MAX, None) - } -} - -impl iter::FusedIterator for Iter -where - D: Distribution, - R: Rng, -{ -} - -/// A [`Distribution`] which maps sampled values to type `S` -/// -/// This `struct` is created by the [`Distribution::map`] method. -/// See its documentation for more. -#[derive(Debug)] -pub struct Map { - distr: D, - func: F, - phantom: core::marker::PhantomData S>, -} - -impl Distribution for Map -where - D: Distribution, - F: Fn(T) -> S, -{ - fn sample(&self, rng: &mut R) -> S { - (self.func)(self.distr.sample(rng)) - } -} - -/// Sample or extend a [`String`] -/// -/// Helper methods to extend a [`String`] or sample a new [`String`]. -#[cfg(feature = "alloc")] -pub trait SampleString { - /// Append `len` random chars to `string` - /// - /// Note: implementations may leave `string` with excess capacity. If this - /// is undesirable, consider calling [`String::shrink_to_fit`] after this - /// method. - fn append_string(&self, rng: &mut R, string: &mut String, len: usize); - - /// Generate a [`String`] of `len` random chars - /// - /// Note: implementations may leave the string with excess capacity. If this - /// is undesirable, consider calling [`String::shrink_to_fit`] after this - /// method. - #[inline] - fn sample_string(&self, rng: &mut R, len: usize) -> String { - let mut s = String::new(); - self.append_string(rng, &mut s, len); - s - } -} - -#[cfg(test)] -mod tests { - use crate::Rng; - use crate::distr::{Distribution, Uniform}; - - #[test] - fn test_distributions_iter() { - use crate::distr::Open01; - let mut rng = crate::test::rng(210); - let distr = Open01; - let mut iter = Distribution::::sample_iter(distr, &mut rng); - let mut sum: f32 = 0.; - for _ in 0..100 { - sum += iter.next().unwrap(); - } - assert!(0. < sum && sum < 100.); - } - - #[test] - fn test_distributions_map() { - 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); - assert!((15..=20).contains(&val)); - } - - #[test] - fn test_make_an_iter() { - fn ten_dice_rolls_other_than_five(rng: &mut R) -> impl Iterator + '_ { - Uniform::new_inclusive(1, 6) - .unwrap() - .sample_iter(rng) - .filter(|x| *x != 5) - .take(10) - } - - let mut rng = crate::test::rng(211); - let mut count = 0; - for val in ten_dice_rolls_other_than_five(&mut rng) { - assert!((1..=6).contains(&val) && val != 5); - count += 1; - } - assert_eq!(count, 10); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_dist_string() { - use crate::distr::{Alphabetic, Alphanumeric, SampleString, StandardUniform}; - use core::str; - let mut rng = crate::test::rng(213); - - let s1 = Alphanumeric.sample_string(&mut rng, 20); - assert_eq!(s1.len(), 20); - assert_eq!(str::from_utf8(s1.as_bytes()), Ok(s1.as_str())); - - let s2 = StandardUniform.sample_string(&mut rng, 20); - assert_eq!(s2.chars().count(), 20); - assert_eq!(str::from_utf8(s2.as_bytes()), Ok(s2.as_str())); - - let s3 = Alphabetic.sample_string(&mut rng, 20); - assert_eq!(s3.len(), 20); - assert_eq!(str::from_utf8(s3.as_bytes()), Ok(s3.as_str())); - } -} diff --git a/src/distr/float.rs b/src/distr/float.rs deleted file mode 100644 index bf952b266b0..00000000000 --- a/src/distr/float.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Basic floating-point number distributions - -use crate::distr::utils::{FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; -use crate::distr::{Distribution, StandardUniform}; -use crate::{Rng, RngExt}; -use core::mem; -#[cfg(feature = "simd_support")] -use core::simd::prelude::*; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// A distribution to sample floating point numbers uniformly in the half-open -/// interval `(0, 1]`, i.e. including 1 but not 0. -/// -/// All values that can be generated are of the form `n * Ξ΅/2`. For `f32` -/// the 24 most significant random bits of a `u32` are used and for `f64` the -/// 53 most significant bits of a `u64` are used. The conversion uses the -/// multiplicative method. -/// -/// See also: [`StandardUniform`] which samples from `[0, 1)`, [`Open01`] -/// which samples from `(0, 1)` and [`Uniform`] which samples from arbitrary -/// ranges. -/// -/// # Example -/// ``` -/// use rand::RngExt; -/// use rand::distr::OpenClosed01; -/// -/// let val: f32 = rand::rng().sample(OpenClosed01); -/// println!("f32 from (0, 1): {}", val); -/// ``` -/// -/// [`StandardUniform`]: crate::distr::StandardUniform -/// [`Open01`]: crate::distr::Open01 -/// [`Uniform`]: crate::distr::uniform::Uniform -#[derive(Clone, Copy, Debug, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct OpenClosed01; - -/// A distribution to sample floating point numbers uniformly in the open -/// interval `(0, 1)`, i.e. not including either endpoint. -/// -/// All values that can be generated are of the form `n * Ξ΅ + Ξ΅/2`. For `f32` -/// the 23 most significant random bits of an `u32` are used, for `f64` 52 from -/// an `u64`. The conversion uses a transmute-based method. -/// -/// See also: [`StandardUniform`] which samples from `[0, 1)`, [`OpenClosed01`] -/// which samples from `(0, 1]` and [`Uniform`] which samples from arbitrary -/// ranges. -/// -/// # Example -/// ``` -/// use rand::RngExt; -/// use rand::distr::Open01; -/// -/// let val: f32 = rand::rng().sample(Open01); -/// println!("f32 from (0, 1): {}", val); -/// ``` -/// -/// [`StandardUniform`]: crate::distr::StandardUniform -/// [`OpenClosed01`]: crate::distr::OpenClosed01 -/// [`Uniform`]: crate::distr::uniform::Uniform -#[derive(Clone, Copy, Debug, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Open01; - -// This trait is needed by both this lib and rand_distr hence is a hidden export -#[doc(hidden)] -pub trait IntoFloat { - type F; - - /// Helper method to combine the fraction and a constant exponent into a - /// float. - /// - /// Only the least significant bits of `self` may be set, 23 for `f32` and - /// 52 for `f64`. - /// The resulting value will fall in a range that depends on the exponent. - /// As an example the range with exponent 0 will be - /// [20..21), which is [1..2). - fn into_float_with_exponent(self, exponent: i32) -> Self::F; -} - -macro_rules! float_impls { - ($($meta:meta)?, $ty:ident, $uty:ident, $f_scalar:ident, $u_scalar:ty, - $fraction_bits:expr, $exponent_bias:expr) => { - $(#[cfg($meta)])? - impl IntoFloat for $uty { - type F = $ty; - #[inline(always)] - fn into_float_with_exponent(self, exponent: i32) -> $ty { - // 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 | $uty::splat(exponent_bits)) - } - } - - $(#[cfg($meta)])? - impl Distribution<$ty> for StandardUniform { - fn sample(&self, rng: &mut R) -> $ty { - // 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 $u_scalar * 8; - let precision = $fraction_bits + 1; - let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar); - - let value: $uty = rng.random(); - let value = value >> $uty::splat(float_size - precision); - $ty::splat(scale) * $ty::cast_from_int(value) - } - } - - $(#[cfg($meta)])? - impl Distribution<$ty> for OpenClosed01 { - fn sample(&self, rng: &mut R) -> $ty { - // 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 $u_scalar * 8; - let precision = $fraction_bits + 1; - let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar); - - let value: $uty = rng.random(); - let value = value >> $uty::splat(float_size - precision); - // Add 1 to shift up; will not overflow because of right-shift: - $ty::splat(scale) * $ty::cast_from_int(value + $uty::splat(1)) - } - } - - $(#[cfg($meta)])? - impl Distribution<$ty> for Open01 { - fn sample(&self, rng: &mut R) -> $ty { - // Transmute-based method; 23/52 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 $u_scalar * 8; - - let value: $uty = rng.random(); - let fraction = value >> $uty::splat(float_size - $fraction_bits); - fraction.into_float_with_exponent(0) - $ty::splat(1.0 - $f_scalar::EPSILON / 2.0) - } - } - } -} - -float_impls! { , f32, u32, f32, u32, 23, 127 } -float_impls! { , f64, u64, f64, u64, 52, 1023 } - -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f32x2, u32x2, f32, u32, 23, 127 } -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f32x4, u32x4, f32, u32, 23, 127 } -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f32x8, u32x8, f32, u32, 23, 127 } -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f32x16, u32x16, f32, u32, 23, 127 } - -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f64x2, u64x2, f64, u64, 52, 1023 } -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f64x4, u64x4, f64, u64, 52, 1023 } -#[cfg(feature = "simd_support")] -float_impls! { feature = "simd_support", f64x8, u64x8, f64, u64, 52, 1023 } - -#[cfg(test)] -mod tests { - use super::*; - use crate::test::const_rng; - - const EPSILON32: f32 = f32::EPSILON; - const EPSILON64: f64 = f64::EPSILON; - - macro_rules! test_f32 { - ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { - #[test] - fn $fnn() { - let two = $ty::splat(2.0); - - // StandardUniform - let mut zeros = const_rng(0); - assert_eq!(zeros.random::<$ty>(), $ZERO); - let mut one = const_rng(1 << 8 | 1 << (8 + 32)); - assert_eq!(one.random::<$ty>(), $EPSILON / two); - let mut max = const_rng(!0); - assert_eq!(max.random::<$ty>(), $ty::splat(1.0) - $EPSILON / two); - - // OpenClosed01 - let mut zeros = const_rng(0); - assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), $ZERO + $EPSILON / two); - let mut one = const_rng(1 << 8 | 1 << (8 + 32)); - assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); - let mut max = const_rng(!0); - assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + $ty::splat(1.0)); - - // Open01 - let mut zeros = const_rng(0); - assert_eq!(zeros.sample::<$ty, _>(Open01), $ZERO + $EPSILON / two); - let mut one = const_rng(1 << 9 | 1 << (9 + 32)); - assert_eq!( - one.sample::<$ty, _>(Open01), - $EPSILON / two * $ty::splat(3.0) - ); - let mut max = const_rng(!0); - assert_eq!( - max.sample::<$ty, _>(Open01), - $ty::splat(1.0) - $EPSILON / two - ); - } - }; - } - test_f32! { f32_edge_cases, f32, 0.0, EPSILON32 } - #[cfg(feature = "simd_support")] - test_f32! { f32x2_edge_cases, f32x2, f32x2::splat(0.0), f32x2::splat(EPSILON32) } - #[cfg(feature = "simd_support")] - test_f32! { f32x4_edge_cases, f32x4, f32x4::splat(0.0), f32x4::splat(EPSILON32) } - #[cfg(feature = "simd_support")] - test_f32! { f32x8_edge_cases, f32x8, f32x8::splat(0.0), f32x8::splat(EPSILON32) } - #[cfg(feature = "simd_support")] - test_f32! { f32x16_edge_cases, f32x16, f32x16::splat(0.0), f32x16::splat(EPSILON32) } - - macro_rules! test_f64 { - ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => { - #[test] - fn $fnn() { - let two = $ty::splat(2.0); - - // StandardUniform - let mut zeros = const_rng(0); - assert_eq!(zeros.random::<$ty>(), $ZERO); - let mut one = const_rng(1 << 11); - assert_eq!(one.random::<$ty>(), $EPSILON / two); - let mut max = const_rng(!0); - assert_eq!(max.random::<$ty>(), $ty::splat(1.0) - $EPSILON / two); - - // OpenClosed01 - let mut zeros = const_rng(0); - assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), $ZERO + $EPSILON / two); - let mut one = const_rng(1 << 11); - assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON); - let mut max = const_rng(!0); - assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + $ty::splat(1.0)); - - // Open01 - let mut zeros = const_rng(0); - assert_eq!(zeros.sample::<$ty, _>(Open01), $ZERO + $EPSILON / two); - let mut one = const_rng(1 << 12); - assert_eq!( - one.sample::<$ty, _>(Open01), - $EPSILON / two * $ty::splat(3.0) - ); - let mut max = const_rng(!0); - assert_eq!( - max.sample::<$ty, _>(Open01), - $ty::splat(1.0) - $EPSILON / two - ); - } - }; - } - test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 } - #[cfg(feature = "simd_support")] - test_f64! { f64x2_edge_cases, f64x2, f64x2::splat(0.0), f64x2::splat(EPSILON64) } - #[cfg(feature = "simd_support")] - test_f64! { f64x4_edge_cases, f64x4, f64x4::splat(0.0), f64x4::splat(EPSILON64) } - #[cfg(feature = "simd_support")] - test_f64! { f64x8_edge_cases, f64x8, f64x8::splat(0.0), f64x8::splat(EPSILON64) } - - #[test] - fn value_stability() { - fn test_samples>( - distr: &D, - zero: T, - expected: &[T], - ) { - let mut rng = crate::test::rng(0x6f44f5646c2a7334); - let mut buf = [zero; 3]; - for x in &mut buf { - *x = rng.sample(distr); - } - assert_eq!(&buf, expected); - } - - test_samples( - &StandardUniform, - 0f32, - &[0.0035963655, 0.7346052, 0.09778172], - ); - test_samples( - &StandardUniform, - 0f64, - &[0.7346051961657583, 0.20298547462974248, 0.8166436635290655], - ); - - test_samples(&OpenClosed01, 0f32, &[0.003596425, 0.73460525, 0.09778178]); - test_samples( - &OpenClosed01, - 0f64, - &[0.7346051961657584, 0.2029854746297426, 0.8166436635290656], - ); - - test_samples(&Open01, 0f32, &[0.0035963655, 0.73460525, 0.09778172]); - test_samples( - &Open01, - 0f64, - &[0.7346051961657584, 0.20298547462974248, 0.8166436635290656], - ); - - #[cfg(feature = "simd_support")] - { - // We only test a sub-set of types here. Values are identical to - // non-SIMD types; we assume this pattern continues across all - // SIMD types. - - test_samples( - &StandardUniform, - 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( - &StandardUniform, - f64x2::from([0.0, 0.0]), - &[ - f64x2::from([0.7346051961657583, 0.20298547462974248]), - f64x2::from([0.8166436635290655, 0.7423708925400552]), - f64x2::from([0.16387782224016323, 0.9087068770169618]), - ], - ); - } - } -} diff --git a/src/distr/integer.rs b/src/distr/integer.rs deleted file mode 100644 index 1d83241513a..00000000000 --- a/src/distr/integer.rs +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of the `StandardUniform` distribution for integer types. - -use crate::distr::{Distribution, StandardUniform}; -use crate::{Rng, RngExt}; -#[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::{ - NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32, - NonZeroU64, NonZeroU128, -}; -#[cfg(feature = "simd_support")] -use core::simd::*; - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> u64 { - rng.next_u64() - } -} - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> u128 { - // Use LE; we explicitly generate one value before the next. - let x = u128::from(rng.next_u64()); - let y = u128::from(rng.next_u64()); - (y << 64) | x - } -} - -macro_rules! impl_int_from_uint { - ($ty:ty, $uty:ty) => { - impl Distribution<$ty> for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> $ty { - rng.random::<$uty>() as $ty - } - } - }; -} - -impl_int_from_uint! { i8, u8 } -impl_int_from_uint! { i16, u16 } -impl_int_from_uint! { i32, u32 } -impl_int_from_uint! { i64, u64 } -impl_int_from_uint! { i128, u128 } - -macro_rules! impl_nzint { - ($ty:ty, $new:path) => { - impl Distribution<$ty> for StandardUniform { - fn sample(&self, rng: &mut R) -> $ty { - loop { - if let Some(nz) = $new(rng.random()) { - break nz; - } - } - } - } - }; -} - -impl_nzint!(NonZeroU8, NonZeroU8::new); -impl_nzint!(NonZeroU16, NonZeroU16::new); -impl_nzint!(NonZeroU32, NonZeroU32::new); -impl_nzint!(NonZeroU64, NonZeroU64::new); -impl_nzint!(NonZeroU128, NonZeroU128::new); - -impl_nzint!(NonZeroI8, NonZeroI8::new); -impl_nzint!(NonZeroI16, NonZeroI16::new); -impl_nzint!(NonZeroI32, NonZeroI32::new); -impl_nzint!(NonZeroI64, NonZeroI64::new); -impl_nzint!(NonZeroI128, NonZeroI128::new); - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl Distribution<__m128i> for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> __m128i { - // NOTE: It's tempting to use the u128 impl here, but confusingly this - // results in different code (return via rdx, r10 instead of rax, rdx - // with u128 impl) and is much slower (+130 time). - - let mut buf = [0_u8; core::mem::size_of::<__m128i>()]; - rng.fill_bytes(&mut buf); - // x86 is little endian so no need for conversion - - // SAFETY: All byte sequences of `buf` represent values of the output type. - unsafe { core::mem::transmute(buf) } - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl Distribution<__m256i> for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> __m256i { - let mut buf = [0_u8; core::mem::size_of::<__m256i>()]; - rng.fill_bytes(&mut buf); - // x86 is little endian so no need for conversion - - // SAFETY: All byte sequences of `buf` represent values of the output type. - unsafe { core::mem::transmute(buf) } - } -} - -#[cfg(all( - any(target_arch = "x86", target_arch = "x86_64"), - feature = "simd_support" -))] -impl Distribution<__m512i> for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> __m512i { - let mut buf = [0_u8; core::mem::size_of::<__m512i>()]; - rng.fill_bytes(&mut buf); - // x86 is little endian so no need for conversion - - // SAFETY: All byte sequences of `buf` represent values of the output type. - unsafe { core::mem::transmute(buf) } - } -} - -#[cfg(feature = "simd_support")] -macro_rules! simd_impl { - ($($ty:ty),+) => {$( - /// Requires nightly Rust and the [`simd_support`] feature - /// - /// [`simd_support`]: https://github.com/rust-random/rand#crate-features - #[cfg(feature = "simd_support")] - impl Distribution> for StandardUniform { - #[inline] - fn sample(&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!(u8, i8, u16, i16, u32, i32, u64, i64); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_integers() { - let mut rng = crate::test::rng(806); - - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - } - - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - #[test] - fn x86_integers() { - let mut rng = crate::test::rng(807); - - rng.sample::<__m128i, _>(StandardUniform); - rng.sample::<__m256i, _>(StandardUniform); - #[cfg(feature = "simd_support")] - rng.sample::<__m512i, _>(StandardUniform); - } - - #[test] - fn value_stability() { - fn test_samples(zero: T, expected: &[T]) - where - StandardUniform: Distribution, - { - let mut rng = crate::test::rng(807); - let mut buf = [zero; 3]; - for x in &mut buf { - *x = rng.sample(StandardUniform); - } - assert_eq!(&buf, expected); - } - - test_samples(0u8, &[9, 247, 111]); - test_samples(0u16, &[32265, 42999, 38255]); - test_samples(0u32, &[2220326409, 2575017975, 2018088303]); - test_samples( - 0u64, - &[ - 11059617991457472009, - 16096616328739788143, - 1487364411147516184, - ], - ); - test_samples( - 0u128, - &[ - 296930161868957086625409848350820761097, - 145644820879247630242265036535529306392, - 111087889832015897993126088499035356354, - ], - ); - - test_samples(0i8, &[9, -9, 111]); - // Skip further i* types: they are simple reinterpretation of u* samples - - #[cfg(feature = "simd_support")] - { - // We only test a sub-set of types here and make assumptions about the rest. - - test_samples( - u8x4::default(), - &[ - u8x4::from([9, 126, 87, 132]), - u8x4::from([247, 167, 123, 153]), - u8x4::from([111, 149, 73, 120]), - ], - ); - test_samples( - u8x8::default(), - &[ - 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::from([ - -7387126082252079607, - -2350127744969763473, - 1487364411147516184, - 7895421560427121838, - 602190064936008898, - 6022086574635100741, - -5080089175222015595, - -4066367846667249123, - ]), - i64x8::from([ - 9180885022207963908, - 3095981199532211089, - 6586075293021332726, - 419343203796414657, - 3186951873057035255, - 5287129228749947252, - 444726432079249540, - -1587028029513790706, - ]), - i64x8::from([ - 6075236523189346388, - 1351763722368165432, - -6192309979959753740, - -7697775502176768592, - -4482022114172078123, - 7522501477800909500, - -1837258847956201231, - -586926753024886735, - ]), - ], - ); - } - } -} diff --git a/src/distr/mod.rs b/src/distr/mod.rs deleted file mode 100644 index 70ecf2410a2..00000000000 --- a/src/distr/mod.rs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Generating random samples from probability distributions -//! -//! This module is the home of the [`Distribution`] trait and several of its -//! implementations. It is the workhorse behind some of the convenient -//! functionality of the [`RngExt`] trait, e.g. [`RngExt::random`] and of course -//! [`RngExt::sample`]. -//! -//! Abstractly, a [probability distribution] describes the probability of -//! occurrence of each value in its sample space. -//! -//! More concretely, an implementation of `Distribution` for type `X` is an -//! algorithm for choosing values from the sample space (a subset of `T`) -//! according to the distribution `X` represents, using an external source of -//! randomness (an RNG supplied to the `sample` function). -//! -//! A type `X` may implement `Distribution` for multiple types `T`. -//! Any type implementing [`Distribution`] is stateless (i.e. immutable), -//! but it may have internal parameters set at construction time (for example, -//! [`Uniform`] allows specification of its sample space as a range within `T`). -//! -//! -//! # The Standard Uniform distribution -//! -//! The [`StandardUniform`] distribution is important to mention. This is the -//! distribution used by [`RngExt::random`] and represents the "default" way to -//! produce a random value for many different types, including most primitive -//! types, tuples, arrays, and a few derived types. See the documentation of -//! [`StandardUniform`] for more details. -//! -//! Implementing [`Distribution`] for [`StandardUniform`] for user types `T` makes it -//! possible to generate type `T` with [`RngExt::random`], and by extension also -//! with the [`random`] function. -//! -//! ## Other standard uniform distributions -//! -//! [`Alphanumeric`] is a simple distribution to sample random letters and -//! numbers of the `char` type; in contrast [`StandardUniform`] may sample any valid -//! `char`. -//! -//! There's also an [`Alphabetic`] distribution which acts similarly to [`Alphanumeric`] but -//! doesn't include digits. -//! -//! For floats (`f32`, `f64`), [`StandardUniform`] samples from `[0, 1)`. Also -//! provided are [`Open01`] (samples from `(0, 1)`) and [`OpenClosed01`] -//! (samples from `(0, 1]`). No option is provided to sample from `[0, 1]`; it -//! is suggested to use one of the above half-open ranges since the failure to -//! sample a value which would have a low chance of being sampled anyway is -//! rarely an issue in practice. -//! -//! # Parameterized Uniform distributions -//! -//! The [`Uniform`] distribution provides uniform sampling over a specified -//! range on a subset of the types supported by the above distributions. -//! -//! Implementations support single-value-sampling via -//! [`Rng::random_range(Range)`](RngExt::random_range). -//! Where a fixed (non-`const`) range will be sampled many times, it is likely -//! faster to pre-construct a [`Distribution`] object using -//! [`Uniform::new`], [`Uniform::new_inclusive`] or `From`. -//! -//! # Non-uniform sampling -//! -//! Sampling a simple true/false outcome with a given probability has a name: -//! the [`Bernoulli`] distribution (this is used by [`RngExt::random_bool`]). -//! -//! For weighted sampling of discrete values see the [`weighted`] module. -//! -//! This crate no longer includes other non-uniform distributions; instead -//! it is recommended that you use either [`rand_distr`] or [`statrs`]. -//! -//! -//! [probability distribution]: https://en.wikipedia.org/wiki/Probability_distribution -//! [`rand_distr`]: https://crates.io/crates/rand_distr -//! [`statrs`]: https://crates.io/crates/statrs - -//! [`random`]: crate::random -//! [`rand_distr`]: https://crates.io/crates/rand_distr -//! [`statrs`]: https://crates.io/crates/statrs - -mod bernoulli; -mod distribution; -mod float; -mod integer; -mod other; -mod utils; - -#[doc(hidden)] -pub mod hidden_export { - pub use super::float::IntoFloat; // used by rand_distr -} -pub mod slice; -pub mod uniform; -#[cfg(feature = "alloc")] -pub mod weighted; - -pub use self::bernoulli::{Bernoulli, BernoulliError}; -#[cfg(feature = "alloc")] -pub use self::distribution::SampleString; -pub use self::distribution::{Distribution, Iter, Map}; -pub use self::float::{Open01, OpenClosed01}; -pub use self::other::{Alphabetic, Alphanumeric}; -#[doc(inline)] -pub use self::uniform::Uniform; - -#[allow(unused)] -use crate::RngExt; - -/// The Standard Uniform distribution -/// -/// This [`Distribution`] is the *standard* parameterization of [`Uniform`]. Bounds -/// are selected according to the output type. -/// -/// Assuming the provided `Rng` is well-behaved, these implementations -/// generate values with the following ranges and distributions: -/// -/// * Integers (`i8`, `i32`, `u64`, etc.) are uniformly distributed -/// over the whole range of the type (thus each possible value may be sampled -/// with equal probability). -/// * `char` is 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. -/// For some uses, the [`Alphanumeric`] or [`Alphabetic`] distribution will be more -/// appropriate. -/// * `bool` samples `false` or `true`, each with probability 0.5. -/// * Floating point types (`f32` and `f64`) are uniformly distributed in the -/// half-open range `[0, 1)`. See also the [notes below](#floating-point-implementation). -/// * Wrapping integers ([`Wrapping`]), besides the type identical to their -/// normal integer variants. -/// * Non-zero integers ([`NonZeroU8`]), which are like their normal integer -/// variants but cannot sample zero. -/// -/// The `StandardUniform` distribution also supports generation of the following -/// compound types where all component types are supported: -/// -/// * Tuples (up to 12 elements): each element is sampled sequentially and -/// independently (thus, assuming a well-behaved RNG, there is no correlation -/// between elements). -/// * Arrays `[T; n]` where `T` is supported. Each element is sampled -/// sequentially and independently. Note that for small `T` this usually -/// results in the RNG discarding random bits; see also [`RngExt::fill`] which -/// offers a more efficient approach to filling an array of integer types -/// with random data. -/// * SIMD types (requires [`simd_support`] feature) like x86's [`__m128i`] -/// and `std::simd`'s [`u32x4`], [`f32x4`] and [`mask32x4`] types are -/// effectively arrays of integer or floating-point types. Each lane is -/// sampled independently, potentially with more efficient random-bit-usage -/// (and a different resulting value) than would be achieved with sequential -/// sampling (as with the array types above). -/// -/// ## Custom implementations -/// -/// The [`StandardUniform`] distribution may be implemented for user types as follows: -/// -/// ``` -/// # #![allow(dead_code)] -/// use rand::{Rng, RngExt}; -/// use rand::distr::{Distribution, StandardUniform}; -/// -/// struct MyF32 { -/// x: f32, -/// } -/// -/// impl Distribution for StandardUniform { -/// fn sample(&self, rng: &mut R) -> MyF32 { -/// MyF32 { x: rng.random() } -/// } -/// } -/// ``` -/// -/// ## Example usage -/// ``` -/// use rand::prelude::*; -/// use rand::distr::StandardUniform; -/// -/// let val: f32 = rand::rng().sample(StandardUniform); -/// println!("f32 from [0, 1): {}", val); -/// ``` -/// -/// # Floating point implementation -/// The floating point implementations for `StandardUniform` generate a random value in -/// the half-open interval `[0, 1)`, i.e. including 0 but not 1. -/// -/// All values that can be generated are of the form `n * Ξ΅/2`. For `f32` -/// the 24 most significant random bits of a `u32` are used and for `f64` the -/// 53 most significant bits of a `u64` are used. The conversion uses the -/// multiplicative method: `(rng.gen::<$uty>() >> N) as $ty * (Ξ΅/2)`. -/// -/// See also: [`Open01`] which samples from `(0, 1)`, [`OpenClosed01`] which -/// samples from `(0, 1]` and `Rng::random_range(0..1)` which also samples from -/// `[0, 1)`. Note that `Open01` uses transmute-based methods which yield 1 bit -/// less precision but may perform faster on some architectures (on modern Intel -/// CPUs all methods have approximately equal performance). -/// -/// [`Uniform`]: uniform::Uniform -/// [`Wrapping`]: 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, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct StandardUniform; diff --git a/src/distr/other.rs b/src/distr/other.rs deleted file mode 100644 index d752552f513..00000000000 --- a/src/distr/other.rs +++ /dev/null @@ -1,443 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of the `StandardUniform` distribution for other built-in types. - -#[cfg(feature = "alloc")] -use alloc::string::String; -use core::array; -use core::char; -use core::num::Wrapping; - -#[cfg(feature = "alloc")] -use crate::distr::SampleString; -use crate::distr::{Distribution, StandardUniform, Uniform}; -use crate::{Rng, RngExt}; - -#[cfg(feature = "simd_support")] -use core::simd::MaskElement; -#[cfg(feature = "simd_support")] -use core::simd::prelude::*; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -// ----- Sampling distributions ----- - -/// Sample a `u8`, uniformly distributed over ASCII letters and numbers: -/// a-z, A-Z and 0-9. -/// -/// # Example -/// -/// ``` -/// use rand::RngExt; -/// use rand::distr::Alphanumeric; -/// -/// let mut rng = rand::rng(); -/// let chars: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); -/// println!("Random chars: {}", chars); -/// ``` -/// -/// The [`SampleString`] trait provides an easier method of generating -/// a random [`String`], and offers more efficient allocation: -/// ``` -/// use rand::distr::{Alphanumeric, SampleString}; -/// let string = Alphanumeric.sample_string(&mut rand::rng(), 16); -/// println!("Random string: {}", string); -/// ``` -/// -/// # Passwords -/// -/// Users sometimes ask whether it is safe to use a string of random characters -/// as a password. In principle, all RNGs in Rand implementing `CryptoRng` are -/// suitable as a source of randomness for generating passwords (if they are -/// properly seeded), but it is more conservative to only use randomness -/// directly from the operating system via the `getrandom` crate, or the -/// corresponding bindings of a crypto library. -/// -/// When generating passwords or keys, it is important to consider the threat -/// model and in some cases the memorability of the password. This is out of -/// scope of the Rand project, and therefore we defer to the following -/// references: -/// -/// - [Wikipedia article on Password Strength](https://en.wikipedia.org/wiki/Password_strength) -/// - [Diceware for generating memorable passwords](https://en.wikipedia.org/wiki/Diceware) -#[derive(Debug, Clone, Copy, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Alphanumeric; - -/// Sample a [`u8`], uniformly distributed over letters: -/// a-z and A-Z. -/// -/// # Example -/// -/// You're able to generate random Alphabetic characters via mapping or via the -/// [`SampleString::sample_string`] method like so: -/// -/// ``` -/// use rand::RngExt; -/// use rand::distr::{Alphabetic, SampleString}; -/// -/// // Manual mapping -/// let mut rng = rand::rng(); -/// let chars: String = (0..7).map(|_| rng.sample(Alphabetic) as char).collect(); -/// println!("Random chars: {}", chars); -/// -/// // Using [`SampleString::sample_string`] -/// let string = Alphabetic.sample_string(&mut rand::rng(), 16); -/// println!("Random string: {}", string); -/// ``` -/// -/// # Passwords -/// -/// Refer to [`Alphanumeric#Passwords`]. -#[derive(Debug, Clone, Copy, Default)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Alphabetic; - -// ----- Implementations of distributions ----- - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> char { - // A valid `char` is either in the interval `[0, 0xD800)` or - // `(0xDFFF, 0x11_0000)`. All `char`s must therefore be in - // `[0, 0x11_0000)` but not in the "gap" `[0xD800, 0xDFFF]` which is - // 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 - // seemed slower. - let range = Uniform::new(GAP_SIZE, 0x11_0000).unwrap(); - - let mut n = range.sample(rng); - if n <= 0xDFFF { - n -= GAP_SIZE; - } - // SAFETY: We ensure above that `n` represents a `char`. - unsafe { char::from_u32_unchecked(n) } - } -} - -#[cfg(feature = "alloc")] -impl SampleString for StandardUniform { - fn append_string(&self, rng: &mut R, s: &mut String, len: usize) { - // A char is encoded with at most four bytes, thus this reservation is - // guaranteed to be sufficient. We do not shrink_to_fit afterwards so - // that repeated usage on the same `String` buffer does not reallocate. - s.reserve(4 * len); - s.extend(Distribution::::sample_iter(self, rng).take(len)); - } -} - -impl Distribution for Alphanumeric { - fn sample(&self, rng: &mut R) -> u8 { - const RANGE: u32 = 26 + 26 + 10; - const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - // We can pick from 62 characters. This is so close to a power of 2, 64, - // that we can do better than `Uniform`. Use a simple bitshift and - // rejection sampling. We do not use a bitmask, because for small RNGs - // the most significant bits are usually of higher quality. - loop { - let var = rng.next_u32() >> (32 - 6); - if var < RANGE { - return GEN_ASCII_STR_CHARSET[var as usize]; - } - } - } -} - -impl Distribution for Alphabetic { - fn sample(&self, rng: &mut R) -> u8 { - const RANGE: u8 = 26 + 26; - - let offset = rng.random_range(0..RANGE) + b'A'; - - // Account for upper-cases - offset + (offset > b'Z') as u8 * (b'a' - b'Z' - 1) - } -} - -#[cfg(feature = "alloc")] -impl SampleString for Alphanumeric { - fn append_string(&self, rng: &mut R, string: &mut String, len: usize) { - // SAFETY: `self` only samples alphanumeric characters, which are valid UTF-8. - unsafe { - let v = string.as_mut_vec(); - v.extend( - self.sample_iter(rng) - .take(len) - .inspect(|b| debug_assert!(b.is_ascii_alphanumeric())), - ); - } - } -} - -#[cfg(feature = "alloc")] -impl SampleString for Alphabetic { - fn append_string(&self, rng: &mut R, string: &mut String, len: usize) { - // SAFETY: With this distribution we guarantee that we're working with valid ASCII - // characters. - // See [#1590](https://github.com/rust-random/rand/issues/1590). - unsafe { - let v = string.as_mut_vec(); - v.reserve_exact(len); - v.extend(self.sample_iter(rng).take(len)); - } - } -} - -impl Distribution for StandardUniform { - #[inline] - fn sample(&self, rng: &mut R) -> bool { - // We can compare against an arbitrary bit of an u32 to get a bool. - // Because the least significant bits of a lower quality RNG can have - // simple patterns, we compare against the most significant bit. This is - // easiest done using a sign test. - (rng.next_u32() as i32) < 0 - } -} - -/// 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.random::<__m128i>()) }; -/// -/// // ...than this -/// let x = rng.random::().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::prelude::*; -/// use rand::prelude::*; -/// let mut rng = rand::rng(); -/// -/// let x = u16x8::splat(rng.random::() 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 Distribution> for StandardUniform -where - T: MaskElement + Default, - StandardUniform: Distribution>, - Simd: SimdPartialOrd>, -{ - #[inline] - fn sample(&self, rng: &mut R) -> Mask { - // `MaskElement` must be a signed integer, so this is equivalent - // to the scalar `i32 < 0` method - let var = rng.random::>(); - var.simd_lt(Simd::default()) - } -} - -/// Implement `Distribution<(A, B, C, ...)> for StandardUniform`, using the list of -/// identifiers -macro_rules! tuple_impl { - ($($tyvar:ident)*) => { - impl< $($tyvar,)* > Distribution<($($tyvar,)*)> for StandardUniform - where $( - StandardUniform: Distribution< $tyvar >, - )* - { - #[inline] - fn sample(&self, rng: &mut R) -> ( $($tyvar,)* ) { - let out = ($( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - rng.random::<$tyvar>() - ,)*); - - // Suppress the unused variable warning for empty tuple - let _rng = rng; - - out - } - } - } -} - -/// Looping wrapper for `tuple_impl`. Given (A, B, C), it also generates -/// implementations for (A, B) and (A,) -macro_rules! tuple_impls { - ($($tyvar:ident)*) => {tuple_impls!{[] $($tyvar)*}}; - - ([$($prefix:ident)*] $head:ident $($tail:ident)*) => { - tuple_impl!{$($prefix)*} - tuple_impls!{[$($prefix)* $head] $($tail)*} - }; - - - ([$($prefix:ident)*]) => { - tuple_impl!{$($prefix)*} - }; - -} - -tuple_impls! {A B C D E F G H I J K L} - -impl Distribution<[T; N]> for StandardUniform -where - StandardUniform: Distribution, -{ - #[inline] - fn sample(&self, rng: &mut R) -> [T; N] { - array::from_fn(|_| rng.random()) - } -} - -impl Distribution> for StandardUniform -where - StandardUniform: Distribution, -{ - #[inline] - fn sample(&self, rng: &mut R) -> Wrapping { - Wrapping(rng.random()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::Rng; - - #[test] - fn test_misc() { - let rng: &mut dyn Rng = &mut crate::test::rng(820); - - rng.sample::(StandardUniform); - rng.sample::(StandardUniform); - } - - #[cfg(feature = "alloc")] - #[test] - fn test_chars() { - use core::iter; - let mut rng = crate::test::rng(805); - - // Test by generating a relatively large number of chars, so we also - // take the rejection sampling path. - let word: String = iter::repeat(()) - .map(|()| rng.random::()) - .take(1000) - .collect(); - assert!(!word.is_empty()); - } - - #[test] - fn test_alphanumeric() { - let mut rng = crate::test::rng(806); - - // Test by generating a relatively large number of chars, so we also - // take the rejection sampling path. - let mut incorrect = false; - for _ in 0..100 { - let c: char = rng.sample(Alphanumeric).into(); - incorrect |= !c.is_ascii_alphanumeric(); - } - assert!(!incorrect); - } - - #[test] - fn test_alphabetic() { - let mut rng = crate::test::rng(806); - - // Test by generating a relatively large number of chars, so we also - // take the rejection sampling path. - let mut incorrect = false; - for _ in 0..100 { - let c: char = rng.sample(Alphabetic).into(); - incorrect |= !c.is_ascii_alphabetic(); - } - assert!(!incorrect); - } - - #[test] - fn value_stability() { - fn test_samples>( - distr: &D, - zero: T, - expected: &[T], - ) { - let mut rng = crate::test::rng(807); - let mut buf = [zero; 5]; - for x in &mut buf { - *x = rng.sample(distr); - } - assert_eq!(&buf, expected); - } - - test_samples( - &StandardUniform, - 'a', - &[ - '\u{8cdac}', - '\u{a346a}', - '\u{80120}', - '\u{ed692}', - '\u{35888}', - ], - ); - test_samples(&Alphanumeric, 0, &[104, 109, 101, 51, 77]); - test_samples(&Alphabetic, 0, &[97, 102, 89, 116, 75]); - test_samples(&StandardUniform, false, &[true, true, false, true, false]); - test_samples( - &StandardUniform, - Wrapping(0i32), - &[ - Wrapping(-2074640887), - Wrapping(-1719949321), - Wrapping(2018088303), - Wrapping(-547181756), - Wrapping(838957336), - ], - ); - - // We test only sub-sets of tuple and array impls - test_samples(&StandardUniform, (), &[(), (), (), (), ()]); - test_samples( - &StandardUniform, - (false,), - &[(true,), (true,), (false,), (true,), (false,)], - ); - test_samples( - &StandardUniform, - (false, false), - &[ - (true, true), - (false, true), - (false, false), - (true, false), - (false, false), - ], - ); - - test_samples(&StandardUniform, [0u8; 0], &[[], [], [], [], []]); - test_samples( - &StandardUniform, - [0u8; 3], - &[ - [9, 247, 111], - [68, 24, 13], - [174, 19, 194], - [172, 69, 213], - [149, 207, 29], - ], - ); - } -} diff --git a/src/distr/slice.rs b/src/distr/slice.rs deleted file mode 100644 index bbe7840e47a..00000000000 --- a/src/distr/slice.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2021 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Distributions over slices - -use core::num::NonZeroUsize; - -use crate::Rng; -use crate::distr::Distribution; -use crate::distr::uniform::{UniformSampler, UniformUsize}; -#[cfg(feature = "alloc")] -use alloc::string::String; - -/// A distribution to uniformly sample elements of a slice -/// -/// Like [`IndexedRandom::choose`], this uniformly samples elements of a slice -/// without modification of the slice (so called "sampling with replacement"). -/// This distribution object may be a little faster for repeated sampling (but -/// slower for small numbers of samples). -/// -/// ## Examples -/// -/// Since this is a distribution, [`Rng::sample_iter`] and -/// [`Distribution::sample_iter`] may be used, for example: -/// ``` -/// use rand::distr::{Distribution, slice::Choose}; -/// -/// let vowels = ['a', 'e', 'i', 'o', 'u']; -/// let vowels_dist = Choose::new(&vowels).unwrap(); -/// -/// // build a string of 10 vowels -/// let vowel_string: String = vowels_dist -/// .sample_iter(&mut rand::rng()) -/// .take(10) -/// .collect(); -/// -/// println!("{}", vowel_string); -/// assert_eq!(vowel_string.len(), 10); -/// assert!(vowel_string.chars().all(|c| vowels.contains(&c))); -/// ``` -/// -/// For a single sample, [`IndexedRandom::choose`] may be preferred: -/// ``` -/// use rand::seq::IndexedRandom; -/// -/// let vowels = ['a', 'e', 'i', 'o', 'u']; -/// let mut rng = rand::rng(); -/// -/// println!("{}", vowels.choose(&mut rng).unwrap()); -/// ``` -/// -/// [`IndexedRandom::choose`]: crate::seq::IndexedRandom::choose -/// [`Rng::sample_iter`]: crate::RngExt::sample_iter -#[derive(Debug, Clone, Copy)] -pub struct Choose<'a, T> { - slice: &'a [T], - range: UniformUsize, - num_choices: NonZeroUsize, -} - -impl<'a, T> Choose<'a, T> { - /// Create a new `Choose` instance which samples uniformly from the slice. - /// - /// Returns error [`Empty`] if the slice is empty. - pub fn new(slice: &'a [T]) -> Result { - let num_choices = NonZeroUsize::new(slice.len()).ok_or(Empty)?; - - Ok(Self { - slice, - range: UniformUsize::new(0, num_choices.get()).unwrap(), - num_choices, - }) - } - - /// Returns the count of choices in this distribution - pub fn num_choices(&self) -> NonZeroUsize { - self.num_choices - } -} - -impl<'a, T> Distribution<&'a T> for Choose<'a, T> { - fn sample(&self, rng: &mut R) -> &'a T { - let idx = self.range.sample(rng); - - debug_assert!( - idx < self.slice.len(), - "Uniform::new(0, {}) somehow returned {}", - self.slice.len(), - idx - ); - - // Safety: at construction time, it was ensured that the slice was - // non-empty, and that the `Uniform` range produces values in range - // for the slice - unsafe { self.slice.get_unchecked(idx) } - } -} - -/// Error: empty slice -/// -/// This error is returned when [`Choose::new`] is given an empty slice. -#[derive(Debug, Clone, Copy)] -pub struct Empty; - -impl core::fmt::Display for Empty { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "Tried to create a `rand::distr::slice::Choose` with an empty slice" - ) - } -} - -impl core::error::Error for Empty {} - -#[cfg(feature = "alloc")] -impl super::SampleString for Choose<'_, char> { - fn append_string(&self, rng: &mut R, string: &mut String, len: usize) { - // Get the max char length to minimize extra space. - // Limit this check to avoid searching for long slice. - let max_char_len = if self.slice.len() < 200 { - self.slice - .iter() - .try_fold(1, |max_len, char| { - // When the current max_len is 4, the result max_char_len will be 4. - Some(max_len.max(char.len_utf8())).filter(|len| *len < 4) - }) - .unwrap_or(4) - } else { - 4 - }; - - // Split the extension of string to reuse the unused capacities. - // Skip the split for small length or only ascii slice. - let mut extend_len = if max_char_len == 1 || len < 100 { - len - } else { - len / 4 - }; - let mut remain_len = len; - while extend_len > 0 { - string.reserve(max_char_len * extend_len); - string.extend(self.sample_iter(&mut *rng).take(extend_len)); - remain_len -= extend_len; - extend_len = extend_len.min(remain_len); - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use core::iter; - - #[test] - fn value_stability() { - let rng = crate::test::rng(651); - let slice = Choose::new(b"escaped emus explore extensively").unwrap(); - let expected = b"eaxee"; - assert!(iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b)); - } -} diff --git a/src/distr/uniform.rs b/src/distr/uniform.rs deleted file mode 100644 index c61a518e9fa..00000000000 --- a/src/distr/uniform.rs +++ /dev/null @@ -1,625 +0,0 @@ -// Copyright 2018-2020 Developers of the Rand project. -// Copyright 2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A distribution uniformly sampling numbers within a given range. -//! -//! [`Uniform`] is the standard distribution to sample uniformly from a range; -//! e.g. `Uniform::new_inclusive(1, 6).unwrap()` can sample integers from 1 to 6, like a -//! standard die. [`RngExt::random_range`] is implemented over [`Uniform`]. -//! -//! # Example usage -//! -//! ``` -//! use rand::RngExt; -//! use rand::distr::Uniform; -//! -//! let mut rng = rand::rng(); -//! let side = Uniform::new(-10.0, 10.0).unwrap(); -//! -//! // sample between 1 and 10 points -//! for _ in 0..rng.random_range(1..=10) { -//! // sample a point from the square with sides -10 - 10 in two dimensions -//! let (x, y) = (rng.sample(side), rng.sample(side)); -//! println!("Point: {}, {}", x, y); -//! } -//! ``` -//! -//! # Extending `Uniform` to support a custom type -//! -//! To extend [`Uniform`] to support your own types, write a back-end which -//! implements the [`UniformSampler`] trait, then implement the [`SampleUniform`] -//! helper trait to "register" your back-end. See the `MyF32` example below. -//! -//! At a minimum, the back-end needs to store any parameters needed for sampling -//! (e.g. the target range) and implement `new`, `new_inclusive` and `sample`. -//! Those methods should include an assertion to check the range is valid (i.e. -//! `low < high`). The example below merely wraps another back-end. -//! -//! The `new`, `new_inclusive`, `sample_single` and `sample_single_inclusive` -//! functions use arguments of -//! type `SampleBorrow` to support passing in values by reference or -//! by value. In the implementation of these functions, you can choose to -//! simply use the reference returned by [`SampleBorrow::borrow`], or you can choose -//! to copy or clone the value, whatever is appropriate for your type. -//! -//! ``` -//! use rand::prelude::*; -//! use rand::distr::uniform::{Uniform, SampleUniform, -//! UniformSampler, UniformFloat, SampleBorrow, Error}; -//! -//! struct MyF32(f32); -//! -//! #[derive(Clone, Copy, Debug)] -//! struct UniformMyF32(UniformFloat); -//! -//! impl UniformSampler for UniformMyF32 { -//! type X = MyF32; -//! -//! fn new(low: B1, high: B2) -> Result -//! where B1: SampleBorrow + Sized, -//! B2: SampleBorrow + Sized -//! { -//! UniformFloat::::new(low.borrow().0, high.borrow().0).map(UniformMyF32) -//! } -//! fn new_inclusive(low: B1, high: B2) -> Result -//! where B1: SampleBorrow + Sized, -//! B2: SampleBorrow + Sized -//! { -//! UniformFloat::::new_inclusive(low.borrow().0, high.borrow().0).map(UniformMyF32) -//! } -//! fn sample(&self, rng: &mut R) -> Self::X { -//! MyF32(self.0.sample(rng)) -//! } -//! } -//! -//! impl SampleUniform for MyF32 { -//! type Sampler = UniformMyF32; -//! } -//! -//! let (low, high) = (MyF32(17.0f32), MyF32(22.0f32)); -//! let uniform = Uniform::new(low, high).unwrap(); -//! let x = uniform.sample(&mut rand::rng()); -//! ``` -//! -//! [`SampleUniform`]: crate::distr::uniform::SampleUniform -//! [`UniformSampler`]: crate::distr::uniform::UniformSampler -//! [`UniformInt`]: crate::distr::uniform::UniformInt -//! [`UniformFloat`]: crate::distr::uniform::UniformFloat -//! [`UniformDuration`]: crate::distr::uniform::UniformDuration -//! [`SampleBorrow::borrow`]: crate::distr::uniform::SampleBorrow::borrow - -#[path = "uniform_float.rs"] -mod float; -#[doc(inline)] -pub use float::UniformFloat; - -#[path = "uniform_int.rs"] -mod int; -#[doc(inline)] -pub use int::{UniformInt, UniformUsize}; - -#[path = "uniform_other.rs"] -mod other; -#[doc(inline)] -pub use other::{UniformChar, UniformDuration}; - -use core::fmt; -use core::ops::{Range, RangeInclusive, RangeTo, RangeToInclusive}; - -use crate::Rng; -use crate::distr::Distribution; - -#[cfg(doc)] -use crate::RngExt; - -/// Error type returned from [`Uniform::new`] and `new_inclusive`. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Error { - /// `low > high`, or equal in case of exclusive range. - EmptyRange, - /// Input or range `high - low` is non-finite. Not relevant to integer types. - NonFinite, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(match self { - Error::EmptyRange => "low > high (or equal if exclusive) in uniform distribution", - Error::NonFinite => "Non-finite range in uniform distribution", - }) - } -} - -impl core::error::Error for Error {} - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Sample values uniformly between two bounds. -/// -/// # Construction -/// -/// [`Uniform::new`] and [`Uniform::new_inclusive`] construct a uniform -/// distribution sampling from the given `low` and `high` limits. `Uniform` may -/// also be constructed via [`TryFrom`] as in `Uniform::try_from(1..=6).unwrap()`. -/// -/// Constructors may do extra work up front to allow faster sampling of multiple -/// values. Where only a single sample is required it is suggested to use -/// [`Rng::random_range`] or one of the `sample_single` methods instead. -/// -/// When sampling from a constant range, many calculations can happen at -/// compile-time and all methods should be fast; for floating-point ranges and -/// the full range of integer types, this should have comparable performance to -/// the [`StandardUniform`](super::StandardUniform) distribution. -/// -/// # Provided implementations -/// -/// - `char` ([`UniformChar`]): samples a range over the implementation for `u32` -/// - `f32`, `f64` ([`UniformFloat`]): samples approximately uniformly within a -/// range; bias may be present in the least-significant bit of the significand -/// and the limits of the input range may be sampled even when an open -/// (exclusive) range is used -/// - Integer types ([`UniformInt`]) may show a small bias relative to the -/// expected uniform distribution of output. In the worst case, bias affects -/// 1 in `2^n` samples where n is 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96 -/// (`i32` and `u32`), 64 (`i64` and `u64`), 128 (`i128` and `u128`). -/// The `unbiased` feature flag fixes this bias. -/// - `usize` ([`UniformUsize`]) is handled specially, using the `u32` -/// implementation where possible to enable portable results across 32-bit and -/// 64-bit CPU architectures. -/// - `Duration` ([`UniformDuration`]): samples a range over the implementation -/// for `u32` or `u64` -/// - SIMD types (requires [`simd_support`] feature) like x86's [`__m128i`] -/// and `std::simd`'s [`u32x4`], [`f32x4`] and [`mask32x4`] types are -/// effectively arrays of integer or floating-point types. Each lane is -/// sampled independently from its own range, potentially with more efficient -/// random-bit-usage than would be achieved with sequential sampling. -/// -/// # Example -/// -/// ``` -/// use rand::distr::{Distribution, Uniform}; -/// -/// let between = Uniform::try_from(10..10000).unwrap(); -/// let mut rng = rand::rng(); -/// let mut sum = 0; -/// for _ in 0..1000 { -/// sum += between.sample(&mut rng); -/// } -/// println!("{}", sum); -/// ``` -/// -/// For a single sample, [`Rng::random_range`] may be preferred: -/// -/// ``` -/// use rand::RngExt; -/// -/// let mut rng = rand::rng(); -/// println!("{}", rng.random_range(0..10)); -/// ``` -/// -/// [`new`]: Uniform::new -/// [`new_inclusive`]: Uniform::new_inclusive -/// [`Rng::random_range`]: RngExt::random_range -/// [`__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, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(bound(serialize = "X::Sampler: Serialize")))] -#[cfg_attr( - feature = "serde", - serde(bound(deserialize = "X::Sampler: Deserialize<'de>")) -)] -pub struct Uniform(X::Sampler); - -impl Uniform { - /// Create a new `Uniform` instance, which samples uniformly from the half - /// open range `[low, high)` (excluding `high`). - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `high`. - /// - /// Fails if `low >= high`, or if `low`, `high` or the range `high - low` is - /// non-finite. In release mode, only the range is checked. - pub fn new(low: B1, high: B2) -> Result, Error> - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - X::Sampler::new(low, high).map(Uniform) - } - - /// Create a new `Uniform` instance, which samples uniformly from the closed - /// range `[low, high]` (inclusive). - /// - /// Fails if `low > high`, or if `low`, `high` or the range `high - low` is - /// non-finite. In release mode, only the range is checked. - pub fn new_inclusive(low: B1, high: B2) -> Result, Error> - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - X::Sampler::new_inclusive(low, high).map(Uniform) - } -} - -impl Distribution for Uniform { - fn sample(&self, rng: &mut R) -> X { - self.0.sample(rng) - } -} - -/// Helper trait for creating objects using the correct implementation of -/// [`UniformSampler`] for the sampling type. -/// -/// See the [module documentation] on how to implement [`Uniform`] range -/// sampling for a custom type. -/// -/// [module documentation]: crate::distr::uniform -pub trait SampleUniform: Sized { - /// The `UniformSampler` implementation supporting type `X`. - type Sampler: UniformSampler; -} - -/// Helper trait handling actual uniform sampling. -/// -/// See the [module documentation] on how to implement [`Uniform`] range -/// sampling for a custom type. -/// -/// Implementation of [`sample_single`] is optional, and is only useful when -/// the implementation can be faster than `Self::new(low, high).sample(rng)`. -/// -/// [module documentation]: crate::distr::uniform -/// [`sample_single`]: UniformSampler::sample_single -pub trait UniformSampler: Sized { - /// The type sampled by this implementation. - type X; - - /// Construct self, with inclusive lower bound and exclusive upper bound `[low, high)`. - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `high`. - /// - /// Usually users should not call this directly but prefer to use - /// [`Uniform::new`]. - fn new(low: B1, high: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized; - - /// Construct self, with inclusive bounds `[low, high]`. - /// - /// Usually users should not call this directly but prefer to use - /// [`Uniform::new_inclusive`]. - fn new_inclusive(low: B1, high: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized; - - /// Sample a value. - fn sample(&self, rng: &mut R) -> Self::X; - - /// Sample a single value uniformly from a range with inclusive lower bound - /// and exclusive upper bound `[low, high)`. - /// - /// For discrete types (e.g. integers), samples will always be strictly less - /// than `high`. For (approximations of) continuous types (e.g. `f32`, `f64`), - /// samples may equal `high` due to loss of precision but may not be - /// greater than `high`. - /// - /// By default this is implemented using - /// `UniformSampler::new(low, high).sample(rng)`. However, for some types - /// more optimal implementations for single usage may be provided via this - /// method (which is the case for integers and floats). - /// Results may not be identical. - /// - /// Note that to use this method in a generic context, the type needs to be - /// retrieved via `SampleUniform::Sampler` as follows: - /// ``` - /// use rand::distr::uniform::{SampleUniform, UniformSampler}; - /// # #[allow(unused)] - /// fn sample_from_range(lb: T, ub: T) -> T { - /// let mut rng = rand::rng(); - /// ::Sampler::sample_single(lb, ub, &mut rng).unwrap() - /// } - /// ``` - fn sample_single( - low: B1, - high: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let uniform: Self = UniformSampler::new(low, high)?; - Ok(uniform.sample(rng)) - } - - /// Sample a single value uniformly from a range with inclusive lower bound - /// and inclusive upper bound `[low, high]`. - /// - /// By default this is implemented using - /// `UniformSampler::new_inclusive(low, high).sample(rng)`. However, for - /// some types more optimal implementations for single usage may be provided - /// via this method. - /// Results may not be identical. - fn sample_single_inclusive( - low: B1, - high: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let uniform: Self = UniformSampler::new_inclusive(low, high)?; - Ok(uniform.sample(rng)) - } -} - -impl TryFrom> for Uniform { - type Error = Error; - - fn try_from(r: Range) -> Result, Error> { - Uniform::new(r.start, r.end) - } -} - -impl TryFrom> for Uniform { - type Error = Error; - - fn try_from(r: ::core::ops::RangeInclusive) -> Result, Error> { - Uniform::new_inclusive(r.start(), r.end()) - } -} - -/// Helper trait similar to [`Borrow`] but implemented -/// only for [`SampleUniform`] and references to [`SampleUniform`] -/// in order to resolve ambiguity issues. -/// -/// [`Borrow`]: std::borrow::Borrow -pub trait SampleBorrow { - /// Immutably borrows from an owned value. See [`Borrow::borrow`] - /// - /// [`Borrow::borrow`]: std::borrow::Borrow::borrow - fn borrow(&self) -> &Borrowed; -} -impl SampleBorrow for Borrowed -where - Borrowed: SampleUniform, -{ - #[inline(always)] - fn borrow(&self) -> &Borrowed { - self - } -} -impl SampleBorrow for &Borrowed -where - Borrowed: SampleUniform, -{ - #[inline(always)] - fn borrow(&self) -> &Borrowed { - self - } -} - -/// Range that supports generating a single sample efficiently. -/// -/// Any type implementing this trait can be used to specify the sampled range -/// for `Rng::random_range`. -pub trait SampleRange { - /// Generate a sample from the given range. - fn sample_single(self, rng: &mut R) -> Result; - - /// Check whether the range is empty. - fn is_empty(&self) -> bool; -} - -impl SampleRange for Range { - #[inline] - fn sample_single(self, rng: &mut R) -> Result { - T::Sampler::sample_single(self.start, self.end, rng) - } - - #[inline] - fn is_empty(&self) -> bool { - !(self.start < self.end) - } -} - -impl SampleRange for RangeInclusive { - #[inline] - fn sample_single(self, rng: &mut R) -> Result { - T::Sampler::sample_single_inclusive(self.start(), self.end(), rng) - } - - #[inline] - fn is_empty(&self) -> bool { - !(self.start() <= self.end()) - } -} - -macro_rules! impl_sample_range_u { - ($t:ty) => { - impl SampleRange<$t> for RangeTo<$t> { - #[inline] - fn sample_single(self, rng: &mut R) -> Result<$t, Error> { - <$t as SampleUniform>::Sampler::sample_single(0, self.end, rng) - } - - #[inline] - fn is_empty(&self) -> bool { - 0 == self.end - } - } - - impl SampleRange<$t> for RangeToInclusive<$t> { - #[inline] - fn sample_single(self, rng: &mut R) -> Result<$t, Error> { - <$t as SampleUniform>::Sampler::sample_single_inclusive(0, self.end, rng) - } - - #[inline] - fn is_empty(&self) -> bool { - false - } - } - }; -} - -impl_sample_range_u!(u8); -impl_sample_range_u!(u16); -impl_sample_range_u!(u32); -impl_sample_range_u!(u64); -impl_sample_range_u!(u128); -impl_sample_range_u!(usize); - -#[cfg(test)] -mod tests { - use super::*; - use crate::RngExt; - use core::time::Duration; - - #[test] - #[cfg(feature = "serde")] - fn test_uniform_serialization() { - let unit_box: Uniform = Uniform::new(-1, 1).unwrap(); - let de_unit_box: Uniform = - postcard::from_bytes(&postcard::to_allocvec(&unit_box).unwrap()).unwrap(); - assert_eq!(unit_box.0, de_unit_box.0); - - let unit_box: Uniform = Uniform::new(-1., 1.).unwrap(); - let de_unit_box: Uniform = - postcard::from_bytes(&postcard::to_allocvec(&unit_box).unwrap()).unwrap(); - assert_eq!(unit_box.0, de_unit_box.0); - } - - #[test] - fn test_custom_uniform() { - use crate::distr::uniform::{SampleBorrow, SampleUniform, UniformFloat, UniformSampler}; - #[derive(Clone, Copy, PartialEq, PartialOrd)] - struct MyF32 { - x: f32, - } - #[derive(Clone, Copy, Debug)] - struct UniformMyF32(UniformFloat); - impl UniformSampler for UniformMyF32 { - type X = MyF32; - - fn new(low: B1, high: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - UniformFloat::::new(low.borrow().x, high.borrow().x).map(UniformMyF32) - } - - fn new_inclusive(low: B1, high: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - UniformSampler::new(low, high) - } - - fn sample(&self, rng: &mut R) -> Self::X { - MyF32 { - x: self.0.sample(rng), - } - } - } - impl SampleUniform for MyF32 { - type Sampler = UniformMyF32; - } - - let (low, high) = (MyF32 { x: 17.0f32 }, MyF32 { x: 22.0f32 }); - let uniform = Uniform::new(low, high).unwrap(); - let mut rng = crate::test::rng(804); - for _ in 0..100 { - let x: MyF32 = rng.sample(uniform); - assert!(low <= x && x < high); - } - } - - #[test] - fn value_stability() { - fn test_samples( - lb: T, - ub: T, - expected_single: &[T], - expected_multiple: &[T], - ) where - Uniform: Distribution, - { - let mut rng = crate::test::rng(897); - let mut buf = [lb; 3]; - - for x in &mut buf { - *x = T::Sampler::sample_single(lb, ub, &mut rng).unwrap(); - } - assert_eq!(&buf, expected_single); - - let distr = Uniform::new(lb, ub).unwrap(); - for x in &mut buf { - *x = rng.sample(&distr); - } - assert_eq!(&buf, expected_multiple); - } - - test_samples( - 0f32, - 1e-2f32, - &[0.0003070104, 0.0026630748, 0.00979833], - &[0.008194133, 0.00398172, 0.007428536], - ); - test_samples( - -1e10f64, - 1e10f64, - &[-4673848682.871551, 6388267422.932352, 4857075081.198343], - &[1173375212.1808167, 1917642852.109581, 2365076174.3153973], - ); - - test_samples( - Duration::new(2, 0), - Duration::new(4, 0), - &[ - Duration::new(2, 532615131), - Duration::new(3, 638826742), - Duration::new(3, 485707508), - ], - &[ - Duration::new(3, 117337521), - Duration::new(3, 191764285), - Duration::new(3, 236507617), - ], - ); - } - - #[test] - fn uniform_distributions_can_be_compared() { - assert_eq!( - Uniform::new(1.0, 2.0).unwrap(), - Uniform::new(1.0, 2.0).unwrap() - ); - - // To cover UniformInt - assert_eq!( - Uniform::new(1_u32, 2_u32).unwrap(), - Uniform::new(1_u32, 2_u32).unwrap() - ); - } -} diff --git a/src/distr/uniform_float.rs b/src/distr/uniform_float.rs deleted file mode 100644 index 3e91c0f3a07..00000000000 --- a/src/distr/uniform_float.rs +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright 2018-2020 Developers of the Rand project. -// Copyright 2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! `UniformFloat` implementation - -use super::{Error, SampleBorrow, SampleUniform, UniformSampler}; -use crate::distr::float::IntoFloat; -use crate::distr::utils::{BoolAsSIMD, FloatAsSIMD, FloatSIMDUtils, IntAsSIMD}; -use crate::{Rng, RngExt}; - -#[cfg(feature = "simd_support")] -use core::simd::prelude::*; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// The back-end implementing [`UniformSampler`] for floating-point types. -/// -/// Unless you are implementing [`UniformSampler`] for your own type, this type -/// should not be used directly, use [`Uniform`] instead. -/// -/// # Implementation notes -/// -/// `UniformFloat` implementations convert RNG output to a float in the range -/// `[1, 2)` via transmutation, map this to `[0, 1)`, then scale and translate -/// to the desired range. Values produced this way have what equals 23 bits of -/// random digits for an `f32` and 52 for an `f64`. -/// -/// # Bias and range errors -/// -/// Bias may be expected within the least-significant bit of the significand. -/// It is not guaranteed that exclusive limits of a range are respected; i.e. -/// when sampling the range `[a, b)` it is not guaranteed that `b` is never -/// sampled. -/// -/// [`new`]: UniformSampler::new -/// [`new_inclusive`]: UniformSampler::new_inclusive -/// [`StandardUniform`]: crate::distr::StandardUniform -/// [`Uniform`]: super::Uniform -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct UniformFloat { - low: X, - scale: X, -} - -macro_rules! uniform_float_impl { - ($($meta:meta)?, $ty:ty, $uty:ident, $f_scalar:ident, $u_scalar:ident, $bits_to_discard:expr) => { - $(#[cfg($meta)])? - impl UniformFloat<$ty> { - /// Construct, reducing `scale` as required to ensure that rounding - /// can never yield values greater than `high`. - /// - /// Note: though it may be tempting to use a variant of this method - /// to ensure that samples from `[low, high)` are always strictly - /// less than `high`, this approach may be very slow where - /// `scale.abs()` is much smaller than `high.abs()` - /// (example: `low=0.99999999997819644, high=1.`). - fn new_bounded(low: $ty, high: $ty, mut scale: $ty) -> Self { - let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); - - loop { - let mask = (scale * max_rand + low).gt_mask(high); - if !mask.any() { - break; - } - scale = scale.decrease_masked(mask); - } - - debug_assert!(<$ty>::splat(0.0).all_le(scale)); - - UniformFloat { low, scale } - } - } - - $(#[cfg($meta)])? - impl SampleUniform for $ty { - type Sampler = UniformFloat<$ty>; - } - - $(#[cfg($meta)])? - impl UniformSampler for UniformFloat<$ty> { - type X = $ty; - - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !(low.all_finite()) || !(high.all_finite()) { - return Err(Error::NonFinite); - } - if !(low.all_lt(high)) { - return Err(Error::EmptyRange); - } - - let scale = high - low; - if !(scale.all_finite()) { - return Err(Error::NonFinite); - } - - Ok(Self::new_bounded(low, high, scale)) - } - - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !(low.all_finite()) || !(high.all_finite()) { - return Err(Error::NonFinite); - } - if !low.all_le(high) { - return Err(Error::EmptyRange); - } - - let max_rand = <$ty>::splat(1.0 as $f_scalar - $f_scalar::EPSILON); - let scale = (high - low) / max_rand; - if !scale.all_finite() { - return Err(Error::NonFinite); - } - - Ok(Self::new_bounded(low, high, scale)) - } - - fn sample(&self, rng: &mut R) -> Self::X { - // Generate a value in the range [1, 2) - let value1_2 = (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); - - // Get a value in the range [0, 1) to avoid overflow when multiplying by scale - let value0_1 = value1_2 - <$ty>::splat(1.0); - - // We don't use `f64::mul_add`, because it is not available with - // `no_std`. Furthermore, it is slower for some targets (but - // faster for others). However, the order of multiplication and - // addition is important, because on some platforms (e.g. ARM) - // it will be optimized to a single (non-FMA) instruction. - value0_1 * self.scale + self.low - } - - #[inline] - fn sample_single(low_b: B1, high_b: B2, rng: &mut R) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - Self::sample_single_inclusive(low_b, high_b, rng) - } - - #[inline] - fn sample_single_inclusive(low_b: B1, high_b: B2, rng: &mut R) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - #[cfg(debug_assertions)] - if !low.all_finite() || !high.all_finite() { - return Err(Error::NonFinite); - } - if !low.all_le(high) { - return Err(Error::EmptyRange); - } - let scale = high - low; - if !scale.all_finite() { - return Err(Error::NonFinite); - } - - // Generate a value in the range [1, 2) - let value1_2 = - (rng.random::<$uty>() >> $uty::splat($bits_to_discard)).into_float_with_exponent(0); - - // Get a value in the range [0, 1) to avoid overflow when multiplying by scale - let value0_1 = value1_2 - <$ty>::splat(1.0); - - // Doing multiply before addition allows some architectures - // to use a single instruction. - Ok(value0_1 * scale + low) - } - } - }; -} - -uniform_float_impl! { , f32, u32, f32, u32, 32 - 23 } -uniform_float_impl! { , f64, u64, f64, u64, 64 - 52 } - -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x2, u32x2, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x4, u32x4, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x8, u32x8, f32, u32, 32 - 23 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f32x16, u32x16, f32, u32, 32 - 23 } - -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f64x2, u64x2, f64, u64, 64 - 52 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f64x4, u64x4, f64, u64, 64 - 52 } -#[cfg(feature = "simd_support")] -uniform_float_impl! { feature = "simd_support", f64x8, u64x8, f64, u64, 64 - 52 } - -#[cfg(test)] -mod tests { - use super::*; - use crate::distr::{Uniform, utils::FloatSIMDScalarUtils}; - use crate::test::{const_rng, step_rng}; - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_floats() { - let mut rng = crate::test::rng(252); - let mut zero_rng = const_rng(0); - let mut max_rng = const_rng(0xffff_ffff_ffff_ffff); - macro_rules! t { - ($ty:ty, $f_scalar:ident, $bits_shifted:expr) => {{ - let v: &[($f_scalar, $f_scalar)] = &[ - (0.0, 100.0), - (-1e35, -1e25), - (1e-35, 1e-25), - (-1e35, 1e35), - (<$f_scalar>::from_bits(0), <$f_scalar>::from_bits(3)), - (-<$f_scalar>::from_bits(10), -<$f_scalar>::from_bits(1)), - (-<$f_scalar>::from_bits(5), 0.0), - (-<$f_scalar>::from_bits(7), -0.0), - (0.1 * $f_scalar::MAX, $f_scalar::MAX), - (-$f_scalar::MAX * 0.2, $f_scalar::MAX * 0.7), - ]; - for &(low_scalar, high_scalar) in v.iter() { - for lane in 0..<$ty>::LEN { - let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar); - let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar); - let my_uniform = Uniform::new(low, high).unwrap(); - let my_incl_uniform = Uniform::new_inclusive(low, high).unwrap(); - for _ in 0..100 { - let v = rng.sample(my_uniform).extract_lane(lane); - assert!(low_scalar <= v && v <= high_scalar); - let v = rng.sample(my_incl_uniform).extract_lane(lane); - assert!(low_scalar <= v && v <= high_scalar); - let v = - <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng) - .unwrap() - .extract_lane(lane); - assert!(low_scalar <= v && v <= high_scalar); - let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, high, &mut rng, - ) - .unwrap() - .extract_lane(lane); - assert!(low_scalar <= v && v <= high_scalar); - } - - assert_eq!( - rng.sample(Uniform::new_inclusive(low, low).unwrap()) - .extract_lane(lane), - low_scalar - ); - - assert_eq!(zero_rng.sample(my_uniform).extract_lane(lane), low_scalar); - assert_eq!( - zero_rng.sample(my_incl_uniform).extract_lane(lane), - low_scalar - ); - assert_eq!( - <$ty as SampleUniform>::Sampler::sample_single( - low, - high, - &mut zero_rng - ) - .unwrap() - .extract_lane(lane), - low_scalar - ); - assert_eq!( - <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, - high, - &mut zero_rng - ) - .unwrap() - .extract_lane(lane), - low_scalar - ); - - assert!(max_rng.sample(my_uniform).extract_lane(lane) <= high_scalar); - assert!(max_rng.sample(my_incl_uniform).extract_lane(lane) <= high_scalar); - // sample_single cannot cope with max_rng: - // assert!(<$ty as SampleUniform>::Sampler - // ::sample_single(low, high, &mut max_rng).unwrap() - // .extract(lane) <= high_scalar); - assert!( - <$ty as SampleUniform>::Sampler::sample_single_inclusive( - low, - high, - &mut max_rng - ) - .unwrap() - .extract_lane(lane) - <= high_scalar - ); - - // Don't run this test for really tiny differences between high and low - // since for those rounding might result in selecting high for a very - // long time. - if (high_scalar - low_scalar) > 0.0001 { - let mut lowering_max_rng = - step_rng(0xffff_ffff_ffff_ffff, (-1i64 << $bits_shifted) as u64); - assert!( - <$ty as SampleUniform>::Sampler::sample_single( - low, - high, - &mut lowering_max_rng - ) - .unwrap() - .extract_lane(lane) - <= high_scalar - ); - } - } - } - - assert_eq!( - rng.sample(Uniform::new_inclusive($f_scalar::MAX, $f_scalar::MAX).unwrap()), - $f_scalar::MAX - ); - assert_eq!( - rng.sample(Uniform::new_inclusive(-$f_scalar::MAX, -$f_scalar::MAX).unwrap()), - -$f_scalar::MAX - ); - }}; - } - - t!(f32, f32, 32 - 23); - t!(f64, f64, 64 - 52); - #[cfg(feature = "simd_support")] - { - t!(f32x2, f32, 32 - 23); - t!(f32x4, f32, 32 - 23); - t!(f32x8, f32, 32 - 23); - t!(f32x16, f32, 32 - 23); - t!(f64x2, f64, 64 - 52); - t!(f64x4, f64, 64 - 52); - t!(f64x8, f64, 64 - 52); - } - } - - #[test] - fn test_float_overflow() { - assert_eq!(Uniform::try_from(f64::MIN..f64::MAX), Err(Error::NonFinite)); - } - - #[test] - #[should_panic] - fn test_float_overflow_single() { - let mut rng = crate::test::rng(252); - rng.random_range(f64::MIN..f64::MAX); - } - - #[test] - #[cfg(all(feature = "std", panic = "unwind"))] - fn test_float_assertions() { - use super::SampleUniform; - fn range(low: T, high: T) -> Result { - let mut rng = crate::test::rng(253); - T::Sampler::sample_single(low, high, &mut rng) - } - - macro_rules! t { - ($ty:ident, $f_scalar:ident) => {{ - let v: &[($f_scalar, $f_scalar)] = &[ - ($f_scalar::NAN, 0.0), - (1.0, $f_scalar::NAN), - ($f_scalar::NAN, $f_scalar::NAN), - (1.0, 0.5), - ($f_scalar::MAX, -$f_scalar::MAX), - ($f_scalar::INFINITY, $f_scalar::INFINITY), - ($f_scalar::NEG_INFINITY, $f_scalar::NEG_INFINITY), - ($f_scalar::NEG_INFINITY, 5.0), - (5.0, $f_scalar::INFINITY), - ($f_scalar::NAN, $f_scalar::INFINITY), - ($f_scalar::NEG_INFINITY, $f_scalar::NAN), - ($f_scalar::NEG_INFINITY, $f_scalar::INFINITY), - ]; - for &(low_scalar, high_scalar) in v.iter() { - for lane in 0..<$ty>::LEN { - let low = <$ty>::splat(0.0 as $f_scalar).replace(lane, low_scalar); - let high = <$ty>::splat(1.0 as $f_scalar).replace(lane, high_scalar); - assert!(range(low, high).is_err()); - assert!(Uniform::new(low, high).is_err()); - assert!(Uniform::new_inclusive(low, high).is_err()); - assert!(Uniform::new(low, low).is_err()); - } - } - }}; - } - - t!(f32, f32); - t!(f64, f64); - #[cfg(feature = "simd_support")] - { - t!(f32x2, f32); - t!(f32x4, f32); - t!(f32x8, f32); - t!(f32x16, f32); - t!(f64x2, f64); - t!(f64x4, f64); - t!(f64x8, f64); - } - } - - #[test] - fn test_uniform_from_std_range() { - let r = Uniform::try_from(2.0f64..7.0).unwrap(); - assert_eq!(r.0.low, 2.0); - assert_eq!(r.0.scale, 5.0); - } - - #[test] - fn test_uniform_from_std_range_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100.0..10.0).is_err()); - assert!(Uniform::try_from(100.0..100.0).is_err()); - } - - #[test] - fn test_uniform_from_std_range_inclusive() { - let r = Uniform::try_from(2.0f64..=7.0).unwrap(); - assert_eq!(r.0.low, 2.0); - assert!(r.0.scale > 5.0); - assert!(r.0.scale < 5.0 + 1e-14); - } - - #[test] - fn test_uniform_from_std_range_inclusive_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100.0..=10.0).is_err()); - assert!(Uniform::try_from(100.0..=99.0).is_err()); - } -} diff --git a/src/distr/uniform_int.rs b/src/distr/uniform_int.rs deleted file mode 100644 index 865d63f69da..00000000000 --- a/src/distr/uniform_int.rs +++ /dev/null @@ -1,899 +0,0 @@ -// Copyright 2018-2020 Developers of the Rand project. -// Copyright 2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! `UniformInt` implementation - -use super::{Error, SampleBorrow, SampleUniform, UniformSampler}; -use crate::distr::utils::WideningMultiply; -#[cfg(feature = "simd_support")] -use crate::distr::{Distribution, StandardUniform}; -use crate::{Rng, RngExt}; - -#[cfg(feature = "simd_support")] -use core::simd::{Select, prelude::*}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// The back-end implementing [`UniformSampler`] for integer types. -/// -/// Unless you are implementing [`UniformSampler`] for your own type, this type -/// should not be used directly, use [`Uniform`] instead. -/// -/// # Implementation notes -/// -/// For simplicity, we use the same generic struct `UniformInt` for all -/// integer types `X`. This gives us only one field type, `X`; to store unsigned -/// values of this size, we take use the fact that these conversions are no-ops. -/// -/// For a closed range, the number of possible numbers we should generate is -/// `range = (high - low + 1)`. To avoid bias, we must ensure that the size of -/// our sample space, `zone`, is a multiple of `range`; other values must be -/// rejected (by replacing with a new random sample). -/// -/// As a special case, we use `range = 0` to represent the full range of the -/// result type (i.e. for `new_inclusive($ty::MIN, $ty::MAX)`). -/// -/// The optimum `zone` is the largest product of `range` which fits in our -/// (unsigned) target type. We calculate this by calculating how many numbers we -/// must reject: `reject = (MAX + 1) % range = (MAX - range + 1) % range`. Any (large) -/// product of `range` will suffice, thus in `sample_single` we multiply by a -/// power of 2 via bit-shifting (faster but may cause more rejections). -/// -/// The smallest integer PRNGs generate is `u32`. For 8- and 16-bit outputs we -/// use `u32` for our `zone` and samples (because it's not slower and because -/// it reduces the chance of having to reject a sample). In this case we cannot -/// store `zone` in the target type since it is too large, however we know -/// `ints_to_reject < range <= $uty::MAX`. -/// -/// An alternative to using a modulus is widening multiply: After a widening -/// multiply by `range`, the result is in the high word. Then comparing the low -/// word against `zone` makes sure our distribution is uniform. -/// -/// # Bias -/// -/// Unless the `unbiased` feature flag is used, outputs may have a small bias. -/// In the worst case, bias affects 1 in `2^n` samples where n is -/// 56 (`i8` and `u8`), 48 (`i16` and `u16`), 96 (`i32` and `u32`), 64 (`i64` -/// and `u64`), 128 (`i128` and `u128`). -/// -/// [`Uniform`]: super::Uniform -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct UniformInt { - pub(super) low: X, - pub(super) range: X, - thresh: X, // effectively 2.pow(max(64, uty_bits)) % range -} - -macro_rules! uniform_int_impl { - ($ty:ty, $uty:ty, $sample_ty:ident) => { - impl SampleUniform for $ty { - type Sampler = UniformInt<$ty>; - } - - impl UniformSampler for UniformInt<$ty> { - // We play free and fast with unsigned vs signed here - // (when $ty is signed), but that's fine, since the - // contract of this macro is for $ty and $uty to be - // "bit-equal", so casting between them is a no-op. - - type X = $ty; - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - UniformSampler::new_inclusive(low, high - 1) - } - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - let range = high.wrapping_sub(low).wrapping_add(1) as $uty; - let thresh = if range > 0 { - let range = $sample_ty::from(range); - (range.wrapping_neg() % range) - } else { - 0 - }; - - Ok(UniformInt { - low, - range: range as $ty, // type: $uty - thresh: thresh as $uty as $ty, // type: $sample_ty - }) - } - - /// Sample from distribution, Lemire's method, unbiased - #[inline] - fn sample(&self, rng: &mut R) -> Self::X { - let range = self.range as $uty as $sample_ty; - if range == 0 { - return rng.random(); - } - - let thresh = self.thresh as $uty as $sample_ty; - let hi = loop { - let (hi, lo) = rng.random::<$sample_ty>().wmul(range); - if lo >= thresh { - break hi; - } - }; - self.low.wrapping_add(hi as $ty) - } - - #[inline] - fn sample_single( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - Self::sample_single_inclusive(low, high - 1, rng) - } - - /// Sample single value, Canon's method, biased - /// - /// In the worst case, bias affects 1 in `2^n` samples where n is - /// 56 (`i8`), 48 (`i16`), 96 (`i32`), 64 (`i64`), 128 (`i128`). - #[cfg(not(feature = "unbiased"))] - #[inline] - fn sample_single_inclusive( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; - if range == 0 { - // Range is MAX+1 (unrepresentable), so we need a special case - return Ok(rng.random()); - } - - // generate a sample using a sensible integer type - let (mut result, lo_order) = rng.random::<$sample_ty>().wmul(range); - - // if the sample is biased... - if lo_order > range.wrapping_neg() { - // ...generate a new sample to reduce bias... - let (new_hi_order, _) = (rng.random::<$sample_ty>()).wmul(range as $sample_ty); - // ... incrementing result on overflow - let is_overflow = lo_order.checked_add(new_hi_order as $sample_ty).is_none(); - result += is_overflow as $sample_ty; - } - - Ok(low.wrapping_add(result as $ty)) - } - - /// Sample single value, Canon's method, unbiased - #[cfg(feature = "unbiased")] - #[inline] - fn sample_single_inclusive( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow<$ty> + Sized, - B2: SampleBorrow<$ty> + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - let range = high.wrapping_sub(low).wrapping_add(1) as $uty as $sample_ty; - if range == 0 { - // Range is MAX+1 (unrepresentable), so we need a special case - return Ok(rng.random()); - } - - let (mut result, mut lo) = rng.random::<$sample_ty>().wmul(range); - - // In contrast to the biased sampler, we use a loop: - while lo > range.wrapping_neg() { - let (new_hi, new_lo) = (rng.random::<$sample_ty>()).wmul(range); - match lo.checked_add(new_hi) { - Some(x) if x < $sample_ty::MAX => { - // Anything less than MAX: last term is 0 - break; - } - None => { - // Overflow: last term is 1 - result += 1; - break; - } - _ => { - // Unlikely case: must check next sample - lo = new_lo; - continue; - } - } - } - - Ok(low.wrapping_add(result as $ty)) - } - } - }; -} - -uniform_int_impl! { i8, u8, u32 } -uniform_int_impl! { i16, u16, u32 } -uniform_int_impl! { i32, u32, u32 } -uniform_int_impl! { i64, u64, u64 } -uniform_int_impl! { i128, u128, u128 } -uniform_int_impl! { u8, u8, u32 } -uniform_int_impl! { u16, u16, u32 } -uniform_int_impl! { u32, u32, u32 } -uniform_int_impl! { u64, u64, u64 } -uniform_int_impl! { u128, u128, u128 } - -#[cfg(feature = "simd_support")] -macro_rules! uniform_simd_int_impl { - ($ty:ident, $unsigned:ident) => { - // The "pick the largest zone that can fit in an `u32`" optimization - // is less useful here. Multiple lanes complicate things, we don't - // know the PRNG's minimal output size, and casting to a larger vector - // is generally a bad idea for SIMD performance. The user can still - // implement it manually. - - #[cfg(feature = "simd_support")] - impl SampleUniform for Simd<$ty, LANES> - where - Simd<$unsigned, LANES>: - WideningMultiply, Simd<$unsigned, LANES>)>, - StandardUniform: Distribution>, - { - type Sampler = UniformInt>; - } - - #[cfg(feature = "simd_support")] - impl UniformSampler for UniformInt> - where - Simd<$unsigned, LANES>: - WideningMultiply, Simd<$unsigned, LANES>)>, - StandardUniform: Distribution>, - { - type X = Simd<$ty, LANES>; - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new(low_b: B1, high_b: B2) -> Result - where B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low.simd_lt(high).all()) { - return Err(Error::EmptyRange); - } - UniformSampler::new_inclusive(low, high - Simd::splat(1)) - } - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low.simd_le(high).all()) { - return Err(Error::EmptyRange); - } - - // NOTE: all `Simd` operations are inherently wrapping, - // see https://doc.rust-lang.org/std/simd/struct.Simd.html - let range: Simd<$unsigned, LANES> = ((high - low) + Simd::splat(1)).cast(); - - // We must avoid divide-by-zero by using 0 % 1 == 0. - let not_full_range = range.simd_gt(Simd::splat(0)); - let modulo = not_full_range.select(range, Simd::splat(1)); - let ints_to_reject = range.wrapping_neg() % modulo; - - Ok(UniformInt { - low, - // These are really $unsigned values, but store as $ty: - range: range.cast(), - thresh: ints_to_reject.cast(), - }) - } - - fn sample(&self, rng: &mut R) -> Self::X { - let range: Simd<$unsigned, LANES> = self.range.cast(); - let thresh: Simd<$unsigned, LANES> = self.thresh.cast(); - - // This might seem very slow, generating a whole new - // SIMD vector for every sample rejection. For most uses - // though, the chance of rejection is small and provides good - // general performance. With multiple lanes, that chance is - // multiplied. To mitigate this, we replace only the lanes of - // the vector which fail, iteratively reducing the chance of - // rejection. The replacement method does however add a little - // overhead. Benchmarking or calculating probabilities might - // reveal contexts where this replacement method is slower. - let mut v: Simd<$unsigned, LANES> = rng.random(); - loop { - let (hi, lo) = v.wmul(range); - let mask = lo.simd_ge(thresh); - if mask.all() { - let hi: Simd<$ty, LANES> = hi.cast(); - // wrapping addition - let result = self.low + hi; - // `select` here compiles to a blend operation - // When `range.eq(0).none()` the compare and blend - // operations are avoided. - let v: Simd<$ty, LANES> = v.cast(); - return range.simd_gt(Simd::splat(0)).select(result, v); - } - // Replace only the failing lanes - v = mask.select(v, rng.random()); - } - } - } - }; - - // bulk implementation - ($(($unsigned:ident, $signed:ident)),+) => { - $( - uniform_simd_int_impl!($unsigned, $unsigned); - uniform_simd_int_impl!($signed, $unsigned); - )+ - }; -} - -#[cfg(feature = "simd_support")] -uniform_simd_int_impl! { (u8, i8), (u16, i16), (u32, i32), (u64, i64) } - -/// The back-end implementing [`UniformSampler`] for `usize`. -/// -/// # Implementation notes -/// -/// Sampling a `usize` value is usually used in relation to the length of an -/// array or other memory structure, thus it is reasonable to assume that the -/// vast majority of use-cases will have a maximum size under [`u32::MAX`]. -/// In part to optimise for this use-case, but mostly to ensure that results -/// are portable across 32-bit and 64-bit architectures (as far as is possible), -/// this implementation will use 32-bit sampling when possible. -#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(all(feature = "serde"), derive(Serialize))] -// To be able to deserialize on 32-bit we need to replace this with a custom -// implementation of the Deserialize trait, to be able to: -// - panic when `mode64` is `true` on 32-bit, -// - assign the default value to `mode64` when it's missing on 64-bit, -// - panic when the `usize` fields are greater than `u32::MAX` on 32-bit. -#[cfg_attr( - all(feature = "serde", target_pointer_width = "64"), - derive(Deserialize) -)] -pub struct UniformUsize { - /// The lowest possible value. - low: usize, - /// The number of possible values. `0` has a special meaning: all. - range: usize, - /// Threshold used when sampling to obtain a uniform distribution. - thresh: usize, - /// Whether the largest possible value is greater than `u32::MAX`. - #[cfg(target_pointer_width = "64")] - // Handle missing field when deserializing on 64-bit an object serialized - // on 32-bit. Can be removed when switching to a custom deserializer. - #[cfg_attr(feature = "serde", serde(default))] - mode64: bool, -} - -#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] -impl SampleUniform for usize { - type Sampler = UniformUsize; -} - -#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] -impl UniformSampler for UniformUsize { - type X = usize; - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - - UniformSampler::new_inclusive(low, high - 1) - } - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - #[cfg(target_pointer_width = "64")] - let mode64 = high > (u32::MAX as usize); - #[cfg(target_pointer_width = "32")] - let mode64 = false; - - let (range, thresh); - if cfg!(target_pointer_width = "64") && !mode64 { - let range32 = (high as u32).wrapping_sub(low as u32).wrapping_add(1); - range = range32 as usize; - thresh = if range32 > 0 { - (range32.wrapping_neg() % range32) as usize - } else { - 0 - }; - } else { - range = high.wrapping_sub(low).wrapping_add(1); - thresh = if range > 0 { - range.wrapping_neg() % range - } else { - 0 - }; - } - - Ok(UniformUsize { - low, - range, - thresh, - #[cfg(target_pointer_width = "64")] - mode64, - }) - } - - #[inline] - fn sample(&self, rng: &mut R) -> usize { - #[cfg(target_pointer_width = "32")] - let mode32 = true; - #[cfg(target_pointer_width = "64")] - let mode32 = !self.mode64; - - if mode32 { - let range = self.range as u32; - if range == 0 { - return rng.random::() as usize; - } - - let thresh = self.thresh as u32; - let hi = loop { - let (hi, lo) = rng.random::().wmul(range); - if lo >= thresh { - break hi; - } - }; - self.low.wrapping_add(hi as usize) - } else { - let range = self.range as u64; - if range == 0 { - return rng.random::() as usize; - } - - let thresh = self.thresh as u64; - let hi = loop { - let (hi, lo) = rng.random::().wmul(range); - if lo >= thresh { - break hi; - } - }; - self.low.wrapping_add(hi as usize) - } - } - - #[inline] - fn sample_single( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - - if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) { - return UniformInt::::sample_single(low as u64, high as u64, rng) - .map(|x| x as usize); - } - - UniformInt::::sample_single(low as u32, high as u32, rng).map(|x| x as usize) - } - - #[inline] - fn sample_single_inclusive( - low_b: B1, - high_b: B2, - rng: &mut R, - ) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - if cfg!(target_pointer_width = "64") && high > (u32::MAX as usize) { - return UniformInt::::sample_single_inclusive(low as u64, high as u64, rng) - .map(|x| x as usize); - } - - UniformInt::::sample_single_inclusive(low as u32, high as u32, rng).map(|x| x as usize) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::distr::{Distribution, Uniform}; - use core::fmt::Debug; - use core::ops::Add; - - #[test] - fn test_uniform_bad_limits_equal_int() { - assert_eq!(Uniform::new(10, 10), Err(Error::EmptyRange)); - } - - #[test] - fn test_uniform_good_limits_equal_int() { - let mut rng = crate::test::rng(804); - let dist = Uniform::new_inclusive(10, 10).unwrap(); - for _ in 0..20 { - assert_eq!(rng.sample(dist), 10); - } - } - - #[test] - fn test_uniform_bad_limits_flipped_int() { - assert_eq!(Uniform::new(10, 5), Err(Error::EmptyRange)); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_integers() { - let mut rng = crate::test::rng(251); - macro_rules! t { - ($ty:ident, $v:expr, $le:expr, $lt:expr) => {{ - for &(low, high) in $v.iter() { - let my_uniform = Uniform::new(low, high).unwrap(); - for _ in 0..1000 { - let v: $ty = rng.sample(my_uniform); - assert!($le(low, v) && $lt(v, high)); - } - - let my_uniform = Uniform::new_inclusive(low, high).unwrap(); - for _ in 0..1000 { - let v: $ty = rng.sample(my_uniform); - assert!($le(low, v) && $le(v, high)); - } - - let my_uniform = Uniform::new(&low, high).unwrap(); - for _ in 0..1000 { - let v: $ty = rng.sample(my_uniform); - assert!($le(low, v) && $lt(v, high)); - } - - let my_uniform = Uniform::new_inclusive(&low, &high).unwrap(); - for _ in 0..1000 { - let v: $ty = rng.sample(my_uniform); - assert!($le(low, v) && $le(v, high)); - } - - for _ in 0..1000 { - let v = <$ty as SampleUniform>::Sampler::sample_single(low, high, &mut rng).unwrap(); - assert!($le(low, v) && $lt(v, high)); - } - - for _ in 0..1000 { - let v = <$ty as SampleUniform>::Sampler::sample_single_inclusive(low, high, &mut rng).unwrap(); - assert!($le(low, v) && $le(v, high)); - } - } - }}; - - // scalar bulk - ($($ty:ident),*) => {{ - $(t!( - $ty, - [(0, 10), (10, 127), ($ty::MIN, $ty::MAX)], - |x, y| x <= y, - |x, y| x < y - );)* - }}; - - // simd bulk - ($($ty:ident),* => $scalar:ident) => {{ - $(t!( - $ty, - [ - ($ty::splat(0), $ty::splat(10)), - ($ty::splat(10), $ty::splat(127)), - ($ty::splat($scalar::MIN), $ty::splat($scalar::MAX)), - ], - |x: $ty, y| x.simd_le(y).all(), - |x: $ty, y| x.simd_lt(y).all() - );)* - }}; - } - t!(i8, i16, i32, i64, i128, u8, u16, u32, u64, usize, u128); - - #[cfg(feature = "simd_support")] - { - t!(u8x4, u8x8, u8x16, u8x32, u8x64 => u8); - t!(i8x4, i8x8, i8x16, i8x32, i8x64 => i8); - t!(u16x2, u16x4, u16x8, u16x16, u16x32 => u16); - t!(i16x2, i16x4, i16x8, i16x16, i16x32 => i16); - t!(u32x2, u32x4, u32x8, u32x16 => u32); - t!(i32x2, i32x4, i32x8, i32x16 => i32); - t!(u64x2, u64x4, u64x8 => u64); - t!(i64x2, i64x4, i64x8 => i64); - } - } - - #[test] - fn test_uniform_from_std_range() { - let r = Uniform::try_from(2u32..7).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); - } - - #[test] - fn test_uniform_from_std_range_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100..10).is_err()); - assert!(Uniform::try_from(100..100).is_err()); - } - - #[test] - fn test_uniform_from_std_range_inclusive() { - let r = Uniform::try_from(2u32..=6).unwrap(); - assert_eq!(r.0.low, 2); - assert_eq!(r.0.range, 5); - } - - #[test] - fn test_uniform_from_std_range_inclusive_bad_limits() { - #![allow(clippy::reversed_empty_ranges)] - assert!(Uniform::try_from(100..=10).is_err()); - assert!(Uniform::try_from(100..=99).is_err()); - } - - #[test] - fn value_stability() { - fn test_samples>( - lb: T, - ub: T, - ub_excl: T, - expected: &[T], - ) where - Uniform: Distribution, - { - let mut rng = crate::test::rng(897); - let mut buf = [lb; 6]; - - for x in &mut buf[0..3] { - *x = T::Sampler::sample_single_inclusive(lb, ub, &mut rng).unwrap(); - } - - let distr = Uniform::new_inclusive(lb, ub).unwrap(); - for x in &mut buf[3..6] { - *x = rng.sample(&distr); - } - assert_eq!(&buf, expected); - - let mut rng = crate::test::rng(897); - - for x in &mut buf[0..3] { - *x = T::Sampler::sample_single(lb, ub_excl, &mut rng).unwrap(); - } - - let distr = Uniform::new(lb, ub_excl).unwrap(); - for x in &mut buf[3..6] { - *x = rng.sample(&distr); - } - assert_eq!(&buf, expected); - } - - test_samples(-105i8, 111, 112, &[-99, -48, 107, 72, -19, 56]); - test_samples(2i16, 1352, 1353, &[43, 361, 1325, 1109, 539, 1005]); - test_samples( - -313853i32, - 13513, - 13514, - &[-303803, -226673, 6912, -45605, -183505, -70668], - ); - test_samples( - 131521i64, - 6542165, - 6542166, - &[1838724, 5384489, 4893692, 3712948, 3951509, 4094926], - ); - test_samples( - -0x8000_0000_0000_0000_0000_0000_0000_0000i128, - -1, - 0, - &[ - -30725222750250982319765550926688025855, - -75088619368053423329503924805178012357, - -64950748766625548510467638647674468829, - -41794017901603587121582892414659436495, - -63623852319608406524605295913876414006, - -17404679390297612013597359206379189023, - ], - ); - test_samples(11u8, 218, 219, &[17, 66, 214, 181, 93, 165]); - test_samples(11u16, 218, 219, &[17, 66, 214, 181, 93, 165]); - test_samples(11u32, 218, 219, &[17, 66, 214, 181, 93, 165]); - test_samples(11u64, 218, 219, &[66, 181, 165, 127, 134, 139]); - test_samples(11u128, 218, 219, &[181, 127, 139, 167, 141, 197]); - test_samples(11usize, 218, 219, &[17, 66, 214, 181, 93, 165]); - - #[cfg(feature = "simd_support")] - { - let lb = Simd::from([11u8, 0, 128, 127]); - let ub = Simd::from([218, 254, 254, 254]); - let ub_excl = ub + Simd::splat(1); - test_samples( - lb, - ub, - ub_excl, - &[ - Simd::from([13, 5, 237, 130]), - Simd::from([126, 186, 149, 161]), - Simd::from([103, 86, 234, 252]), - Simd::from([35, 18, 225, 231]), - Simd::from([106, 153, 246, 177]), - Simd::from([195, 168, 149, 222]), - ], - ); - } - } - - #[test] - fn test_uniform_usize_empty_range() { - assert_eq!(UniformUsize::new(10, 10), Err(Error::EmptyRange)); - assert!(UniformUsize::new(10, 11).is_ok()); - - assert_eq!(UniformUsize::new_inclusive(10, 9), Err(Error::EmptyRange)); - assert!(UniformUsize::new_inclusive(10, 10).is_ok()); - } - - #[test] - fn test_uniform_usize_constructors() { - assert_eq!( - UniformUsize::new_inclusive(u32::MAX as usize, u32::MAX as usize), - Ok(UniformUsize { - low: u32::MAX as usize, - range: 1, - thresh: 0, - #[cfg(target_pointer_width = "64")] - mode64: false - }) - ); - assert_eq!( - UniformUsize::new_inclusive(0, u32::MAX as usize), - Ok(UniformUsize { - low: 0, - range: 0, - thresh: 0, - #[cfg(target_pointer_width = "64")] - mode64: false - }) - ); - #[cfg(target_pointer_width = "64")] - assert_eq!( - UniformUsize::new_inclusive(0, u32::MAX as usize + 1), - Ok(UniformUsize { - low: 0, - range: u32::MAX as usize + 2, - thresh: 1, - mode64: true - }) - ); - #[cfg(target_pointer_width = "64")] - assert_eq!( - UniformUsize::new_inclusive(u32::MAX as usize, u64::MAX as usize), - Ok(UniformUsize { - low: u32::MAX as usize, - range: u64::MAX as usize - u32::MAX as usize + 1, - thresh: u32::MAX as usize, - mode64: true - }) - ); - } - - // This could be run also on 32-bit when deserialization is implemented. - #[cfg(all(feature = "serde", target_pointer_width = "64"))] - #[test] - fn test_uniform_usize_deserialization() { - use serde_json; - let original = UniformUsize::new_inclusive(10, 100).expect("creation"); - let serialized = serde_json::to_string(&original).expect("serialization"); - let deserialized: UniformUsize = - serde_json::from_str(&serialized).expect("deserialization"); - assert_eq!(deserialized, original); - } - - #[cfg(all(feature = "serde", target_pointer_width = "64"))] - #[test] - fn test_uniform_usize_deserialization_from_32bit() { - use serde_json; - let serialized_on_32bit = r#"{"low":10,"range":91,"thresh":74}"#; - let deserialized: UniformUsize = - serde_json::from_str(serialized_on_32bit).expect("deserialization"); - assert_eq!( - deserialized, - UniformUsize::new_inclusive(10, 100).expect("creation") - ); - } - - #[cfg(all(feature = "serde", target_pointer_width = "64"))] - #[test] - fn test_uniform_usize_deserialization_64bit() { - use serde_json; - let original = UniformUsize::new_inclusive(1, u64::MAX as usize - 1).expect("creation"); - assert!(original.mode64); - let serialized = serde_json::to_string(&original).expect("serialization"); - let deserialized: UniformUsize = - serde_json::from_str(&serialized).expect("deserialization"); - assert_eq!(deserialized, original); - } -} diff --git a/src/distr/uniform_other.rs b/src/distr/uniform_other.rs deleted file mode 100644 index a1813b761ae..00000000000 --- a/src/distr/uniform_other.rs +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2018-2020 Developers of the Rand project. -// Copyright 2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! `UniformChar`, `UniformDuration` implementations - -use super::{Error, SampleBorrow, SampleUniform, Uniform, UniformInt, UniformSampler}; -use crate::Rng; -use crate::distr::Distribution; -use core::time::Duration; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -impl SampleUniform for char { - type Sampler = UniformChar; -} - -/// The back-end implementing [`UniformSampler`] for `char`. -/// -/// Unless you are implementing [`UniformSampler`] for your own type, this type -/// should not be used directly, use [`Uniform`] instead. -/// -/// This differs from integer range sampling since the range `0xD800..=0xDFFF` -/// are used for surrogate pairs in UCS and UTF-16, and consequently are not -/// valid Unicode code points. We must therefore avoid sampling values in this -/// range. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct UniformChar { - sampler: UniformInt, -} - -/// UTF-16 surrogate range start -const CHAR_SURROGATE_START: u32 = 0xD800; -/// UTF-16 surrogate range size -const CHAR_SURROGATE_LEN: u32 = 0xE000 - CHAR_SURROGATE_START; - -/// Convert `char` to compressed `u32` -fn char_to_comp_u32(c: char) -> u32 { - match c as u32 { - c if c >= CHAR_SURROGATE_START => c - CHAR_SURROGATE_LEN, - c => c, - } -} - -impl UniformSampler for UniformChar { - type X = char; - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = char_to_comp_u32(*low_b.borrow()); - let high = char_to_comp_u32(*high_b.borrow()); - let sampler = UniformInt::::new(low, high); - sampler.map(|sampler| UniformChar { sampler }) - } - - #[inline] // if the range is constant, this helps LLVM to do the - // calculations at compile-time. - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = char_to_comp_u32(*low_b.borrow()); - let high = char_to_comp_u32(*high_b.borrow()); - let sampler = UniformInt::::new_inclusive(low, high); - sampler.map(|sampler| UniformChar { sampler }) - } - - fn sample(&self, rng: &mut R) -> Self::X { - let mut x = self.sampler.sample(rng); - if x >= CHAR_SURROGATE_START { - x += CHAR_SURROGATE_LEN; - } - // SAFETY: x must not be in surrogate range or greater than char::MAX. - // This relies on range constructors which accept char arguments. - // Validity of input char values is assumed. - unsafe { core::char::from_u32_unchecked(x) } - } -} - -#[cfg(feature = "alloc")] -impl crate::distr::SampleString for Uniform { - fn append_string( - &self, - rng: &mut R, - string: &mut alloc::string::String, - len: usize, - ) { - // Getting the hi value to assume the required length to reserve in string. - let mut hi = self.0.sampler.low + self.0.sampler.range - 1; - if hi >= CHAR_SURROGATE_START { - hi += CHAR_SURROGATE_LEN; - } - // Get the utf8 length of hi to minimize extra space. - let max_char_len = char::from_u32(hi).map(char::len_utf8).unwrap_or(4); - string.reserve(max_char_len * len); - string.extend(self.sample_iter(rng).take(len)) - } -} - -/// The back-end implementing [`UniformSampler`] for `Duration`. -/// -/// Unless you are implementing [`UniformSampler`] for your own types, this type -/// should not be used directly, use [`Uniform`] instead. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct UniformDuration { - mode: UniformDurationMode, - offset: u32, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -enum UniformDurationMode { - Small { - secs: u64, - nanos: Uniform, - }, - Medium { - nanos: Uniform, - }, - Large { - max_secs: u64, - max_nanos: u32, - secs: Uniform, - }, -} - -impl SampleUniform for Duration { - type Sampler = UniformDuration; -} - -impl UniformSampler for UniformDuration { - type X = Duration; - - #[inline] - fn new(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low < high) { - return Err(Error::EmptyRange); - } - UniformDuration::new_inclusive(low, high - Duration::new(0, 1)) - } - - #[inline] - fn new_inclusive(low_b: B1, high_b: B2) -> Result - where - B1: SampleBorrow + Sized, - B2: SampleBorrow + Sized, - { - let low = *low_b.borrow(); - let high = *high_b.borrow(); - if !(low <= high) { - return Err(Error::EmptyRange); - } - - let low_s = low.as_secs(); - let low_n = low.subsec_nanos(); - let mut high_s = high.as_secs(); - let mut high_n = high.subsec_nanos(); - - if high_n < low_n { - high_s -= 1; - high_n += 1_000_000_000; - } - - let mode = if low_s == high_s { - UniformDurationMode::Small { - secs: low_s, - nanos: Uniform::new_inclusive(low_n, high_n)?, - } - } else { - let max = high_s - .checked_mul(1_000_000_000) - .and_then(|n| n.checked_add(u64::from(high_n))); - - if let Some(higher_bound) = max { - let lower_bound = low_s * 1_000_000_000 + u64::from(low_n); - UniformDurationMode::Medium { - nanos: Uniform::new_inclusive(lower_bound, higher_bound)?, - } - } else { - // An offset is applied to simplify generation of nanoseconds - let max_nanos = high_n - low_n; - UniformDurationMode::Large { - max_secs: high_s, - max_nanos, - secs: Uniform::new_inclusive(low_s, high_s)?, - } - } - }; - Ok(UniformDuration { - mode, - offset: low_n, - }) - } - - #[inline] - fn sample(&self, rng: &mut R) -> Duration { - match self.mode { - UniformDurationMode::Small { secs, nanos } => { - let n = nanos.sample(rng); - Duration::new(secs, n) - } - UniformDurationMode::Medium { nanos } => { - let nanos = nanos.sample(rng); - Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) - } - UniformDurationMode::Large { - max_secs, - max_nanos, - secs, - } => { - // constant folding means this is at least as fast as `Rng::sample(Range)` - let nano_range = Uniform::new(0, 1_000_000_000).unwrap(); - loop { - let s = secs.sample(rng); - let n = nano_range.sample(rng); - if !(s == max_secs && n > max_nanos) { - let sum = n + self.offset; - break Duration::new(s, sum); - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::RngExt; - - #[test] - #[cfg(feature = "serde")] - fn test_serialization_uniform_duration() { - let distr = UniformDuration::new(Duration::from_secs(10), Duration::from_secs(60)).unwrap(); - let de_distr: UniformDuration = - postcard::from_bytes(&postcard::to_allocvec(&distr).unwrap()).unwrap(); - assert_eq!(distr, de_distr); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_char() { - let mut rng = crate::test::rng(891); - let mut max = core::char::from_u32(0).unwrap(); - for _ in 0..100 { - let c = rng.random_range('A'..='Z'); - assert!(c.is_ascii_uppercase()); - max = max.max(c); - } - assert_eq!(max, 'Z'); - let d = Uniform::new( - core::char::from_u32(0xD7F0).unwrap(), - core::char::from_u32(0xE010).unwrap(), - ) - .unwrap(); - for _ in 0..100 { - let c = d.sample(&mut rng); - assert!((c as u32) < 0xD800 || (c as u32) > 0xDFFF); - } - #[cfg(feature = "alloc")] - { - use crate::distr::SampleString; - let string1 = d.sample_string(&mut rng, 100); - assert_eq!(string1.capacity(), 300); - let string2 = Uniform::new( - core::char::from_u32(0x0000).unwrap(), - core::char::from_u32(0x0080).unwrap(), - ) - .unwrap() - .sample_string(&mut rng, 100); - assert_eq!(string2.capacity(), 100); - let string3 = Uniform::new_inclusive( - core::char::from_u32(0x0000).unwrap(), - core::char::from_u32(0x0080).unwrap(), - ) - .unwrap() - .sample_string(&mut rng, 100); - assert_eq!(string3.capacity(), 200); - } - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_durations() { - let mut rng = crate::test::rng(253); - - let v = &[ - (Duration::new(10, 50000), Duration::new(100, 1234)), - (Duration::new(0, 100), Duration::new(1, 50)), - (Duration::new(0, 0), Duration::new(u64::MAX, 999_999_999)), - ]; - for &(low, high) in v.iter() { - let my_uniform = Uniform::new(low, high).unwrap(); - for _ in 0..1000 { - let v = rng.sample(my_uniform); - assert!(low <= v && v < high); - } - } - } -} diff --git a/src/distr/utils.rs b/src/distr/utils.rs deleted file mode 100644 index d3cfbf6a0f8..00000000000 --- a/src/distr/utils.rs +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Math helper functions - -#[cfg(feature = "simd_support")] -use core::simd::SimdElement; -#[cfg(feature = "simd_support")] -use core::simd::prelude::*; - -pub(crate) trait WideningMultiply { - type Output; - - fn wmul(self, x: RHS) -> Self::Output; -} - -macro_rules! wmul_impl { - ($ty:ty, $wide:ty, $shift:expr) => { - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - fn wmul(self, x: $ty) -> Self::Output { - let tmp = (self as $wide) * (x as $wide); - ((tmp >> $shift) as $ty, tmp as $ty) - } - } - }; - - // simd bulk implementation - ($(($ty:ident, $wide:ty),)+, $shift:expr) => { - $( - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - fn wmul(self, x: $ty) -> Self::Output { - // For supported vectors, this should compile to a couple - // supported multiply & swizzle instructions (no actual - // casting). - // TODO: optimize - let y: $wide = self.cast(); - let x: $wide = x.cast(); - let tmp = y * x; - let hi: $ty = (tmp >> Simd::splat($shift)).cast(); - let lo: $ty = tmp.cast(); - (hi, lo) - } - } - )+ - }; -} -wmul_impl! { u8, u16, 8 } -wmul_impl! { u16, u32, 16 } -wmul_impl! { u32, u64, 32 } -wmul_impl! { u64, u128, 64 } - -// This code is a translation of the __mulddi3 function in LLVM's -// compiler-rt. It is an optimised variant of the common method -// `(a + b) * (c + d) = ac + ad + bc + bd`. -// -// For some reason LLVM can optimise the C version very well, but -// keeps shuffling registers in this Rust translation. -macro_rules! wmul_impl_large { - ($ty:ty, $half:expr) => { - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - fn wmul(self, b: $ty) -> Self::Output { - const LOWER_MASK: $ty = !0 >> $half; - let mut low = (self & LOWER_MASK).wrapping_mul(b & LOWER_MASK); - let mut t = low >> $half; - low &= LOWER_MASK; - t += (self >> $half).wrapping_mul(b & LOWER_MASK); - low += (t & LOWER_MASK) << $half; - let mut high = t >> $half; - t = low >> $half; - low &= LOWER_MASK; - t += (b >> $half).wrapping_mul(self & LOWER_MASK); - low += (t & LOWER_MASK) << $half; - high += t >> $half; - high += (self >> $half).wrapping_mul(b >> $half); - - (high, low) - } - } - }; - - // simd bulk implementation - (($($ty:ty,)+) $scalar:ty, $half:expr) => { - $( - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - fn wmul(self, b: $ty) -> Self::Output { - // needs wrapping multiplication - 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) - } - } - )+ - }; -} -wmul_impl_large! { u128, 64 } - -macro_rules! wmul_impl_usize { - ($ty:ty) => { - impl WideningMultiply for usize { - type Output = (usize, usize); - - #[inline(always)] - fn wmul(self, x: usize) -> Self::Output { - let (high, low) = (self as $ty).wmul(x as $ty); - (high as usize, low as usize) - } - } - }; -} -#[cfg(target_pointer_width = "16")] -wmul_impl_usize! { u16 } -#[cfg(target_pointer_width = "32")] -wmul_impl_usize! { u32 } -#[cfg(target_pointer_width = "64")] -wmul_impl_usize! { u64 } - -#[cfg(feature = "simd_support")] -mod simd_wmul { - use super::*; - #[cfg(target_arch = "x86")] - use core::arch::x86::*; - #[cfg(target_arch = "x86_64")] - use core::arch::x86_64::*; - - wmul_impl! { - (u8x4, u16x4), - (u8x8, u16x8), - (u8x16, u16x16), - (u8x32, u16x32), - (u8x64, Simd),, - 8 - } - - wmul_impl! { (u16x2, u32x2),, 16 } - wmul_impl! { (u16x4, u32x4),, 16 } - #[cfg(not(target_feature = "sse2"))] - wmul_impl! { (u16x8, u32x8),, 16 } - #[cfg(not(target_feature = "avx2"))] - wmul_impl! { (u16x16, u32x16),, 16 } - #[cfg(not(target_feature = "avx512bw"))] - wmul_impl! { (u16x32, Simd),, 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, $mulhi:ident, $mullo:ident) => { - impl WideningMultiply for $ty { - type Output = ($ty, $ty); - - #[inline(always)] - #[allow(clippy::undocumented_unsafe_blocks)] - fn wmul(self, x: $ty) -> Self::Output { - let hi = unsafe { $mulhi(self.into(), x.into()) }.into(); - let lo = unsafe { $mullo(self.into(), x.into()) }.into(); - (hi, lo) - } - } - }; - } - - #[cfg(target_feature = "sse2")] - wmul_impl_16! { u16x8, _mm_mulhi_epu16, _mm_mullo_epi16 } - #[cfg(target_feature = "avx2")] - 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), - (u32x16, Simd),, - 32 - } - - wmul_impl_large! { (u64x2, u64x4, u64x8,) u64, 32 } -} - -/// Helper trait when dealing with scalar and SIMD floating point types. -pub(crate) trait FloatSIMDUtils { - // `PartialOrd` for vectors compares lexicographically. We want to compare all - // the individual SIMD lanes instead, and get the combined result over all - // lanes. This is possible using something like `a.lt(b).all()`, but we - // implement it as a trait so we can write the same code for `f32` and `f64`. - // Only the comparison functions we need are implemented. - fn all_lt(self, other: Self) -> bool; - fn all_le(self, other: Self) -> bool; - fn all_finite(self) -> bool; - - type Mask; - fn gt_mask(self, other: Self) -> Self::Mask; - - // Decrease all lanes where the mask is `true` to the next lower value - // representable by the floating-point type. At least one of the lanes - // must be set. - fn decrease_masked(self, mask: Self::Mask) -> Self; - - // Convert from int value. Conversion is done while retaining the numerical - // value, not by retaining the binary representation. - type UInt; - fn cast_from_int(i: Self::UInt) -> Self; -} - -#[cfg(test)] -pub(crate) trait FloatSIMDScalarUtils: FloatSIMDUtils { - type Scalar; - - fn replace(self, index: usize, new_value: Self::Scalar) -> Self; - fn extract_lane(self, index: usize) -> Self::Scalar; -} - -/// Implement functions on f32/f64 to give them APIs similar to SIMD types -pub(crate) trait FloatAsSIMD: Sized { - #[cfg(test)] - const LEN: usize = 1; - - #[inline(always)] - fn splat(scalar: Self) -> Self { - scalar - } -} - -pub(crate) trait IntAsSIMD: Sized { - #[inline(always)] - fn splat(scalar: Self) -> Self { - scalar - } -} - -impl IntAsSIMD for u32 {} -impl IntAsSIMD for u64 {} - -pub(crate) trait BoolAsSIMD: Sized { - fn any(self) -> bool; -} - -impl BoolAsSIMD for bool { - #[inline(always)] - fn any(self) -> bool { - self - } -} - -macro_rules! scalar_float_impl { - ($ty:ident, $uty:ident) => { - impl FloatSIMDUtils for $ty { - type Mask = bool; - type UInt = $uty; - - #[inline(always)] - fn all_lt(self, other: Self) -> bool { - self < other - } - - #[inline(always)] - fn all_le(self, other: Self) -> bool { - self <= other - } - - #[inline(always)] - fn all_finite(self) -> bool { - self.is_finite() - } - - #[inline(always)] - fn gt_mask(self, other: Self) -> Self::Mask { - self > other - } - - #[inline(always)] - fn decrease_masked(self, mask: Self::Mask) -> Self { - debug_assert!(mask, "At least one lane must be set"); - <$ty>::from_bits(self.to_bits() - 1) - } - - #[inline] - fn cast_from_int(i: Self::UInt) -> Self { - i as $ty - } - } - - #[cfg(test)] - impl FloatSIMDScalarUtils for $ty { - type Scalar = $ty; - - #[inline] - fn replace(self, index: usize, new_value: Self::Scalar) -> Self { - debug_assert_eq!(index, 0); - new_value - } - - #[inline] - fn extract_lane(self, index: usize) -> Self::Scalar { - debug_assert_eq!(index, 0); - self - } - } - - impl FloatAsSIMD for $ty {} - }; -} - -scalar_float_impl!(f32, u32); -scalar_float_impl!(f64, u64); - -#[cfg(feature = "simd_support")] -macro_rules! simd_impl { - ($fty:ident, $uty:ident) => { - impl FloatSIMDUtils for Simd<$fty, LANES> { - type Mask = Mask<<$fty as SimdElement>::Mask, LANES>; - type UInt = Simd<$uty, LANES>; - - #[inline(always)] - fn all_lt(self, other: Self) -> bool { - self.simd_lt(other).all() - } - - #[inline(always)] - fn all_le(self, other: Self) -> bool { - self.simd_le(other).all() - } - - #[inline(always)] - fn all_finite(self) -> bool { - self.is_finite().all() - } - - #[inline(always)] - fn gt_mask(self, other: Self) -> Self::Mask { - self.simd_gt(other) - } - - #[inline(always)] - fn decrease_masked(self, mask: Self::Mask) -> Self { - // Casting a mask into ints will produce all bits set for - // 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 $fty. This works even when the - // current value is infinity. - debug_assert!(mask.any(), "At least one lane must be set"); - Self::from_bits(self.to_bits() + mask.to_simd().cast()) - } - - #[inline] - fn cast_from_int(i: Self::UInt) -> Self { - i.cast() - } - } - - #[cfg(test)] - impl FloatSIMDScalarUtils for Simd<$fty, LANES> { - type Scalar = $fty; - - #[inline] - fn replace(mut self, index: usize, new_value: Self::Scalar) -> Self { - self.as_mut_array()[index] = new_value; - self - } - - #[inline] - fn extract_lane(self, index: usize) -> Self::Scalar { - self.as_array()[index] - } - } - }; -} - -#[cfg(feature = "simd_support")] -simd_impl!(f32, u32); -#[cfg(feature = "simd_support")] -simd_impl!(f64, u64); diff --git a/src/distr/weighted/mod.rs b/src/distr/weighted/mod.rs deleted file mode 100644 index 2bc012b1973..00000000000 --- a/src/distr/weighted/mod.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Weighted (index) sampling -//! -//! Primarily, this module houses the [`WeightedIndex`] distribution. -//! See also [`rand_distr::weighted`] for alternative implementations supporting -//! potentially-faster sampling or a more easily modifiable tree structure. -//! -//! [`rand_distr::weighted`]: https://docs.rs/rand_distr/latest/rand_distr/weighted/index.html - -use core::fmt; -mod weighted_index; - -pub use weighted_index::WeightedIndex; - -/// Bounds on a weight -/// -/// See usage in [`WeightedIndex`]. -pub trait Weight: Clone { - /// Representation of 0 - const ZERO: Self; - - /// Checked addition - /// - /// - `Result::Ok`: On success, `v` is added to `self` - /// - `Result::Err`: Returns an error when `Self` cannot represent the - /// result of `self + v` (i.e. overflow). The value of `self` should be - /// discarded. - #[allow(clippy::result_unit_err)] - fn checked_add_assign(&mut self, v: &Self) -> Result<(), ()>; -} - -macro_rules! impl_weight_int { - ($t:ty) => { - impl Weight for $t { - const ZERO: Self = 0; - fn checked_add_assign(&mut self, v: &Self) -> Result<(), ()> { - match self.checked_add(*v) { - Some(sum) => { - *self = sum; - Ok(()) - } - None => Err(()), - } - } - } - }; - ($t:ty, $($tt:ty),*) => { - impl_weight_int!($t); - impl_weight_int!($($tt),*); - } -} -impl_weight_int!(i8, i16, i32, i64, i128, isize); -impl_weight_int!(u8, u16, u32, u64, u128, usize); - -macro_rules! impl_weight_float { - ($t:ty) => { - impl Weight for $t { - const ZERO: Self = 0.0; - - fn checked_add_assign(&mut self, v: &Self) -> Result<(), ()> { - // Floats have an explicit representation for overflow - *self += *v; - Ok(()) - } - } - }; -} -impl_weight_float!(f32); -impl_weight_float!(f64); - -/// Invalid weight errors -/// -/// This type represents errors from [`WeightedIndex::new`], -/// [`WeightedIndex::update_weights`] and other weighted distributions. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -// Marked non_exhaustive to allow a new error code in the solution to #1476. -#[non_exhaustive] -pub enum Error { - /// The input weight sequence is empty, too long, or wrongly ordered - InvalidInput, - - /// A weight is negative, too large for the distribution, or not a valid number - InvalidWeight, - - /// Not enough non-zero weights are available to sample values - /// - /// When attempting to sample a single value this implies that all weights - /// are zero. When attempting to sample `amount` values this implies that - /// less than `amount` weights are greater than zero. - InsufficientNonZero, - - /// Overflow when calculating the sum of weights - Overflow, -} - -impl core::error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - Error::InvalidInput => "Weights sequence is empty/too long/unordered", - Error::InvalidWeight => "A weight is negative, too large or not a valid number", - Error::InsufficientNonZero => "Not enough weights > zero", - Error::Overflow => "Overflow when summing weights", - }) - } -} diff --git a/src/distr/weighted/weighted_index.rs b/src/distr/weighted/weighted_index.rs deleted file mode 100644 index ad8cf8ca637..00000000000 --- a/src/distr/weighted/weighted_index.rs +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::{Error, Weight}; -use crate::Rng; -use crate::distr::Distribution; -use crate::distr::uniform::{SampleBorrow, SampleUniform, UniformSampler}; - -// Note that this whole module is only imported if feature="alloc" is enabled. -use alloc::vec::Vec; -use core::fmt::{self, Debug}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// A distribution using weighted sampling of discrete items. -/// -/// 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 -/// weight of the element. The weights can use any type `X` for which an -/// implementation of [`Uniform`] exists. The implementation guarantees that -/// elements with zero weight are never picked, even when the weights are -/// floating point numbers. -/// -/// # Performance -/// -/// Time complexity of sampling from `WeightedIndex` is `O(log N)` where -/// `N` is the number of weights. -/// See also [`rand_distr::weighted`] for alternative implementations supporting -/// potentially-faster sampling or a more easily modifiable tree structure. -/// -/// A `WeightedIndex` contains a `Vec` and a [`Uniform`] and so its -/// size is the sum of the size of those objects, possibly plus some alignment. -/// -/// Creating a `WeightedIndex` will allocate enough space to hold `N - 1` -/// 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 an instance of `X::Sampler`, this might cause additional allocations, -/// though for primitive types, [`Uniform`] doesn't allocate any memory. -/// -/// Sampling from `WeightedIndex` will result in a single call to -/// `Uniform::sample` (method of the [`Distribution`] trait), which typically -/// will request a single value from the underlying [`Rng`], though the -/// exact number depends on the implementation of `Uniform::sample`. -/// -/// # Example -/// -/// ``` -/// use rand::prelude::*; -/// use rand::distr::weighted::WeightedIndex; -/// -/// let choices = ['a', 'b', 'c']; -/// let weights = [2, 1, 1]; -/// let dist = WeightedIndex::new(&weights).unwrap(); -/// let mut rng = rand::rng(); -/// for _ in 0..100 { -/// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c' -/// println!("{}", choices[dist.sample(&mut rng)]); -/// } -/// -/// 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' -/// println!("{}", items[dist2.sample(&mut rng)].0); -/// } -/// ``` -/// -/// [`Uniform`]: crate::distr::Uniform -/// [`Rng`]: crate::Rng -/// [`rand_distr::weighted`]: https://docs.rs/rand_distr/latest/rand_distr/weighted/index.html -#[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct WeightedIndex { - cumulative_weights: Vec, - total_weight: X, - weight_distribution: X::Sampler, -} - -impl WeightedIndex { - /// Creates a new a `WeightedIndex` [`Distribution`] using the values - /// in `weights`. The weights can use any type `X` for which an - /// implementation of [`Uniform`] exists. - /// - /// Error cases: - /// - [`Error::InvalidInput`] when the iterator `weights` is empty. - /// - [`Error::InvalidWeight`] when a weight is not-a-number or negative. - /// - [`Error::InsufficientNonZero`] when the sum of all weights is zero. - /// - [`Error::Overflow`] when the sum of all weights overflows. - /// - /// [`Uniform`]: crate::distr::uniform::Uniform - pub fn new(weights: I) -> Result, Error> - where - I: IntoIterator, - I::Item: SampleBorrow, - X: Weight, - { - let mut iter = weights.into_iter(); - let mut total_weight: X = iter.next().ok_or(Error::InvalidInput)?.borrow().clone(); - - let zero = X::ZERO; - if !(total_weight >= zero) { - return Err(Error::InvalidWeight); - } - - let mut weights = Vec::::with_capacity(iter.size_hint().0); - for w in iter { - // Note that `!(w >= x)` is not equivalent to `w < x` for partially - // ordered types due to NaNs which are equal to nothing. - if !(w.borrow() >= &zero) { - return Err(Error::InvalidWeight); - } - weights.push(total_weight.clone()); - - if let Err(()) = total_weight.checked_add_assign(w.borrow()) { - return Err(Error::Overflow); - } - } - - if total_weight == zero { - return Err(Error::InsufficientNonZero); - } - let distr = X::Sampler::new(zero, total_weight.clone()).unwrap(); - - Ok(WeightedIndex { - cumulative_weights: weights, - total_weight, - weight_distribution: distr, - }) - } - - /// Update a subset of weights, without changing the number of weights. - /// - /// `new_weights` must be sorted by the index. - /// - /// Using this method instead of `new` might be more efficient if only a small number of - /// weights is modified. No allocations are performed, unless the weight type `X` uses - /// allocation internally. - /// - /// In case of error, `self` is not modified. Error cases: - /// - [`Error::InvalidInput`] when `new_weights` are not ordered by - /// index or an index is too large. - /// - [`Error::InvalidWeight`] when a weight is not-a-number or negative. - /// - [`Error::InsufficientNonZero`] when the sum of all weights is zero. - /// Note that due to floating-point loss of precision, this case is not - /// always correctly detected; usage of a fixed-point weight type may be - /// preferred. - /// - /// Updates take `O(N)` time. If you need to frequently update weights, consider - /// [`rand_distr::weighted_tree`](https://docs.rs/rand_distr/*/rand_distr/weighted_tree/index.html) - /// as an alternative where an update is `O(log N)`. - pub fn update_weights(&mut self, new_weights: &[(usize, &X)]) -> Result<(), Error> - where - X: for<'a> core::ops::AddAssign<&'a X> - + for<'a> core::ops::SubAssign<&'a X> - + Clone - + Default, - { - if new_weights.is_empty() { - return Ok(()); - } - - let zero = ::default(); - - let mut total_weight = self.total_weight.clone(); - - // Check for errors first, so we don't modify `self` in case something - // goes wrong. - let mut prev_i = None; - for &(i, w) in new_weights { - if let Some(old_i) = prev_i { - if old_i >= i { - return Err(Error::InvalidInput); - } - } - if !(*w >= zero) { - return Err(Error::InvalidWeight); - } - if i > self.cumulative_weights.len() { - return Err(Error::InvalidInput); - } - - let mut old_w = if i < self.cumulative_weights.len() { - self.cumulative_weights[i].clone() - } else { - self.total_weight.clone() - }; - if i > 0 { - old_w -= &self.cumulative_weights[i - 1]; - } - - total_weight -= &old_w; - total_weight += w; - prev_i = Some(i); - } - if total_weight <= zero { - return Err(Error::InsufficientNonZero); - } - - // Update the weights. Because we checked all the preconditions in the - // previous loop, this should never panic. - let mut iter = new_weights.iter(); - - let mut prev_weight = zero.clone(); - let mut next_new_weight = iter.next(); - let &(first_new_index, _) = next_new_weight.unwrap(); - let mut cumulative_weight = if first_new_index > 0 { - self.cumulative_weights[first_new_index - 1].clone() - } else { - zero.clone() - }; - for i in first_new_index..self.cumulative_weights.len() { - match next_new_weight { - Some(&(j, w)) if i == j => { - cumulative_weight += w; - next_new_weight = iter.next(); - } - _ => { - let mut tmp = self.cumulative_weights[i].clone(); - tmp -= &prev_weight; // We know this is positive. - cumulative_weight += &tmp; - } - } - prev_weight = cumulative_weight.clone(); - core::mem::swap(&mut prev_weight, &mut self.cumulative_weights[i]); - } - - self.total_weight = total_weight; - self.weight_distribution = X::Sampler::new(zero, self.total_weight.clone()).unwrap(); - - Ok(()) - } -} - -/// A lazy-loading iterator over the weights of a `WeightedIndex` distribution. -/// This is returned by [`WeightedIndex::weights`]. -pub struct WeightedIndexIter<'a, X: SampleUniform + PartialOrd> { - weighted_index: &'a WeightedIndex, - index: usize, -} - -impl Debug for WeightedIndexIter<'_, X> -where - X: SampleUniform + PartialOrd + Debug, - X::Sampler: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("WeightedIndexIter") - .field("weighted_index", &self.weighted_index) - .field("index", &self.index) - .finish() - } -} - -impl Clone for WeightedIndexIter<'_, X> -where - X: SampleUniform + PartialOrd, -{ - fn clone(&self) -> Self { - WeightedIndexIter { - weighted_index: self.weighted_index, - index: self.index, - } - } -} - -impl Iterator for WeightedIndexIter<'_, X> -where - X: for<'b> core::ops::SubAssign<&'b X> + SampleUniform + PartialOrd + Clone, -{ - type Item = X; - - fn next(&mut self) -> Option { - match self.weighted_index.weight(self.index) { - None => None, - Some(weight) => { - self.index += 1; - Some(weight) - } - } - } -} - -impl WeightedIndex { - /// Returns the weight at the given index, if it exists. - /// - /// If the index is out of bounds, this will return `None`. - /// - /// # Example - /// - /// ``` - /// use rand::distr::weighted::WeightedIndex; - /// - /// let weights = [0, 1, 2]; - /// let dist = WeightedIndex::new(&weights).unwrap(); - /// assert_eq!(dist.weight(0), Some(0)); - /// assert_eq!(dist.weight(1), Some(1)); - /// assert_eq!(dist.weight(2), Some(2)); - /// assert_eq!(dist.weight(3), None); - /// ``` - pub fn weight(&self, index: usize) -> Option - where - X: for<'a> core::ops::SubAssign<&'a X>, - { - use core::cmp::Ordering::*; - - let mut weight = match index.cmp(&self.cumulative_weights.len()) { - Less => self.cumulative_weights[index].clone(), - Equal => self.total_weight.clone(), - Greater => return None, - }; - - if index > 0 { - weight -= &self.cumulative_weights[index - 1]; - } - Some(weight) - } - - /// Returns a lazy-loading iterator containing the current weights of this distribution. - /// - /// If this distribution has not been updated since its creation, this will return the - /// same weights as were passed to `new`. - /// - /// # Example - /// - /// ``` - /// use rand::distr::weighted::WeightedIndex; - /// - /// let weights = [1, 2, 3]; - /// let mut dist = WeightedIndex::new(&weights).unwrap(); - /// assert_eq!(dist.weights().collect::>(), vec![1, 2, 3]); - /// dist.update_weights(&[(0, &2)]).unwrap(); - /// assert_eq!(dist.weights().collect::>(), vec![2, 2, 3]); - /// ``` - pub fn weights(&self) -> WeightedIndexIter<'_, X> - where - X: for<'a> core::ops::SubAssign<&'a X>, - { - WeightedIndexIter { - weighted_index: self, - index: 0, - } - } - - /// Returns the sum of all weights in this distribution. - pub fn total_weight(&self) -> X { - self.total_weight.clone() - } -} - -impl Distribution for WeightedIndex -where - X: SampleUniform + PartialOrd, -{ - fn sample(&self, rng: &mut R) -> usize { - let chosen_weight = self.weight_distribution.sample(rng); - // Find the first item which has a weight *higher* than the chosen weight. - self.cumulative_weights - .partition_point(|w| w <= &chosen_weight) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::RngExt; - - #[cfg(feature = "serde")] - #[test] - fn test_weightedindex_serde() { - let weighted_index = WeightedIndex::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).unwrap(); - - let ser_weighted_index = postcard::to_allocvec(&weighted_index).unwrap(); - let de_weighted_index: WeightedIndex = - postcard::from_bytes(&ser_weighted_index).unwrap(); - - assert_eq!( - de_weighted_index.cumulative_weights, - weighted_index.cumulative_weights - ); - assert_eq!(de_weighted_index.total_weight, weighted_index.total_weight); - } - - #[test] - fn test_accepting_nan() { - assert_eq!( - WeightedIndex::new([f32::NAN, 0.5]).unwrap_err(), - Error::InvalidWeight, - ); - assert_eq!( - WeightedIndex::new([f32::NAN]).unwrap_err(), - Error::InvalidWeight, - ); - assert_eq!( - WeightedIndex::new([0.5, f32::NAN]).unwrap_err(), - Error::InvalidWeight, - ); - - assert_eq!( - WeightedIndex::new([0.5, 7.0]) - .unwrap() - .update_weights(&[(0, &f32::NAN)]) - .unwrap_err(), - Error::InvalidWeight, - ) - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_weightedindex() { - let mut r = crate::test::rng(700); - const N_REPS: u32 = 5000; - let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; - let total_weight = weights.iter().sum::() as f32; - - let verify = |result: [i32; 14]| { - for (i, count) in result.iter().enumerate() { - let exp = (weights[i] * N_REPS) as f32 / total_weight; - let mut err = (*count as f32 - exp).abs(); - if err != 0.0 { - err /= exp; - } - assert!(err <= 0.25); - } - }; - - // WeightedIndex from vec - let mut chosen = [0i32; 14]; - let distr = WeightedIndex::new(weights.to_vec()).unwrap(); - for _ in 0..N_REPS { - chosen[distr.sample(&mut r)] += 1; - } - verify(chosen); - - // WeightedIndex from slice - chosen = [0i32; 14]; - let distr = WeightedIndex::new(&weights[..]).unwrap(); - for _ in 0..N_REPS { - chosen[distr.sample(&mut r)] += 1; - } - verify(chosen); - - // WeightedIndex from iterator - chosen = [0i32; 14]; - let distr = WeightedIndex::new(weights.iter()).unwrap(); - for _ in 0..N_REPS { - chosen[distr.sample(&mut r)] += 1; - } - verify(chosen); - - for _ in 0..5 { - assert_eq!(WeightedIndex::new([0, 1]).unwrap().sample(&mut r), 1); - assert_eq!(WeightedIndex::new([1, 0]).unwrap().sample(&mut r), 0); - assert_eq!( - WeightedIndex::new([0, 0, 0, 0, 10, 0]) - .unwrap() - .sample(&mut r), - 4 - ); - } - - assert_eq!( - WeightedIndex::new(&[10][0..0]).unwrap_err(), - Error::InvalidInput - ); - assert_eq!( - WeightedIndex::new([0]).unwrap_err(), - Error::InsufficientNonZero - ); - assert_eq!( - WeightedIndex::new([10, 20, -1, 30]).unwrap_err(), - Error::InvalidWeight - ); - assert_eq!( - WeightedIndex::new([-10, 20, 1, 30]).unwrap_err(), - Error::InvalidWeight - ); - assert_eq!(WeightedIndex::new([-10]).unwrap_err(), Error::InvalidWeight); - } - - #[test] - fn test_update_weights() { - let data = [ - ( - &[10u32, 2, 3, 4][..], - &[(1, &100), (2, &4)][..], // positive change - &[10, 100, 4, 4][..], - ), - ( - &[1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7][..], - &[(2, &1), (5, &1), (13, &100)][..], // negative change and last element - &[1u32, 2, 1, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 100][..], - ), - ]; - - for (weights, update, expected_weights) in data.iter() { - let total_weight = weights.iter().sum::(); - let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); - assert_eq!(distr.total_weight, total_weight); - - distr.update_weights(update).unwrap(); - let expected_total_weight = expected_weights.iter().sum::(); - let expected_distr = WeightedIndex::new(expected_weights.to_vec()).unwrap(); - assert_eq!(distr.total_weight, expected_total_weight); - assert_eq!(distr.total_weight, expected_distr.total_weight); - assert_eq!(distr.cumulative_weights, expected_distr.cumulative_weights); - } - } - - #[test] - fn test_update_weights_errors() { - let data = [ - ( - &[1i32, 0, 0][..], - &[(0, &0)][..], - Error::InsufficientNonZero, - ), - ( - &[10, 10, 10, 10][..], - &[(1, &-11)][..], - Error::InvalidWeight, // A weight is negative - ), - ( - &[1, 2, 3, 4, 5][..], - &[(1, &5), (0, &5)][..], // Wrong order - Error::InvalidInput, - ), - ( - &[1][..], - &[(1, &1)][..], // Index too large - Error::InvalidInput, - ), - ]; - - for (weights, update, err) in data.iter() { - let total_weight = weights.iter().sum::(); - let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); - assert_eq!(distr.total_weight, total_weight); - match distr.update_weights(update) { - Ok(_) => panic!("Expected update_weights to fail, but it succeeded"), - Err(e) => assert_eq!(e, *err), - } - } - } - - #[test] - fn test_weight_at() { - let data = [ - &[1][..], - &[10, 2, 3, 4][..], - &[1, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7][..], - &[u32::MAX][..], - ]; - - for weights in data.iter() { - let distr = WeightedIndex::new(weights.to_vec()).unwrap(); - for (i, weight) in weights.iter().enumerate() { - assert_eq!(distr.weight(i), Some(*weight)); - } - assert_eq!(distr.weight(weights.len()), None); - } - } - - #[test] - fn test_weights() { - let data = [ - &[1][..], - &[10, 2, 3, 4][..], - &[1, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7][..], - &[u32::MAX][..], - ]; - - for weights in data.iter() { - let distr = WeightedIndex::new(weights.to_vec()).unwrap(); - assert_eq!(distr.weights().collect::>(), weights.to_vec()); - } - } - - #[test] - fn value_stability() { - fn test_samples( - weights: I, - buf: &mut [usize], - expected: &[usize], - ) where - I: IntoIterator, - I::Item: SampleBorrow, - { - assert_eq!(buf.len(), expected.len()); - let distr = WeightedIndex::new(weights).unwrap(); - let mut rng = crate::test::rng(701); - for r in buf.iter_mut() { - *r = rng.sample(&distr); - } - assert_eq!(buf, expected); - } - - let mut buf = [0; 10]; - test_samples( - [1i32, 1, 1, 1, 1, 1, 1, 1, 1], - &mut buf, - &[0, 6, 2, 6, 3, 4, 7, 8, 2, 5], - ); - test_samples( - [0.7f32, 0.1, 0.1, 0.1], - &mut buf, - &[0, 0, 0, 1, 0, 0, 2, 3, 0, 0], - ); - test_samples( - [1.0f64, 0.999, 0.998, 0.997], - &mut buf, - &[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])); - } - - #[test] - fn overflow() { - assert_eq!(WeightedIndex::new([2, usize::MAX]), Err(Error::Overflow)); - } -} diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs new file mode 100644 index 00000000000..1be1f8d59b2 --- /dev/null +++ b/src/distributions/exponential.rs @@ -0,0 +1,124 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The exponential distribution. + +use {Rng, Rand}; +use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; + +/// A wrapper around an `f64` to generate Exp(1) random numbers. +/// +/// See `Exp` for the general exponential distribution. +/// +/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The +/// exact description in the paper was adjusted to use tables for the +/// exponential distribution rather than normal. +/// +/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to +/// Generate Normal Random +/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield +/// College, Oxford +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::exponential::Exp1; +/// +/// let Exp1(x) = rand::random(); +/// println!("{}", x); +/// ``` +#[derive(Clone, Copy)] +pub struct Exp1(pub f64); + +// This could be done via `-rng.gen::().ln()` but that is slower. +impl Rand for Exp1 { + #[inline] + fn rand(rng: &mut R) -> Exp1 { + #[inline] + fn pdf(x: f64) -> f64 { + (-x).exp() + } + #[inline] + fn zero_case(rng: &mut R, _u: f64) -> f64 { + ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() + } + + Exp1(ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, + pdf, zero_case)) + } +} + +/// The exponential distribution `Exp(lambda)`. +/// +/// This distribution has density function: `f(x) = lambda * +/// exp(-lambda * x)` for `x > 0`. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{Exp, IndependentSample}; +/// +/// let exp = Exp::new(2.0); +/// let v = exp.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from a Exp(2) distribution", v); +/// ``` +#[derive(Clone, Copy)] +pub struct Exp { + /// `lambda` stored as `1/lambda`, since this is what we scale by. + lambda_inverse: f64 +} + +impl Exp { + /// Construct a new `Exp` with the given shape parameter + /// `lambda`. Panics if `lambda <= 0`. + #[inline] + pub fn new(lambda: f64) -> Exp { + assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0"); + Exp { lambda_inverse: 1.0 / lambda } + } +} + +impl Sample for Exp { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for Exp { + fn ind_sample(&self, rng: &mut R) -> f64 { + let Exp1(n) = rng.gen::(); + n * self.lambda_inverse + } +} + +#[cfg(test)] +mod test { + use distributions::{Sample, IndependentSample}; + use super::Exp; + + #[test] + fn test_exp() { + let mut exp = Exp::new(10.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + assert!(exp.sample(&mut rng) >= 0.0); + assert!(exp.ind_sample(&mut rng) >= 0.0); + } + } + #[test] + #[should_panic] + fn test_exp_invalid_lambda_zero() { + Exp::new(0.0); + } + #[test] + #[should_panic] + fn test_exp_invalid_lambda_neg() { + Exp::new(-10.0); + } +} diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs new file mode 100644 index 00000000000..7ecbc03fb3f --- /dev/null +++ b/src/distributions/gamma.rs @@ -0,0 +1,384 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// ignore-lexer-test FIXME #15679 + +//! The Gamma and derived distributions. + +use self::GammaRepr::*; +use self::ChiSquaredRepr::*; + +use {Rng, Open01}; +use super::normal::StandardNormal; +use super::{IndependentSample, Sample, Exp}; + +/// The Gamma distribution `Gamma(shape, scale)` distribution. +/// +/// The density function of this distribution is +/// +/// ```text +/// f(x) = x^(k - 1) * exp(-x / ΞΈ) / (Ξ“(k) * ΞΈ^k) +/// ``` +/// +/// where `Ξ“` is the Gamma function, `k` is the shape and `ΞΈ` is the +/// scale and both `k` and `ΞΈ` are strictly positive. +/// +/// The algorithm used is that described by Marsaglia & Tsang 2000[1], +/// falling back to directly sampling from an Exponential for `shape +/// == 1`, and using the boosting technique described in [1] for +/// `shape < 1`. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{IndependentSample, Gamma}; +/// +/// let gamma = Gamma::new(2.0, 5.0); +/// let v = gamma.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from a Gamma(2, 5) distribution", v); +/// ``` +/// +/// [1]: George Marsaglia and Wai Wan Tsang. 2000. "A Simple Method +/// for Generating Gamma Variables" *ACM Trans. Math. Softw.* 26, 3 +/// (September 2000), +/// 363-372. DOI:[10.1145/358407.358414](http://doi.acm.org/10.1145/358407.358414) +#[derive(Clone, Copy)] +pub struct Gamma { + repr: GammaRepr, +} + +#[derive(Clone, Copy)] +enum GammaRepr { + Large(GammaLargeShape), + One(Exp), + Small(GammaSmallShape) +} + +// These two helpers could be made public, but saving the +// match-on-Gamma-enum branch from using them directly (e.g. if one +// knows that the shape is always > 1) doesn't appear to be much +// faster. + +/// Gamma distribution where the shape parameter is less than 1. +/// +/// Note, samples from this require a compulsory floating-point `pow` +/// call, which makes it significantly slower than sampling from a +/// gamma distribution where the shape parameter is greater than or +/// equal to 1. +/// +/// See `Gamma` for sampling from a Gamma distribution with general +/// shape parameters. +#[derive(Clone, Copy)] +struct GammaSmallShape { + inv_shape: f64, + large_shape: GammaLargeShape +} + +/// Gamma distribution where the shape parameter is larger than 1. +/// +/// See `Gamma` for sampling from a Gamma distribution with general +/// shape parameters. +#[derive(Clone, Copy)] +struct GammaLargeShape { + scale: f64, + c: f64, + d: f64 +} + +impl Gamma { + /// Construct an object representing the `Gamma(shape, scale)` + /// distribution. + /// + /// Panics if `shape <= 0` or `scale <= 0`. + #[inline] + pub fn new(shape: f64, scale: f64) -> Gamma { + assert!(shape > 0.0, "Gamma::new called with shape <= 0"); + assert!(scale > 0.0, "Gamma::new called with scale <= 0"); + + let repr = match shape { + 1.0 => One(Exp::new(1.0 / scale)), + 0.0 ... 1.0 => Small(GammaSmallShape::new_raw(shape, scale)), + _ => Large(GammaLargeShape::new_raw(shape, scale)) + }; + Gamma { repr: repr } + } +} + +impl GammaSmallShape { + fn new_raw(shape: f64, scale: f64) -> GammaSmallShape { + GammaSmallShape { + inv_shape: 1. / shape, + large_shape: GammaLargeShape::new_raw(shape + 1.0, scale) + } + } +} + +impl GammaLargeShape { + fn new_raw(shape: f64, scale: f64) -> GammaLargeShape { + let d = shape - 1. / 3.; + GammaLargeShape { + scale: scale, + c: 1. / (9. * d).sqrt(), + d: d + } + } +} + +impl Sample for Gamma { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl Sample for GammaSmallShape { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl Sample for GammaLargeShape { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} + +impl IndependentSample for Gamma { + fn ind_sample(&self, rng: &mut R) -> f64 { + match self.repr { + Small(ref g) => g.ind_sample(rng), + One(ref g) => g.ind_sample(rng), + Large(ref g) => g.ind_sample(rng), + } + } +} +impl IndependentSample for GammaSmallShape { + fn ind_sample(&self, rng: &mut R) -> f64 { + let Open01(u) = rng.gen::>(); + + self.large_shape.ind_sample(rng) * u.powf(self.inv_shape) + } +} +impl IndependentSample for GammaLargeShape { + fn ind_sample(&self, rng: &mut R) -> f64 { + loop { + let StandardNormal(x) = rng.gen::(); + let v_cbrt = 1.0 + self.c * x; + if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0 + continue + } + + let v = v_cbrt * v_cbrt * v_cbrt; + let Open01(u) = rng.gen::>(); + + let x_sqr = x * x; + if u < 1.0 - 0.0331 * x_sqr * x_sqr || + u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) { + return self.d * v * self.scale + } + } + } +} + +/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of +/// freedom. +/// +/// For `k > 0` integral, this distribution is the sum of the squares +/// of `k` independent standard normal random variables. For other +/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2, +/// 2)`. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{ChiSquared, IndependentSample}; +/// +/// let chi = ChiSquared::new(11.0); +/// let v = chi.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from a χ²(11) distribution", v) +/// ``` +#[derive(Clone, Copy)] +pub struct ChiSquared { + repr: ChiSquaredRepr, +} + +#[derive(Clone, Copy)] +enum ChiSquaredRepr { + // k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1, + // e.g. when alpha = 1/2 as it would be for this case, so special- + // casing and using the definition of N(0,1)^2 is faster. + DoFExactlyOne, + DoFAnythingElse(Gamma), +} + +impl ChiSquared { + /// Create a new chi-squared distribution with degrees-of-freedom + /// `k`. Panics if `k < 0`. + pub fn new(k: f64) -> ChiSquared { + let repr = if k == 1.0 { + DoFExactlyOne + } else { + assert!(k > 0.0, "ChiSquared::new called with `k` < 0"); + DoFAnythingElse(Gamma::new(0.5 * k, 2.0)) + }; + ChiSquared { repr: repr } + } +} +impl Sample for ChiSquared { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for ChiSquared { + fn ind_sample(&self, rng: &mut R) -> f64 { + match self.repr { + DoFExactlyOne => { + // k == 1 => N(0,1)^2 + let StandardNormal(norm) = rng.gen::(); + norm * norm + } + DoFAnythingElse(ref g) => g.ind_sample(rng) + } + } +} + +/// The Fisher F distribution `F(m, n)`. +/// +/// This distribution is equivalent to the ratio of two normalised +/// chi-squared distributions, that is, `F(m,n) = (χ²(m)/m) / +/// (χ²(n)/n)`. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{FisherF, IndependentSample}; +/// +/// let f = FisherF::new(2.0, 32.0); +/// let v = f.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from an F(2, 32) distribution", v) +/// ``` +#[derive(Clone, Copy)] +pub struct FisherF { + numer: ChiSquared, + denom: ChiSquared, + // denom_dof / numer_dof so that this can just be a straight + // multiplication, rather than a division. + dof_ratio: f64, +} + +impl FisherF { + /// Create a new `FisherF` distribution, with the given + /// parameter. Panics if either `m` or `n` are not positive. + pub fn new(m: f64, n: f64) -> FisherF { + assert!(m > 0.0, "FisherF::new called with `m < 0`"); + assert!(n > 0.0, "FisherF::new called with `n < 0`"); + + FisherF { + numer: ChiSquared::new(m), + denom: ChiSquared::new(n), + dof_ratio: n / m + } + } +} +impl Sample for FisherF { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for FisherF { + fn ind_sample(&self, rng: &mut R) -> f64 { + self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio + } +} + +/// The Student t distribution, `t(nu)`, where `nu` is the degrees of +/// freedom. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{StudentT, IndependentSample}; +/// +/// let t = StudentT::new(11.0); +/// let v = t.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from a t(11) distribution", v) +/// ``` +#[derive(Clone, Copy)] +pub struct StudentT { + chi: ChiSquared, + dof: f64 +} + +impl StudentT { + /// Create a new Student t distribution with `n` degrees of + /// freedom. Panics if `n <= 0`. + pub fn new(n: f64) -> StudentT { + assert!(n > 0.0, "StudentT::new called with `n <= 0`"); + StudentT { + chi: ChiSquared::new(n), + dof: n + } + } +} +impl Sample for StudentT { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for StudentT { + fn ind_sample(&self, rng: &mut R) -> f64 { + let StandardNormal(norm) = rng.gen::(); + norm * (self.dof / self.chi.ind_sample(rng)).sqrt() + } +} + +#[cfg(test)] +mod test { + use distributions::{Sample, IndependentSample}; + use super::{ChiSquared, StudentT, FisherF}; + + #[test] + fn test_chi_squared_one() { + let mut chi = ChiSquared::new(1.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + chi.sample(&mut rng); + chi.ind_sample(&mut rng); + } + } + #[test] + fn test_chi_squared_small() { + let mut chi = ChiSquared::new(0.5); + let mut rng = ::test::rng(); + for _ in 0..1000 { + chi.sample(&mut rng); + chi.ind_sample(&mut rng); + } + } + #[test] + fn test_chi_squared_large() { + let mut chi = ChiSquared::new(30.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + chi.sample(&mut rng); + chi.ind_sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_chi_squared_invalid_dof() { + ChiSquared::new(-1.0); + } + + #[test] + fn test_f() { + let mut f = FisherF::new(2.0, 32.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + f.sample(&mut rng); + f.ind_sample(&mut rng); + } + } + + #[test] + fn test_t() { + let mut t = StudentT::new(11.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + t.sample(&mut rng); + t.ind_sample(&mut rng); + } + } +} diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs new file mode 100644 index 00000000000..8a9e6d21558 --- /dev/null +++ b/src/distributions/mod.rs @@ -0,0 +1,401 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Sampling from random distributions. +//! +//! This is a generalization of `Rand` to allow parameters to control the +//! exact properties of the generated values, e.g. the mean and standard +//! deviation of a normal distribution. The `Sample` trait is the most +//! general, and allows for generating values that change some state +//! internally. The `IndependentSample` trait is for generating values +//! that do not need to record state. + +#![allow(missing_debug_implementations)] + +use std::marker; + +use {Rng, Rand}; + +pub use self::range::Range; +pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; +pub use self::normal::{Normal, LogNormal}; +pub use self::exponential::Exp; + +pub mod range; +pub mod gamma; +pub mod normal; +pub mod exponential; + +/// Types that can be used to create a random instance of `Support`. +pub trait Sample { + /// Generate a random value of `Support`, using `rng` as the + /// source of randomness. + fn sample(&mut self, rng: &mut R) -> Support; +} + +/// `Sample`s that do not require keeping track of state. +/// +/// Since no state is recorded, each sample is (statistically) +/// independent of all others, assuming the `Rng` used has this +/// property. +// FIXME maybe having this separate is overkill (the only reason is to +// take &self rather than &mut self)? or maybe this should be the +// trait called `Sample` and the other should be `DependentSample`. +pub trait IndependentSample: Sample { + /// Generate a random value. + fn ind_sample(&self, &mut R) -> Support; +} + +/// A wrapper for generating types that implement `Rand` via the +/// `Sample` & `IndependentSample` traits. +pub struct RandSample { + _marker: marker::PhantomData Sup>, +} + +impl Copy for RandSample {} +impl Clone for RandSample { + fn clone(&self) -> Self { *self } +} + +impl Sample for RandSample { + fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } +} + +impl IndependentSample for RandSample { + fn ind_sample(&self, rng: &mut R) -> Sup { + rng.gen() + } +} + +impl RandSample { + pub fn new() -> RandSample { + RandSample { _marker: marker::PhantomData } + } +} + +/// A value with a particular weight for use with `WeightedChoice`. +#[derive(Copy)] +#[derive(Clone)] +pub struct Weighted { + /// The numerical weight of this item + pub weight: u32, + /// The actual item which is being weighted + pub item: T, +} + +/// A distribution that selects from a finite collection of weighted items. +/// +/// Each item has an associated weight that influences how likely it +/// is to be chosen: higher weight is more likely. +/// +/// The `Clone` restriction is a limitation of the `Sample` and +/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for +/// all `T`, as is `u32`, so one can store references or indices into +/// another vector. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{Weighted, WeightedChoice, IndependentSample}; +/// +/// let mut items = vec!(Weighted { weight: 2, item: 'a' }, +/// Weighted { weight: 4, item: 'b' }, +/// Weighted { weight: 1, item: 'c' }); +/// let wc = WeightedChoice::new(&mut items); +/// let mut rng = rand::thread_rng(); +/// for _ in 0..16 { +/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. +/// println!("{}", wc.ind_sample(&mut rng)); +/// } +/// ``` +pub struct WeightedChoice<'a, T:'a> { + items: &'a mut [Weighted], + weight_range: Range +} + +impl<'a, T: Clone> WeightedChoice<'a, T> { + /// Create a new `WeightedChoice`. + /// + /// Panics if: + /// - `v` is empty + /// - the total weight is 0 + /// - the total weight is larger than a `u32` can contain. + pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { + // strictly speaking, this is subsumed by the total weight == 0 case + assert!(!items.is_empty(), "WeightedChoice::new called with no items"); + + let mut running_total: u32 = 0; + + // we convert the list from individual weights to cumulative + // weights so we can binary search. This *could* drop elements + // with weight == 0 as an optimisation. + for item in items.iter_mut() { + running_total = match running_total.checked_add(item.weight) { + Some(n) => n, + None => panic!("WeightedChoice::new called with a total weight \ + larger than a u32 can contain") + }; + + item.weight = running_total; + } + assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); + + WeightedChoice { + items: items, + // we're likely to be generating numbers in this range + // relatively often, so might as well cache it + weight_range: Range::new(0, running_total) + } + } +} + +impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { + fn sample(&mut self, rng: &mut R) -> T { self.ind_sample(rng) } +} + +impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { + fn ind_sample(&self, rng: &mut R) -> T { + // we want to find the first element that has cumulative + // weight > sample_weight, which we do by binary since the + // cumulative weights of self.items are sorted. + + // choose a weight in [0, total_weight) + let sample_weight = self.weight_range.ind_sample(rng); + + // short circuit when it's the first item + if sample_weight < self.items[0].weight { + return self.items[0].item.clone(); + } + + let mut idx = 0; + let mut modifier = self.items.len(); + + // now we know that every possibility has an element to the + // left, so we can just search for the last element that has + // cumulative weight <= sample_weight, then the next one will + // be "it". (Note that this greatest element will never be the + // last element of the vector, since sample_weight is chosen + // in [0, total_weight) and the cumulative weight of the last + // one is exactly the total weight.) + while modifier > 1 { + let i = idx + modifier / 2; + if self.items[i].weight <= sample_weight { + // we're small, so look to the right, but allow this + // exact element still. + idx = i; + // we need the `/ 2` to round up otherwise we'll drop + // the trailing elements when `modifier` is odd. + modifier += 1; + } else { + // otherwise we're too big, so go left. (i.e. do + // nothing) + } + modifier /= 2; + } + return self.items[idx + 1].item.clone(); + } +} + +mod ziggurat_tables; + +/// Sample a random number using the Ziggurat method (specifically the +/// ZIGNOR variant from Doornik 2005). Most of the arguments are +/// directly from the paper: +/// +/// * `rng`: source of randomness +/// * `symmetric`: whether this is a symmetric distribution, or one-sided with P(x < 0) = 0. +/// * `X`: the $x_i$ abscissae. +/// * `F`: precomputed values of the PDF at the $x_i$, (i.e. $f(x_i)$) +/// * `F_DIFF`: precomputed values of $f(x_i) - f(x_{i+1})$ +/// * `pdf`: the probability density function +/// * `zero_case`: manual sampling from the tail when we chose the +/// bottom box (i.e. i == 0) + +// the perf improvement (25-50%) is definitely worth the extra code +// size from force-inlining. +#[inline(always)] +fn ziggurat( + rng: &mut R, + symmetric: bool, + x_tab: ziggurat_tables::ZigTable, + f_tab: ziggurat_tables::ZigTable, + mut pdf: P, + mut zero_case: Z) + -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { + const SCALE: f64 = (1u64 << 53) as f64; + loop { + // reimplement the f64 generation as an optimisation suggested + // by the Doornik paper: we have a lot of precision-space + // (i.e. there are 11 bits of the 64 of a u64 to use after + // creating a f64), so we might as well reuse some to save + // generating a whole extra random number. (Seems to be 15% + // faster.) + // + // This unfortunately misses out on the benefits of direct + // floating point generation if an RNG like dSMFT is + // used. (That is, such RNGs create floats directly, highly + // efficiently and overload next_f32/f64, so by not calling it + // this may be slower than it would be otherwise.) + // FIXME: investigate/optimise for the above. + let bits: u64 = rng.gen(); + let i = (bits & 0xff) as usize; + let f = (bits >> 11) as f64 / SCALE; + + // u is either U(-1, 1) or U(0, 1) depending on if this is a + // symmetric distribution or not. + let u = if symmetric {2.0 * f - 1.0} else {f}; + let x = u * x_tab[i]; + + let test_x = if symmetric { x.abs() } else {x}; + + // algebraically equivalent to |u| < x_tab[i+1]/x_tab[i] (or u < x_tab[i+1]/x_tab[i]) + if test_x < x_tab[i + 1] { + return x; + } + if i == 0 { + return zero_case(rng, u); + } + // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::() < pdf(x) { + return x; + } + } +} + +#[cfg(test)] +mod tests { + + use {Rng, Rand}; + use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; + + #[derive(PartialEq, Debug)] + struct ConstRand(usize); + impl Rand for ConstRand { + fn rand(_: &mut R) -> ConstRand { + ConstRand(0) + } + } + + // 0, 1, 2, 3, ... + struct CountingRng { i: u32 } + impl Rng for CountingRng { + fn next_u32(&mut self) -> u32 { + self.i += 1; + self.i - 1 + } + fn next_u64(&mut self) -> u64 { + self.next_u32() as u64 + } + } + + #[test] + fn test_rand_sample() { + let mut rand_sample = RandSample::::new(); + + assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); + assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0)); + } + #[test] + fn test_weighted_choice() { + // this makes assumptions about the internal implementation of + // WeightedChoice, specifically: it doesn't reorder the items, + // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to + // 1, internally; modulo a modulo operation). + + macro_rules! t { + ($items:expr, $expected:expr) => {{ + let mut items = $items; + let wc = WeightedChoice::new(&mut items); + let expected = $expected; + + let mut rng = CountingRng { i: 0 }; + + for &val in expected.iter() { + assert_eq!(wc.ind_sample(&mut rng), val) + } + }} + } + + t!(vec!(Weighted { weight: 1, item: 10}), [10]); + + // skip some + t!(vec!(Weighted { weight: 0, item: 20}, + Weighted { weight: 2, item: 21}, + Weighted { weight: 0, item: 22}, + Weighted { weight: 1, item: 23}), + [21,21, 23]); + + // different weights + t!(vec!(Weighted { weight: 4, item: 30}, + Weighted { weight: 3, item: 31}), + [30,30,30,30, 31,31,31]); + + // check that we're binary searching + // correctly with some vectors of odd + // length. + t!(vec!(Weighted { weight: 1, item: 40}, + Weighted { weight: 1, item: 41}, + Weighted { weight: 1, item: 42}, + Weighted { weight: 1, item: 43}, + Weighted { weight: 1, item: 44}), + [40, 41, 42, 43, 44]); + t!(vec!(Weighted { weight: 1, item: 50}, + Weighted { weight: 1, item: 51}, + Weighted { weight: 1, item: 52}, + Weighted { weight: 1, item: 53}, + Weighted { weight: 1, item: 54}, + Weighted { weight: 1, item: 55}, + Weighted { weight: 1, item: 56}), + [50, 51, 52, 53, 54, 55, 56]); + } + + #[test] + fn test_weighted_clone_initialization() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let clone = initial.clone(); + assert_eq!(initial.weight, clone.weight); + assert_eq!(initial.item, clone.item); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_weight() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.weight = 5; + assert_eq!(initial.weight, clone.weight); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_item() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.item = 5; + assert_eq!(initial.item, clone.item); + + } + + #[test] #[should_panic] + fn test_weighted_choice_no_items() { + WeightedChoice::::new(&mut []); + } + #[test] #[should_panic] + fn test_weighted_choice_zero_weight() { + WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, + Weighted { weight: 0, item: 1}]); + } + #[test] #[should_panic] + fn test_weighted_choice_weight_overflows() { + let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow + WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, + Weighted { weight: 1, item: 1 }, + Weighted { weight: x, item: 2 }, + Weighted { weight: 1, item: 3 }]); + } +} diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs new file mode 100644 index 00000000000..42872fa5190 --- /dev/null +++ b/src/distributions/normal.rs @@ -0,0 +1,201 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The normal and derived distributions. + +use {Rng, Rand, Open01}; +use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; + +/// A wrapper around an `f64` to generate N(0, 1) random numbers +/// (a.k.a. a standard normal, or Gaussian). +/// +/// See `Normal` for the general normal distribution. +/// +/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. +/// +/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to +/// Generate Normal Random +/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield +/// College, Oxford +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::normal::StandardNormal; +/// +/// let StandardNormal(x) = rand::random(); +/// println!("{}", x); +/// ``` +#[derive(Clone, Copy)] +pub struct StandardNormal(pub f64); + +impl Rand for StandardNormal { + fn rand(rng: &mut R) -> StandardNormal { + #[inline] + fn pdf(x: f64) -> f64 { + (-x*x/2.0).exp() + } + #[inline] + fn zero_case(rng: &mut R, u: f64) -> f64 { + // compute a random number in the tail by hand + + // strange initial conditions, because the loop is not + // do-while, so the condition should be true on the first + // run, they get overwritten anyway (0 < 1, so these are + // good). + let mut x = 1.0f64; + let mut y = 0.0f64; + + while -2.0 * y < x * x { + let Open01(x_) = rng.gen::>(); + let Open01(y_) = rng.gen::>(); + + x = x_.ln() / ziggurat_tables::ZIG_NORM_R; + y = y_.ln(); + } + + if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } + } + + StandardNormal(ziggurat( + rng, + true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, + pdf, zero_case)) + } +} + +/// The normal distribution `N(mean, std_dev**2)`. +/// +/// This uses the ZIGNOR variant of the Ziggurat method, see +/// `StandardNormal` for more details. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{Normal, IndependentSample}; +/// +/// // mean 2, standard deviation 3 +/// let normal = Normal::new(2.0, 3.0); +/// let v = normal.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from a N(2, 9) distribution", v) +/// ``` +#[derive(Clone, Copy)] +pub struct Normal { + mean: f64, + std_dev: f64, +} + +impl Normal { + /// Construct a new `Normal` distribution with the given mean and + /// standard deviation. + /// + /// # Panics + /// + /// Panics if `std_dev < 0`. + #[inline] + pub fn new(mean: f64, std_dev: f64) -> Normal { + assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0"); + Normal { + mean: mean, + std_dev: std_dev + } + } +} +impl Sample for Normal { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for Normal { + fn ind_sample(&self, rng: &mut R) -> f64 { + let StandardNormal(n) = rng.gen::(); + self.mean + self.std_dev * n + } +} + + +/// The log-normal distribution `ln N(mean, std_dev**2)`. +/// +/// If `X` is log-normal distributed, then `ln(X)` is `N(mean, +/// std_dev**2)` distributed. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{LogNormal, IndependentSample}; +/// +/// // mean 2, standard deviation 3 +/// let log_normal = LogNormal::new(2.0, 3.0); +/// let v = log_normal.ind_sample(&mut rand::thread_rng()); +/// println!("{} is from an ln N(2, 9) distribution", v) +/// ``` +#[derive(Clone, Copy)] +pub struct LogNormal { + norm: Normal +} + +impl LogNormal { + /// Construct a new `LogNormal` distribution with the given mean + /// and standard deviation. + /// + /// # Panics + /// + /// Panics if `std_dev < 0`. + #[inline] + pub fn new(mean: f64, std_dev: f64) -> LogNormal { + assert!(std_dev >= 0.0, "LogNormal::new called with `std_dev` < 0"); + LogNormal { norm: Normal::new(mean, std_dev) } + } +} +impl Sample for LogNormal { + fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } +} +impl IndependentSample for LogNormal { + fn ind_sample(&self, rng: &mut R) -> f64 { + self.norm.ind_sample(rng).exp() + } +} + +#[cfg(test)] +mod tests { + use distributions::{Sample, IndependentSample}; + use super::{Normal, LogNormal}; + + #[test] + fn test_normal() { + let mut norm = Normal::new(10.0, 10.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + norm.sample(&mut rng); + norm.ind_sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_normal_invalid_sd() { + Normal::new(10.0, -1.0); + } + + + #[test] + fn test_log_normal() { + let mut lnorm = LogNormal::new(10.0, 10.0); + let mut rng = ::test::rng(); + for _ in 0..1000 { + lnorm.sample(&mut rng); + lnorm.ind_sample(&mut rng); + } + } + #[test] + #[should_panic] + fn test_log_normal_invalid_sd() { + LogNormal::new(10.0, -1.0); + } +} diff --git a/src/distributions/range.rs b/src/distributions/range.rs new file mode 100644 index 00000000000..3a3f3e7e1a4 --- /dev/null +++ b/src/distributions/range.rs @@ -0,0 +1,233 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generating numbers between two others. + +// this is surprisingly complicated to be both generic & correct + +use std::num::Wrapping as w; + +use Rng; +use distributions::{Sample, IndependentSample}; + +/// Sample values uniformly between two bounds. +/// +/// This gives a uniform distribution (assuming the RNG used to sample +/// it is itself uniform & the `SampleRange` implementation for the +/// given type is correct), even for edge cases like `low = 0u8`, +/// `high = 170u8`, for which a naive modulo operation would return +/// numbers less than 85 with double the probability to those greater +/// than 85. +/// +/// Types should attempt to sample in `[low, high)`, i.e., not +/// including `high`, but this may be very difficult. All the +/// primitive integer types satisfy this property, and the float types +/// normally satisfy it, but rounding may mean `high` can occur. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::{IndependentSample, Range}; +/// +/// fn main() { +/// let between = Range::new(10, 10000); +/// let mut rng = rand::thread_rng(); +/// let mut sum = 0; +/// for _ in 0..1000 { +/// sum += between.ind_sample(&mut rng); +/// } +/// println!("{}", sum); +/// } +/// ``` +#[derive(Clone, Copy)] +pub struct Range { + low: X, + range: X, + accept_zone: X +} + +impl Range { + /// Create a new `Range` instance that samples uniformly from + /// `[low, high)`. Panics if `low >= high`. + pub fn new(low: X, high: X) -> Range { + assert!(low < high, "Range::new called with `low >= high`"); + SampleRange::construct_range(low, high) + } +} + +impl Sample for Range { + #[inline] + fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } +} +impl IndependentSample for Range { + fn ind_sample(&self, rng: &mut R) -> Sup { + SampleRange::sample_range(self, rng) + } +} + +/// The helper trait for types that have a sensible way to sample +/// uniformly between two values. This should not be used directly, +/// and is only to facilitate `Range`. +pub trait SampleRange : Sized { + /// Construct the `Range` object that `sample_range` + /// requires. This should not ever be called directly, only via + /// `Range::new`, which will check that `low < high`, so this + /// function doesn't have to repeat the check. + fn construct_range(low: Self, high: Self) -> Range; + + /// Sample a value from the given `Range` with the given `Rng` as + /// a source of randomness. + fn sample_range(r: &Range, rng: &mut R) -> Self; +} + +macro_rules! integer_impl { + ($ty:ty, $unsigned:ident) => { + impl SampleRange for $ty { + // we play free and fast with unsigned vs signed here + // (when $ty is signed), but that's fine, since the + // contract of this macro is for $ty and $unsigned to be + // "bit-equal", so casting between them is a no-op & a + // bijection. + + #[inline] + fn construct_range(low: $ty, high: $ty) -> Range<$ty> { + let range = (w(high as $unsigned) - w(low as $unsigned)).0; + let unsigned_max: $unsigned = ::std::$unsigned::MAX; + + // this is the largest number that fits into $unsigned + // that `range` divides evenly, so, if we've sampled + // `n` uniformly from this region, then `n % range` is + // uniform in [0, range) + let zone = unsigned_max - unsigned_max % range; + + Range { + low: low, + range: range as $ty, + accept_zone: zone as $ty + } + } + + #[inline] + fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + loop { + // rejection sample + let v = rng.gen::<$unsigned>(); + // until we find something that fits into the + // region which r.range evenly divides (this will + // be uniformly distributed) + if v < r.accept_zone as $unsigned { + // and return it, with some adjustments + return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0; + } + } + } + } + } +} + +integer_impl! { i8, u8 } +integer_impl! { i16, u16 } +integer_impl! { i32, u32 } +integer_impl! { i64, u64 } +integer_impl! { isize, usize } +integer_impl! { u8, u8 } +integer_impl! { u16, u16 } +integer_impl! { u32, u32 } +integer_impl! { u64, u64 } +integer_impl! { usize, usize } + +macro_rules! float_impl { + ($ty:ty) => { + impl SampleRange for $ty { + fn construct_range(low: $ty, high: $ty) -> Range<$ty> { + Range { + low: low, + range: high - low, + accept_zone: 0.0 // unused + } + } + fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + r.low + r.range * rng.gen::<$ty>() + } + } + } +} + +float_impl! { f32 } +float_impl! { f64 } + +#[cfg(test)] +mod tests { + use distributions::{Sample, IndependentSample}; + use super::Range as Range; + + #[should_panic] + #[test] + fn test_range_bad_limits_equal() { + Range::new(10, 10); + } + #[should_panic] + #[test] + fn test_range_bad_limits_flipped() { + Range::new(10, 5); + } + + #[test] + fn test_integers() { + let mut rng = ::test::rng(); + macro_rules! t { + ($($ty:ident),*) => {{ + $( + let v: &[($ty, $ty)] = &[(0, 10), + (10, 127), + (::std::$ty::MIN, ::std::$ty::MAX)]; + for &(low, high) in v.iter() { + let mut sampler: Range<$ty> = Range::new(low, high); + for _ in 0..1000 { + let v = sampler.sample(&mut rng); + assert!(low <= v && v < high); + let v = sampler.ind_sample(&mut rng); + assert!(low <= v && v < high); + } + } + )* + }} + } + t!(i8, i16, i32, i64, isize, + u8, u16, u32, u64, usize) + } + + #[test] + fn test_floats() { + let mut rng = ::test::rng(); + macro_rules! t { + ($($ty:ty),*) => {{ + $( + let v: &[($ty, $ty)] = &[(0.0, 100.0), + (-1e35, -1e25), + (1e-35, 1e-25), + (-1e35, 1e35)]; + for &(low, high) in v.iter() { + let mut sampler: Range<$ty> = Range::new(low, high); + for _ in 0..1000 { + let v = sampler.sample(&mut rng); + assert!(low <= v && v < high); + let v = sampler.ind_sample(&mut rng); + assert!(low <= v && v < high); + } + } + )* + }} + } + + t!(f32, f64) + } + +} diff --git a/src/distributions/ziggurat_tables.rs b/src/distributions/ziggurat_tables.rs new file mode 100644 index 00000000000..b6de4bf892c --- /dev/null +++ b/src/distributions/ziggurat_tables.rs @@ -0,0 +1,280 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tables for distributions which are sampled using the ziggurat +// algorithm. Autogenerated by `ziggurat_tables.py`. + +pub type ZigTable = &'static [f64; 257]; +pub const ZIG_NORM_R: f64 = 3.654152885361008796; +pub static ZIG_NORM_X: [f64; 257] = + [3.910757959537090045, 3.654152885361008796, 3.449278298560964462, 3.320244733839166074, + 3.224575052047029100, 3.147889289517149969, 3.083526132001233044, 3.027837791768635434, + 2.978603279880844834, 2.934366867207854224, 2.894121053612348060, 2.857138730872132548, + 2.822877396825325125, 2.790921174000785765, 2.760944005278822555, 2.732685359042827056, + 2.705933656121858100, 2.680514643284522158, 2.656283037575502437, 2.633116393630324570, + 2.610910518487548515, 2.589575986706995181, 2.569035452680536569, 2.549221550323460761, + 2.530075232158516929, 2.511544441625342294, 2.493583041269680667, 2.476149939669143318, + 2.459208374333311298, 2.442725318198956774, 2.426670984935725972, 2.411018413899685520, + 2.395743119780480601, 2.380822795170626005, 2.366237056715818632, 2.351967227377659952, + 2.337996148795031370, 2.324308018869623016, 2.310888250599850036, 2.297723348901329565, + 2.284800802722946056, 2.272108990226823888, 2.259637095172217780, 2.247375032945807760, + 2.235313384928327984, 2.223443340090905718, 2.211756642882544366, 2.200245546609647995, + 2.188902771624720689, 2.177721467738641614, 2.166695180352645966, 2.155817819875063268, + 2.145083634046203613, 2.134487182844320152, 2.124023315687815661, 2.113687150684933957, + 2.103474055713146829, 2.093379631137050279, 2.083399693996551783, 2.073530263516978778, + 2.063767547809956415, 2.054107931648864849, 2.044547965215732788, 2.035084353727808715, + 2.025713947862032960, 2.016433734904371722, 2.007240830558684852, 1.998132471356564244, + 1.989106007615571325, 1.980158896898598364, 1.971288697931769640, 1.962493064942461896, + 1.953769742382734043, 1.945116560006753925, 1.936531428273758904, 1.928012334050718257, + 1.919557336591228847, 1.911164563769282232, 1.902832208548446369, 1.894558525668710081, + 1.886341828534776388, 1.878180486290977669, 1.870072921069236838, 1.862017605397632281, + 1.854013059758148119, 1.846057850283119750, 1.838150586580728607, 1.830289919680666566, + 1.822474540091783224, 1.814703175964167636, 1.806974591348693426, 1.799287584547580199, + 1.791640986550010028, 1.784033659547276329, 1.776464495522344977, 1.768932414909077933, + 1.761436365316706665, 1.753975320315455111, 1.746548278279492994, 1.739154261283669012, + 1.731792314050707216, 1.724461502945775715, 1.717160915015540690, 1.709889657069006086, + 1.702646854797613907, 1.695431651932238548, 1.688243209434858727, 1.681080704722823338, + 1.673943330923760353, 1.666830296159286684, 1.659740822855789499, 1.652674147080648526, + 1.645629517902360339, 1.638606196773111146, 1.631603456932422036, 1.624620582830568427, + 1.617656869570534228, 1.610711622367333673, 1.603784156023583041, 1.596873794420261339, + 1.589979870021648534, 1.583101723393471438, 1.576238702733332886, 1.569390163412534456, + 1.562555467528439657, 1.555733983466554893, 1.548925085471535512, 1.542128153226347553, + 1.535342571438843118, 1.528567729435024614, 1.521803020758293101, 1.515047842773992404, + 1.508301596278571965, 1.501563685112706548, 1.494833515777718391, 1.488110497054654369, + 1.481394039625375747, 1.474683555695025516, 1.467978458615230908, 1.461278162507407830, + 1.454582081885523293, 1.447889631277669675, 1.441200224845798017, 1.434513276002946425, + 1.427828197027290358, 1.421144398672323117, 1.414461289772464658, 1.407778276843371534, + 1.401094763676202559, 1.394410150925071257, 1.387723835686884621, 1.381035211072741964, + 1.374343665770030531, 1.367648583594317957, 1.360949343030101844, 1.354245316759430606, + 1.347535871177359290, 1.340820365893152122, 1.334098153216083604, 1.327368577624624679, + 1.320630975217730096, 1.313884673146868964, 1.307128989027353860, 1.300363230327433728, + 1.293586693733517645, 1.286798664489786415, 1.279998415710333237, 1.273185207661843732, + 1.266358287014688333, 1.259516886060144225, 1.252660221891297887, 1.245787495544997903, + 1.238897891102027415, 1.231990574742445110, 1.225064693752808020, 1.218119375481726552, + 1.211153726239911244, 1.204166830140560140, 1.197157747875585931, 1.190125515422801650, + 1.183069142678760732, 1.175987612011489825, 1.168879876726833800, 1.161744859441574240, + 1.154581450355851802, 1.147388505416733873, 1.140164844363995789, 1.132909248648336975, + 1.125620459211294389, 1.118297174115062909, 1.110938046009249502, 1.103541679420268151, + 1.096106627847603487, 1.088631390649514197, 1.081114409698889389, 1.073554065787871714, + 1.065948674757506653, 1.058296483326006454, 1.050595664586207123, 1.042844313139370538, + 1.035040439828605274, 1.027181966030751292, 1.019266717460529215, 1.011292417434978441, + 1.003256679539591412, 0.995156999629943084, 0.986990747093846266, 0.978755155288937750, + 0.970447311058864615, 0.962064143217605250, 0.953602409875572654, 0.945058684462571130, + 0.936429340280896860, 0.927710533396234771, 0.918898183643734989, 0.909987953490768997, + 0.900975224455174528, 0.891855070726792376, 0.882622229578910122, 0.873271068082494550, + 0.863795545546826915, 0.854189171001560554, 0.844444954902423661, 0.834555354079518752, + 0.824512208745288633, 0.814306670128064347, 0.803929116982664893, 0.793369058833152785, + 0.782615023299588763, 0.771654424216739354, 0.760473406422083165, 0.749056662009581653, + 0.737387211425838629, 0.725446140901303549, 0.713212285182022732, 0.700661841097584448, + 0.687767892786257717, 0.674499822827436479, 0.660822574234205984, 0.646695714884388928, + 0.632072236375024632, 0.616896989996235545, 0.601104617743940417, 0.584616766093722262, + 0.567338257040473026, 0.549151702313026790, 0.529909720646495108, 0.509423329585933393, + 0.487443966121754335, 0.463634336771763245, 0.437518402186662658, 0.408389134588000746, + 0.375121332850465727, 0.335737519180459465, 0.286174591747260509, 0.215241895913273806, + 0.000000000000000000]; +pub static ZIG_NORM_F: [f64; 257] = + [0.000477467764586655, 0.001260285930498598, 0.002609072746106363, 0.004037972593371872, + 0.005522403299264754, 0.007050875471392110, 0.008616582769422917, 0.010214971439731100, + 0.011842757857943104, 0.013497450601780807, 0.015177088307982072, 0.016880083152595839, + 0.018605121275783350, 0.020351096230109354, 0.022117062707379922, 0.023902203305873237, + 0.025705804008632656, 0.027527235669693315, 0.029365939758230111, 0.031221417192023690, + 0.033093219458688698, 0.034980941461833073, 0.036884215688691151, 0.038802707404656918, + 0.040736110656078753, 0.042684144916619378, 0.044646552251446536, 0.046623094902089664, + 0.048613553216035145, 0.050617723861121788, 0.052635418276973649, 0.054666461325077916, + 0.056710690106399467, 0.058767952921137984, 0.060838108349751806, 0.062921024437977854, + 0.065016577971470438, 0.067124653828023989, 0.069245144397250269, 0.071377949059141965, + 0.073522973714240991, 0.075680130359194964, 0.077849336702372207, 0.080030515814947509, + 0.082223595813495684, 0.084428509570654661, 0.086645194450867782, 0.088873592068594229, + 0.091113648066700734, 0.093365311913026619, 0.095628536713353335, 0.097903279039215627, + 0.100189498769172020, 0.102487158942306270, 0.104796225622867056, 0.107116667775072880, + 0.109448457147210021, 0.111791568164245583, 0.114145977828255210, 0.116511665626037014, + 0.118888613443345698, 0.121276805485235437, 0.123676228202051403, 0.126086870220650349, + 0.128508722280473636, 0.130941777174128166, 0.133386029692162844, 0.135841476571757352, + 0.138308116449064322, 0.140785949814968309, 0.143274978974047118, 0.145775208006537926, + 0.148286642733128721, 0.150809290682410169, 0.153343161060837674, 0.155888264725064563, + 0.158444614156520225, 0.161012223438117663, 0.163591108232982951, 0.166181285765110071, + 0.168782774801850333, 0.171395595638155623, 0.174019770082499359, 0.176655321444406654, + 0.179302274523530397, 0.181960655600216487, 0.184630492427504539, 0.187311814224516926, + 0.190004651671193070, 0.192709036904328807, 0.195425003514885592, 0.198152586546538112, + 0.200891822495431333, 0.203642749311121501, 0.206405406398679298, 0.209179834621935651, + 0.211966076307852941, 0.214764175252008499, 0.217574176725178370, 0.220396127481011589, + 0.223230075764789593, 0.226076071323264877, 0.228934165415577484, 0.231804410825248525, + 0.234686861873252689, 0.237581574432173676, 0.240488605941449107, 0.243408015423711988, + 0.246339863502238771, 0.249284212419516704, 0.252241126056943765, 0.255210669955677150, + 0.258192911338648023, 0.261187919133763713, 0.264195763998317568, 0.267216518344631837, + 0.270250256366959984, 0.273297054069675804, 0.276356989296781264, 0.279430141762765316, + 0.282516593084849388, 0.285616426816658109, 0.288729728483353931, 0.291856585618280984, + 0.294997087801162572, 0.298151326697901342, 0.301319396102034120, 0.304501391977896274, + 0.307697412505553769, 0.310907558127563710, 0.314131931597630143, 0.317370638031222396, + 0.320623784958230129, 0.323891482377732021, 0.327173842814958593, 0.330470981380537099, + 0.333783015832108509, 0.337110066638412809, 0.340452257045945450, 0.343809713148291340, + 0.347182563958251478, 0.350570941482881204, 0.353974980801569250, 0.357394820147290515, + 0.360830600991175754, 0.364282468130549597, 0.367750569780596226, 0.371235057669821344, + 0.374736087139491414, 0.378253817247238111, 0.381788410875031348, 0.385340034841733958, + 0.388908860020464597, 0.392495061461010764, 0.396098818517547080, 0.399720314981931668, + 0.403359739222868885, 0.407017284331247953, 0.410693148271983222, 0.414387534042706784, + 0.418100649839684591, 0.421832709231353298, 0.425583931339900579, 0.429354541031341519, + 0.433144769114574058, 0.436954852549929273, 0.440785034667769915, 0.444635565397727750, + 0.448506701509214067, 0.452398706863882505, 0.456311852680773566, 0.460246417814923481, + 0.464202689050278838, 0.468180961407822172, 0.472181538469883255, 0.476204732721683788, + 0.480250865911249714, 0.484320269428911598, 0.488413284707712059, 0.492530263646148658, + 0.496671569054796314, 0.500837575128482149, 0.505028667945828791, 0.509245245998136142, + 0.513487720749743026, 0.517756517232200619, 0.522052074674794864, 0.526374847174186700, + 0.530725304406193921, 0.535103932383019565, 0.539511234259544614, 0.543947731192649941, + 0.548413963257921133, 0.552910490428519918, 0.557437893621486324, 0.561996775817277916, + 0.566587763258951771, 0.571211506738074970, 0.575868682975210544, 0.580559996103683473, + 0.585286179266300333, 0.590047996335791969, 0.594846243770991268, 0.599681752622167719, + 0.604555390700549533, 0.609468064928895381, 0.614420723892076803, 0.619414360609039205, + 0.624450015550274240, 0.629528779928128279, 0.634651799290960050, 0.639820277456438991, + 0.645035480824251883, 0.650298743114294586, 0.655611470583224665, 0.660975147780241357, + 0.666391343912380640, 0.671861719900766374, 0.677388036222513090, 0.682972161648791376, + 0.688616083008527058, 0.694321916130032579, 0.700091918140490099, 0.705928501336797409, + 0.711834248882358467, 0.717811932634901395, 0.723864533472881599, 0.729995264565802437, + 0.736207598131266683, 0.742505296344636245, 0.748892447223726720, 0.755373506511754500, + 0.761953346841546475, 0.768637315803334831, 0.775431304986138326, 0.782341832659861902, + 0.789376143571198563, 0.796542330428254619, 0.803849483176389490, 0.811307874318219935, + 0.818929191609414797, 0.826726833952094231, 0.834716292992930375, 0.842915653118441077, + 0.851346258465123684, 0.860033621203008636, 0.869008688043793165, 0.878309655816146839, + 0.887984660763399880, 0.898095921906304051, 0.908726440060562912, 0.919991505048360247, + 0.932060075968990209, 0.945198953453078028, 0.959879091812415930, 0.977101701282731328, + 1.000000000000000000]; +pub const ZIG_EXP_R: f64 = 7.697117470131050077; +pub static ZIG_EXP_X: [f64; 257] = + [8.697117470131052741, 7.697117470131050077, 6.941033629377212577, 6.478378493832569696, + 6.144164665772472667, 5.882144315795399869, 5.666410167454033697, 5.482890627526062488, + 5.323090505754398016, 5.181487281301500047, 5.054288489981304089, 4.938777085901250530, + 4.832939741025112035, 4.735242996601741083, 4.644491885420085175, 4.559737061707351380, + 4.480211746528421912, 4.405287693473573185, 4.334443680317273007, 4.267242480277365857, + 4.203313713735184365, 4.142340865664051464, 4.084051310408297830, 4.028208544647936762, + 3.974606066673788796, 3.923062500135489739, 3.873417670399509127, 3.825529418522336744, + 3.779270992411667862, 3.734528894039797375, 3.691201090237418825, 3.649195515760853770, + 3.608428813128909507, 3.568825265648337020, 3.530315889129343354, 3.492837654774059608, + 3.456332821132760191, 3.420748357251119920, 3.386035442460300970, 3.352149030900109405, + 3.319047470970748037, 3.286692171599068679, 3.255047308570449882, 3.224079565286264160, + 3.193757903212240290, 3.164053358025972873, 3.134938858084440394, 3.106389062339824481, + 3.078380215254090224, 3.050890016615455114, 3.023897504455676621, 2.997382949516130601, + 2.971327759921089662, 2.945714394895045718, 2.920526286512740821, 2.895747768600141825, + 2.871364012015536371, 2.847360965635188812, 2.823725302450035279, 2.800444370250737780, + 2.777506146439756574, 2.754899196562344610, 2.732612636194700073, 2.710636095867928752, + 2.688959688741803689, 2.667573980773266573, 2.646469963151809157, 2.625639026797788489, + 2.605072938740835564, 2.584763820214140750, 2.564704126316905253, 2.544886627111869970, + 2.525304390037828028, 2.505950763528594027, 2.486819361740209455, 2.467904050297364815, + 2.449198932978249754, 2.430698339264419694, 2.412396812688870629, 2.394289099921457886, + 2.376370140536140596, 2.358635057409337321, 2.341079147703034380, 2.323697874390196372, + 2.306486858283579799, 2.289441870532269441, 2.272558825553154804, 2.255833774367219213, + 2.239262898312909034, 2.222842503111036816, 2.206569013257663858, 2.190438966723220027, + 2.174449009937774679, 2.158595893043885994, 2.142876465399842001, 2.127287671317368289, + 2.111826546019042183, 2.096490211801715020, 2.081275874393225145, 2.066180819490575526, + 2.051202409468584786, 2.036338080248769611, 2.021585338318926173, 2.006941757894518563, + 1.992404978213576650, 1.977972700957360441, 1.963642687789548313, 1.949412758007184943, + 1.935280786297051359, 1.921244700591528076, 1.907302480018387536, 1.893452152939308242, + 1.879691795072211180, 1.866019527692827973, 1.852433515911175554, 1.838931967018879954, + 1.825513128903519799, 1.812175288526390649, 1.798916770460290859, 1.785735935484126014, + 1.772631179231305643, 1.759600930889074766, 1.746643651946074405, 1.733757834985571566, + 1.720942002521935299, 1.708194705878057773, 1.695514524101537912, 1.682900062917553896, + 1.670349953716452118, 1.657862852574172763, 1.645437439303723659, 1.633072416535991334, + 1.620766508828257901, 1.608518461798858379, 1.596327041286483395, 1.584191032532688892, + 1.572109239386229707, 1.560080483527888084, 1.548103603714513499, 1.536177455041032092, + 1.524300908219226258, 1.512472848872117082, 1.500692176842816750, 1.488957805516746058, + 1.477268661156133867, 1.465623682245745352, 1.454021818848793446, 1.442462031972012504, + 1.430943292938879674, 1.419464582769983219, 1.408024891569535697, 1.396623217917042137, + 1.385258568263121992, 1.373929956328490576, 1.362636402505086775, 1.351376933258335189, + 1.340150580529504643, 1.328956381137116560, 1.317793376176324749, 1.306660610415174117, + 1.295557131686601027, 1.284481990275012642, 1.273434238296241139, 1.262412929069615330, + 1.251417116480852521, 1.240445854334406572, 1.229498195693849105, 1.218573192208790124, + 1.207669893426761121, 1.196787346088403092, 1.185924593404202199, 1.175080674310911677, + 1.164254622705678921, 1.153445466655774743, 1.142652227581672841, 1.131873919411078511, + 1.121109547701330200, 1.110358108727411031, 1.099618588532597308, 1.088889961938546813, + 1.078171191511372307, 1.067461226479967662, 1.056759001602551429, 1.046063435977044209, + 1.035373431790528542, 1.024687873002617211, 1.014005623957096480, 1.003325527915696735, + 0.992646405507275897, 0.981967053085062602, 0.971286240983903260, 0.960602711668666509, + 0.949915177764075969, 0.939222319955262286, 0.928522784747210395, 0.917815182070044311, + 0.907098082715690257, 0.896370015589889935, 0.885629464761751528, 0.874874866291025066, + 0.864104604811004484, 0.853317009842373353, 0.842510351810368485, 0.831682837734273206, + 0.820832606554411814, 0.809957724057418282, 0.799056177355487174, 0.788125868869492430, + 0.777164609759129710, 0.766170112735434672, 0.755139984181982249, 0.744071715500508102, + 0.732962673584365398, 0.721810090308756203, 0.710611050909655040, 0.699362481103231959, + 0.688061132773747808, 0.676703568029522584, 0.665286141392677943, 0.653804979847664947, + 0.642255960424536365, 0.630634684933490286, 0.618936451394876075, 0.607156221620300030, + 0.595288584291502887, 0.583327712748769489, 0.571267316532588332, 0.559100585511540626, + 0.546820125163310577, 0.534417881237165604, 0.521885051592135052, 0.509211982443654398, + 0.496388045518671162, 0.483401491653461857, 0.470239275082169006, 0.456886840931420235, + 0.443327866073552401, 0.429543940225410703, 0.415514169600356364, 0.401214678896277765, + 0.386617977941119573, 0.371692145329917234, 0.356399760258393816, 0.340696481064849122, + 0.324529117016909452, 0.307832954674932158, 0.290527955491230394, 0.272513185478464703, + 0.253658363385912022, 0.233790483059674731, 0.212671510630966620, 0.189958689622431842, + 0.165127622564187282, 0.137304980940012589, 0.104838507565818778, 0.063852163815001570, + 0.000000000000000000]; +pub static ZIG_EXP_F: [f64; 257] = + [0.000167066692307963, 0.000454134353841497, 0.000967269282327174, 0.001536299780301573, + 0.002145967743718907, 0.002788798793574076, 0.003460264777836904, 0.004157295120833797, + 0.004877655983542396, 0.005619642207205489, 0.006381905937319183, 0.007163353183634991, + 0.007963077438017043, 0.008780314985808977, 0.009614413642502212, 0.010464810181029981, + 0.011331013597834600, 0.012212592426255378, 0.013109164931254991, 0.014020391403181943, + 0.014945968011691148, 0.015885621839973156, 0.016839106826039941, 0.017806200410911355, + 0.018786700744696024, 0.019780424338009740, 0.020787204072578114, 0.021806887504283581, + 0.022839335406385240, 0.023884420511558174, 0.024942026419731787, 0.026012046645134221, + 0.027094383780955803, 0.028188948763978646, 0.029295660224637411, 0.030414443910466622, + 0.031545232172893622, 0.032687963508959555, 0.033842582150874358, 0.035009037697397431, + 0.036187284781931443, 0.037377282772959382, 0.038578995503074871, 0.039792391023374139, + 0.041017441380414840, 0.042254122413316254, 0.043502413568888197, 0.044762297732943289, + 0.046033761076175184, 0.047316792913181561, 0.048611385573379504, 0.049917534282706379, + 0.051235237055126281, 0.052564494593071685, 0.053905310196046080, 0.055257689676697030, + 0.056621641283742870, 0.057997175631200659, 0.059384305633420280, 0.060783046445479660, + 0.062193415408541036, 0.063615431999807376, 0.065049117786753805, 0.066494496385339816, + 0.067951593421936643, 0.069420436498728783, 0.070901055162371843, 0.072393480875708752, + 0.073897746992364746, 0.075413888734058410, 0.076941943170480517, 0.078481949201606435, + 0.080033947542319905, 0.081597980709237419, 0.083174093009632397, 0.084762330532368146, + 0.086362741140756927, 0.087975374467270231, 0.089600281910032886, 0.091237516631040197, + 0.092887133556043569, 0.094549189376055873, 0.096223742550432825, 0.097910853311492213, + 0.099610583670637132, 0.101322997425953631, 0.103048160171257702, 0.104786139306570145, + 0.106537004050001632, 0.108300825451033755, 0.110077676405185357, 0.111867631670056283, + 0.113670767882744286, 0.115487163578633506, 0.117316899211555525, 0.119160057175327641, + 0.121016721826674792, 0.122886979509545108, 0.124770918580830933, 0.126668629437510671, + 0.128580204545228199, 0.130505738468330773, 0.132445327901387494, 0.134399071702213602, + 0.136367070926428829, 0.138349428863580176, 0.140346251074862399, 0.142357645432472146, + 0.144383722160634720, 0.146424593878344889, 0.148480375643866735, 0.150551185001039839, + 0.152637142027442801, 0.154738369384468027, 0.156854992369365148, 0.158987138969314129, + 0.161134939917591952, 0.163298528751901734, 0.165478041874935922, 0.167673618617250081, + 0.169885401302527550, 0.172113535315319977, 0.174358169171353411, 0.176619454590494829, + 0.178897546572478278, 0.181192603475496261, 0.183504787097767436, 0.185834262762197083, + 0.188181199404254262, 0.190545769663195363, 0.192928149976771296, 0.195328520679563189, + 0.197747066105098818, 0.200183974691911210, 0.202639439093708962, 0.205113656293837654, + 0.207606827724221982, 0.210119159388988230, 0.212650861992978224, 0.215202151075378628, + 0.217773247148700472, 0.220364375843359439, 0.222975768058120111, 0.225607660116683956, + 0.228260293930716618, 0.230933917169627356, 0.233628783437433291, 0.236345152457059560, + 0.239083290262449094, 0.241843469398877131, 0.244625969131892024, 0.247431075665327543, + 0.250259082368862240, 0.253110290015629402, 0.255985007030415324, 0.258883549749016173, + 0.261806242689362922, 0.264753418835062149, 0.267725419932044739, 0.270722596799059967, + 0.273745309652802915, 0.276793928448517301, 0.279868833236972869, 0.282970414538780746, + 0.286099073737076826, 0.289255223489677693, 0.292439288161892630, 0.295651704281261252, + 0.298892921015581847, 0.302163400675693528, 0.305463619244590256, 0.308794066934560185, + 0.312155248774179606, 0.315547685227128949, 0.318971912844957239, 0.322428484956089223, + 0.325917972393556354, 0.329440964264136438, 0.332998068761809096, 0.336589914028677717, + 0.340217149066780189, 0.343880444704502575, 0.347580494621637148, 0.351318016437483449, + 0.355093752866787626, 0.358908472948750001, 0.362762973354817997, 0.366658079781514379, + 0.370594648435146223, 0.374573567615902381, 0.378595759409581067, 0.382662181496010056, + 0.386773829084137932, 0.390931736984797384, 0.395136981833290435, 0.399390684475231350, + 0.403694012530530555, 0.408048183152032673, 0.412454465997161457, 0.416914186433003209, + 0.421428728997616908, 0.425999541143034677, 0.430628137288459167, 0.435316103215636907, + 0.440065100842354173, 0.444876873414548846, 0.449753251162755330, 0.454696157474615836, + 0.459707615642138023, 0.464789756250426511, 0.469944825283960310, 0.475175193037377708, + 0.480483363930454543, 0.485871987341885248, 0.491343869594032867, 0.496901987241549881, + 0.502549501841348056, 0.508289776410643213, 0.514126393814748894, 0.520063177368233931, + 0.526104213983620062, 0.532253880263043655, 0.538516872002862246, 0.544898237672440056, + 0.551403416540641733, 0.558038282262587892, 0.564809192912400615, 0.571723048664826150, + 0.578787358602845359, 0.586010318477268366, 0.593400901691733762, 0.600968966365232560, + 0.608725382079622346, 0.616682180915207878, 0.624852738703666200, 0.633251994214366398, + 0.641896716427266423, 0.650805833414571433, 0.660000841079000145, 0.669506316731925177, + 0.679350572264765806, 0.689566496117078431, 0.700192655082788606, 0.711274760805076456, + 0.722867659593572465, 0.735038092431424039, 0.747868621985195658, 0.761463388849896838, + 0.775956852040116218, 0.791527636972496285, 0.808421651523009044, 0.826993296643051101, + 0.847785500623990496, 0.871704332381204705, 0.900469929925747703, 0.938143680862176477, + 1.000000000000000000]; diff --git a/src/isaac.rs b/src/isaac.rs new file mode 100644 index 00000000000..b70a8e61d3f --- /dev/null +++ b/src/isaac.rs @@ -0,0 +1,635 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ISAAC random number generator. + +#![allow(non_camel_case_types)] + +use std::slice; +use std::iter::repeat; +use std::num::Wrapping as w; +use std::fmt; + +use {Rng, SeedableRng, Rand, w32, w64}; + +const RAND_SIZE_LEN: usize = 8; +const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; +const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; + +/// A random number generator that uses the ISAAC algorithm[1]. +/// +/// The ISAAC algorithm is generally accepted as suitable for +/// cryptographic purposes, but this implementation has not be +/// verified as such. Prefer a generator like `OsRng` that defers to +/// the operating system for cases that need high security. +/// +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number +/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +#[derive(Copy)] +pub struct IsaacRng { + cnt: u32, + rsl: [w32; RAND_SIZE_USIZE], + mem: [w32; RAND_SIZE_USIZE], + a: w32, + b: w32, + c: w32, +} + +static EMPTY: IsaacRng = IsaacRng { + cnt: 0, + rsl: [w(0); RAND_SIZE_USIZE], + mem: [w(0); RAND_SIZE_USIZE], + a: w(0), b: w(0), c: w(0), +}; + +impl IsaacRng { + + /// Create an ISAAC random number generator using the default + /// fixed seed. + pub fn new_unseeded() -> IsaacRng { + let mut rng = EMPTY; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + fn init(&mut self, use_rsl: bool) { + let mut a = w(0x9e3779b9); + let mut b = a; + let mut c = a; + let mut d = a; + let mut e = a; + let mut f = a; + let mut g = a; + let mut h = a; + + macro_rules! mix { + () => {{ + a=a^(b<<11); d=d+a; b=b+c; + b=b^(c>>2); e=e+b; c=c+d; + c=c^(d<<8); f=f+c; d=d+e; + d=d^(e>>16); g=g+d; e=e+f; + e=e^(f<<10); h=h+e; f=f+g; + f=f^(g>>4); a=a+f; g=g+h; + g=g^(h<<8); b=b+g; h=h+a; + h=h^(a>>9); c=c+h; a=a+b; + }} + } + + for _ in 0..4 { + mix!(); + } + + if use_rsl { + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { + a=a+$arr[i ]; b=b+$arr[i+1]; + c=c+$arr[i+2]; d=d+$arr[i+3]; + e=e+$arr[i+4]; f=f+$arr[i+5]; + g=g+$arr[i+6]; h=h+$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + } + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac(); + } + + /// Refills the output buffer (`self.rsl`) + #[inline] + fn isaac(&mut self) { + self.c = self.c + w(1); + // abbreviations + let mut a = self.a; + let mut b = self.b + self.c; + + const MIDPOINT: usize = RAND_SIZE_USIZE / 2; + + macro_rules! ind { + ($x:expr) => ( self.mem[($x >> 2usize).0 as usize & (RAND_SIZE_USIZE - 1)] ) + } + + let r = [(0, MIDPOINT), (MIDPOINT, 0)]; + for &(mr_offset, m2_offset) in r.iter() { + + macro_rules! rngstepp { + ($j:expr, $shift:expr) => {{ + let base = $j; + let mix = a << $shift; + + let x = self.mem[base + mr_offset]; + a = (a ^ mix) + self.mem[base + m2_offset]; + let y = ind!(x) + a + b; + self.mem[base + mr_offset] = y; + + b = ind!(y >> RAND_SIZE_LEN) + x; + self.rsl[base + mr_offset] = b; + }} + } + + macro_rules! rngstepn { + ($j:expr, $shift:expr) => {{ + let base = $j; + let mix = a >> $shift; + + let x = self.mem[base + mr_offset]; + a = (a ^ mix) + self.mem[base + m2_offset]; + let y = ind!(x) + a + b; + self.mem[base + mr_offset] = y; + + b = ind!(y >> RAND_SIZE_LEN) + x; + self.rsl[base + mr_offset] = b; + }} + } + + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstepp!(i + 0, 13); + rngstepn!(i + 1, 6); + rngstepp!(i + 2, 2); + rngstepn!(i + 3, 16); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE; + } +} + +// Cannot be derived because [u32; 256] does not implement Clone +impl Clone for IsaacRng { + fn clone(&self) -> IsaacRng { + *self + } +} + +impl Rng for IsaacRng { + #[inline] + fn next_u32(&mut self) -> u32 { + if self.cnt == 0 { + // make some more numbers + self.isaac(); + } + self.cnt -= 1; + + // self.cnt is at most RAND_SIZE, but that is before the + // subtraction above. We want to index without bounds + // checking, but this could lead to incorrect code if someone + // misrefactors, so we check, sometimes. + // + // (Changes here should be reflected in Isaac64Rng.next_u64.) + debug_assert!(self.cnt < RAND_SIZE); + + // (the % is cheaply telling the optimiser that we're always + // in bounds, without unsafe. NB. this is a power of two, so + // it optimises to a bitwise mask). + self.rsl[(self.cnt % RAND_SIZE) as usize].0 + } +} + +impl<'a> SeedableRng<&'a [u32]> for IsaacRng { + fn reseed(&mut self, seed: &'a [u32]) { + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); + + for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + self.cnt = 0; + self.a = w(0); + self.b = w(0); + self.c = w(0); + + self.init(true); + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u32]) -> IsaacRng { + let mut rng = EMPTY; + rng.reseed(seed); + rng + } +} + +impl Rand for IsaacRng { + fn rand(other: &mut R) -> IsaacRng { + let mut ret = EMPTY; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } +} + +impl fmt::Debug for IsaacRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacRng {{}}") + } +} + +const RAND_SIZE_64_LEN: usize = 8; +const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; + +/// A random number generator that uses ISAAC-64[1], the 64-bit +/// variant of the ISAAC algorithm. +/// +/// The ISAAC algorithm is generally accepted as suitable for +/// cryptographic purposes, but this implementation has not be +/// verified as such. Prefer a generator like `OsRng` that defers to +/// the operating system for cases that need high security. +/// +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number +/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +#[derive(Copy)] +pub struct Isaac64Rng { + cnt: usize, + rsl: [w64; RAND_SIZE_64], + mem: [w64; RAND_SIZE_64], + a: w64, + b: w64, + c: w64, +} + +static EMPTY_64: Isaac64Rng = Isaac64Rng { + cnt: 0, + rsl: [w(0); RAND_SIZE_64], + mem: [w(0); RAND_SIZE_64], + a: w(0), b: w(0), c: w(0), +}; + +impl Isaac64Rng { + /// Create a 64-bit ISAAC random number generator using the + /// default fixed seed. + pub fn new_unseeded() -> Isaac64Rng { + let mut rng = EMPTY_64; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + fn init(&mut self, use_rsl: bool) { + macro_rules! init { + ($var:ident) => ( + let mut $var = w(0x9e3779b97f4a7c13); + ) + } + init!(a); init!(b); init!(c); init!(d); + init!(e); init!(f); init!(g); init!(h); + + macro_rules! mix { + () => {{ + a=a-e; f=f^(h>>9); h=h+a; + b=b-f; g=g^(a<<9); a=a+b; + c=c-g; h=h^(b>>23); b=b+c; + d=d-h; a=a^(c<<15); c=c+d; + e=e-a; b=b^(d>>14); d=d+e; + f=f-b; c=c^(e<<20); e=e+f; + g=g-c; d=d^(f>>17); f=f+g; + h=h-d; e=e^(g<<14); g=g+h; + }} + } + + for _ in 0..4 { + mix!(); + } + + if use_rsl { + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + a=a+$arr[i ]; b=b+$arr[i+1]; + c=c+$arr[i+2]; d=d+$arr[i+3]; + e=e+$arr[i+4]; f=f+$arr[i+5]; + g=g+$arr[i+6]; h=h+$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + } + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac64(); + } + + /// Refills the output buffer (`self.rsl`) + fn isaac64(&mut self) { + self.c = self.c + w(1); + // abbreviations + let mut a = self.a; + let mut b = self.b + self.c; + const MIDPOINT: usize = RAND_SIZE_64 / 2; + const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; + macro_rules! ind { + ($x:expr) => { + *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) + } + } + + for &(mr_offset, m2_offset) in MP_VEC.iter() { + for base in (0..MIDPOINT / 4).map(|i| i * 4) { + + macro_rules! rngstepp { + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = a ^ (a << $shift); + let mix = if $j == 0 {!mix} else {mix}; + + unsafe { + let x = *self.mem.get_unchecked(base + mr_offset); + a = mix + *self.mem.get_unchecked(base + m2_offset); + let y = ind!(x) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y; + + b = ind!(y >> RAND_SIZE_64_LEN) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b; + } + }} + } + + macro_rules! rngstepn { + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = a ^ (a >> $shift); + let mix = if $j == 0 {!mix} else {mix}; + + unsafe { + let x = *self.mem.get_unchecked(base + mr_offset); + a = mix + *self.mem.get_unchecked(base + m2_offset); + let y = ind!(x) + a + b; + *self.mem.get_unchecked_mut(base + mr_offset) = y; + + b = ind!(y >> RAND_SIZE_64_LEN) + x; + *self.rsl.get_unchecked_mut(base + mr_offset) = b; + } + }} + } + + rngstepp!(0, 21); + rngstepn!(1, 5); + rngstepp!(2, 12); + rngstepn!(3, 33); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE_64; + } +} + +// Cannot be derived because [u32; 256] does not implement Clone +impl Clone for Isaac64Rng { + fn clone(&self) -> Isaac64Rng { + *self + } +} + +impl Rng for Isaac64Rng { + // FIXME #7771: having next_u32 like this should be unnecessary + #[inline] + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + + #[inline] + fn next_u64(&mut self) -> u64 { + if self.cnt == 0 { + // make some more numbers + self.isaac64(); + } + self.cnt -= 1; + + // See corresponding location in IsaacRng.next_u32 for + // explanation. + debug_assert!(self.cnt < RAND_SIZE_64); + self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 + } +} + +impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { + fn reseed(&mut self, seed: &'a [u64]) { + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); + + for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + self.cnt = 0; + self.a = w(0); + self.b = w(0); + self.c = w(0); + + self.init(true); + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u64]) -> Isaac64Rng { + let mut rng = EMPTY_64; + rng.reseed(seed); + rng + } +} + +impl Rand for Isaac64Rng { + fn rand(other: &mut R) -> Isaac64Rng { + let mut ret = EMPTY_64; + unsafe { + let ptr = ret.rsl.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + other.fill_bytes(slice); + } + ret.cnt = 0; + ret.a = w(0); + ret.b = w(0); + ret.c = w(0); + + ret.init(true); + return ret; + } +} + +impl fmt::Debug for Isaac64Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Rng {{}}") + } +} + +#[cfg(test)] +mod test { + use {Rng, SeedableRng}; + use super::{IsaacRng, Isaac64Rng}; + + #[test] + fn test_rng_32_rand_seeded() { + let s = ::test::rng().gen_iter::().take(256).collect::>(); + let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); + let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + #[test] + fn test_rng_64_rand_seeded() { + let s = ::test::rng().gen_iter::().take(256).collect::>(); + let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); + let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_32_seeded() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: IsaacRng = SeedableRng::from_seed(seed); + let mut rb: IsaacRng = SeedableRng::from_seed(seed); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + #[test] + fn test_rng_64_seeded() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_32_reseed() { + let s = ::test::rng().gen_iter::().take(256).collect::>(); + let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed(&s[..]); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + #[test] + fn test_rng_64_reseed() { + let s = ::test::rng().gen_iter::().take(256).collect::>(); + let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed(&s[..]); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + + #[test] + fn test_rng_32_true_values() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: IsaacRng = SeedableRng::from_seed(seed); + // Regression test that isaac is actually using the above vector + let v = (0..10).map(|_| ra.next_u32()).collect::>(); + assert_eq!(v, + vec!(2558573138, 873787463, 263499565, 2103644246, 3595684709, + 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); + + let seed: &[_] = &[12345, 67890, 54321, 9876]; + let mut rb: IsaacRng = SeedableRng::from_seed(seed); + // skip forward to the 10000th number + for _ in 0..10000 { rb.next_u32(); } + + let v = (0..10).map(|_| rb.next_u32()).collect::>(); + assert_eq!(v, + vec!(3676831399, 3183332890, 2834741178, 3854698763, 2717568474, + 1576568959, 3507990155, 179069555, 141456972, 2478885421)); + } + #[test] + fn test_rng_64_true_values() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + // Regression test that isaac is actually using the above vector + let v = (0..10).map(|_| ra.next_u64()).collect::>(); + assert_eq!(v, + vec!(547121783600835980, 14377643087320773276, 17351601304698403469, + 1238879483818134882, 11952566807690396487, 13970131091560099343, + 4469761996653280935, 15552757044682284409, 6860251611068737823, + 13722198873481261842)); + + let seed: &[_] = &[12345, 67890, 54321, 9876]; + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + // skip forward to the 10000th number + for _ in 0..10000 { rb.next_u64(); } + + let v = (0..10).map(|_| rb.next_u64()).collect::>(); + assert_eq!(v, + vec!(18143823860592706164, 8491801882678285927, 2699425367717515619, + 17196852593171130876, 2606123525235546165, 15790932315217671084, + 596345674630742204, 9947027391921273664, 11788097613744130851, + 10391409374914919106)); + } + + #[test] + fn test_rng_clone() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); + let mut clone = rng.clone(); + for _ in 0..16 { + assert_eq!(rng.next_u64(), clone.next_u64()); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e53c6b44f1d..955a3c8dee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,168 +1,938 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2017 The Rust Project Developers. +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license -// , at your +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Utilities for random number generation //! -//! Rand provides utilities to generate random numbers, to convert them to -//! useful types and distributions, and some randomness-related algorithms. +//! The key functions are `random()` and `Rng::gen()`. These are polymorphic and +//! so can be used to generate any type that implements `Rand`. Type inference +//! means that often a simple call to `rand::random()` or `rng.gen()` will +//! suffice, but sometimes an annotation is required, e.g. +//! `rand::random::()`. //! -//! # Quick Start +//! See the `distributions` submodule for sampling random numbers from +//! distributions like normal and exponential. //! +//! # Usage +//! +//! This crate is [on crates.io](https://crates.io/crates/rand) and can be +//! used by adding `rand` to the dependencies in your project's `Cargo.toml`. +//! +//! ```toml +//! [dependencies] +//! rand = "0.3" +//! ``` +//! +//! and this to your crate root: +//! +//! ```rust +//! extern crate rand; +//! ``` +//! +//! # Thread-local RNG +//! +//! There is built-in support for a RNG associated with each thread stored +//! in thread-local storage. This RNG can be accessed via `thread_rng`, or +//! used implicitly via `random`. This RNG is normally randomly seeded +//! from an operating-system source of randomness, e.g. `/dev/urandom` on +//! Unix systems, and will automatically reseed itself from this source +//! after generating 32 KiB of random data. +//! +//! # Cryptographic security +//! +//! An application that requires an entropy source for cryptographic purposes +//! must use `OsRng`, which reads randomness from the source that the operating +//! system provides (e.g. `/dev/urandom` on Unixes or `CryptGenRandom()` on +//! Windows). +//! The other random number generators provided by this module are not suitable +//! for such purposes. +//! +//! *Note*: many Unix systems provide `/dev/random` as well as `/dev/urandom`. +//! This module uses `/dev/urandom` for the following reasons: +//! +//! - On Linux, `/dev/random` may block if entropy pool is empty; +//! `/dev/urandom` will not block. This does not mean that `/dev/random` +//! provides better output than `/dev/urandom`; the kernel internally runs a +//! cryptographically secure pseudorandom number generator (CSPRNG) based on +//! entropy pool for random number generation, so the "quality" of +//! `/dev/random` is not better than `/dev/urandom` in most cases. However, +//! this means that `/dev/urandom` can yield somewhat predictable randomness +//! if the entropy pool is very small, such as immediately after first +//! booting. Linux 3.17 added the `getrandom(2)` system call which solves +//! the issue: it blocks if entropy pool is not initialized yet, but it does +//! not block once initialized. `OsRng` tries to use `getrandom(2)` if +//! available, and use `/dev/urandom` fallback if not. If an application +//! does not have `getrandom` and likely to be run soon after first booting, +//! or on a system with very few entropy sources, one should consider using +//! `/dev/random` via `ReadRng`. +//! - On some systems (e.g. FreeBSD, OpenBSD and Mac OS X) there is no +//! difference between the two sources. (Also note that, on some systems +//! e.g. FreeBSD, both `/dev/random` and `/dev/urandom` may block once if +//! the CSPRNG has not seeded yet.) +//! +//! # Examples +//! +//! ```rust +//! use rand::Rng; +//! +//! let mut rng = rand::thread_rng(); +//! if rng.gen() { // random bool +//! println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +//! } //! ``` -//! // The prelude import enables methods we use below, specifically -//! // Rng::random, Rng::sample, SliceRandom::shuffle and IndexedRandom::choose. -//! use rand::prelude::*; -//! -//! // Get an RNG: -//! let mut rng = rand::rng(); -//! -//! // Try printing a random unicode code point (probably a bad idea)! -//! println!("char: '{}'", rng.random::()); -//! // Try printing a random alphanumeric value instead! -//! println!("alpha: '{}'", rng.sample(rand::distr::Alphanumeric) as char); -//! -//! // Generate and shuffle a sequence: -//! let mut nums: Vec = (1..100).collect(); -//! nums.shuffle(&mut rng); -//! // And take a random pick (yes, we didn't need to shuffle first!): -//! let _ = nums.choose(&mut rng); +//! +//! ```rust +//! let tuple = rand::random::<(f64, char)>(); +//! println!("{:?}", tuple) +//! ``` +//! +//! ## Monte Carlo estimation of Ο€ +//! +//! For this example, imagine we have a square with sides of length 2 and a unit +//! circle, both centered at the origin. Since the area of a unit circle is Ο€, +//! we have: +//! +//! ```text +//! (area of unit circle) / (area of square) = Ο€ / 4 //! ``` //! -//! # The Book +//! 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. +//! +//! ``` +//! use rand::distributions::{IndependentSample, Range}; +//! +//! fn main() { +//! let between = Range::new(-1f64, 1.); +//! let mut rng = rand::thread_rng(); +//! +//! let total = 1_000_000; +//! let mut in_circle = 0; +//! +//! for _ in 0..total { +//! let a = between.ind_sample(&mut rng); +//! let b = between.ind_sample(&mut rng); +//! if a*a + b*b <= 1. { +//! in_circle += 1; +//! } +//! } +//! +//! // prints something close to 3.14159... +//! println!("{}", 4. * (in_circle as f64) / (total as f64)); +//! } +//! ``` +//! +//! ## Monty Hall Problem +//! +//! This is a simulation of the [Monty Hall Problem][]: +//! +//! > Suppose you're on a game show, and you're given the choice of three doors: +//! > Behind one door is a car; behind the others, goats. You pick a door, say +//! > No. 1, and the host, who knows what's behind the doors, opens another +//! > door, say No. 3, which has a goat. He then says to you, "Do you want to +//! > pick door No. 2?" Is it to your advantage to switch your choice? +//! +//! The rather unintuitive answer is that you will have a 2/3 chance of winning +//! if you switch and a 1/3 chance of winning if you don't, so it's better to +//! switch. +//! +//! This program will simulate the game show and with large enough simulation +//! steps it will indeed confirm that it is better to switch. +//! +//! [Monty Hall Problem]: http://en.wikipedia.org/wiki/Monty_Hall_problem +//! +//! ``` +//! use rand::Rng; +//! use rand::distributions::{IndependentSample, Range}; +//! +//! struct SimulationResult { +//! win: bool, +//! switch: bool, +//! } +//! +//! // Run a single simulation of the Monty Hall problem. +//! fn simulate(random_door: &Range, rng: &mut R) +//! -> SimulationResult { +//! let car = random_door.ind_sample(rng); +//! +//! // This is our initial choice +//! let mut choice = random_door.ind_sample(rng); //! -//! For the user guide and further documentation, please read -//! [The Rust Rand Book](https://rust-random.github.io/book). +//! // The game host opens a door +//! let open = game_host_open(car, choice, rng); +//! +//! // Shall we switch? +//! let switch = rng.gen(); +//! if switch { +//! choice = switch_door(choice, open); +//! } +//! +//! SimulationResult { win: choice == car, switch: switch } +//! } +//! +//! // Returns the door the game host opens given our choice and knowledge of +//! // where the car is. The game host will never open the door with the car. +//! fn game_host_open(car: u32, choice: u32, rng: &mut R) -> u32 { +//! let choices = free_doors(&[car, choice]); +//! rand::sample(rng, choices.into_iter(), 1)[0] +//! } +//! +//! // Returns the door we switch to, given our current choice and +//! // the open door. There will only be one valid door. +//! fn switch_door(choice: u32, open: u32) -> u32 { +//! free_doors(&[choice, open])[0] +//! } +//! +//! fn free_doors(blocked: &[u32]) -> Vec { +//! (0..3).filter(|x| !blocked.contains(x)).collect() +//! } +//! +//! fn main() { +//! // The estimation will be more accurate with more simulations +//! let num_simulations = 10000; +//! +//! let mut rng = rand::thread_rng(); +//! let random_door = Range::new(0, 3); +//! +//! let (mut switch_wins, mut switch_losses) = (0, 0); +//! let (mut keep_wins, mut keep_losses) = (0, 0); +//! +//! println!("Running {} simulations...", num_simulations); +//! for _ in 0..num_simulations { +//! let result = simulate(&random_door, &mut rng); +//! +//! match (result.win, result.switch) { +//! (true, true) => switch_wins += 1, +//! (true, false) => keep_wins += 1, +//! (false, true) => switch_losses += 1, +//! (false, false) => keep_losses += 1, +//! } +//! } +//! +//! let total_switches = switch_wins + switch_losses; +//! let total_keeps = keep_wins + keep_losses; +//! +//! println!("Switched door {} times with {} wins and {} losses", +//! total_switches, switch_wins, switch_losses); +//! +//! println!("Kept our choice {} times with {} wins and {} losses", +//! total_keeps, keep_wins, keep_losses); +//! +//! // With a large number of simulations, the values should converge to +//! // 0.667 and 0.333 respectively. +//! println!("Estimated chance to win if we switch: {}", +//! switch_wins as f32 / total_switches as f32); +//! println!("Estimated chance to win if we don't: {}", +//! keep_wins as f32 / total_keeps as f32); +//! } +//! ``` + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/rand/")] -#![doc( - html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", - html_favicon_url = "https://www.rust-lang.org/favicon.ico" -)] -#![deny(missing_docs)] #![deny(missing_debug_implementations)] -#![doc(test(attr(allow(unused_variables), deny(warnings))))] -#![no_std] -#![cfg_attr(feature = "simd_support", feature(portable_simd))] -#![cfg_attr( - all(feature = "simd_support", target_feature = "avx512bw"), - feature(stdarch_x86_avx512) -)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![allow( - clippy::float_cmp, - clippy::neg_cmp_op_on_partial_ord, - clippy::nonminimal_bool -)] -#![deny(clippy::undocumented_unsafe_blocks)] - -#[cfg(feature = "alloc")] -extern crate alloc; -#[cfg(feature = "std")] -extern crate std; - -// Re-export rand_core itself -pub use rand_core; - -// Re-exports from rand_core -pub use rand_core::{CryptoRng, Rng, SeedableRng, TryCryptoRng, TryRng}; - -#[macro_use] -mod log_macros; - -// Public modules -pub mod distr; -pub mod prelude; -mod rng; -pub mod rngs; -pub mod seq; - -// Public exports -#[cfg(feature = "thread_rng")] -pub use crate::rngs::thread::rng; - -pub use rng::{Fill, RngExt}; - -#[cfg(feature = "thread_rng")] -use crate::distr::{Distribution, StandardUniform}; - -/// Construct and seed an RNG -/// -/// This method yields a seeded RNG, using [`rng`] ([`ThreadRng`]) if enabled or -/// [`SysRng`] otherwise. + +#[cfg(test)] #[macro_use] extern crate log; + + +use std::cell::RefCell; +use std::marker; +use std::mem; +use std::io; +use std::rc::Rc; +use std::num::Wrapping as w; + +pub use os::OsRng; + +pub use isaac::{IsaacRng, Isaac64Rng}; +pub use chacha::ChaChaRng; + +#[cfg(target_pointer_width = "32")] +use IsaacRng as IsaacWordRng; +#[cfg(target_pointer_width = "64")] +use Isaac64Rng as IsaacWordRng; + +use distributions::{Range, IndependentSample}; +use distributions::range::SampleRange; + +pub mod distributions; +pub mod isaac; +pub mod chacha; +pub mod reseeding; +mod rand_impls; +pub mod os; +pub mod read; + +#[allow(bad_style)] +type w64 = w; +#[allow(bad_style)] +type w32 = w; + +/// A type that can be randomly generated using an `Rng`. +pub trait Rand : Sized { + /// Generates a random instance of this type using the specified source of + /// randomness. + fn rand(rng: &mut R) -> Self; +} + +/// A random number generator. +pub trait Rng { + /// Return the next random u32. + /// + /// This rarely needs to be called directly, prefer `r.gen()` to + /// `r.next_u32()`. + // FIXME #7771: Should be implemented in terms of next_u64 + fn next_u32(&mut self) -> u32; + + /// Return the next random u64. + /// + /// By default this is implemented in terms of `next_u32`. An + /// implementation of this trait must provide at least one of + /// these two methods. Similarly to `next_u32`, this rarely needs + /// to be called directly, prefer `r.gen()` to `r.next_u64()`. + fn next_u64(&mut self) -> u64 { + ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) + } + + /// Return the next random f32 selected from the half-open + /// interval `[0, 1)`. + /// + /// This uses a technique described by Saito and Matsumoto at + /// MCQMC'08. Given that the IEEE floating point numbers are + /// uniformly distributed over [1,2), we generate a number in + /// this range and then offset it onto the range [0,1). Our + /// choice of bits (masking v. shifting) is arbitrary and + /// should be immaterial for high quality generators. For low + /// quality generators (ex. LCG), prefer bitshifting due to + /// correlation between sequential low order bits. + /// + /// See: + /// A PRNG specialized in double precision floating point numbers using + /// an affine transition + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf + /// + /// By default this is implemented in terms of `next_u32`, but a + /// random number generator which can generate numbers satisfying + /// the requirements directly can overload this for performance. + /// It is required that the return value lies in `[0, 1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, and + /// `Open01` for the open interval `(0,1)`. + fn next_f32(&mut self) -> f32 { + const UPPER_MASK: u32 = 0x3F800000; + const LOWER_MASK: u32 = 0x7FFFFF; + let tmp = UPPER_MASK | (self.next_u32() & LOWER_MASK); + let result: f32 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } + + /// Return the next random f64 selected from the half-open + /// interval `[0, 1)`. + /// + /// By default this is implemented in terms of `next_u64`, but a + /// random number generator which can generate numbers satisfying + /// the requirements directly can overload this for performance. + /// It is required that the return value lies in `[0, 1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, and + /// `Open01` for the open interval `(0,1)`. + fn next_f64(&mut self) -> f64 { + const UPPER_MASK: u64 = 0x3FF0000000000000; + const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; + let tmp = UPPER_MASK | (self.next_u64() & LOWER_MASK); + let result: f64 = unsafe { mem::transmute(tmp) }; + result - 1.0 + } + + /// Fill `dest` with random data. + /// + /// This has a default implementation in terms of `next_u64` and + /// `next_u32`, but should be overridden by implementations that + /// offer a more efficient solution than just calling those + /// methods repeatedly. + /// + /// This method does *not* have a requirement to bear any fixed + /// relationship to the other methods, for example, it does *not* + /// have to result in the same output as progressively filling + /// `dest` with `self.gen::()`, and any such behaviour should + /// not be relied upon. + /// + /// This method should guarantee that `dest` is entirely filled + /// with new data, and may panic if this is impossible + /// (e.g. reading past the end of a file that is being used as the + /// source of randomness). + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut v = [0u8; 13579]; + /// thread_rng().fill_bytes(&mut v); + /// println!("{:?}", &v[..]); + /// ``` + fn fill_bytes(&mut self, dest: &mut [u8]) { + // this could, in theory, be done by transmuting dest to a + // [u64], but this is (1) likely to be undefined behaviour for + // LLVM, (2) has to be very careful about alignment concerns, + // (3) adds more `unsafe` that needs to be checked, (4) + // probably doesn't give much performance gain if + // optimisations are on. + let mut count = 0; + let mut num = 0; + for byte in dest.iter_mut() { + if count == 0 { + // we could micro-optimise here by generating a u32 if + // we only need a few more bytes to fill the vector + // (i.e. at most 4). + num = self.next_u64(); + count = 8; + } + + *byte = (num & 0xff) as u8; + num >>= 8; + count -= 1; + } + } + + /// Return a random value of a `Rand` type. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// let x: u32 = rng.gen(); + /// println!("{}", x); + /// println!("{:?}", rng.gen::<(f64, bool)>()); + /// ``` + #[inline(always)] + fn gen(&mut self) -> T where Self: Sized { + Rand::rand(self) + } + + /// Return an iterator that will yield an infinite number of randomly + /// generated items. + /// + /// # Example + /// + /// ``` + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// let x = rng.gen_iter::().take(10).collect::>(); + /// println!("{:?}", x); + /// println!("{:?}", rng.gen_iter::<(f64, bool)>().take(5) + /// .collect::>()); + /// ``` + fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized { + Generator { rng: self, _marker: marker::PhantomData } + } + + /// Generate a random value in the range [`low`, `high`). + /// + /// This is a convenience wrapper around + /// `distributions::Range`. If this function will be called + /// repeatedly with the same arguments, one should use `Range`, as + /// that will amortize the computations that allow for perfect + /// uniformity, as they only happen on initialization. + /// + /// # Panics + /// + /// Panics if `low >= high`. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// let n: u32 = rng.gen_range(0, 10); + /// println!("{}", n); + /// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64); + /// println!("{}", m); + /// ``` + fn gen_range(&mut self, low: T, high: T) -> T where Self: Sized { + assert!(low < high, "Rng.gen_range called with low >= high"); + Range::new(low, high).ind_sample(self) + } + + /// Return a bool with a 1 in n chance of true + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// println!("{}", rng.gen_weighted_bool(3)); + /// ``` + fn gen_weighted_bool(&mut self, n: u32) -> bool where Self: Sized { + n <= 1 || self.gen_range(0, n) == 0 + } + + /// Return an iterator of random characters from the set A-Z,a-z,0-9. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let s: String = thread_rng().gen_ascii_chars().take(10).collect(); + /// println!("{}", s); + /// ``` + fn gen_ascii_chars<'a>(&'a mut self) -> AsciiGenerator<'a, Self> where Self: Sized { + AsciiGenerator { rng: self } + } + + /// Return a random element from `values`. + /// + /// Return `None` if `values` is empty. + /// + /// # Example + /// + /// ``` + /// use rand::{thread_rng, Rng}; + /// + /// let choices = [1, 2, 4, 8, 16, 32]; + /// let mut rng = thread_rng(); + /// println!("{:?}", rng.choose(&choices)); + /// assert_eq!(rng.choose(&choices[..0]), None); + /// ``` + fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> where Self: Sized { + if values.is_empty() { + None + } else { + Some(&values[self.gen_range(0, values.len())]) + } + } + + /// Return a mutable pointer to a random element from `values`. + /// + /// Return `None` if `values` is empty. + fn choose_mut<'a, T>(&mut self, values: &'a mut [T]) -> Option<&'a mut T> where Self: Sized { + if values.is_empty() { + None + } else { + let len = values.len(); + Some(&mut values[self.gen_range(0, len)]) + } + } + + /// Shuffle a mutable slice in place. + /// + /// # Example + /// + /// ```rust + /// use rand::{thread_rng, Rng}; + /// + /// let mut rng = thread_rng(); + /// let mut y = [1, 2, 3]; + /// rng.shuffle(&mut y); + /// println!("{:?}", y); + /// rng.shuffle(&mut y); + /// println!("{:?}", y); + /// ``` + fn shuffle(&mut self, values: &mut [T]) where Self: Sized { + let mut i = values.len(); + while i >= 2 { + // invariant: elements with index >= i have been locked in place. + i -= 1; + // lock element i in place. + values.swap(i, self.gen_range(0, i + 1)); + } + } +} + +impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + fn next_f32(&mut self) -> f32 { + (**self).next_f32() + } + + fn next_f64(&mut self) -> f64 { + (**self).next_f64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } +} + +impl Rng for Box where R: Rng { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + fn next_f32(&mut self) -> f32 { + (**self).next_f32() + } + + fn next_f64(&mut self) -> f64 { + (**self).next_f64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } +} + +/// Iterator which will generate a stream of random items. /// -/// # Examples +/// This iterator is created via the [`gen_iter`] method on [`Rng`]. /// -/// ``` -/// let mut rng: rand::rngs::SmallRng = rand::make_rng(); -/// # let _ = rand::Rng::next_u32(&mut rng); -/// ``` +/// [`gen_iter`]: trait.Rng.html#method.gen_iter +/// [`Rng`]: trait.Rng.html +#[derive(Debug)] +pub struct Generator<'a, T, R:'a> { + rng: &'a mut R, + _marker: marker::PhantomData T>, +} + +impl<'a, T: Rand, R: Rng> Iterator for Generator<'a, T, R> { + type Item = T; + + fn next(&mut self) -> Option { + Some(self.rng.gen()) + } +} + +/// Iterator which will continuously generate random ascii characters. /// -/// # Security +/// This iterator is created via the [`gen_ascii_chars`] method on [`Rng`]. +/// +/// [`gen_ascii_chars`]: trait.Rng.html#method.gen_ascii_chars +/// [`Rng`]: trait.Rng.html +#[derive(Debug)] +pub struct AsciiGenerator<'a, R:'a> { + rng: &'a mut R, +} + +impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { + type Item = char; + + fn next(&mut self) -> Option { + const GEN_ASCII_STR_CHARSET: &'static [u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; + Some(*self.rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char) + } +} + +/// A random number generator that can be explicitly seeded to produce +/// the same stream of randomness multiple times. +pub trait SeedableRng: Rng { + /// Reseed an RNG with the given seed. + /// + /// # Example + /// + /// ```rust + /// use rand::{Rng, SeedableRng, StdRng}; + /// + /// let seed: &[_] = &[1, 2, 3, 4]; + /// let mut rng: StdRng = SeedableRng::from_seed(seed); + /// println!("{}", rng.gen::()); + /// rng.reseed(&[5, 6, 7, 8]); + /// println!("{}", rng.gen::()); + /// ``` + fn reseed(&mut self, Seed); + + /// Create a new RNG with the given seed. + /// + /// # Example + /// + /// ```rust + /// use rand::{Rng, SeedableRng, StdRng}; + /// + /// let seed: &[_] = &[1, 2, 3, 4]; + /// let mut rng: StdRng = SeedableRng::from_seed(seed); + /// println!("{}", rng.gen::()); + /// ``` + fn from_seed(seed: Seed) -> Self; +} + +/// An Xorshift[1] random number +/// generator. /// -/// Refer to [`ThreadRng#Security`]. +/// The Xorshift algorithm is not suitable for cryptographic purposes +/// but is very fast. If you do not know for sure that it fits your +/// requirements, use a more secure one such as `IsaacRng` or `OsRng`. /// -/// [`SysRng`]: crate::rngs::SysRng -/// [`ThreadRng`]: crate::rngs::ThreadRng -/// [`ThreadRng#Security`]: crate::rngs::ThreadRng#security -#[cfg(feature = "sys_rng")] -pub fn make_rng() -> R { - #[cfg(feature = "thread_rng")] - { - R::from_rng(&mut rng()) +/// [1]: Marsaglia, George (July 2003). ["Xorshift +/// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of +/// Statistical Software*. Vol. 8 (Issue 14). +#[allow(missing_copy_implementations)] +#[derive(Clone, Debug)] +pub struct XorShiftRng { + x: w32, + y: w32, + z: w32, + w: w32, +} + +impl XorShiftRng { + /// Creates a new XorShiftRng instance which is not seeded. + /// + /// The initial values of this RNG are constants, so all generators created + /// by this function will yield the same stream of random numbers. It is + /// highly recommended that this is created through `SeedableRng` instead of + /// this function + pub fn new_unseeded() -> XorShiftRng { + XorShiftRng { + x: w(0x193a6754), + y: w(0xa8a7d469), + z: w(0x97830e05), + w: w(0x113ba7bb), + } } +} - #[cfg(not(feature = "thread_rng"))] - { - R::try_from_rng(&mut rngs::SysRng).expect("unexpected failure from SysRng") +impl Rng for XorShiftRng { + #[inline] + fn next_u32(&mut self) -> u32 { + let x = self.x; + let t = x ^ (x << 11); + self.x = self.y; + self.y = self.z; + self.z = self.w; + let w_ = self.w; + self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); + self.w.0 + } +} + +impl SeedableRng<[u32; 4]> for XorShiftRng { + /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. + fn reseed(&mut self, seed: [u32; 4]) { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng.reseed called with an all zero seed."); + + self.x = w(seed[0]); + self.y = w(seed[1]); + self.z = w(seed[2]); + self.w = w(seed[3]); + } + + /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. + fn from_seed(seed: [u32; 4]) -> XorShiftRng { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng::from_seed called with an all zero seed."); + + XorShiftRng { + x: w(seed[0]), + y: w(seed[1]), + z: w(seed[2]), + w: w(seed[3]), + } } } -/// Adapter to support [`std::io::Read`] over a [`TryRng`] +impl Rand for XorShiftRng { + fn rand(rng: &mut R) -> XorShiftRng { + let mut tuple: (u32, u32, u32, u32) = rng.gen(); + while tuple == (0, 0, 0, 0) { + tuple = rng.gen(); + } + let (x, y, z, w_) = tuple; + XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + } +} + +/// A wrapper for generating floating point numbers uniformly in the +/// open interval `(0,1)` (not including either endpoint). /// -/// # Examples +/// Use `Closed01` for the closed interval `[0,1]`, and the default +/// `Rand` implementation for `f32` and `f64` for the half-open +/// `[0,1)`. +/// +/// # Example +/// ```rust +/// use rand::{random, Open01}; +/// +/// let Open01(val) = random::>(); +/// println!("f32 from (0,1): {}", val); +/// ``` +#[derive(Debug)] +pub struct Open01(pub F); + +/// A wrapper for generating floating point numbers uniformly in the +/// closed interval `[0,1]` (including both endpoints). /// -/// ```no_run -/// use std::{io, io::Read}; -/// use std::fs::File; -/// use rand::{rngs::SysRng, RngReader}; +/// Use `Open01` for the closed interval `(0,1)`, and the default +/// `Rand` implementation of `f32` and `f64` for the half-open +/// `[0,1)`. /// -/// io::copy( -/// &mut RngReader(SysRng).take(100), -/// &mut File::create("/tmp/random.bytes").unwrap() -/// ).unwrap(); +/// # Example +/// +/// ```rust +/// use rand::{random, Closed01}; +/// +/// let Closed01(val) = random::>(); +/// println!("f32 from [0,1]: {}", val); /// ``` -#[cfg(feature = "std")] -pub struct RngReader(pub R); +#[derive(Debug)] +pub struct Closed01(pub F); + +/// The standard RNG. This is designed to be efficient on the current +/// platform. +#[derive(Copy, Clone, Debug)] +pub struct StdRng { + rng: IsaacWordRng, +} -#[cfg(feature = "std")] -impl std::io::Read for RngReader { +impl StdRng { + /// Create a randomly seeded instance of `StdRng`. + /// + /// This is a very expensive operation as it has to read + /// randomness from the operating system and use this in an + /// expensive seeding operation. If one is only generating a small + /// number of random numbers, or doesn't need the utmost speed for + /// generating each number, `thread_rng` and/or `random` may be more + /// appropriate. + /// + /// Reading the randomness from the OS may fail, and any error is + /// propagated via the `io::Result` return value. + pub fn new() -> io::Result { + OsRng::new().map(|mut r| StdRng { rng: r.gen() }) + } +} + +impl Rng for StdRng { #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { - self.0 - .try_fill_bytes(buf) - .map_err(|err| std::io::Error::other(std::format!("RNG error: {err}")))?; - Ok(buf.len()) + fn next_u32(&mut self) -> u32 { + self.rng.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.rng.next_u64() } } -#[cfg(feature = "std")] -impl std::fmt::Debug for RngReader { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("RngReader").finish() +impl<'a> SeedableRng<&'a [usize]> for StdRng { + fn reseed(&mut self, seed: &'a [usize]) { + // the internal RNG can just be seeded from the above + // randomness. + self.rng.reseed(unsafe {mem::transmute(seed)}) + } + + fn from_seed(seed: &'a [usize]) -> StdRng { + StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) } } } -/// Generate a random value using the thread-local random number generator. +/// Create a weak random number generator with a default algorithm and seed. /// -/// This function is shorthand for [rng()].[random()](RngExt::random): +/// It returns the fastest `Rng` algorithm currently available in Rust without +/// consideration for cryptography or security. If you require a specifically +/// seeded `Rng` for consistency over time you should pick one algorithm and +/// create the `Rng` yourself. /// -/// - See [`ThreadRng`] for documentation of the generator and security -/// - See [`StandardUniform`] for documentation of supported types and distributions +/// This will read randomness from the operating system to seed the +/// generator. +pub fn weak_rng() -> XorShiftRng { + match OsRng::new() { + Ok(mut r) => r.gen(), + Err(e) => panic!("weak_rng: failed to create seeded RNG: {:?}", e) + } +} + +/// Controls how the thread-local RNG is reseeded. +#[derive(Debug)] +struct ThreadRngReseeder; + +impl reseeding::Reseeder for ThreadRngReseeder { + fn reseed(&mut self, rng: &mut StdRng) { + *rng = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not reseed thread_rng: {}", e) + } + } +} +const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +type ThreadRngInner = reseeding::ReseedingRng; + +/// The thread-local RNG. +#[derive(Clone, Debug)] +pub struct ThreadRng { + rng: Rc>, +} + +/// 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::()`. +/// +/// The RNG provided will reseed itself from the operating system +/// after generating a certain amount of randomness. +/// +/// The internal RNG used is platform and architecture dependent, even +/// if the operating system random number generator is rigged to give +/// the same sequence always. If absolute consistency is required, +/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. +pub fn thread_rng() -> ThreadRng { + // used to make space in TLS for a random number generator + thread_local!(static THREAD_RNG_KEY: Rc> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize thread_rng: {}", e) + }; + let rng = reseeding::ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + ThreadRngReseeder); + Rc::new(RefCell::new(rng)) + }); + + ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } +} + +impl Rng for ThreadRng { + fn next_u32(&mut self) -> u32 { + self.rng.borrow_mut().next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.rng.borrow_mut().next_u64() + } + + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.rng.borrow_mut().fill_bytes(bytes) + } +} + +/// Generates a random value using the thread-local random number generator. +/// +/// `random()` can generate various types of random things, and so may require +/// type hinting to generate the specific type you want. +/// +/// This function uses the thread local random number generator. This means +/// that if you're calling `random()` in a loop, caching the generator can +/// increase performance. An example is shown below. /// /// # Examples /// @@ -178,232 +948,303 @@ impl std::fmt::Debug for RngReader { /// } /// ``` /// -/// If you're calling `random()` repeatedly, consider using a local `rng` -/// handle to save an initialization-check on each usage: +/// Caching the thread local random number generator: /// /// ``` -/// use rand::RngExt; // provides the `random` method -/// -/// let mut rng = rand::rng(); // a local handle to the generator +/// use rand::Rng; /// /// let mut v = vec![1, 2, 3]; /// /// for x in v.iter_mut() { -/// *x = rng.random(); +/// *x = rand::random() /// } -/// ``` /// -/// [`StandardUniform`]: distr::StandardUniform -/// [`ThreadRng`]: rngs::ThreadRng -#[cfg(feature = "thread_rng")] -#[inline] -pub fn random() -> T -where - StandardUniform: Distribution, -{ - rng().random() -} - -/// Return an iterator over [`random()`] variates +/// // would be faster as /// -/// This function is shorthand for -/// [rng()].[random_iter](RngExt::random_iter)(). +/// let mut rng = rand::thread_rng(); /// -/// # Example -/// -/// ``` -/// let v: Vec = rand::random_iter().take(5).collect(); -/// println!("{v:?}"); +/// for x in v.iter_mut() { +/// *x = rng.gen(); +/// } /// ``` -#[cfg(feature = "thread_rng")] #[inline] -pub fn random_iter() -> distr::Iter -where - StandardUniform: Distribution, -{ - rng().random_iter() +pub fn random() -> T { + thread_rng().gen() } -/// Generate a random value in the given range using the thread-local random number generator. -/// -/// This function is shorthand for -/// [rng()].[random_range](RngExt::random_range)(range). +/// Randomly sample up to `amount` elements from an iterator. /// /// # Example /// -/// ``` -/// let y: f32 = rand::random_range(0.0..=1e9); -/// println!("{}", y); +/// ```rust +/// use rand::{thread_rng, sample}; /// -/// let words: Vec<&str> = "Mary had a little lamb".split(' ').collect(); -/// println!("{}", words[rand::random_range(..words.len())]); +/// let mut rng = thread_rng(); +/// let sample = sample(&mut rng, 1..100, 5); +/// println!("{:?}", sample); /// ``` -/// Note that the first example can also be achieved (without `collect`'ing -/// to a `Vec`) using [`seq::IteratorRandom::choose`]. -#[cfg(feature = "thread_rng")] -#[inline] -pub fn random_range(range: R) -> T -where - T: distr::uniform::SampleUniform, - R: distr::uniform::SampleRange, +pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec + where I: IntoIterator, + R: Rng, { - rng().random_range(range) -} - -/// Return a bool with a probability `p` of being true. -/// -/// This function is shorthand for -/// [rng()].[random_bool](RngExt::random_bool)(p). -/// -/// # Example -/// -/// ``` -/// println!("{}", rand::random_bool(1.0 / 3.0)); -/// ``` -/// -/// # Panics -/// -/// If `p < 0` or `p > 1`. -#[cfg(feature = "thread_rng")] -#[inline] -#[track_caller] -pub fn random_bool(p: f64) -> bool { - rng().random_bool(p) -} - -/// Return a bool with a probability of `numerator/denominator` of being -/// true. -/// -/// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of -/// returning true. If `numerator == denominator`, then the returned value -/// is guaranteed to be `true`. If `numerator == 0`, then the returned -/// value is guaranteed to be `false`. -/// -/// See also the [`Bernoulli`] distribution, which may be faster if -/// sampling from the same `numerator` and `denominator` repeatedly. -/// -/// This function is shorthand for -/// [rng()].[random_ratio](RngExt::random_ratio)(numerator, denominator). -/// -/// # Panics -/// -/// If `denominator == 0` or `numerator > denominator`. -/// -/// # Example -/// -/// ``` -/// println!("{}", rand::random_ratio(2, 3)); -/// ``` -/// -/// [`Bernoulli`]: distr::Bernoulli -#[cfg(feature = "thread_rng")] -#[inline] -#[track_caller] -pub fn random_ratio(numerator: u32, denominator: u32) -> bool { - rng().random_ratio(numerator, denominator) -} - -/// Fill any type implementing [`Fill`] with random data -/// -/// This function is shorthand for -/// [rng()].[fill](RngExt::fill)(dest). -/// -/// # Example -/// -/// ``` -/// let mut arr = [0i8; 20]; -/// rand::fill(&mut arr[..]); -/// ``` -/// -/// Note that you can instead use [`random()`] to generate an array of random -/// data, though this is slower for small elements (smaller than the RNG word -/// size). -#[cfg(feature = "thread_rng")] -#[inline] -#[track_caller] -pub fn fill(dest: &mut [T]) { - Fill::fill_slice(dest, &mut rng()) + let mut iter = iterable.into_iter(); + let mut reservoir: Vec = iter.by_ref().take(amount).collect(); + // continue unless the iterator was exhausted + if reservoir.len() == amount { + for (i, elem) in iter.enumerate() { + let k = rng.gen_range(0, i + 1 + amount); + if let Some(spot) = reservoir.get_mut(k) { + *spot = elem; + } + } + } + reservoir } #[cfg(test)] mod test { - use super::*; - use core::convert::Infallible; + use super::{Rng, thread_rng, random, SeedableRng, StdRng, sample}; + use std::iter::repeat; - /// Construct a deterministic RNG with the given seed - pub fn rng(seed: u64) -> impl Rng { - // For tests, we want a statistically good, fast, reproducible RNG. - // PCG32 will do fine, and will be easy to embed if we ever need to. - const INC: u64 = 11634580027462260723; - rand_pcg::Pcg32::new(seed, INC) + pub struct MyRng { inner: R } + + impl Rng for MyRng { + fn next_u32(&mut self) -> u32 { + fn next(t: &mut T) -> u32 { + t.next_u32() + } + next(&mut self.inner) + } } - /// Construct a generator yielding a constant value - pub fn const_rng(x: u64) -> StepRng { - StepRng(x, 0) + pub fn rng() -> MyRng<::ThreadRng> { + MyRng { inner: ::thread_rng() } } - /// Construct a generator yielding an arithmetic sequence - pub fn step_rng(x: u64, increment: u64) -> StepRng { - StepRng(x, increment) + struct ConstRng { i: u64 } + impl Rng for ConstRng { + fn next_u32(&mut self) -> u32 { self.i as u32 } + fn next_u64(&mut self) -> u64 { self.i } + + // no fill_bytes on purpose } - #[derive(Clone)] - pub struct StepRng(u64, u64); - impl TryRng for StepRng { - type Error = Infallible; + pub fn iter_eq(i: I, j: J) -> bool + where I: IntoIterator, + J: IntoIterator, + I::Item: Eq + { + // make sure the iterators have equal length + let mut i = i.into_iter(); + let mut j = j.into_iter(); + loop { + match (i.next(), j.next()) { + (Some(ref ei), Some(ref ej)) if ei == ej => { } + (None, None) => return true, + _ => return false, + } + } + } + + #[test] + fn test_fill_bytes_default() { + let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 }; - fn try_next_u32(&mut self) -> Result { - self.try_next_u64().map(|x| x as u32) + // check every remainder mod 8, both in small and big vectors. + let lengths = [0, 1, 2, 3, 4, 5, 6, 7, + 80, 81, 82, 83, 84, 85, 86, 87]; + for &n in lengths.iter() { + let mut v = repeat(0u8).take(n).collect::>(); + r.fill_bytes(&mut v); + + // use this to get nicer error messages. + for (i, &byte) in v.iter().enumerate() { + if byte == 0 { + panic!("byte {} of {} is zero", i, n) + } + } } + } - fn try_next_u64(&mut self) -> Result { - let res = self.0; - self.0 = self.0.wrapping_add(self.1); - Ok(res) + #[test] + fn test_gen_range() { + let mut r = thread_rng(); + for _ in 0..1000 { + let a = r.gen_range(-3, 42); + assert!(a >= -3 && a < 42); + assert_eq!(r.gen_range(0, 1), 0); + assert_eq!(r.gen_range(-12, -11), -12); } - fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { - rand_core::utils::fill_bytes_via_next_word(dst, || self.try_next_u64()) + for _ in 0..1000 { + let a = r.gen_range(10, 42); + assert!(a >= 10 && a < 42); + assert_eq!(r.gen_range(0, 1), 0); + assert_eq!(r.gen_range(3_000_000, 3_000_001), 3_000_000); } + + } + + #[test] + #[should_panic] + fn test_gen_range_panic_int() { + let mut r = thread_rng(); + r.gen_range(5, -2); + } + + #[test] + #[should_panic] + fn test_gen_range_panic_usize() { + let mut r = thread_rng(); + r.gen_range(5, 2); + } + + #[test] + fn test_gen_f64() { + let mut r = thread_rng(); + let a = r.gen::(); + let b = r.gen::(); + debug!("{:?}", (a, b)); + } + + #[test] + fn test_gen_weighted_bool() { + let mut r = thread_rng(); + assert_eq!(r.gen_weighted_bool(0), true); + assert_eq!(r.gen_weighted_bool(1), true); + } + + #[test] + fn test_gen_ascii_str() { + let mut r = thread_rng(); + assert_eq!(r.gen_ascii_chars().take(0).count(), 0); + assert_eq!(r.gen_ascii_chars().take(10).count(), 10); + assert_eq!(r.gen_ascii_chars().take(16).count(), 16); + } + + #[test] + fn test_gen_vec() { + let mut r = thread_rng(); + assert_eq!(r.gen_iter::().take(0).count(), 0); + assert_eq!(r.gen_iter::().take(10).count(), 10); + assert_eq!(r.gen_iter::().take(16).count(), 16); + } + + #[test] + fn test_choose() { + let mut r = thread_rng(); + assert_eq!(r.choose(&[1, 1, 1]).map(|&x|x), Some(1)); + + let v: &[isize] = &[]; + assert_eq!(r.choose(v), None); } - #[cfg(feature = "std")] #[test] - fn rng_reader() { - use std::io::Read; + fn test_shuffle() { + let mut r = thread_rng(); + let empty: &mut [isize] = &mut []; + r.shuffle(empty); + let mut one = [1]; + r.shuffle(&mut one); + let b: &[_] = &[1]; + assert_eq!(one, b); + + let mut two = [1, 2]; + r.shuffle(&mut two); + assert!(two == [1, 2] || two == [2, 1]); - let mut rng = StepRng(255, 1); - let mut buf = [0u8; 24]; - let expected = [ - 255, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, - ]; + let mut x = [1, 1, 1]; + r.shuffle(&mut x); + let b: &[_] = &[1, 1, 1]; + assert_eq!(x, b); + } - RngReader(&mut rng).read_exact(&mut buf).unwrap(); - assert_eq!(&buf, &expected); + #[test] + fn test_thread_rng() { + let mut r = thread_rng(); + r.gen::(); + let mut v = [1, 1, 1]; + r.shuffle(&mut v); + let b: &[_] = &[1, 1, 1]; + assert_eq!(v, b); + assert_eq!(r.gen_range(0, 1), 0); + } - RngReader(StepRng(255, 1)).read_exact(&mut buf).unwrap(); - assert_eq!(&buf, &expected); + #[test] + fn test_rng_trait_object() { + let mut rng = thread_rng(); + { + let mut r = &mut rng as &mut Rng; + r.next_u32(); + (&mut r).gen::(); + let mut v = [1, 1, 1]; + (&mut r).shuffle(&mut v); + let b: &[_] = &[1, 1, 1]; + assert_eq!(v, b); + assert_eq!((&mut r).gen_range(0, 1), 0); + } + { + let mut r = Box::new(rng) as Box; + r.next_u32(); + r.gen::(); + let mut v = [1, 1, 1]; + r.shuffle(&mut v); + let b: &[_] = &[1, 1, 1]; + assert_eq!(v, b); + assert_eq!(r.gen_range(0, 1), 0); + } } #[test] - #[cfg(feature = "thread_rng")] fn test_random() { - let _n: u64 = random(); - let _f: f32 = random(); - #[allow(clippy::type_complexity)] - let _many: ( - (), - [(u32, bool); 3], - (u8, i8, u16, i16, u32, i32, u64, i64), - (f32, (f64, (f64,))), - ) = random(); + // not sure how to test this aside from just getting some values + let _n : usize = random(); + let _f : f32 = random(); + let _o : Option> = random(); + let _many : ((), + (usize, + isize, + Option<(u32, (bool,))>), + (u8, i8, u16, i16, u32, i32, u64, i64), + (f32, (f64, (f64,)))) = random(); } #[test] - #[cfg(feature = "thread_rng")] - fn test_range() { - let _n: usize = random_range(42..=43); - let _f: f32 = random_range(42.0..43.0); + fn test_sample() { + let min_val = 1; + let max_val = 100; + + let mut r = thread_rng(); + let vals = (min_val..max_val).collect::>(); + let small_sample = sample(&mut r, vals.iter(), 5); + let large_sample = sample(&mut r, vals.iter(), vals.len() + 5); + + assert_eq!(small_sample.len(), 5); + assert_eq!(large_sample.len(), vals.len()); + + assert!(small_sample.iter().all(|e| { + **e >= min_val && **e <= max_val + })); + } + + #[test] + fn test_std_rng_seeded() { + let s = thread_rng().gen_iter::().take(256).collect::>(); + let mut ra: StdRng = SeedableRng::from_seed(&s[..]); + let mut rb: StdRng = SeedableRng::from_seed(&s[..]); + assert!(iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_std_rng_reseed() { + let s = thread_rng().gen_iter::().take(256).collect::>(); + let mut r: StdRng = SeedableRng::from_seed(&s[..]); + let string1 = r.gen_ascii_chars().take(100).collect::(); + + r.reseed(&s); + + let string2 = r.gen_ascii_chars().take(100).collect::(); + assert_eq!(string1, string2); } } diff --git a/src/log_macros.rs b/src/log_macros.rs deleted file mode 100644 index fe0059c8f02..00000000000 --- a/src/log_macros.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(unused)] - -macro_rules! trace { ($($x:tt)*) => ( - #[cfg(feature = "log")] - log::trace!($($x)*); - - #[cfg(not(feature = "log"))] - let _ = || { let _ = format_args!($($x)*); }; -) } - -macro_rules! debug { ($($x:tt)*) => ( - #[cfg(feature = "log")] - log::debug!($($x)*) - - #[cfg(not(feature = "log"))] - let _ = || { let _ = format_args!($($x)*); }; -) } - -macro_rules! info { ($($x:tt)*) => ( - #[cfg(feature = "log")] - log::info!($($x)*); - - #[cfg(not(feature = "log"))] - let _ = || { let _ = format_args!($($x)*); }; -) } - -macro_rules! warn { ($($x:tt)*) => ( - #[cfg(feature = "log")] - log::warn!($($x)*); - - #[cfg(not(feature = "log"))] - let _ = || { let _ = format_args!($($x)*); }; -) } - -macro_rules! error { ($($x:tt)*) => ( - #[cfg(feature = "log")] - log::error!($($x)*); - - #[cfg(not(feature = "log"))] - let _ = || { let _ = format_args!($($x)*); }; -) } diff --git a/src/os.rs b/src/os.rs new file mode 100644 index 00000000000..acb112c3800 --- /dev/null +++ b/src/os.rs @@ -0,0 +1,596 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interfaces to the operating system provided random number +//! generators. + +use std::{io, mem, fmt}; +use Rng; + +/// A random number generator that retrieves randomness straight from +/// the operating system. Platform sources: +/// +/// - Unix-like systems (Linux, Android, Mac OSX): read directly from +/// `/dev/urandom`, or from `getrandom(2)` system call if available. +/// - OpenBSD: calls `getentropy(2)` +/// - FreeBSD: uses the `kern.arandom` `sysctl(2)` mib +/// - Windows: calls `CryptGenRandom`, using the default cryptographic +/// service provider with the `PROV_RSA_FULL` type. +/// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. +/// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface. +/// +/// This does not block. +pub struct OsRng(imp::OsRng); + +impl OsRng { + /// Create a new `OsRng`. + pub fn new() -> io::Result { + imp::OsRng::new().map(OsRng) + } +} + +impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { self.0.next_u32() } + fn next_u64(&mut self) -> u64 { self.0.next_u64() } + fn fill_bytes(&mut self, v: &mut [u8]) { self.0.fill_bytes(v) } +} + +impl fmt::Debug for OsRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OsRng {{}}") + } +} + +fn next_u32(mut fill_buf: &mut FnMut(&mut [u8])) -> u32 { + let mut buf: [u8; 4] = [0; 4]; + fill_buf(&mut buf); + unsafe { mem::transmute::<[u8; 4], u32>(buf) } +} + +fn next_u64(mut fill_buf: &mut FnMut(&mut [u8])) -> u64 { + let mut buf: [u8; 8] = [0; 8]; + fill_buf(&mut buf); + unsafe { mem::transmute::<[u8; 8], u64>(buf) } +} + +#[cfg(all(unix, not(target_os = "ios"), + not(target_os = "nacl"), + not(target_os = "freebsd"), + not(target_os = "openbsd"), + not(target_os = "redox")))] +mod imp { + extern crate libc; + + use super::{next_u32, next_u64}; + use self::OsRngInner::*; + + use std::io; + use std::fs::File; + use Rng; + use read::ReadRng; + + #[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "powerpc")))] + fn getrandom(buf: &mut [u8]) -> libc::c_long { + extern "C" { + fn syscall(number: libc::c_long, ...) -> libc::c_long; + } + + #[cfg(target_arch = "x86_64")] + const NR_GETRANDOM: libc::c_long = 318; + #[cfg(target_arch = "x86")] + const NR_GETRANDOM: libc::c_long = 355; + #[cfg(target_arch = "arm")] + const NR_GETRANDOM: libc::c_long = 384; + #[cfg(target_arch = "aarch64")] + const NR_GETRANDOM: libc::c_long = 278; + #[cfg(target_arch = "powerpc")] + const NR_GETRANDOM: libc::c_long = 384; + + unsafe { + syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + } + } + + #[cfg(not(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "powerpc"))))] + fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } + + fn getrandom_fill_bytes(v: &mut [u8]) { + let mut read = 0; + let len = v.len(); + while read < len { + let result = getrandom(&mut v[read..]); + if result == -1 { + let err = io::Error::last_os_error(); + if err.kind() == io::ErrorKind::Interrupted { + continue + } else { + panic!("unexpected getrandom error: {}", err); + } + } else { + read += result as usize; + } + } + } + + #[cfg(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "powerpc")))] + fn is_getrandom_available() -> bool { + use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; + use std::sync::{Once, ONCE_INIT}; + + static CHECKER: Once = ONCE_INIT; + static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT; + + CHECKER.call_once(|| { + let mut buf: [u8; 0] = []; + let result = getrandom(&mut buf); + let available = if result == -1 { + let err = io::Error::last_os_error().raw_os_error(); + err != Some(libc::ENOSYS) + } else { + true + }; + AVAILABLE.store(available, Ordering::Relaxed); + }); + + AVAILABLE.load(Ordering::Relaxed) + } + + #[cfg(not(all(target_os = "linux", + any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "aarch64", + target_arch = "powerpc"))))] + fn is_getrandom_available() -> bool { false } + + pub struct OsRng { + inner: OsRngInner, + } + + enum OsRngInner { + OsGetrandomRng, + OsReadRng(ReadRng), + } + + impl OsRng { + pub fn new() -> io::Result { + if is_getrandom_available() { + return Ok(OsRng { inner: OsGetrandomRng }); + } + + let reader = try!(File::open("/dev/urandom")); + let reader_rng = ReadRng::new(reader); + + Ok(OsRng { inner: OsReadRng(reader_rng) }) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + match self.inner { + OsGetrandomRng => next_u32(&mut getrandom_fill_bytes), + OsReadRng(ref mut rng) => rng.next_u32(), + } + } + fn next_u64(&mut self) -> u64 { + match self.inner { + OsGetrandomRng => next_u64(&mut getrandom_fill_bytes), + OsReadRng(ref mut rng) => rng.next_u64(), + } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + match self.inner { + OsGetrandomRng => getrandom_fill_bytes(v), + OsReadRng(ref mut rng) => rng.fill_bytes(v) + } + } + } +} + +#[cfg(target_os = "ios")] +mod imp { + extern crate libc; + + use super::{next_u32, next_u64}; + + use std::io; + use Rng; + use self::libc::{c_int, size_t}; + + #[derive(Debug)] + pub struct OsRng; + + enum SecRandom {} + + #[allow(non_upper_case_globals)] + const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom; + + #[link(name = "Security", kind = "framework")] + extern { + fn SecRandomCopyBytes(rnd: *const SecRandom, + count: size_t, bytes: *mut u8) -> c_int; + } + + impl OsRng { + pub fn new() -> io::Result { + Ok(OsRng) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + let ret = unsafe { + SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) + }; + if ret == -1 { + panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + } + } + } +} + +#[cfg(target_os = "freebsd")] +mod imp { + extern crate libc; + + use std::{io, ptr}; + use Rng; + + use super::{next_u32, next_u64}; + + #[derive(Debug)] + pub struct OsRng; + + impl OsRng { + pub fn new() -> io::Result { + Ok(OsRng) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + let mib = [libc::CTL_KERN, libc::KERN_ARND]; + // kern.arandom permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let mut s_len = s.len(); + let ret = unsafe { + libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint, + s.as_mut_ptr() as *mut _, &mut s_len, + ptr::null(), 0) + }; + if ret == -1 || s_len != s.len() { + panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", + ret, s.len(), s_len); + } + } + } + } +} + +#[cfg(target_os = "openbsd")] +mod imp { + extern crate libc; + + use std::io; + use Rng; + + use super::{next_u32, next_u64}; + + #[derive(Debug)] + pub struct OsRng; + + impl OsRng { + pub fn new() -> io::Result { + Ok(OsRng) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + // getentropy(2) permits a maximum buffer size of 256 bytes + for s in v.chunks_mut(256) { + let ret = unsafe { + libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) + }; + if ret == -1 { + let err = io::Error::last_os_error(); + panic!("getentropy failed: {}", err); + } + } + } + } +} + +#[cfg(target_os = "redox")] +mod imp { + use std::io; + use std::fs::File; + use Rng; + use read::ReadRng; + + #[derive(Debug)] + pub struct OsRng { + inner: ReadRng, + } + + impl OsRng { + pub fn new() -> io::Result { + let reader = try!(File::open("rand:")); + let reader_rng = ReadRng::new(reader); + + Ok(OsRng { inner: reader_rng }) + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + fn fill_bytes(&mut self, v: &mut [u8]) { + self.inner.fill_bytes(v) + } + } +} + +#[cfg(windows)] +mod imp { + use std::io; + use std::ptr; + use Rng; + + use super::{next_u32, next_u64}; + + type BOOL = i32; + type LPCSTR = *const i8; + type DWORD = u32; + type HCRYPTPROV = usize; + type BYTE = u8; + + const PROV_RSA_FULL: DWORD = 1; + const CRYPT_SILENT: DWORD = 0x00000040; + const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; + + #[link(name = "advapi32")] + extern "system" { + fn CryptAcquireContextA(phProv: *mut HCRYPTPROV, + szContainer: LPCSTR, + szProvider: LPCSTR, + dwProvType: DWORD, + dwFlags: DWORD) -> BOOL; + fn CryptGenRandom(hProv: HCRYPTPROV, + dwLen: DWORD, + pbBuffer: *mut BYTE) -> BOOL; + fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; + } + + #[derive(Debug)] + pub struct OsRng { + hcryptprov: HCRYPTPROV + } + + impl OsRng { + pub fn new() -> io::Result { + let mut hcp = 0; + let ret = unsafe { + CryptAcquireContextA(&mut hcp, ptr::null(), ptr::null(), + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) + }; + + if ret == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(OsRng { hcryptprov: hcp }) + } + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + // CryptGenRandom takes a DWORD (u32) for the length so we need to + // split up the buffer. + for slice in v.chunks_mut(::max_value() as usize) { + let ret = unsafe { + CryptGenRandom(self.hcryptprov, slice.len() as DWORD, + slice.as_mut_ptr()) + }; + if ret == 0 { + panic!("couldn't generate random bytes: {}", + io::Error::last_os_error()); + } + } + } + } + + impl Drop for OsRng { + fn drop(&mut self) { + let ret = unsafe { + CryptReleaseContext(self.hcryptprov, 0) + }; + if ret == 0 { + panic!("couldn't release context: {}", + io::Error::last_os_error()); + } + } + } +} + +#[cfg(target_os = "nacl")] +mod imp { + extern crate libc; + + use std::io; + use std::mem; + use Rng; + + use super::{next_u32, next_u64}; + + #[derive(Debug)] + pub struct OsRng(extern fn(dest: *mut libc::c_void, + bytes: libc::size_t, + read: *mut libc::size_t) -> libc::c_int); + + extern { + fn nacl_interface_query(name: *const libc::c_char, + table: *mut libc::c_void, + table_size: libc::size_t) -> libc::size_t; + } + + const INTERFACE: &'static [u8] = b"nacl-irt-random-0.1\0"; + + #[repr(C)] + struct NaClIRTRandom { + get_random_bytes: Option libc::c_int>, + } + + impl OsRng { + pub fn new() -> io::Result { + let mut iface = NaClIRTRandom { + get_random_bytes: None, + }; + let result = unsafe { + nacl_interface_query(INTERFACE.as_ptr() as *const _, + mem::transmute(&mut iface), + mem::size_of::() as libc::size_t) + }; + if result != 0 { + assert!(iface.get_random_bytes.is_some()); + let result = OsRng(iface.get_random_bytes.take().unwrap()); + Ok(result) + } else { + let error = io::ErrorKind::NotFound; + let error = io::Error::new(error, "IRT random interface missing"); + Err(error) + } + } + } + + impl Rng for OsRng { + fn next_u32(&mut self) -> u32 { + next_u32(&mut |v| self.fill_bytes(v)) + } + fn next_u64(&mut self) -> u64 { + next_u64(&mut |v| self.fill_bytes(v)) + } + fn fill_bytes(&mut self, v: &mut [u8]) { + let mut read = 0; + loop { + let mut r: libc::size_t = 0; + let len = v.len(); + let error = (self.0)(v[read..].as_mut_ptr() as *mut _, + (len - read) as libc::size_t, + &mut r as *mut _); + assert!(error == 0, "`get_random_bytes` failed!"); + read += r as usize; + + if read >= v.len() { break; } + } + } + } +} + + +#[cfg(test)] +mod test { + use std::sync::mpsc::channel; + use Rng; + use OsRng; + use std::thread; + + #[test] + fn test_os_rng() { + let mut r = OsRng::new().unwrap(); + + r.next_u32(); + r.next_u64(); + + let mut v = [0u8; 1000]; + r.fill_bytes(&mut v); + } + + #[test] + fn test_os_rng_tasks() { + + let mut txs = vec!(); + for _ in 0..20 { + let (tx, rx) = channel(); + txs.push(tx); + + thread::spawn(move|| { + // wait until all the tasks are ready to go. + rx.recv().unwrap(); + + // deschedule to attempt to interleave things as much + // as possible (XXX: is this a good test?) + let mut r = OsRng::new().unwrap(); + thread::yield_now(); + let mut v = [0u8; 1000]; + + for _ in 0..100 { + r.next_u32(); + thread::yield_now(); + r.next_u64(); + thread::yield_now(); + r.fill_bytes(&mut v); + thread::yield_now(); + } + }); + } + + // start all the tasks + for tx in txs.iter() { + tx.send(()).unwrap(); + } + } +} diff --git a/src/prelude.rs b/src/prelude.rs deleted file mode 100644 index e7500acc39d..00000000000 --- a/src/prelude.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Convenience re-export of common members -//! -//! Like the standard library's prelude, this module simplifies importing of -//! common items. Unlike the standard prelude, the contents of this module must -//! be imported manually: -//! -//! ``` -//! use rand::prelude::*; -//! # let mut r: StdRng = rand::make_rng(); -//! # let _: f32 = r.random(); -//! ``` - -#[doc(no_inline)] -pub use crate::distr::Distribution; -#[doc(no_inline)] -pub use crate::rngs::SmallRng; -#[cfg(feature = "std_rng")] -#[doc(no_inline)] -pub use crate::rngs::StdRng; -#[doc(no_inline)] -#[cfg(feature = "thread_rng")] -pub use crate::rngs::ThreadRng; -#[doc(no_inline)] -pub use crate::seq::{IndexedMutRandom, IndexedRandom, IteratorRandom, SliceRandom}; -#[doc(no_inline)] -pub use crate::{CryptoRng, Rng, RngExt, SeedableRng}; diff --git a/src/rand_impls.rs b/src/rand_impls.rs new file mode 100644 index 00000000000..5a7e3de0b03 --- /dev/null +++ b/src/rand_impls.rs @@ -0,0 +1,283 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The implementations of `Rand` for the built-in types. + +use std::char; +use std::mem; + +use {Rand,Rng}; + +impl Rand for isize { + #[inline] + fn rand(rng: &mut R) -> isize { + if mem::size_of::() == 4 { + rng.gen::() as isize + } else { + rng.gen::() as isize + } + } +} + +impl Rand for i8 { + #[inline] + fn rand(rng: &mut R) -> i8 { + rng.next_u32() as i8 + } +} + +impl Rand for i16 { + #[inline] + fn rand(rng: &mut R) -> i16 { + rng.next_u32() as i16 + } +} + +impl Rand for i32 { + #[inline] + fn rand(rng: &mut R) -> i32 { + rng.next_u32() as i32 + } +} + +impl Rand for i64 { + #[inline] + fn rand(rng: &mut R) -> i64 { + rng.next_u64() as i64 + } +} + +impl Rand for usize { + #[inline] + fn rand(rng: &mut R) -> usize { + if mem::size_of::() == 4 { + rng.gen::() as usize + } else { + rng.gen::() as usize + } + } +} + +impl Rand for u8 { + #[inline] + fn rand(rng: &mut R) -> u8 { + rng.next_u32() as u8 + } +} + +impl Rand for u16 { + #[inline] + fn rand(rng: &mut R) -> u16 { + rng.next_u32() as u16 + } +} + +impl Rand for u32 { + #[inline] + fn rand(rng: &mut R) -> u32 { + rng.next_u32() + } +} + +impl Rand for u64 { + #[inline] + fn rand(rng: &mut R) -> u64 { + rng.next_u64() + } +} + +macro_rules! float_impls { + ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { + mod $mod_name { + use {Rand, Rng, Open01, Closed01}; + + const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; + + impl Rand for $ty { + /// Generate a floating point number in the half-open + /// interval `[0,1)`. + /// + /// See `Closed01` for the closed interval `[0,1]`, + /// and `Open01` for the open interval `(0,1)`. + #[inline] + fn rand(rng: &mut R) -> $ty { + rng.$method_name() + } + } + impl Rand for Open01<$ty> { + #[inline] + fn rand(rng: &mut R) -> Open01<$ty> { + // add a small amount (specifically 2 bits below + // the precision of f64/f32 at 1.0), so that small + // numbers are larger than 0, but large numbers + // aren't pushed to/above 1. + Open01(rng.$method_name() + 0.25 / SCALE) + } + } + impl Rand for Closed01<$ty> { + #[inline] + fn rand(rng: &mut R) -> Closed01<$ty> { + // rescale so that 1.0 - epsilon becomes 1.0 + // precisely. + Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) + } + } + } + } +} +float_impls! { f64_rand_impls, f64, 53, next_f64 } +float_impls! { f32_rand_impls, f32, 24, next_f32 } + +impl Rand for char { + #[inline] + fn rand(rng: &mut R) -> char { + // a char is 21 bits + const CHAR_MASK: u32 = 0x001f_ffff; + loop { + // Rejection sampling. About 0.2% of numbers with at most + // 21-bits are invalid codepoints (surrogates), so this + // will succeed first go almost every time. + match char::from_u32(rng.next_u32() & CHAR_MASK) { + Some(c) => return c, + None => {} + } + } + } +} + +impl Rand for bool { + #[inline] + fn rand(rng: &mut R) -> bool { + rng.gen::() & 1 == 1 + } +} + +macro_rules! tuple_impl { + // use variables to indicate the arity of the tuple + ($($tyvar:ident),* ) => { + // the trailing commas are for the 1 tuple + impl< + $( $tyvar : Rand ),* + > Rand for ( $( $tyvar ),* , ) { + + #[inline] + fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { + ( + // use the $tyvar's to get the appropriate number of + // repeats (they're not actually needed) + $( + _rng.gen::<$tyvar>() + ),* + , + ) + } + } + } +} + +impl Rand for () { + #[inline] + fn rand(_: &mut R) -> () { () } +} +tuple_impl!{A} +tuple_impl!{A, B} +tuple_impl!{A, B, C} +tuple_impl!{A, B, C, D} +tuple_impl!{A, B, C, D, E} +tuple_impl!{A, B, C, D, E, F} +tuple_impl!{A, B, C, D, E, F, G} +tuple_impl!{A, B, C, D, E, F, G, H} +tuple_impl!{A, B, C, D, E, F, G, H, I} +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} + +macro_rules! array_impl { + {$n:expr, $t:ident, $($ts:ident,)*} => { + array_impl!{($n - 1), $($ts,)*} + + impl Rand for [T; $n] where T: Rand { + #[inline] + fn rand(_rng: &mut R) -> [T; $n] { + [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*] + } + } + }; + {$n:expr,} => { + impl Rand for [T; $n] { + fn rand(_rng: &mut R) -> [T; $n] { [] } + } + }; +} + +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 Rand for Option { + #[inline] + fn rand(rng: &mut R) -> Option { + if rng.gen() { + Some(rng.gen()) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use {Rng, thread_rng, Open01, Closed01}; + + struct ConstantRng(u64); + impl Rng for ConstantRng { + fn next_u32(&mut self) -> u32 { + let ConstantRng(v) = *self; + v as u32 + } + fn next_u64(&mut self) -> u64 { + let ConstantRng(v) = *self; + v + } + } + + #[test] + fn floating_point_edge_cases() { + // the test for exact equality is correct here. + assert!(ConstantRng(0xffff_ffff).gen::() != 1.0); + assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::() != 1.0); + } + + #[test] + fn rand_open() { + // this is unlikely to catch an incorrect implementation that + // generates exactly 0 or 1, but it keeps it sane. + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let Open01(f) = rng.gen::>(); + assert!(0.0 < f && f < 1.0); + + let Open01(f) = rng.gen::>(); + assert!(0.0 < f && f < 1.0); + } + } + + #[test] + fn rand_closed() { + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let Closed01(f) = rng.gen::>(); + assert!(0.0 <= f && f <= 1.0); + + let Closed01(f) = rng.gen::>(); + assert!(0.0 <= f && f <= 1.0); + } + } +} diff --git a/src/read.rs b/src/read.rs new file mode 100644 index 00000000000..c7351b75937 --- /dev/null +++ b/src/read.rs @@ -0,0 +1,123 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A wrapper around any Read to treat it as an RNG. + +use std::io::{self, Read}; +use std::mem; +use Rng; + +/// An RNG that reads random bytes straight from a `Read`. This will +/// work best with an infinite reader, but this is not required. +/// +/// # Panics +/// +/// It will panic if it there is insufficient data to fulfill a request. +/// +/// # Example +/// +/// ```rust +/// use rand::{read, Rng}; +/// +/// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; +/// let mut rng = read::ReadRng::new(&data[..]); +/// println!("{:x}", rng.gen::()); +/// ``` +#[derive(Debug)] +pub struct ReadRng { + reader: R +} + +impl ReadRng { + /// Create a new `ReadRng` from a `Read`. + pub fn new(r: R) -> ReadRng { + ReadRng { + reader: r + } + } +} + +impl Rng for ReadRng { + fn next_u32(&mut self) -> u32 { + // This is designed for speed: reading a LE integer on a LE + // platform just involves blitting the bytes into the memory + // of the u32, similarly for BE on BE; avoiding byteswapping. + let mut buf = [0; 4]; + fill(&mut self.reader, &mut buf).unwrap(); + unsafe { *(buf.as_ptr() as *const u32) } + } + fn next_u64(&mut self) -> u64 { + // see above for explanation. + let mut buf = [0; 8]; + fill(&mut self.reader, &mut buf).unwrap(); + unsafe { *(buf.as_ptr() as *const u64) } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + if v.len() == 0 { return } + fill(&mut self.reader, v).unwrap(); + } +} + +fn fill(r: &mut Read, mut buf: &mut [u8]) -> io::Result<()> { + while buf.len() > 0 { + match try!(r.read(buf)) { + 0 => return Err(io::Error::new(io::ErrorKind::Other, + "end of file reached")), + n => buf = &mut mem::replace(&mut buf, &mut [])[n..], + } + } + Ok(()) +} + +#[cfg(test)] +mod test { + use super::ReadRng; + use Rng; + + #[test] + fn test_reader_rng_u64() { + // transmute from the target to avoid endianness concerns. + let v = vec![0u8, 0, 0, 0, 0, 0, 0, 1, + 0 , 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 3]; + let mut rng = ReadRng::new(&v[..]); + + assert_eq!(rng.next_u64(), 1_u64.to_be()); + assert_eq!(rng.next_u64(), 2_u64.to_be()); + assert_eq!(rng.next_u64(), 3_u64.to_be()); + } + #[test] + fn test_reader_rng_u32() { + let v = vec![0u8, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]; + let mut rng = ReadRng::new(&v[..]); + + assert_eq!(rng.next_u32(), 1_u32.to_be()); + assert_eq!(rng.next_u32(), 2_u32.to_be()); + assert_eq!(rng.next_u32(), 3_u32.to_be()); + } + #[test] + fn test_reader_rng_fill_bytes() { + let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let mut w = [0u8; 8]; + + let mut rng = ReadRng::new(&v[..]); + rng.fill_bytes(&mut w); + + assert!(v == w); + } + + #[test] + #[should_panic] + fn test_reader_rng_insufficient_bytes() { + let mut rng = ReadRng::new(&[][..]); + let mut v = [0u8; 3]; + rng.fill_bytes(&mut v); + } +} diff --git a/src/reseeding.rs b/src/reseeding.rs new file mode 100644 index 00000000000..2fba9f4ad60 --- /dev/null +++ b/src/reseeding.rs @@ -0,0 +1,229 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A wrapper around another RNG that reseeds it after it +//! generates a certain number of random bytes. + +use std::default::Default; + +use {Rng, SeedableRng}; + +/// How many bytes of entropy the underling RNG is allowed to generate +/// before it is reseeded +const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; + +/// A wrapper around any RNG which reseeds the underlying RNG after it +/// has generated a certain number of random bytes. +#[derive(Debug)] +pub struct ReseedingRng { + rng: R, + generation_threshold: u64, + bytes_generated: u64, + /// Controls the behaviour when reseeding the RNG. + pub reseeder: Rsdr, +} + +impl> ReseedingRng { + /// Create a new `ReseedingRng` with the given parameters. + /// + /// # Arguments + /// + /// * `rng`: the random number generator to use. + /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. + /// * `reseeder`: the reseeding object to use. + pub fn new(rng: R, generation_threshold: u64, reseeder: Rsdr) -> ReseedingRng { + ReseedingRng { + rng: rng, + generation_threshold: generation_threshold, + bytes_generated: 0, + reseeder: reseeder + } + } + + /// Reseed the internal RNG if the number of bytes that have been + /// generated exceed the threshold. + pub fn reseed_if_necessary(&mut self) { + if self.bytes_generated >= self.generation_threshold { + self.reseeder.reseed(&mut self.rng); + self.bytes_generated = 0; + } + } +} + + +impl> Rng for ReseedingRng { + fn next_u32(&mut self) -> u32 { + self.reseed_if_necessary(); + self.bytes_generated += 4; + self.rng.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.reseed_if_necessary(); + self.bytes_generated += 8; + self.rng.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.reseed_if_necessary(); + self.bytes_generated += dest.len() as u64; + self.rng.fill_bytes(dest) + } +} + +impl, Rsdr: Reseeder + Default> + SeedableRng<(Rsdr, S)> for ReseedingRng { + fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { + self.rng.reseed(seed); + self.reseeder = rsdr; + self.bytes_generated = 0; + } + + /// Create a new `ReseedingRng` from the given reseeder and + /// seed. This uses a default value for `generation_threshold`. + fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { + ReseedingRng { + rng: SeedableRng::from_seed(seed), + generation_threshold: DEFAULT_GENERATION_THRESHOLD, + bytes_generated: 0, + reseeder: rsdr + } + } +} + +/// Something that can be used to reseed an RNG via `ReseedingRng`. +/// +/// # Example +/// +/// ```rust +/// use rand::{Rng, SeedableRng, StdRng}; +/// use rand::reseeding::{Reseeder, ReseedingRng}; +/// +/// struct TickTockReseeder { tick: bool } +/// impl Reseeder for TickTockReseeder { +/// fn reseed(&mut self, rng: &mut StdRng) { +/// let val = if self.tick {0} else {1}; +/// rng.reseed(&[val]); +/// self.tick = !self.tick; +/// } +/// } +/// fn main() { +/// let rsdr = TickTockReseeder { tick: true }; +/// +/// let inner = StdRng::new().unwrap(); +/// let mut rng = ReseedingRng::new(inner, 10, rsdr); +/// +/// // this will repeat, because it gets reseeded very regularly. +/// let s: String = rng.gen_ascii_chars().take(100).collect(); +/// println!("{}", s); +/// } +/// +/// ``` +pub trait Reseeder { + /// Reseed the given RNG. + fn reseed(&mut self, rng: &mut R); +} + +/// Reseed an RNG using a `Default` instance. This reseeds by +/// replacing the RNG with the result of a `Default::default` call. +#[derive(Clone, Copy, Debug)] +pub struct ReseedWithDefault; + +impl Reseeder for ReseedWithDefault { + fn reseed(&mut self, rng: &mut R) { + *rng = Default::default(); + } +} +impl Default for ReseedWithDefault { + fn default() -> ReseedWithDefault { ReseedWithDefault } +} + +#[cfg(test)] +mod test { + use std::default::Default; + use std::iter::repeat; + use super::{ReseedingRng, ReseedWithDefault}; + use {SeedableRng, Rng}; + + struct Counter { + i: u32 + } + + impl Rng for Counter { + fn next_u32(&mut self) -> u32 { + self.i += 1; + // very random + self.i - 1 + } + } + impl Default for Counter { + fn default() -> Counter { + Counter { i: 0 } + } + } + impl SeedableRng for Counter { + fn reseed(&mut self, seed: u32) { + self.i = seed; + } + fn from_seed(seed: u32) -> Counter { + Counter { i: seed } + } + } + type MyRng = ReseedingRng; + + #[test] + fn test_reseeding() { + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); + + let mut i = 0; + for _ in 0..1000 { + assert_eq!(rs.next_u32(), i % 100); + i += 1; + } + } + + #[test] + fn test_rng_seeded() { + let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_reseed() { + let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed((ReseedWithDefault, 3)); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + + const FILL_BYTES_V_LEN: usize = 13579; + #[test] + fn test_rng_fill_bytes() { + let mut v = repeat(0u8).take(FILL_BYTES_V_LEN).collect::>(); + ::test::rng().fill_bytes(&mut v); + + // Sanity test: if we've gotten here, `fill_bytes` has not infinitely + // recursed. + assert_eq!(v.len(), FILL_BYTES_V_LEN); + + // To test that `fill_bytes` actually did something, check that the + // average of `v` is not 0. + let mut sum = 0.0; + for &x in v.iter() { + sum += x as f64; + } + assert!(sum / v.len() as f64 != 0.0); + } +} diff --git a/src/rng.rs b/src/rng.rs deleted file mode 100644 index ce4286a85f0..00000000000 --- a/src/rng.rs +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// Copyright 2013-2017 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! [`RngExt`] trait - -use crate::distr::uniform::{SampleRange, SampleUniform}; -use crate::distr::{self, Distribution, StandardUniform}; -use core::num::Wrapping; -use core::{mem, slice}; -use rand_core::Rng; - -/// User-level interface for RNGs -/// -/// [`Rng`] is the `dyn`-safe implementation-level interface for Random -/// (Number) Generators. This trait, `Rng`, provides a user-level interface on -/// RNGs. It is implemented automatically for any `R: Rng`. -/// -/// This trait must usually be brought into scope via `use rand::RngExt;` or -/// `use rand::prelude::*;`. -/// -/// # Generic usage -/// -/// The basic pattern is `fn foo(rng: &mut R)`. Some -/// things are worth noting here: -/// -/// - Since `RngExt: Rng` and every `RngExt` implements `Rng`, it makes no -/// difference whether we use `R: Rng` or `R: RngExt` for `R: Sized`. -/// - Only `Rng` is dyn safe, supporting `&mut dyn Rng` and `R: Rng + ?Sized`. -/// -/// An alternative pattern is possible: `fn foo(rng: R)`. This has some -/// trade-offs. It allows the argument to be consumed directly without a `&mut`; -/// also it still works directly -/// on references (including type-erased references). Unfortunately within the -/// function `foo` it is not known whether `rng` is a reference type or not, -/// hence many uses of `rng` require an extra reference, either explicitly -/// (`distr.sample(&mut rng)`) or implicitly (`rng.random()`); one may hope the -/// optimiser can remove redundant references later. -/// -/// Example: -/// -/// ``` -/// use rand::{Rng, RngExt}; -/// -/// fn foo(rng: &mut R) -> f32 { -/// rng.random() -/// } -/// -/// # let v = foo(&mut rand::rng()); -/// ``` -pub trait RngExt: Rng { - /// Return a random value via the [`StandardUniform`] distribution. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut rng = rand::rng(); - /// let x: u32 = rng.random(); - /// println!("{}", x); - /// println!("{:?}", rng.random::<(f64, bool)>()); - /// ``` - /// - /// # Arrays and tuples - /// - /// The `rng.random()` method is able to generate arrays - /// and tuples (up to 12 elements), so long as all element types can be - /// generated. - /// - /// For arrays of integers, especially for those with small element types - /// (< 64 bit), it will likely be faster to instead use [`RngExt::fill`], - /// though note that generated values will differ. - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut rng = rand::rng(); - /// let tuple: (u8, i32, char) = rng.random(); // arbitrary tuple support - /// - /// let arr1: [f32; 32] = rng.random(); // array construction - /// let mut arr2 = [0u8; 128]; - /// rng.fill(&mut arr2); // array fill - /// ``` - /// - /// [`StandardUniform`]: distr::StandardUniform - #[inline] - fn random(&mut self) -> T - where - StandardUniform: Distribution, - { - StandardUniform.sample(self) - } - - /// Return an iterator over [`random`](Self::random) variates - /// - /// This is a just a wrapper over [`RngExt::sample_iter`] using - /// [`distr::StandardUniform`]. - /// - /// Note: this method consumes its argument. Use - /// `(&mut rng).random_iter()` to avoid consuming the RNG. - /// - /// # Example - /// - /// ``` - /// use rand::{rngs::SmallRng, RngExt, SeedableRng}; - /// - /// let rng = SmallRng::seed_from_u64(0); - /// let v: Vec = rng.random_iter().take(5).collect(); - /// assert_eq!(v.len(), 5); - /// ``` - #[inline] - fn random_iter(self) -> distr::Iter - where - Self: Sized, - StandardUniform: Distribution, - { - StandardUniform.sample_iter(self) - } - - /// Generate a random value in the given range. - /// - /// This function is optimised for the case that only a single sample is - /// made from the given range. See also the [`Uniform`] distribution - /// type which may be faster if sampling from the same range repeatedly. - /// - /// All types support `low..high_exclusive` and `low..=high` range syntax. - /// Unsigned integer types also support `..high_exclusive` and `..=high` syntax. - /// - /// # Panics - /// - /// Panics if the range is empty, or if `high - low` overflows for floats. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut rng = rand::rng(); - /// - /// // Exclusive range - /// let n: u32 = rng.random_range(..10); - /// println!("{}", n); - /// let m: f64 = rng.random_range(-40.0..1.3e5); - /// println!("{}", m); - /// - /// // Inclusive range - /// let n: u32 = rng.random_range(..=10); - /// println!("{}", n); - /// ``` - /// - /// [`Uniform`]: distr::uniform::Uniform - #[track_caller] - fn random_range(&mut self, range: R) -> T - where - T: SampleUniform, - R: SampleRange, - { - assert!(!range.is_empty(), "cannot sample empty range"); - range.sample_single(self).unwrap() - } - - /// Return a bool with a probability `p` of being true. - /// - /// See also the [`Bernoulli`] distribution, which may be faster if - /// sampling from the same probability repeatedly. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut rng = rand::rng(); - /// println!("{}", rng.random_bool(1.0 / 3.0)); - /// ``` - /// - /// # Panics - /// - /// If `p < 0` or `p > 1`. - /// - /// [`Bernoulli`]: distr::Bernoulli - #[inline] - #[track_caller] - fn random_bool(&mut self, p: f64) -> bool { - match distr::Bernoulli::new(p) { - Ok(d) => self.sample(d), - Err(_) => panic!("p={:?} is outside range [0.0, 1.0]", p), - } - } - - /// Return a bool with a probability of `numerator/denominator` of being - /// true. - /// - /// That is, `random_ratio(2, 3)` has chance of 2 in 3, or about 67%, of - /// returning true. If `numerator == denominator`, then the returned value - /// is guaranteed to be `true`. If `numerator == 0`, then the returned - /// value is guaranteed to be `false`. - /// - /// See also the [`Bernoulli`] distribution, which may be faster if - /// sampling from the same `numerator` and `denominator` repeatedly. - /// - /// # Panics - /// - /// If `denominator == 0` or `numerator > denominator`. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut rng = rand::rng(); - /// println!("{}", rng.random_ratio(2, 3)); - /// ``` - /// - /// [`Bernoulli`]: distr::Bernoulli - #[inline] - #[track_caller] - fn random_ratio(&mut self, numerator: u32, denominator: u32) -> bool { - match distr::Bernoulli::from_ratio(numerator, denominator) { - Ok(d) => self.sample(d), - Err(_) => panic!( - "p={}/{} is outside range [0.0, 1.0]", - numerator, denominator - ), - } - } - - /// Sample a new value, using the given distribution. - /// - /// ### Example - /// - /// ``` - /// use rand::RngExt; - /// use rand::distr::Uniform; - /// - /// let mut rng = rand::rng(); - /// 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::(Uniform::new(10, 15).unwrap()); - /// ``` - fn sample>(&mut self, distr: D) -> T { - distr.sample(self) - } - - /// Create an iterator that generates values using the given distribution. - /// - /// Note: this method consumes its arguments. Use - /// `(&mut rng).sample_iter(..)` to avoid consuming the RNG. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// use rand::distr::{Alphanumeric, Uniform, StandardUniform}; - /// - /// let mut rng = rand::rng(); - /// - /// // Vec of 16 x f32: - /// let v: Vec = (&mut rng).sample_iter(StandardUniform).take(16).collect(); - /// - /// // String: - /// let s: String = (&mut rng).sample_iter(Alphanumeric) - /// .take(7) - /// .map(char::from) - /// .collect(); - /// - /// // Combined values - /// println!("{:?}", (&mut rng).sample_iter(StandardUniform).take(5) - /// .collect::>()); - /// - /// // Dice-rolling: - /// 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!"); - /// } - /// ``` - fn sample_iter(self, distr: D) -> distr::Iter - where - D: Distribution, - Self: Sized, - { - distr.sample_iter(self) - } - - /// Fill any type implementing [`Fill`] with random data - /// - /// This method is implemented for types which may be safely reinterpreted - /// as an (aligned) `[u8]` slice then filled with random data. It is often - /// faster than using [`RngExt::random`] but not value-equivalent. - /// - /// The distribution is expected to be uniform with portable results, but - /// this cannot be guaranteed for third-party implementations. - /// - /// # Example - /// - /// ``` - /// use rand::RngExt; - /// - /// let mut arr = [0i8; 20]; - /// rand::rng().fill(&mut arr[..]); - /// ``` - /// - /// [`fill_bytes`]: Rng::fill_bytes - #[track_caller] - fn fill(&mut self, dest: &mut [T]) { - Fill::fill_slice(dest, self) - } -} - -impl RngExt for R {} - -/// Support filling a slice with random data -/// -/// This trait allows slices of "plain data" types to be efficiently filled -/// with random data. -/// -/// Implementations are expected to be portable across machines unless -/// clearly documented otherwise (see the -/// [Chapter on Portability](https://rust-random.github.io/book/portability.html)). -/// The implementations provided achieve this by byte-swapping on big-endian -/// machines. -pub trait Fill: Sized { - /// Fill this with random data - fn fill_slice(this: &mut [Self], rng: &mut R); -} - -impl Fill for u8 { - fn fill_slice(this: &mut [Self], rng: &mut R) { - rng.fill_bytes(this) - } -} - -/// Call target for unsafe macros -const unsafe fn __unsafe() {} - -/// Implement `Fill` for given type `$t`. -/// -/// # Safety -/// All bit patterns of `[u8; size_of::<$t>()]` must represent values of `$t`. -macro_rules! impl_fill { - () => {}; - (to_le! plain $x:ident) => { - $x.to_le() - }; - (to_le! wrapping $x:ident) => { - Wrapping($x.0.to_le()) - }; - (fill_slice! $t:ty, $to_le:tt) => { - fn fill_slice(this: &mut [Self], rng: &mut R) { - if this.len() > 0 { - let size = mem::size_of_val(this); - rng.fill_bytes( - // SAFETY: `this` non-null and valid for reads and writes within its `size` - // bytes. `this` meets the alignment requirements of `&mut [u8]`. - // The contents of `this` are initialized. Both `[u8]` and `[$t]` are valid - // for all bit-patterns of their contents (note that the SAFETY requirement - // on callers of this macro). `this` is not borrowed. - unsafe { - slice::from_raw_parts_mut(this.as_mut_ptr() - as *mut u8, - size - ) - } - ); - for x in this { - *x = impl_fill!(to_le! $to_le x); - } - } - } - }; - ($t:ty) => {{ - // Force caller to wrap with an `unsafe` block - __unsafe(); - - impl Fill for $t { - impl_fill!(fill_slice! $t, plain); - } - - impl Fill for Wrapping<$t> { - impl_fill!(fill_slice! $t, wrapping); - }} - }; - ($t:ty, $($tt:ty,)*) => {{ - impl_fill!($t); - // TODO: this could replace above impl once Rust #32463 is fixed - // impl_fill!(Wrapping<$t>); - impl_fill!($($tt,)*); - }} -} - -// SAFETY: All bit patterns of `[u8; size_of::<$t>()]` represent values of `u*`. -const _: () = unsafe { impl_fill!(u16, u32, u64, u128,) }; -// SAFETY: All bit patterns of `[u8; size_of::<$t>()]` represent values of `i*`. -const _: () = unsafe { impl_fill!(i8, i16, i32, i64, i128,) }; - -#[cfg(test)] -mod test { - use super::*; - use crate::test::{const_rng, rng}; - #[cfg(feature = "alloc")] - use alloc::boxed::Box; - - #[test] - fn test_fill_bytes_default() { - let mut r = const_rng(0x11_22_33_44_55_66_77_88); - - // check every remainder mod 8, both in small and big vectors. - let lengths = [0, 1, 2, 3, 4, 5, 6, 7, 80, 81, 82, 83, 84, 85, 86, 87]; - for &n in lengths.iter() { - let mut buffer = [0u8; 87]; - let v = &mut buffer[0..n]; - r.fill_bytes(v); - - // use this to get nicer error messages. - for (i, &byte) in v.iter().enumerate() { - if byte == 0 { - panic!("byte {} of {} is zero", i, n) - } - } - } - } - - #[test] - fn test_fill() { - let x = 9041086907909331047; // a random u64 - let mut rng = const_rng(x); - - // Convert to byte sequence and back to u64; byte-swap twice if BE. - let mut array = [0u64; 2]; - rng.fill(&mut array); - assert_eq!(array, [x, x]); - assert_eq!(rng.next_u64(), x); - - // Convert to bytes then u32 in LE order - let mut array = [0u32; 2]; - rng.fill(&mut array); - assert_eq!(array, [x as u32, (x >> 32) as u32]); - assert_eq!(rng.next_u32(), x as u32); - - // Check equivalence using wrapped arrays - let mut warray = [Wrapping(0u32); 2]; - rng.fill(&mut warray); - assert_eq!(array[0], warray[0].0); - assert_eq!(array[1], warray[1].0); - } - - #[test] - fn test_fill_empty() { - let mut array = [0u32; 0]; - let mut rng = rng(1); - rng.fill(&mut array); - rng.fill(&mut array[..]); - } - - #[test] - fn test_random_range_int() { - let mut r = rng(101); - for _ in 0..1000 { - let a = r.random_range(-4711..17); - assert!((-4711..17).contains(&a)); - let a: i8 = r.random_range(-3..42); - assert!((-3..42).contains(&a)); - let a: u16 = r.random_range(10..99); - assert!((10..99).contains(&a)); - let a: i32 = r.random_range(-100..2000); - assert!((-100..2000).contains(&a)); - let a: u32 = r.random_range(12..=24); - assert!((12..=24).contains(&a)); - - assert_eq!(r.random_range(..1u32), 0u32); - assert_eq!(r.random_range(-12i64..-11), -12i64); - assert_eq!(r.random_range(3_000_000..3_000_001), 3_000_000); - } - } - - #[test] - fn test_random_range_float() { - let mut r = rng(101); - for _ in 0..1000 { - let a = r.random_range(-4.5..1.7); - assert!((-4.5..1.7).contains(&a)); - let a = r.random_range(-1.1..=-0.3); - assert!((-1.1..=-0.3).contains(&a)); - - assert_eq!(r.random_range(0.0f32..=0.0), 0.); - assert_eq!(r.random_range(-11.0..=-11.0), -11.); - assert_eq!(r.random_range(3_000_000.0..=3_000_000.0), 3_000_000.); - } - } - - #[test] - #[should_panic] - #[allow(clippy::reversed_empty_ranges)] - fn test_random_range_panic_int() { - let mut r = rng(102); - r.random_range(5..-2); - } - - #[test] - #[should_panic] - #[allow(clippy::reversed_empty_ranges)] - fn test_random_range_panic_usize() { - let mut r = rng(103); - r.random_range(5..2); - } - - #[test] - #[allow(clippy::bool_assert_comparison)] - fn test_random_bool() { - let mut r = rng(105); - for _ in 0..5 { - assert_eq!(r.random_bool(0.0), false); - assert_eq!(r.random_bool(1.0), true); - } - } - - #[test] - fn test_rng_mut_ref() { - fn use_rng(mut r: impl RngExt) { - let _ = r.next_u32(); - } - - let mut rng = rng(109); - use_rng(&mut rng); - } - - #[test] - fn test_rng_trait_object() { - use crate::distr::{Distribution, StandardUniform}; - let mut rng = rng(109); - let mut r = &mut rng as &mut dyn Rng; - r.next_u32(); - r.random::(); - assert_eq!(r.random_range(0..1), 0); - let _c: u8 = StandardUniform.sample(&mut r); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_rng_boxed_trait() { - use crate::distr::{Distribution, StandardUniform}; - let rng = rng(110); - let mut r = Box::new(rng) as Box; - r.next_u32(); - r.random::(); - assert_eq!(r.random_range(0..1), 0); - let _c: u8 = StandardUniform.sample(&mut r); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_gen_ratio_average() { - const NUM: u32 = 3; - const DENOM: u32 = 10; - const N: u32 = 100_000; - - let mut sum: u32 = 0; - let mut rng = rng(111); - for _ in 0..N { - if rng.random_ratio(NUM, DENOM) { - sum += 1; - } - } - // Have Binomial(N, NUM/DENOM) distribution - let expected = (NUM * N) / DENOM; // exact integer - assert!(((sum - expected) as i32).abs() < 500); - } -} diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs deleted file mode 100644 index 51b107fd08b..00000000000 --- a/src/rngs/mod.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Random number generators and adapters -//! -//! ## Generators -//! -//! This crate provides a small selection of generators. -//! See also [Types of generators] and [Our RNGs] in the book. -//! -//! ##### Non-deterministic generators -//! -//! - [`SysRng`] is a stateless interface over the operating system's random number -//! source. This is typically secure with some form of periodic re-seeding. -//! - [`ThreadRng`], provided by [`crate::rng()`], is a handle to a -//! thread-local generator with periodic seeding from [`SysRng`]. Because this -//! is local, it is typically much faster than [`SysRng`]. It should be -//! secure, but see documentation on [`ThreadRng`]. -//! -//! ##### Standard generators -//! -//! These use selected best-in-class algorithms. They are deterministic but not -//! portable: the algorithms may be changed in any release and may be -//! platform-dependent. -//! -//! - [`StdRng`] is a CSPRNG chosen for good performance and trust of security -//! (based on reviews, maturity and usage). The current algorithm is -//! [`ChaCha12Rng`], which is well established and rigorously analysed. -//! [`StdRng`] is the deterministic generator used by [`ThreadRng`] but -//! without the periodic reseeding or thread-local management. -//! - [`SmallRng`] is a relatively simple, insecure generator designed to be -//! fast, use little memory, and pass various statistical tests of -//! randomness quality. The current algorithm is one of the Xoshiro -//! generators below, depending on the target's pointer size. -//! -//! ##### Named portable generators -//! -//! These are similar to the [standard generators](#standard-generators), but -//! with the additional [guarantees of reproducibility]: -//! -//! - [`Xoshiro256PlusPlus`] is a very fast 64-bit insecure generator using -//! 256 bits of state with good performance in statistical tests of quality -//! - [`Xoshiro128PlusPlus`] is a very fast 32-bit insecure generator using -//! 128 bits of state with good performance in statistical tests of quality -//! - [`ChaCha8Rng`], [`ChaCha12Rng`] and [`ChaCha20Rng`] are generators over -//! the ChaCha stream cipher designed by Daniel J. Bernstein[^1]. -//! -//! ### Additional generators -//! -//! - The [`rdrand`] crate provides an interface to the RDRAND and RDSEED -//! instructions available in modern Intel and AMD CPUs. -//! - The [`rand_jitter`] crate provides a user-space implementation of -//! entropy harvesting from CPU timer jitter, but is very slow and has -//! [security issues](https://github.com/rust-random/rand/issues/699). -//! - The [`rand_pcg`] crate provides portable implementations of a subset -//! of the [PCG] family of small, insecure generators -//! - The [`rand_xoshiro`] crate provides portable implementations of the -//! [xoshiro] family of small, insecure generators -//! -//! For more, search [crates with the `rng` tag]. -//! -//! ## Traits and functionality -//! -//! All generators implement [`TryRng`]. Most implement [`Rng`] (i.e. -//! `TryRng`) and thus also implement [`Rng`][crate::Rng]. -//! See also the [Random Values] chapter in the book. -//! -//! Secure RNGs may additionally implement the [`CryptoRng`] trait. -//! -//! Use the [`rand_core`] crate when implementing your own RNGs. -//! -//! [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*](https://cr.yp.to/chacha.html) -//! -//! [guarantees of reproducibility]: https://rust-random.github.io/book/crate-reprod.html -//! [Types of generators]: https://rust-random.github.io/book/guide-gen.html -//! [Our RNGs]: https://rust-random.github.io/book/guide-rngs.html -//! [Random Values]: https://rust-random.github.io/book/guide-values.html -//! [`Rng`]: crate::RngExt -//! [`TryRng`]: crate::TryRng -//! [`Rng`]: crate::Rng -//! [`CryptoRng`]: crate::CryptoRng -//! [`SeedableRng`]: crate::SeedableRng -//! [`rdrand`]: https://crates.io/crates/rdrand -//! [`rand_jitter`]: https://crates.io/crates/rand_jitter -//! [`rand_pcg`]: https://crates.io/crates/rand_pcg -//! [`rand_xoshiro`]: https://crates.io/crates/rand_xoshiro -//! [crates with the `rng` tag]: https://crates.io/keywords/rng -//! [chacha]: https://cr.yp.to/chacha.html -//! [PCG]: https://www.pcg-random.org/ -//! [xoshiro]: https://prng.di.unimi.it/ - -mod small; -mod xoshiro128plusplus; -mod xoshiro256plusplus; - -#[cfg(feature = "std_rng")] -mod std; -#[cfg(feature = "thread_rng")] -pub(crate) mod thread; - -pub use self::small::SmallRng; -pub use xoshiro128plusplus::Xoshiro128PlusPlus; -pub use xoshiro256plusplus::Xoshiro256PlusPlus; - -#[cfg(feature = "std_rng")] -pub use self::std::StdRng; -#[cfg(feature = "thread_rng")] -pub use self::thread::ThreadRng; - -#[cfg(feature = "chacha")] -pub use chacha20::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}; - -#[cfg(feature = "sys_rng")] -pub use getrandom::{Error as SysError, SysRng}; diff --git a/src/rngs/small.rs b/src/rngs/small.rs deleted file mode 100644 index 9535be35bbe..00000000000 --- a/src/rngs/small.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A small fast RNG - -use core::convert::Infallible; -use rand_core::{SeedableRng, TryRng}; - -#[cfg(any(target_pointer_width = "32", target_pointer_width = "16"))] -type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus; -#[cfg(target_pointer_width = "64")] -type Rng = super::xoshiro256plusplus::Xoshiro256PlusPlus; - -/// A small-state, fast, non-crypto, non-portable PRNG -/// -/// This is the "standard small" RNG, a generator with the following properties: -/// -/// - Non-[portable]: any future library version may replace the algorithm -/// and results may be platform-dependent. -/// (For a small portable generator, use the [rand_pcg] or [rand_xoshiro] crate.) -/// - Non-cryptographic: output is easy to predict (insecure) -/// - [Quality]: statistically good quality -/// - Fast: the RNG is fast for both bulk generation and single values, with -/// consistent cost of method calls -/// - Fast initialization -/// - Small state: little memory usage (current state size is 16-32 bytes -/// depending on platform) -/// -/// The current algorithm is -/// `Xoshiro256PlusPlus` on 64-bit platforms and `Xoshiro128PlusPlus` on 32-bit -/// platforms. Both are also implemented by the [rand_xoshiro] crate. -/// -/// ## Seeding (construction) -/// -/// This generator implements the [`SeedableRng`] trait. All methods are -/// suitable for seeding, but note that, even with a fixed seed, output is not -/// [portable]. Some suggestions: -/// -/// 1. To automatically seed with a unique seed, use [`rand::make_rng()`]: -/// ``` -/// use rand::rngs::SmallRng; -/// let mut rng: SmallRng = rand::make_rng(); -/// # let _ = rand::Rng::next_u32(&mut rng); -/// ``` -/// 2. To use a deterministic integral seed, use `seed_from_u64`. This uses a -/// hash function internally to yield a (typically) good seed from any -/// input. -/// ``` -/// # use rand::{SeedableRng, rngs::SmallRng}; -/// let rng = SmallRng::seed_from_u64(1); -/// # let _: SmallRng = rng; -/// ``` -/// 3. To seed deterministically from text or other input, use [`rand_seeder`]. -/// -/// See also [Seeding RNGs] in the book. -/// -/// ## Generation -/// -/// The generators implements [`Rng`] and thus also [`Rng`][crate::Rng]. -/// See also the [Random Values] chapter in the book. -/// -/// [portable]: https://rust-random.github.io/book/crate-reprod.html -/// [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html -/// [Random Values]: https://rust-random.github.io/book/guide-values.html -/// [Quality]: https://rust-random.github.io/book/guide-rngs.html#quality -/// [`StdRng`]: crate::rngs::StdRng -/// [rand_pcg]: https://crates.io/crates/rand_pcg -/// [rand_xoshiro]: https://crates.io/crates/rand_xoshiro -/// [`rand_seeder`]: https://docs.rs/rand_seeder/latest/rand_seeder/ -/// [`Rng`]: rand_core::Rng -/// [`rand::make_rng()`]: crate::make_rng -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SmallRng(Rng); - -impl SeedableRng for SmallRng { - // Fix to 256 bits. Changing this is a breaking change! - type Seed = [u8; 32]; - - #[inline(always)] - fn from_seed(seed: Self::Seed) -> Self { - // This is for compatibility with 32-bit platforms where Rng::Seed has a different seed size - // With MSRV >= 1.77: let seed = *seed.first_chunk().unwrap() - const LEN: usize = core::mem::size_of::<::Seed>(); - let seed = (&seed[..LEN]).try_into().unwrap(); - SmallRng(Rng::from_seed(seed)) - } - - #[inline(always)] - fn seed_from_u64(state: u64) -> Self { - SmallRng(Rng::seed_from_u64(state)) - } -} - -impl TryRng for SmallRng { - type Error = Infallible; - - #[inline(always)] - fn try_next_u32(&mut self) -> Result { - self.0.try_next_u32() - } - - #[inline(always)] - fn try_next_u64(&mut self) -> Result { - self.0.try_next_u64() - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> { - self.0.try_fill_bytes(dest) - } -} diff --git a/src/rngs/std.rs b/src/rngs/std.rs deleted file mode 100644 index 25675fc076f..00000000000 --- a/src/rngs/std.rs +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The standard RNG - -use core::convert::Infallible; -use rand_core::{SeedableRng, TryCryptoRng, TryRng}; - -use chacha20::ChaCha12Rng as Rng; - -/// A strong, fast (amortized), non-portable RNG -/// -/// This is the "standard" RNG, a generator with the following properties: -/// -/// - Non-[portable]: any future library version may replace the algorithm -/// and results may be platform-dependent. -/// (For a portable version, use the [chacha20] crate directly.) -/// - [CSPRNG]: statistically good quality of randomness and [unpredictable] -/// - Fast ([amortized](https://en.wikipedia.org/wiki/Amortized_analysis)): -/// the RNG is fast for bulk generation, but the cost of method calls is not -/// consistent due to usage of an output buffer. -/// -/// The current algorithm used is the ChaCha block cipher with 12 rounds. Please -/// see this relevant [rand issue] for the discussion. This may change as new -/// evidence of cipher security and performance becomes available. -/// -/// ## Seeding (construction) -/// -/// This generator implements the [`SeedableRng`] trait. Any method may be used, -/// but note that `seed_from_u64` is not suitable for usage where security is -/// important. Also note that, even with a fixed seed, output is not [portable]. -/// -/// Using a fresh seed **direct from the OS** is the most secure option: -/// ``` -/// # use rand::{SeedableRng, rngs::{StdRng, SysRng}}; -/// let rng = StdRng::try_from_rng(&mut SysRng).unwrap(); -/// # let _: StdRng = rng; -/// ``` -/// -/// Seeding via [`rand::make_rng()`] or [`rand::rng()`] may be -/// faster: -/// ``` -/// # use rand::rngs::StdRng; -/// let mut rng: StdRng = rand::make_rng(); -/// # let _ = rand::Rng::next_u32(&mut rng); -/// ``` -/// -/// Any [`SeedableRng`] method may be used, but note that `seed_from_u64` is not -/// suitable where security is required. See also [Seeding RNGs] in the book. -/// -/// ## Generation -/// -/// The generators implements [`Rng`] and thus also [`Rng`][crate::Rng]. -/// See also the [Random Values] chapter in the book. -/// -/// [portable]: https://rust-random.github.io/book/crate-reprod.html -/// [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html -/// [unpredictable]: https://rust-random.github.io/book/guide-rngs.html#security -/// [Random Values]: https://rust-random.github.io/book/guide-values.html -/// [CSPRNG]: https://rust-random.github.io/book/guide-gen.html#cryptographically-secure-pseudo-random-number-generator -/// [chacha20]: https://crates.io/crates/chacha20 -/// [rand issue]: https://github.com/rust-random/rand/issues/932 -/// [`Rng`]: rand_core::Rng -/// [`rand::make_rng()`]: crate::make_rng -/// [`rand::rng()`]: crate::rng -#[derive(Debug, PartialEq, Eq)] -pub struct StdRng(Rng); - -impl TryRng for StdRng { - type Error = Infallible; - - #[inline(always)] - fn try_next_u32(&mut self) -> Result { - self.0.try_next_u32() - } - - #[inline(always)] - fn try_next_u64(&mut self) -> Result { - self.0.try_next_u64() - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { - self.0.try_fill_bytes(dst) - } -} - -impl SeedableRng for StdRng { - // Fix to 256 bits. Changing this is a breaking change! - type Seed = [u8; 32]; - - #[inline(always)] - fn from_seed(seed: Self::Seed) -> Self { - StdRng(Rng::from_seed(seed)) - } -} - -impl TryCryptoRng for StdRng {} - -#[cfg(test)] -mod test { - use crate::rngs::StdRng; - use crate::{Rng, RngExt, SeedableRng}; - - #[test] - fn test_stdrng_construction() { - // Test value-stability of StdRng. This is expected to break any time - // the algorithm is changed. - #[rustfmt::skip] - let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, - 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; - - let target = [10719222850664546238, 14064965282130556830]; - - let mut rng0 = StdRng::from_seed(seed); - - let x0 = rng0.next_u64(); - - let mut rng1 = StdRng::from_rng(&mut rng0); - let x1 = rng1.next_u64(); - - assert_eq!([x0, x1], target); - } - - #[test] - fn test_chacha_true_values_1() { - // Source: Strombergson 2013, Test Vectors for the Stream Cipher ChaCha - // draft-strombergson-chacha-test-vectors-01 - // https://datatracker.ietf.org/doc/html/draft-strombergson-chacha-test-vectors-01 - // Converted to LE u128 form (four u128 to one block). - // TC: all zero key and IV, rounds 12, 256-bit key - - let seed = [0u8; 32]; - let mut rng = StdRng::from_seed(seed); - - let mut results = [0u128; 8]; - rng.fill(&mut results); - let expected = [ - 0xd583265f12ce1f8153f955076a9af49b, - 0x5f15ae2ea589007e1474e049bbc32904, - 0x798cfaac3428e82cc0e37ad279f86405, - 0xbe2613412fe80b611969dea02c9f623a, - 0x3d17e08c3371fc86fe743e204188d50b, - 0xb489c04c21851515cccbbd19b7eb28c6, - 0x43c88c1b97b802c611f14ca1cd8d2542, - 0x1693e617b0a64427c0515190ca461ee9, - ]; - assert_eq!(results, expected); - - assert_eq!(rng.0.get_word_pos(), 32); - } - - #[test] - fn test_chacha_true_values_2() { - // Source: Strombergson 2013, Test Vectors for the Stream Cipher ChaCha - // TC2: single bit set in key, all zero IV, rounds 12, 256-bit key - - let mut seed = [0u8; 32]; - seed[0] = 1; - let mut rng = StdRng::from_seed(seed); - - let mut results = [0u128; 8]; - rng.fill(&mut results); - let expected = [ - 0x9a225cdf090f0eef6b0565d596e0512, - 0x10dd4d0bff1802930f5d5290278c2449, - 0xfefdfe067d7a109ee254a4d9392200a6, - 0xc029dc60c972179bf2f944a0eb0f21f0, - 0x2a37692ab05e660e2404c6cbc566730c, - 0xc8a72980b8c4c72a0978bb6fb279f97a, - 0xaf15ba8e302e43907dfcbb17c23b5154, - 0xa9177125baafe601560d10ef48eb5ac6, - ]; - assert_eq!(results, expected); - - assert_eq!(rng.0.get_word_pos(), 32); - } - - #[test] - fn test_chacha_true_values_3() { - // Source: Strombergson 2013, Test Vectors for the Stream Cipher ChaCha - // TC3: all zero key, single bit set in IV, rounds 12, 256-bit key - - let seed = [0u8; 32]; - let mut rng = StdRng::from_seed(seed); - rng.0.set_stream(1); - - let mut results = [0u128; 8]; - rng.fill(&mut results); - let expected = [ - 0x3de08d69eff7ba6d4b8c827bf8bdb864, - 0x6929e19be5ad36988f411457633fb3f8, - 0xa5995d1de898cb9efccf8ef3a053c946, - 0xf1d8f021fb3f31ee4b9450a9a8ffced, - 0x28886a59a2b923fe42c422f2a7b49d55, - 0x23c72a9150a17ca76e8963134fee2251, - 0x67b7d07029cb2037e802f6a024bf0bf, - 0x6fa2523bbd836d3a01c8137c82b91afc, - ]; - assert_eq!(results, expected); - - assert_eq!(rng.0.get_word_pos(), 32); - } - - #[test] - fn test_chacha_true_values_8() { - // Source: Strombergson 2013, Test Vectors for the Stream Cipher ChaCha - // TC8: key: 'All your base are belong to us!', IV: IETF2013, rounds 12, 256-bit key - - #[rustfmt::skip] - let seed = [ - 0xc4, 0x6e, 0xc1, 0xb1, 0x8c, 0xe8, 0xa8, 0x78, - 0x72, 0x5a, 0x37, 0xe7, 0x80, 0xdf, 0xb7, 0x35, - 0x1f, 0x68, 0xed, 0x2e, 0x19, 0x4c, 0x79, 0xfb, - 0xc6, 0xae, 0xbe, 0xe1, 0xa6, 0x67, 0x97, 0x5d, - ]; - let iv = [0x1a, 0xda, 0x31, 0xd5, 0xcf, 0x68, 0x82, 0x21]; - let mut rng = StdRng::from_seed(seed); - rng.0.set_stream(u64::from_le_bytes(iv)); - - let mut results = [0u128; 8]; - rng.fill(&mut results); - let expected = [ - 0x10c08b11dc3be7b4066dbc8427078214, - 0xc19c7e1f25aa8669e018a96c7876793c, - 0x207c8db0992e2d24b483ee160a9a74b2, - 0xabfb0f9db3b1613b28876c46bc802b09, - 0x5495b60d624f9e9b32dbebc16b114bd9, - 0x31d66e96ad465a970c3d47689b3d8e4a, - 0x3c11e5a1df7a04d8c7ead50a53ff2ae4, - 0x2ba4a57be08f1cac89d1f183b8e3f391, - ]; - assert_eq!(results, expected); - - assert_eq!(rng.0.get_word_pos(), 32); - } - - #[test] - fn test_chacha_counter() { - // Source: rand_chacha implementation - // We test six blocks: counter=u32::MAX, four blocks from 2^32 (backends - // which yield four blocks at a time may need to handle this specially) - // and the first block after this wrap-logic completes. - // Test: all zero key and IV, block set to u32::MAX, rounds 12, 256-bit key - - let seed = [0u8; 32]; - let mut rng = StdRng::from_seed(seed); - let block = u32::MAX; - let words_per_block = 16; - rng.0.set_word_pos((block as u128) * words_per_block); - - let mut results = [0u128; 4 * 6]; - rng.fill(&mut results); - let expected = [ - 0xf106e2fcbb524248292ac9f150afa6d7, - 0x12032ef6c183b50a83a3309513dd017d, - 0x2c93ff300438eaed6c958a9aa1619382, - 0x74fc0624270ab858508377945edb52d0, - 0xe5f4f4a8b8810524264d8911dc537bcc, - 0x18a6a6cbdc1f823fb1231280056740af, - 0xabdae0a44b1f45edbccc83dcd3f8638a, - 0xad6b649f12f70de567cc39740dbb8a22, - 0x37512785327825dc30ecfaf37a38f5a0, - 0x5af852d2df0dc286c2dd19af39b54e39, - 0xb04dc185c27497ac9f4a4f6769d1b5d, - 0x816492be66439cecd2498c9865284377, - 0x724fe95e0b6cbb8a55b707c06166147f, - 0xe3e7cda19d92b5318024abb34aa31329, - 0x1a3594d7283c077017cd511144bf3db3, - 0x99ab26cf14f38b11d78e413bdce6424c, - 0x553deaed89d3bf630de05408c0f655e8, - 0x86c46a5676fef18f0dc0dff3ee16507c, - 0xd33d6cf5ade97b000b29e3ce614faf51, - 0x5b62dcc48c0fc60326afc5783c40d40c, - 0x44eedc777ed030f43d382d4921eba244, - 0xa2d66a5893ade34a0d17c706e8d89dba, - 0xd229d1f3a07526e47cabd035135012fd, - 0xefae0722059b654dea6945547e535052, - ]; - assert_eq!(results, expected); - - assert_eq!(rng.0.get_word_pos(), (block as u128) * words_per_block + 96); - } -} diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs deleted file mode 100644 index 63d334dae7d..00000000000 --- a/src/rngs/thread.rs +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Thread-local random number generator - -use core::{cell::UnsafeCell, convert::Infallible}; -use std::fmt; -use std::rc::Rc; -use std::thread_local; - -use super::{SysError, SysRng}; -use rand_core::SeedableRng; -use rand_core::block::{BlockRng, Generator}; -use rand_core::{TryCryptoRng, TryRng}; - -// Rationale for using `UnsafeCell` in `ThreadRng`: -// -// Previously we used a `RefCell`, with an overhead of ~15%. There will only -// ever be one mutable reference to the interior of the `UnsafeCell`, because -// we only have such a reference inside `next_u32`, `next_u64`, etc. Within a -// single thread (which is the definition of `ThreadRng`), there will only ever -// be one of these methods active at a time. -// -// A possible scenario where there could be multiple mutable references is if -// `ThreadRng` is used inside `next_u32` and co. But the implementation is -// completely under our control. We just have to ensure none of them use -// `ThreadRng` internally, which is nonsensical anyway. We should also never run -// `ThreadRng` in destructors of its implementation, which is also nonsensical. - -// Number of generated bytes after which to reseed `ThreadRng`. -// According to benchmarks, reseeding has a noticeable impact with thresholds -// of 32 kB and less. We choose 64 kiB output to avoid significant overhead; -// since a block consists of 16 4-byte words this equals 1024 blocks. -const RESEED_BLOCK_THRESHOLD: u64 = 1024; - -type Core = chacha20::ChaChaCore; -type Results = ::Output; - -struct ReseedingCore { - inner: Core, -} - -impl Generator for ReseedingCore { - type Output = Results; - - #[inline(always)] - fn generate(&mut self, results: &mut Results) { - if self.inner.get_block_pos() >= RESEED_BLOCK_THRESHOLD { - self.try_to_reseed(); - } - self.inner.generate(results); - } -} - -impl ReseedingCore { - /// Reseed the internal PRNG. - fn reseed(&mut self) -> Result<(), SysError> { - Core::try_from_rng(&mut SysRng).map(|result| self.inner = result) - } - - #[cold] - #[inline(never)] - fn try_to_reseed(&mut self) { - trace!("Reseeding RNG (periodic reseed)"); - - if let Err(e) = self.reseed() { - warn!("Reseeding RNG failed: {e}"); - } - } -} - -/// 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 [`rand::rng()`][crate::rng()] or via -/// [`ThreadRng::default()`]. -/// The handle cannot be passed between threads (is not `Send` or `Sync`). -/// -/// # Security -/// -/// Security must be considered relative to a threat model and validation -/// requirements. The Rand project can provide no guarantee of fitness for -/// purpose. The design criteria for `ThreadRng` are as follows: -/// -/// - Automatic seeding via [`SysRng`] and after every 64 kB of output. -/// Limitation: there is no automatic reseeding on process fork (see [below](#fork)). -/// - A rigorusly analyzed, unpredictable (cryptographic) pseudo-random generator -/// (see [the book on security](https://rust-random.github.io/book/guide-rngs.html#security)). -/// The currently selected algorithm is ChaCha (12-rounds). -/// See also [`StdRng`] documentation. -/// - Not to leak internal state through [`Debug`] or serialization -/// implementations. -/// - No further protections exist to in-memory state. In particular, the -/// implementation is not required to zero memory on exit (of the process or -/// thread). (This may change in the future.) -/// - Be fast enough for general-purpose usage. Note in particular that -/// `ThreadRng` is designed to be a "fast, reasonably secure generator" -/// (where "reasonably secure" implies the above criteria). -/// -/// We leave it to the user to determine whether this generator meets their -/// security requirements. For an alternative, see [`SysRng`]. -/// -/// # Fork -/// -/// `ThreadRng` is not automatically reseeded on fork. It is recommended to -/// explicitly call [`ThreadRng::reseed`] immediately after a fork, for example: -/// ```ignore -/// fn do_fork() { -/// let pid = unsafe { libc::fork() }; -/// if pid == 0 { -/// // Reseed ThreadRng in child processes: -/// rand::rng().reseed(); -/// } -/// } -/// ``` -/// -/// Methods on `ThreadRng` are not reentrant-safe and thus should not be called -/// from an interrupt (e.g. a fork handler) unless it can be guaranteed that no -/// other method on the same `ThreadRng` is currently executing. -/// -/// [`StdRng`]: crate::rngs::StdRng -#[derive(Clone)] -pub struct ThreadRng { - // Rc is explicitly !Send and !Sync - rng: Rc>>, -} - -impl ThreadRng { - /// Immediately reseed the generator - /// - /// This discards any remaining random data in the cache. - pub fn reseed(&mut self) -> Result<(), SysError> { - // SAFETY: We must make sure to stop using `rng` before anyone else - // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.reset_and_skip(0); - rng.core.reseed() - } -} - -/// 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 ThreadRng is used - // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { - Rc::new(UnsafeCell::new(BlockRng::new(ReseedingCore { - inner: Core::try_from_rng(&mut SysRng).unwrap_or_else(|err| { - panic!("could not initialize ThreadRng: {}", err) - }), - }))) - } -); - -/// Access a fast, pre-initialized generator -/// -/// This is a handle to the local [`ThreadRng`]. -/// -/// See also [`crate::rngs`] for alternatives. -/// -/// # Example -/// -/// ``` -/// use rand::prelude::*; -/// -/// # fn main() { -/// -/// let mut numbers = [1, 2, 3, 4, 5]; -/// numbers.shuffle(&mut rand::rng()); -/// println!("Numbers: {numbers:?}"); -/// -/// // Using a local binding avoids an initialization-check on each usage: -/// let mut rng = rand::rng(); -/// -/// println!("True or false: {}", rng.random::()); -/// println!("A simulated die roll: {}", rng.random_range(1..=6)); -/// # } -/// ``` -/// -/// # Security -/// -/// Refer to [`ThreadRng#Security`]. -pub fn rng() -> ThreadRng { - let rng = THREAD_RNG_KEY.with(|t| t.clone()); - ThreadRng { rng } -} - -impl Default for ThreadRng { - fn default() -> ThreadRng { - rng() - } -} - -impl TryRng for ThreadRng { - type Error = Infallible; - - #[inline(always)] - fn try_next_u32(&mut self) -> Result { - // SAFETY: We must make sure to stop using `rng` before anyone else - // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - Ok(rng.next_word()) - } - - #[inline(always)] - fn try_next_u64(&mut self) -> Result { - // SAFETY: We must make sure to stop using `rng` before anyone else - // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - Ok(rng.next_u64_from_u32()) - } - - #[inline(always)] - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> { - // SAFETY: We must make sure to stop using `rng` before anyone else - // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.fill_bytes(dest); - Ok(()) - } -} - -impl TryCryptoRng for ThreadRng {} - -#[cfg(test)] -mod test { - #[test] - fn test_thread_rng() { - use crate::RngExt; - let mut r = crate::rng(); - r.random::(); - assert_eq!(r.random_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::rng()), "ThreadRng { .. }"); - } -} diff --git a/src/rngs/xoshiro128plusplus.rs b/src/rngs/xoshiro128plusplus.rs deleted file mode 100644 index 9661e93bf49..00000000000 --- a/src/rngs/xoshiro128plusplus.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::convert::Infallible; -use rand_core::{SeedableRng, TryRng, utils}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// A xoshiro128++ random number generator. -/// -/// The xoshiro128++ algorithm is not suitable for cryptographic purposes, but -/// is very fast and has excellent statistical properties. -/// -/// The algorithm used here is translated from [the `xoshiro128plusplus.c` -/// reference source code](http://xoshiro.di.unimi.it/xoshiro128plusplus.c) by -/// David Blackman and Sebastiano Vigna. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Xoshiro128PlusPlus { - s: [u32; 4], -} - -impl SeedableRng for Xoshiro128PlusPlus { - type Seed = [u8; 16]; - - /// Create a new `Xoshiro128PlusPlus`. If `seed` is entirely 0, it will be - /// mapped to a different seed. - #[inline] - fn from_seed(seed: [u8; 16]) -> Xoshiro128PlusPlus { - let state = utils::read_words(&seed); - // Check for zero on aligned integers for better code generation. - // Furtermore, seed_from_u64(0) will expand to a constant when optimized. - if state.iter().all(|&x| x == 0) { - return Self::seed_from_u64(0); - } - Xoshiro128PlusPlus { s: state } - } - - /// Create a new `Xoshiro128PlusPlus` from a `u64` seed. - /// - /// This uses the SplitMix64 generator internally. - #[inline] - fn seed_from_u64(mut state: u64) -> Self { - const PHI: u64 = 0x9e3779b97f4a7c15; - let mut s = [0; 4]; - for i in s.chunks_exact_mut(2) { - state = state.wrapping_add(PHI); - let mut z = state; - z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); - z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); - z = z ^ (z >> 31); - i[0] = z as u32; - i[1] = (z >> 32) as u32; - } - // By using a non-zero PHI we are guaranteed to generate a non-zero state - // Thus preventing a recursion between from_seed and seed_from_u64. - debug_assert_ne!(s, [0; 4]); - Xoshiro128PlusPlus { s } - } -} - -impl TryRng for Xoshiro128PlusPlus { - type Error = Infallible; - - #[inline] - fn try_next_u32(&mut self) -> Result { - let res = self.s[0] - .wrapping_add(self.s[3]) - .rotate_left(7) - .wrapping_add(self.s[0]); - - let t = self.s[1] << 9; - - self.s[2] ^= self.s[0]; - self.s[3] ^= self.s[1]; - self.s[1] ^= self.s[2]; - self.s[0] ^= self.s[3]; - - self.s[2] ^= t; - - self.s[3] = self.s[3].rotate_left(11); - - Ok(res) - } - - #[inline] - fn try_next_u64(&mut self) -> Result { - utils::next_u64_via_u32(self) - } - - #[inline] - fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { - utils::fill_bytes_via_next_word(dst, || self.try_next_u32()) - } -} - -#[cfg(test)] -mod tests { - use super::Xoshiro128PlusPlus; - use rand_core::{Rng, SeedableRng}; - - #[test] - fn reference() { - let mut rng = - Xoshiro128PlusPlus::from_seed([1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0]); - // These values were produced with the reference implementation: - // http://xoshiro.di.unimi.it/xoshiro128plusplus.c - let expected = [ - 641, 1573767, 3222811527, 3517856514, 836907274, 4247214768, 3867114732, 1355841295, - 495546011, 621204420, - ]; - for &e in &expected { - assert_eq!(rng.next_u32(), e); - } - } - - #[test] - fn stable_seed_from_u64_and_from_seed() { - // We don't guarantee value-stability for SmallRng but this - // could influence keeping stability whenever possible (e.g. after optimizations). - let mut rng = Xoshiro128PlusPlus::seed_from_u64(0); - // from_seed([0; 16]) should produce the same state as seed_from_u64(0). - let mut rng_from_seed_0 = Xoshiro128PlusPlus::from_seed([0; 16]); - let expected = [ - 1179900579, 1938959192, 3089844957, 3657088315, 1015453891, 479942911, 3433842246, - 669252886, 3985671746, 2737205563, - ]; - for &e in &expected { - assert_eq!(rng.next_u32(), e); - assert_eq!(rng_from_seed_0.next_u32(), e); - } - } -} diff --git a/src/rngs/xoshiro256plusplus.rs b/src/rngs/xoshiro256plusplus.rs deleted file mode 100644 index 20bbe410fe4..00000000000 --- a/src/rngs/xoshiro256plusplus.rs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::convert::Infallible; -use rand_core::{SeedableRng, TryRng, utils}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// A xoshiro256++ random number generator. -/// -/// The xoshiro256++ algorithm is not suitable for cryptographic purposes, but -/// is very fast and has excellent statistical properties. -/// -/// The algorithm used here is translated from [the `xoshiro256plusplus.c` -/// reference source code](http://xoshiro.di.unimi.it/xoshiro256plusplus.c) by -/// David Blackman and Sebastiano Vigna. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Xoshiro256PlusPlus { - s: [u64; 4], -} - -impl SeedableRng for Xoshiro256PlusPlus { - type Seed = [u8; 32]; - - /// Create a new `Xoshiro256PlusPlus`. If `seed` is entirely 0, it will be - /// mapped to a different seed. - #[inline] - fn from_seed(seed: [u8; 32]) -> Xoshiro256PlusPlus { - let state = utils::read_words(&seed); - // Check for zero on aligned integers for better code generation. - // Furtermore, seed_from_u64(0) will expand to a constant when optimized. - if state.iter().all(|&x| x == 0) { - return Self::seed_from_u64(0); - } - Xoshiro256PlusPlus { s: state } - } - - /// Create a new `Xoshiro256PlusPlus` from a `u64` seed. - /// - /// This uses the SplitMix64 generator internally. - #[inline] - fn seed_from_u64(mut state: u64) -> Self { - const PHI: u64 = 0x9e3779b97f4a7c15; - let mut s = [0; 4]; - for i in s.iter_mut() { - state = state.wrapping_add(PHI); - let mut z = state; - z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); - z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); - z = z ^ (z >> 31); - *i = z; - } - // By using a non-zero PHI we are guaranteed to generate a non-zero state - // Thus preventing a recursion between from_seed and seed_from_u64. - debug_assert_ne!(s, [0; 4]); - Xoshiro256PlusPlus { s } - } -} - -impl TryRng for Xoshiro256PlusPlus { - type Error = Infallible; - - #[inline] - fn try_next_u32(&mut self) -> Result { - // The lowest bits have some linear dependencies, so we use the - // upper bits instead. - self.try_next_u64().map(|val| (val >> 32) as u32) - } - - #[inline] - fn try_next_u64(&mut self) -> Result { - let res = self.s[0] - .wrapping_add(self.s[3]) - .rotate_left(23) - .wrapping_add(self.s[0]); - - let t = self.s[1] << 17; - - self.s[2] ^= self.s[0]; - self.s[3] ^= self.s[1]; - self.s[1] ^= self.s[2]; - self.s[0] ^= self.s[3]; - - self.s[2] ^= t; - - self.s[3] = self.s[3].rotate_left(45); - - Ok(res) - } - - #[inline] - fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> { - utils::fill_bytes_via_next_word(dst, || self.try_next_u64()) - } -} - -#[cfg(test)] -mod tests { - use super::Xoshiro256PlusPlus; - use rand_core::{Rng, SeedableRng}; - - #[test] - fn reference() { - let mut rng = Xoshiro256PlusPlus::from_seed([ - 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, - 0, 0, 0, - ]); - // These values were produced with the reference implementation: - // http://xoshiro.di.unimi.it/xoshiro256plusplus.c - let expected = [ - 41943041, - 58720359, - 3588806011781223, - 3591011842654386, - 9228616714210784205, - 9973669472204895162, - 14011001112246962877, - 12406186145184390807, - 15849039046786891736, - 10450023813501588000, - ]; - for &e in &expected { - assert_eq!(rng.next_u64(), e); - } - } - - #[test] - fn stable_seed_from_u64_and_from_seed() { - // We don't guarantee value-stability for SmallRng but this - // could influence keeping stability whenever possible (e.g. after optimizations). - let mut rng = Xoshiro256PlusPlus::seed_from_u64(0); - // from_seed([0; 32]) should produce the same state as seed_from_u64(0). - let mut rng_from_seed_0 = Xoshiro256PlusPlus::from_seed([0; 32]); - let expected = [ - 5987356902031041503, - 7051070477665621255, - 6633766593972829180, - 211316841551650330, - 9136120204379184874, - 379361710973160858, - 15813423377499357806, - 15596884590815070553, - 5439680534584881407, - 1369371744833522710, - ]; - for &e in &expected { - assert_eq!(rng.next_u64(), e); - assert_eq!(rng_from_seed_0.next_u64(), e); - } - } -} diff --git a/src/seq/coin_flipper.rs b/src/seq/coin_flipper.rs deleted file mode 100644 index c8d075c9e3d..00000000000 --- a/src/seq/coin_flipper.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::Rng; - -pub(crate) struct CoinFlipper { - pub rng: R, - chunk: u32, // TODO(opt): this should depend on RNG word size - chunk_remaining: u32, -} - -impl CoinFlipper { - 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 random_ratio_one_over(&mut self, d: usize) -> bool { - debug_assert_ne!(d, 0); - // This uses the same logic as `random_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 `random_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; - self.random_ratio(numerator, d) - } else { - false - } - } - - #[inline] - /// Returns true with a probability of n / d - /// Uses an expected two bits of randomness - fn random_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 - } - } - } - } -} diff --git a/src/seq/increasing_uniform.rs b/src/seq/increasing_uniform.rs deleted file mode 100644 index ece9d8c849c..00000000000 --- a/src/seq/increasing_uniform.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use crate::{Rng, RngExt}; - -/// Similar to a Uniform distribution, -/// but after returning a number in the range [0,n], n is increased by 1. -pub(crate) struct IncreasingUniform { - 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 IncreasingUniform { - /// 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: - // random_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.random_range(..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) -} diff --git a/src/seq/index.rs b/src/seq/index.rs deleted file mode 100644 index 65f55f64c62..00000000000 --- a/src/seq/index.rs +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Low-level API for sampling indices -use alloc::vec::{self, Vec}; -use core::slice; -use core::{hash::Hash, ops::AddAssign}; -// BTreeMap is not as fast in tests, but better than nothing. -#[cfg(feature = "std")] -use super::WeightError; -use crate::distr::uniform::SampleUniform; -use crate::distr::{Distribution, Uniform}; -use crate::{Rng, RngExt}; -#[cfg(not(feature = "std"))] -use alloc::collections::BTreeSet; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "std")] -use std::collections::HashSet; - -#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] -compile_error!("unsupported pointer width"); - -/// A vector of indices. -/// -/// Multiple internal representations are possible. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum IndexVec { - #[doc(hidden)] - U32(Vec), - #[cfg(target_pointer_width = "64")] - #[doc(hidden)] - U64(Vec), -} - -impl IndexVec { - /// Returns the number of indices - #[inline] - pub fn len(&self) -> usize { - match self { - IndexVec::U32(v) => v.len(), - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => v.len(), - } - } - - /// Returns `true` if the length is 0. - #[inline] - pub fn is_empty(&self) -> bool { - match self { - IndexVec::U32(v) => v.is_empty(), - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => v.is_empty(), - } - } - - /// Return the value at the given `index`. - /// - /// (Note: we cannot implement [`std::ops::Index`] because of lifetime - /// restrictions.) - #[inline] - pub fn index(&self, index: usize) -> usize { - match self { - IndexVec::U32(v) => v[index] as usize, - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => v[index] as usize, - } - } - - /// Return result as a `Vec`. Conversion may or may not be trivial. - #[inline] - pub fn into_vec(self) -> Vec { - match self { - IndexVec::U32(v) => v.into_iter().map(|i| i as usize).collect(), - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => v.into_iter().map(|i| i as usize).collect(), - } - } - - /// Iterate over the indices as a sequence of `usize` values - #[inline] - pub fn iter(&self) -> IndexVecIter<'_> { - match self { - IndexVec::U32(v) => IndexVecIter::U32(v.iter()), - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => IndexVecIter::U64(v.iter()), - } - } -} - -impl IntoIterator for IndexVec { - type IntoIter = IndexVecIntoIter; - type Item = usize; - - /// Convert into an iterator over the indices as a sequence of `usize` values - #[inline] - fn into_iter(self) -> IndexVecIntoIter { - match self { - IndexVec::U32(v) => IndexVecIntoIter::U32(v.into_iter()), - #[cfg(target_pointer_width = "64")] - IndexVec::U64(v) => IndexVecIntoIter::U64(v.into_iter()), - } - } -} - -impl PartialEq for IndexVec { - fn eq(&self, other: &IndexVec) -> bool { - use self::IndexVec::*; - match (self, other) { - (U32(v1), U32(v2)) => v1 == v2, - #[cfg(target_pointer_width = "64")] - (U64(v1), U64(v2)) => v1 == v2, - #[cfg(target_pointer_width = "64")] - (U32(v1), U64(v2)) => { - (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x as u64 == *y)) - } - #[cfg(target_pointer_width = "64")] - (U64(v1), U32(v2)) => { - (v1.len() == v2.len()) && (v1.iter().zip(v2.iter()).all(|(x, y)| *x == *y as u64)) - } - } - } -} - -impl From> for IndexVec { - #[inline] - fn from(v: Vec) -> Self { - IndexVec::U32(v) - } -} - -#[cfg(target_pointer_width = "64")] -impl From> for IndexVec { - #[inline] - fn from(v: Vec) -> Self { - IndexVec::U64(v) - } -} - -/// Return type of `IndexVec::iter`. -#[derive(Debug)] -pub enum IndexVecIter<'a> { - #[doc(hidden)] - U32(slice::Iter<'a, u32>), - #[cfg(target_pointer_width = "64")] - #[doc(hidden)] - U64(slice::Iter<'a, u64>), -} - -impl Iterator for IndexVecIter<'_> { - type Item = usize; - - #[inline] - fn next(&mut self) -> Option { - use self::IndexVecIter::*; - match self { - U32(iter) => iter.next().map(|i| *i as usize), - #[cfg(target_pointer_width = "64")] - U64(iter) => iter.next().map(|i| *i as usize), - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - match self { - IndexVecIter::U32(v) => v.size_hint(), - #[cfg(target_pointer_width = "64")] - IndexVecIter::U64(v) => v.size_hint(), - } - } -} - -impl ExactSizeIterator for IndexVecIter<'_> {} - -/// Return type of `IndexVec::into_iter`. -#[derive(Clone, Debug)] -pub enum IndexVecIntoIter { - #[doc(hidden)] - U32(vec::IntoIter), - #[cfg(target_pointer_width = "64")] - #[doc(hidden)] - U64(vec::IntoIter), -} - -impl Iterator for IndexVecIntoIter { - type Item = usize; - - #[inline] - fn next(&mut self) -> Option { - use self::IndexVecIntoIter::*; - match self { - U32(v) => v.next().map(|i| i as usize), - #[cfg(target_pointer_width = "64")] - U64(v) => v.next().map(|i| i as usize), - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - use self::IndexVecIntoIter::*; - match self { - U32(v) => v.size_hint(), - #[cfg(target_pointer_width = "64")] - U64(v) => v.size_hint(), - } - } -} - -impl ExactSizeIterator for IndexVecIntoIter {} - -/// Randomly sample exactly `amount` distinct indices from `0..length`, and -/// return them in random order (fully shuffled). -/// -/// This method is used internally by the slice sampling methods, but it can -/// sometimes be useful to have the indices themselves so this is provided as -/// an alternative. -/// -/// The implementation used is not specified; we automatically select the -/// fastest available algorithm for the `length` and `amount` parameters -/// (based on detailed profiling on an Intel Haswell CPU). Roughly speaking, -/// complexity is `O(amount)`, except that when `amount` is small, performance -/// is closer to `O(amount^2)`, and when `length` is close to `amount` then -/// `O(length)`. -/// -/// Note that performance is significantly better over `u32` indices than over -/// `u64` indices. Because of this we hide the underlying type behind an -/// abstraction, `IndexVec`. -/// -/// If an allocation-free `no_std` function is required, it is suggested -/// to adapt the internal `sample_floyd` implementation. -/// -/// Panics if `amount > length`. -#[track_caller] -pub fn sample(rng: &mut R, length: usize, amount: usize) -> IndexVec -where - R: Rng + ?Sized, -{ - if amount > length { - panic!("`amount` of samples must be less than or equal to `length`"); - } - if length > (u32::MAX as usize) { - #[cfg(target_pointer_width = "32")] - unreachable!(); - - // We never want to use inplace here, but could use floyd's alg - // Lazy version: always use the cache alg. - #[cfg(target_pointer_width = "64")] - return sample_rejection(rng, length as u64, amount as u64); - } - let amount = amount as u32; - let length = length as u32; - - // Choice of algorithm here depends on both length and amount. See: - // https://github.com/rust-random/rand/pull/479 - // We do some calculations with f32. Accuracy is not very important. - - if amount < 163 { - const C: [[f32; 2]; 2] = [[1.6, 8.0 / 45.0], [10.0, 70.0 / 9.0]]; - 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 - if amount > 11 && (length as f32) < (C[1][j] + m4) * amount_fp { - sample_inplace(rng, length, amount) - } else { - sample_floyd(rng, length, amount) - } - } else { - const C: [f32; 2] = [270.0, 330.0 / 9.0]; - let j = usize::from(length >= 500_000); - if (length as f32) < C[j] * (amount as f32) { - sample_inplace(rng, length, amount) - } else { - sample_rejection(rng, length, amount) - } - } -} - -/// Randomly sample `amount` distinct indices from `0..length` -/// -/// The result may contain less than `amount` indices if insufficient non-zero -/// weights are available. Results are returned in an arbitrary order (there is -/// no guarantee of shuffling or ordering). -/// -/// Function `weight` is called once for each index to provide weights. -/// -/// This method is used internally by the slice sampling methods, but it can -/// sometimes be useful to have the indices themselves so this is provided as -/// an alternative. -/// -/// Error cases: -/// - [`WeightError::InvalidWeight`] when a weight is not-a-number or negative. -/// -/// This implementation uses `O(length + amount)` space and `O(length)` time. -#[cfg(feature = "std")] -pub fn sample_weighted( - rng: &mut R, - length: usize, - weight: F, - amount: usize, -) -> Result -where - R: Rng + ?Sized, - F: Fn(usize) -> X, - X: Into, -{ - if length > (u32::MAX as usize) { - #[cfg(target_pointer_width = "32")] - unreachable!(); - - #[cfg(target_pointer_width = "64")] - { - let amount = amount as u64; - let length = length as u64; - sample_efraimidis_spirakis(rng, length, weight, amount) - } - } else { - assert!(amount <= u32::MAX as usize); - let amount = amount as u32; - let length = length as u32; - sample_efraimidis_spirakis(rng, length, weight, amount) - } -} - -/// Randomly sample `amount` distinct indices from `0..length` -/// -/// The result may contain less than `amount` indices if insufficient non-zero -/// weights are available. Results are returned in an arbitrary order (there is -/// no guarantee of shuffling or ordering). -/// -/// Function `weight` is called once for each index to provide weights. -/// -/// This implementation is based on the algorithm A-ExpJ as found in -/// [Efraimidis and Spirakis, 2005](https://doi.org/10.1016/j.ipl.2005.11.003). -/// It uses `O(length + amount)` space and `O(length)` time. -/// -/// Error cases: -/// - [`WeightError::InvalidWeight`] when a weight is not-a-number or negative. -#[cfg(feature = "std")] -fn sample_efraimidis_spirakis( - rng: &mut R, - length: N, - weight: F, - amount: N, -) -> Result -where - R: Rng + ?Sized, - F: Fn(usize) -> X, - X: Into, - N: UInt, - IndexVec: From>, -{ - use std::{cmp::Ordering, collections::BinaryHeap}; - - if amount == N::zero() { - return Ok(IndexVec::U32(Vec::new())); - } - - struct Element { - index: N, - key: f64, - } - - impl PartialOrd for Element { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl Ord for Element { - fn cmp(&self, other: &Self) -> Ordering { - // unwrap() should not panic since weights should not be NaN - // We reverse so that BinaryHeap::peek shows the smallest item - self.key.partial_cmp(&other.key).unwrap().reverse() - } - } - - impl PartialEq for Element { - fn eq(&self, other: &Self) -> bool { - self.key == other.key - } - } - - impl Eq for Element {} - - let mut candidates = BinaryHeap::with_capacity(amount.as_usize()); - let mut index = N::zero(); - while index < length && candidates.len() < amount.as_usize() { - let weight = weight(index.as_usize()).into(); - if weight > 0.0 { - // We use the log of the key used in A-ExpJ to improve precision - // for small weights: - let key = rng.random::().ln() / weight; - candidates.push(Element { index, key }); - } else if !(weight >= 0.0) { - return Err(WeightError::InvalidWeight); - } - - index += N::one(); - } - - if index < length { - let mut x = rng.random::().ln() / candidates.peek().unwrap().key; - while index < length { - let weight = weight(index.as_usize()).into(); - if weight > 0.0 { - x -= weight; - if x <= 0.0 { - let min_candidate = candidates.pop().unwrap(); - let t = (min_candidate.key * weight).exp(); - let key = rng.random_range(t..1.0).ln() / weight; - candidates.push(Element { index, key }); - - x = rng.random::().ln() / candidates.peek().unwrap().key; - } - } else if !(weight >= 0.0) { - return Err(WeightError::InvalidWeight); - } - - index += N::one(); - } - } - - Ok(IndexVec::from( - candidates.iter().map(|elt| elt.index).collect(), - )) -} - -/// Randomly sample exactly `amount` indices from `0..length`, using Floyd's -/// combination algorithm. -/// -/// The output values are fully shuffled. (Overhead is under 50%.) -/// -/// This implementation uses `O(amount)` memory and `O(amount^2)` time. -fn sample_floyd(rng: &mut R, length: u32, amount: u32) -> IndexVec -where - R: Rng + ?Sized, -{ - // Note that the values returned by `rng.random_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.random_range(..=j); - if let Some(pos) = indices.iter().position(|&x| x == t) { - indices[pos] = j; - } - indices.push(t); - } - IndexVec::from(indices) -} - -/// Randomly sample exactly `amount` indices from `0..length`, using an inplace -/// partial Fisher-Yates method. -/// Sample an amount of indices using an inplace partial fisher yates method. -/// -/// This allocates the entire `length` of indices and randomizes only the first `amount`. -/// It then truncates to `amount` and returns. -/// -/// This method is not appropriate for large `length` and potentially uses a lot -/// of memory; because of this we only implement for `u32` index (which improves -/// performance in all cases). -/// -/// Set-up is `O(length)` time and memory and shuffling is `O(amount)` time. -fn sample_inplace(rng: &mut R, length: u32, amount: u32) -> IndexVec -where - R: Rng + ?Sized, -{ - debug_assert!(amount <= length); - let mut indices: Vec = Vec::with_capacity(length as usize); - indices.extend(0..length); - for i in 0..amount { - let j: u32 = rng.random_range(i..length); - indices.swap(i as usize, j as usize); - } - indices.truncate(amount as usize); - debug_assert_eq!(indices.len(), amount as usize); - IndexVec::from(indices) -} - -trait UInt: Copy + PartialOrd + Ord + PartialEq + Eq + SampleUniform + Hash + AddAssign { - fn zero() -> Self; - #[cfg_attr(feature = "alloc", allow(dead_code))] - fn one() -> Self; - fn as_usize(self) -> usize; -} - -impl UInt for u32 { - #[inline] - fn zero() -> Self { - 0 - } - - #[inline] - fn one() -> Self { - 1 - } - - #[inline] - fn as_usize(self) -> usize { - self as usize - } -} - -#[cfg(target_pointer_width = "64")] -impl UInt for u64 { - #[inline] - fn zero() -> Self { - 0 - } - - #[inline] - fn one() -> Self { - 1 - } - - #[inline] - fn as_usize(self) -> usize { - self as usize - } -} - -/// Randomly sample exactly `amount` indices from `0..length`, using rejection -/// sampling. -/// -/// Since `amount <<< length` there is a low chance of a random sample in -/// `0..length` being a duplicate. We test for duplicates and resample where -/// necessary. The algorithm is `O(amount)` time and memory. -/// -/// This function is generic over X primarily so that results are value-stable -/// over 32-bit and 64-bit platforms. -fn sample_rejection(rng: &mut R, length: X, amount: X) -> IndexVec -where - R: Rng + ?Sized, - IndexVec: From>, -{ - debug_assert!(amount < length); - #[cfg(feature = "std")] - 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).unwrap(); - let mut indices = Vec::with_capacity(amount.as_usize()); - for _ in 0..amount.as_usize() { - let mut pos = distr.sample(rng); - while !cache.insert(pos) { - pos = distr.sample(rng); - } - indices.push(pos); - } - - debug_assert_eq!(indices.len(), amount.as_usize()); - IndexVec::from(indices) -} - -#[cfg(test)] -mod test { - use super::*; - use alloc::vec; - - #[test] - #[cfg(feature = "serde")] - fn test_serialization_index_vec() { - let some_index_vec = IndexVec::from(vec![254_u32, 234, 2, 1]); - let de_some_index_vec: IndexVec = - postcard::from_bytes(&postcard::to_allocvec(&some_index_vec).unwrap()).unwrap(); - assert_eq!(some_index_vec, de_some_index_vec); - } - - #[test] - fn test_sample_boundaries() { - let mut r = crate::test::rng(404); - - assert_eq!(sample_inplace(&mut r, 0, 0).len(), 0); - assert_eq!(sample_inplace(&mut r, 1, 0).len(), 0); - assert_eq!(sample_inplace(&mut r, 1, 1).into_vec(), vec![0]); - - assert_eq!(sample_rejection(&mut r, 1u32, 0).len(), 0); - - assert_eq!(sample_floyd(&mut r, 0, 0).len(), 0); - assert_eq!(sample_floyd(&mut r, 1, 0).len(), 0); - assert_eq!(sample_floyd(&mut r, 1, 1).into_vec(), vec![0]); - - // These algorithms should be fast with big numbers. Test average. - let sum: usize = sample_rejection(&mut r, 1 << 25, 10u32).into_iter().sum(); - assert!(1 << 25 < sum && sum < (1 << 25) * 25); - - let sum: usize = sample_floyd(&mut r, 1 << 25, 10).into_iter().sum(); - assert!(1 << 25 < sum && sum < (1 << 25) * 25); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_sample_alg() { - let seed_rng = crate::test::rng; - - // We can't test which algorithm is used directly, but Floyd's alg - // should produce different results from the others. (Also, `inplace` - // and `cached` currently use different sizes thus produce different results.) - - // A small length and relatively large amount should use inplace - let (length, amount): (usize, usize) = (100, 50); - let v1 = sample(&mut seed_rng(420), length, amount); - let v2 = sample_inplace(&mut seed_rng(420), length as u32, amount as u32); - assert!(v1.iter().all(|e| e < length)); - assert_eq!(v1, v2); - - // Test Floyd's alg does produce different results - let v3 = sample_floyd(&mut seed_rng(420), length as u32, amount as u32); - assert!(v1 != v3); - - // A large length and small amount should use Floyd - let (length, amount): (usize, usize) = (1 << 20, 50); - let v1 = sample(&mut seed_rng(421), length, amount); - let v2 = sample_floyd(&mut seed_rng(421), length as u32, amount as u32); - assert!(v1.iter().all(|e| e < length)); - assert_eq!(v1, v2); - - // A large length and larger amount should use cache - let (length, amount): (usize, usize) = (1 << 20, 600); - let v1 = sample(&mut seed_rng(422), length, amount); - let v2 = sample_rejection(&mut seed_rng(422), length as u32, amount as u32); - assert!(v1.iter().all(|e| e < length)); - assert_eq!(v1, v2); - } - - #[cfg(feature = "std")] - #[test] - fn test_sample_weighted() { - let seed_rng = crate::test::rng; - for &(amount, len) in &[(0, 10), (5, 10), (9, 10)] { - let v = sample_weighted(&mut seed_rng(423), len, |i| i as f64, amount).unwrap(); - match v { - IndexVec::U32(mut indices) => { - assert_eq!(indices.len(), amount); - indices.sort_unstable(); - indices.dedup(); - assert_eq!(indices.len(), amount); - for &i in &indices { - assert!((i as usize) < len); - } - } - #[cfg(target_pointer_width = "64")] - _ => panic!("expected `IndexVec::U32`"), - } - } - - let r = sample_weighted(&mut seed_rng(423), 10, |i| i as f64, 10); - assert_eq!(r.unwrap().len(), 9); - } - - #[test] - fn value_stability_sample() { - let do_test = |length, amount, values: &[u32]| { - let mut buf = [0u32; 8]; - let mut rng = crate::test::rng(410); - - let res = sample(&mut rng, length, amount); - let len = res.len().min(buf.len()); - for (x, y) in res.into_iter().zip(buf.iter_mut()) { - *y = x as u32; - } - assert_eq!( - &buf[0..len], - values, - "failed sampling {}, {}", - length, - amount - ); - }; - - do_test(10, 6, &[0, 9, 5, 4, 6, 8]); // floyd - do_test(25, 10, &[24, 20, 19, 9, 22, 16, 0, 14]); // floyd - do_test(300, 8, &[30, 283, 243, 150, 218, 240, 1, 189]); // floyd - do_test(300, 80, &[31, 289, 248, 154, 221, 243, 7, 192]); // inplace - do_test(300, 180, &[31, 289, 248, 154, 221, 243, 7, 192]); // inplace - - do_test( - 1_000_000, - 8, - &[103717, 963485, 826422, 509101, 736394, 807035, 5327, 632573], - ); // floyd - do_test( - 1_000_000, - 180, - &[103718, 963490, 826426, 509103, 736396, 807036, 5327, 632573], - ); // rejection - } -} diff --git a/src/seq/iterator.rs b/src/seq/iterator.rs deleted file mode 100644 index b2a8c1fa022..00000000000 --- a/src/seq/iterator.rs +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright 2018-2024 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! `IteratorRandom` - -#[allow(unused)] -use super::IndexedRandom; -use super::coin_flipper::CoinFlipper; -use crate::{Rng, RngExt}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; - -/// Extension trait on iterators, providing random sampling methods. -/// -/// This trait is implemented on all iterators `I` where `I: Iterator + Sized` -/// and provides methods for -/// choosing one or more elements. You must `use` this trait: -/// -/// ``` -/// use rand::seq::IteratorRandom; -/// -/// let faces = "πŸ˜€πŸ˜ŽπŸ˜πŸ˜•πŸ˜ πŸ˜’"; -/// println!("I am {}!", faces.chars().choose(&mut rand::rng()).unwrap()); -/// ``` -/// Example output (non-deterministic): -/// ```none -/// I am πŸ˜€! -/// ``` -pub trait IteratorRandom: Iterator + Sized { - /// Uniformly sample one element - /// - /// Assuming that the [`Iterator::size_hint`] is correct, this method - /// returns one uniformly-sampled random element of the slice, or `None` - /// only if the slice is empty. Incorrect bounds on the `size_hint` may - /// cause this method to incorrectly return `None` if fewer elements than - /// the advertised `lower` bound are present and may prevent sampling of - /// elements beyond an advertised `upper` bound (i.e. incorrect `size_hint` - /// is memory-safe, but may result in unexpected `None` result and - /// non-uniform distribution). - /// - /// With an accurate [`Iterator::size_hint`] and where [`Iterator::nth`] is - /// a constant-time operation, this method can offer `O(1)` performance. - /// Where no size hint is - /// available, complexity is `O(n)` where `n` is the iterator length. - /// Partial hints (where `lower > 0`) also improve performance. - /// - /// Note further that [`Iterator::size_hint`] may affect the number of RNG - /// samples used as well as the result (while remaining uniform sampling). - /// Consider instead using [`IteratorRandom::choose_stable`] to avoid - /// [`Iterator`] combinators which only change size hints from affecting the - /// results. - /// - /// # Example - /// - /// ``` - /// use rand::seq::IteratorRandom; - /// - /// let words = "Mary had a little lamb".split(' '); - /// println!("{}", words.choose(&mut rand::rng()).unwrap()); - /// ``` - fn choose(mut self, rng: &mut R) -> Option - where - R: Rng + ?Sized, - { - let (mut lower, mut upper) = self.size_hint(); - 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 match lower { - 0 => None, - 1 => self.next(), - _ => self.nth(rng.random_range(..lower)), - }; - } - - let mut coin_flipper = CoinFlipper::new(rng); - let mut consumed = 0; - - // Continue until the iterator is exhausted - loop { - if lower > 1 { - let ix = coin_flipper.rng.random_range(..lower + consumed); - let skip = if ix < lower { - result = self.nth(ix); - lower - (ix + 1) - } else { - lower - }; - if upper == Some(lower) { - return result; - } - consumed += lower; - if skip > 0 { - self.nth(skip - 1); - } - } else { - let elem = self.next(); - if elem.is_none() { - return result; - } - consumed += 1; - if coin_flipper.random_ratio_one_over(consumed) { - result = elem; - } - } - - let hint = self.size_hint(); - lower = hint.0; - upper = hint.1; - } - } - - /// Uniformly sample one element (stable) - /// - /// This method is very similar to [`choose`] except that the result - /// only depends on the length of the iterator and the values produced by - /// `rng`. Notably for any iterator of a given length this will make the - /// same requests to `rng` and if the same sequence of values are produced - /// the same index will be selected from `self`. This may be useful if you - /// need consistent results no matter what type of iterator you are working - /// with. If you do not need this stability prefer [`choose`]. - /// - /// Note that this method still uses [`Iterator::size_hint`] to skip - /// constructing elements where possible, however the selection and `rng` - /// calls are the same in the face of this optimization. If you want to - /// force every element to be created regardless call `.inspect(|e| ())`. - /// - /// [`choose`]: IteratorRandom::choose - // - // Clippy is wrong here: we need to iterate over all entries with the RNG to - // ensure that choosing is *stable*. - // "allow(unknown_lints)" can be removed when switching to at least - // rust-version 1.86.0, see: - // https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last - #[allow(unknown_lints)] - #[allow(clippy::double_ended_iterator_last)] - fn choose_stable(mut self, rng: &mut R) -> Option - 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 - // store what index to access next here. - // This should be replaced by `advance_by()` once it is stable: - // https://github.com/rust-lang/rust/issues/77404 - let mut next = 0; - - let (lower, _) = self.size_hint(); - if lower >= 2 { - let highest_selected = (0..lower) - .filter(|ix| coin_flipper.random_ratio_one_over(consumed + ix + 1)) - .last(); - - consumed += lower; - next = lower; - - if let Some(ix) = highest_selected { - result = self.nth(ix); - next -= ix + 1; - debug_assert!(result.is_some(), "iterator shorter than size_hint().0"); - } - } - - let elem = self.nth(next); - if elem.is_none() { - return result; - } - - if coin_flipper.random_ratio_one_over(consumed + 1) { - result = elem; - } - consumed += 1; - } - } - - /// Uniformly sample `amount` distinct elements into a buffer - /// - /// Collects values at random from the iterator into a supplied buffer - /// until that buffer is filled. - /// - /// Although the elements are selected randomly, the order of elements in - /// the buffer is neither stable nor fully random. If random ordering is - /// desired, shuffle the result. - /// - /// Returns the number of elements added to the buffer. This equals the length - /// of the buffer unless the iterator contains insufficient elements, in which - /// case this equals the number of elements available. - /// - /// Complexity is `O(n)` where `n` is the length of the iterator. - /// For slices, prefer [`IndexedRandom::sample`]. - fn sample_fill(mut self, rng: &mut R, buf: &mut [Self::Item]) -> usize - where - R: Rng + ?Sized, - { - let amount = buf.len(); - let mut len = 0; - while len < amount { - if let Some(elem) = self.next() { - buf[len] = elem; - len += 1; - } else { - // Iterator exhausted; stop early - return len; - } - } - - // Continue, since the iterator was not exhausted - for (i, elem) in self.enumerate() { - let k = rng.random_range(..i + 1 + amount); - if let Some(slot) = buf.get_mut(k) { - *slot = elem; - } - } - len - } - - /// Uniformly sample `amount` distinct elements into a [`Vec`] - /// - /// This is equivalent to `sample_fill` except for the result type. - /// - /// Although the elements are selected randomly, the order of elements in - /// the buffer is neither stable nor fully random. If random ordering is - /// desired, shuffle the result. - /// - /// The length of the returned vector equals `amount` unless the iterator - /// contains insufficient elements, in which case it equals the number of - /// elements available. - /// - /// Complexity is `O(n)` where `n` is the length of the iterator. - /// For slices, prefer [`IndexedRandom::sample`]. - #[cfg(feature = "alloc")] - fn sample(mut self, rng: &mut R, amount: usize) -> Vec - where - R: Rng + ?Sized, - { - let mut reservoir = Vec::from_iter(self.by_ref().take(amount)); - - // Continue unless the iterator was exhausted - // - // note: this prevents iterators that "restart" from causing problems. - // If the iterator stops once, then so do we. - if reservoir.len() == amount { - for (i, elem) in self.enumerate() { - let k = rng.random_range(..i + 1 + amount); - if let Some(slot) = reservoir.get_mut(k) { - *slot = elem; - } - } - } - reservoir - } - - /// Deprecated: use [`Self::sample_fill`] instead - #[deprecated(since = "0.10.0", note = "Renamed to `sample_fill`")] - fn choose_multiple_fill(self, rng: &mut R, buf: &mut [Self::Item]) -> usize - where - R: Rng + ?Sized, - { - self.sample_fill(rng, buf) - } - - /// Deprecated: use [`Self::sample`] instead - #[cfg(feature = "alloc")] - #[deprecated(since = "0.10.0", note = "Renamed to `sample`")] - fn choose_multiple(self, rng: &mut R, amount: usize) -> Vec - where - R: Rng + ?Sized, - { - self.sample(rng, amount) - } -} - -impl IteratorRandom for I where I: Iterator + Sized {} - -#[cfg(test)] -mod test { - use super::*; - #[cfg(all(feature = "alloc", not(feature = "std")))] - use alloc::vec::Vec; - - #[derive(Clone)] - struct UnhintedIterator { - iter: I, - } - impl Iterator for UnhintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } - } - - #[derive(Clone)] - struct ChunkHintedIterator { - iter: I, - chunk_remaining: usize, - chunk_size: usize, - hint_total_size: bool, - } - impl Iterator for ChunkHintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - if self.chunk_remaining == 0 { - self.chunk_remaining = core::cmp::min(self.chunk_size, self.iter.len()); - } - self.chunk_remaining = self.chunk_remaining.saturating_sub(1); - - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - ( - self.chunk_remaining, - if self.hint_total_size { - Some(self.iter.len()) - } else { - None - }, - ) - } - } - - #[derive(Clone)] - struct WindowHintedIterator { - iter: I, - window_size: usize, - hint_total_size: bool, - } - impl Iterator for WindowHintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - ( - core::cmp::min(self.iter.len(), self.window_size), - if self.hint_total_size { - Some(self.iter.len()) - } else { - None - }, - ) - } - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_iterator_choose() { - let r = &mut crate::test::rng(109); - fn test_iter + Clone>(r: &mut R, iter: Iter) { - let mut chosen = [0i32; 9]; - for _ in 0..1000 { - let picked = iter.clone().choose(r).unwrap(); - chosen[picked] += 1; - } - for count in chosen.iter() { - // Samples should follow Binomial(1000, 1/9) - // Octave: binopdf(x, 1000, 1/9) gives the prob of *count == x - // Note: have seen 153, which is unlikely but not impossible. - assert!( - 72 < *count && *count < 154, - "count not close to 1000/9: {}", - count - ); - } - } - - test_iter(r, 0..9); - test_iter(r, [0, 1, 2, 3, 4, 5, 6, 7, 8].iter().cloned()); - #[cfg(feature = "alloc")] - test_iter(r, (0..9).collect::>().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, - }, - ); - - assert_eq!((0..0).choose(r), None); - assert_eq!(UnhintedIterator { iter: 0..0 }.choose(r), None); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_iterator_choose_stable() { - let r = &mut crate::test::rng(109); - fn test_iter + Clone>(r: &mut R, iter: Iter) { - let mut chosen = [0i32; 9]; - for _ in 0..1000 { - let picked = iter.clone().choose_stable(r).unwrap(); - chosen[picked] += 1; - } - for count in chosen.iter() { - // Samples should follow Binomial(1000, 1/9) - // Octave: binopdf(x, 1000, 1/9) gives the prob of *count == x - // Note: have seen 153, which is unlikely but not impossible. - assert!( - 72 < *count && *count < 154, - "count not close to 1000/9: {}", - count - ); - } - } - - test_iter(r, 0..9); - test_iter(r, [0, 1, 2, 3, 4, 5, 6, 7, 8].iter().cloned()); - #[cfg(feature = "alloc")] - test_iter(r, (0..9).collect::>().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, - }, - ); - - assert_eq!((0..0).choose(r), None); - assert_eq!(UnhintedIterator { iter: 0..0 }.choose(r), None); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_iterator_choose_stable_stability() { - fn test_iter(iter: impl Iterator + Clone) -> [i32; 9] { - let r = &mut crate::test::rng(109); - let mut chosen = [0i32; 9]; - for _ in 0..1000 { - let picked = iter.clone().choose_stable(r).unwrap(); - chosen[picked] += 1; - } - chosen - } - - let reference = test_iter(0..9); - 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::>().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 - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_sample_iter() { - let min_val = 1; - let max_val = 100; - - let mut r = crate::test::rng(401); - let vals = (min_val..max_val).collect::>(); - let small_sample = vals.iter().sample(&mut r, 5); - let large_sample = vals.iter().sample(&mut r, vals.len() + 5); - - assert_eq!(small_sample.len(), 5); - assert_eq!(large_sample.len(), vals.len()); - // no randomization happens when amount >= len - assert_eq!(large_sample, vals.iter().collect::>()); - - assert!( - small_sample - .iter() - .all(|e| { **e >= min_val && **e <= max_val }) - ); - } - - #[test] - fn value_stability_choose() { - fn choose>(iter: I) -> Option { - let mut rng = crate::test::rng(411); - iter.choose(&mut rng) - } - - assert_eq!(choose([].iter().cloned()), None); - assert_eq!(choose(0..100), Some(33)); - assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(27)); - assert_eq!( - choose(ChunkHintedIterator { - iter: 0..100, - chunk_size: 32, - chunk_remaining: 32, - hint_total_size: false, - }), - Some(91) - ); - assert_eq!( - choose(ChunkHintedIterator { - iter: 0..100, - chunk_size: 32, - chunk_remaining: 32, - hint_total_size: true, - }), - Some(91) - ); - assert_eq!( - choose(WindowHintedIterator { - iter: 0..100, - window_size: 32, - hint_total_size: false, - }), - Some(34) - ); - assert_eq!( - choose(WindowHintedIterator { - iter: 0..100, - window_size: 32, - hint_total_size: true, - }), - Some(34) - ); - } - - #[test] - fn value_stability_choose_stable() { - fn choose>(iter: I) -> Option { - let mut rng = crate::test::rng(411); - iter.choose_stable(&mut rng) - } - - assert_eq!(choose([].iter().cloned()), None); - assert_eq!(choose(0..100), Some(27)); - assert_eq!(choose(UnhintedIterator { iter: 0..100 }), Some(27)); - assert_eq!( - choose(ChunkHintedIterator { - iter: 0..100, - chunk_size: 32, - chunk_remaining: 32, - hint_total_size: false, - }), - Some(27) - ); - assert_eq!( - choose(ChunkHintedIterator { - iter: 0..100, - chunk_size: 32, - chunk_remaining: 32, - hint_total_size: true, - }), - Some(27) - ); - assert_eq!( - choose(WindowHintedIterator { - iter: 0..100, - window_size: 32, - hint_total_size: false, - }), - Some(27) - ); - assert_eq!( - choose(WindowHintedIterator { - iter: 0..100, - window_size: 32, - hint_total_size: true, - }), - Some(27) - ); - } - - #[test] - fn value_stability_sample() { - fn do_test>(iter: I, v: &[u32]) { - let mut rng = crate::test::rng(412); - let mut buf = [0u32; 8]; - assert_eq!(iter.clone().sample_fill(&mut rng, &mut buf), v.len()); - assert_eq!(&buf[0..v.len()], v); - - #[cfg(feature = "alloc")] - { - let mut rng = crate::test::rng(412); - assert_eq!(iter.sample(&mut rng, v.len()), v); - } - } - - do_test(0..4, &[0, 1, 2, 3]); - do_test(0..8, &[0, 1, 2, 3, 4, 5, 6, 7]); - do_test(0..100, &[77, 95, 38, 23, 25, 8, 58, 40]); - } -} diff --git a/src/seq/mod.rs b/src/seq/mod.rs deleted file mode 100644 index ea2b0ee420e..00000000000 --- a/src/seq/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Sequence-related functionality -//! -//! This module provides: -//! -//! * [`IndexedRandom`] for sampling slices and other indexable lists -//! * [`IndexedMutRandom`] for sampling slices and other mutably indexable lists -//! * [`SliceRandom`] for mutating slices -//! * [`IteratorRandom`] for sampling iterators -//! * [`index::sample`] low-level API to choose multiple indices from -//! `0..length` -//! -//! Also see: -//! -//! * [`crate::distr::weighted::WeightedIndex`] distribution which provides -//! weighted index sampling. -//! -//! In order to make results reproducible across 32-64 bit architectures, all -//! `usize` indices are sampled as a `u32` where possible (also providing a -//! small performance boost in some cases). - -mod coin_flipper; -mod increasing_uniform; -mod iterator; -mod slice; - -#[cfg(feature = "alloc")] -#[path = "index.rs"] -mod index_; - -#[cfg(feature = "alloc")] -#[doc(no_inline)] -pub use crate::distr::weighted::Error as WeightError; -pub use iterator::IteratorRandom; -#[cfg(feature = "alloc")] -pub use slice::IndexedSamples; -#[allow(deprecated)] -#[cfg(feature = "alloc")] -pub use slice::SliceChooseIter; -pub use slice::{IndexedMutRandom, IndexedRandom, SliceRandom}; - -/// Low-level API for sampling indices -pub mod index { - use crate::{Rng, RngExt}; - - #[cfg(feature = "alloc")] - #[doc(inline)] - pub use super::index_::*; - - /// Randomly sample exactly `N` distinct indices from `0..len`, and - /// return them in random order (fully shuffled). - /// - /// This is implemented via Floyd's algorithm. Time complexity is `O(N^2)` - /// and memory complexity is `O(N)`. - /// - /// Returns `None` if (and only if) `N > len`. - pub fn sample_array(rng: &mut R, len: usize) -> Option<[usize; N]> - where - R: Rng + ?Sized, - { - if N > len { - return None; - } - - // Floyd's algorithm - let mut indices = [0; N]; - for (i, j) in (len - N..len).enumerate() { - let t = rng.random_range(..j + 1); - if let Some(pos) = indices[0..i].iter().position(|&x| x == t) { - indices[pos] = j; - } - indices[i] = t; - } - Some(indices) - } -} diff --git a/src/seq/slice.rs b/src/seq/slice.rs deleted file mode 100644 index 1b8a068eae4..00000000000 --- a/src/seq/slice.rs +++ /dev/null @@ -1,858 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! `IndexedRandom`, `IndexedMutRandom`, `SliceRandom` - -use super::increasing_uniform::IncreasingUniform; -use super::index; -#[cfg(feature = "alloc")] -use crate::distr::uniform::{SampleBorrow, SampleUniform}; -#[cfg(feature = "alloc")] -use crate::distr::weighted::{Error as WeightError, Weight}; -use crate::{Rng, RngExt}; -use core::ops::{Index, IndexMut}; - -/// Extension trait on indexable lists, providing random sampling methods. -/// -/// This trait is implemented on `[T]` slice types. Other types supporting -/// [`std::ops::Index`] may implement this (only [`Self::len`] must be -/// specified). -pub trait IndexedRandom: Index { - /// The length - fn len(&self) -> usize; - - /// True when the length is zero - #[inline] - fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Uniformly sample one element - /// - /// Returns a reference to one uniformly-sampled random element of - /// the slice, or `None` if the slice is empty. - /// - /// For slices, complexity is `O(1)`. - /// - /// # Example - /// - /// ``` - /// use rand::seq::IndexedRandom; - /// - /// let choices = [1, 2, 4, 8, 16, 32]; - /// let mut rng = rand::rng(); - /// println!("{:?}", choices.choose(&mut rng)); - /// assert_eq!(choices[..0].choose(&mut rng), None); - /// ``` - fn choose(&self, rng: &mut R) -> Option<&Self::Output> - where - R: Rng + ?Sized, - { - if self.is_empty() { - None - } else { - Some(&self[rng.random_range(..self.len())]) - } - } - - /// Return an iterator which samples from `self` with replacement - /// - /// Returns `None` if and only if `self.is_empty()`. - /// - /// # Example - /// - /// ``` - /// use rand::seq::IndexedRandom; - /// - /// let choices = [1, 2, 4, 8, 16, 32]; - /// let mut rng = rand::rng(); - /// for choice in choices.choose_iter(&mut rng).unwrap().take(3) { - /// println!("{:?}", choice); - /// } - /// ``` - fn choose_iter(&self, rng: &mut R) -> Option> - where - R: Rng + ?Sized, - { - let distr = crate::distr::Uniform::new(0, self.len()).ok()?; - Some(rng.sample_iter(distr).map(|i| &self[i])) - } - - /// Uniformly sample `amount` distinct elements from self - /// - /// Chooses `amount` elements from the slice at random, without repetition, - /// and in random order. The returned iterator is appropriate both for - /// collection into a `Vec` and filling an existing buffer (see example). - /// - /// In case this API is not sufficiently flexible, use [`index::sample`]. - /// - /// For slices, complexity is the same as [`index::sample`]. - /// - /// # Example - /// ``` - /// use rand::seq::IndexedRandom; - /// - /// let mut rng = &mut rand::rng(); - /// let sample = "Hello, audience!".as_bytes(); - /// - /// // collect the results into a vector: - /// let v: Vec = sample.sample(&mut rng, 3).cloned().collect(); - /// - /// // store in a buffer: - /// let mut buf = [0u8; 5]; - /// for (b, slot) in sample.sample(&mut rng, buf.len()).zip(buf.iter_mut()) { - /// *slot = *b; - /// } - /// ``` - #[cfg(feature = "alloc")] - fn sample(&self, rng: &mut R, amount: usize) -> IndexedSamples<'_, Self, Self::Output> - where - Self::Output: Sized, - R: Rng + ?Sized, - { - let amount = core::cmp::min(amount, self.len()); - IndexedSamples { - slice: self, - _phantom: Default::default(), - indices: index::sample(rng, self.len(), amount).into_iter(), - } - } - - /// Uniformly sample a fixed-size array of distinct elements from self - /// - /// Chooses `N` elements from the slice at random, without repetition, - /// and in random order. - /// - /// For slices, complexity is the same as [`index::sample_array`]. - /// - /// # Example - /// ``` - /// use rand::seq::IndexedRandom; - /// - /// let mut rng = &mut rand::rng(); - /// let sample = "Hello, audience!".as_bytes(); - /// - /// let a: [u8; 3] = sample.sample_array(&mut rng).unwrap(); - /// ``` - fn sample_array(&self, rng: &mut R) -> Option<[Self::Output; N]> - where - Self::Output: Clone + Sized, - R: Rng + ?Sized, - { - let indices = index::sample_array(rng, self.len())?; - Some(indices.map(|index| self[index].clone())) - } - - /// Biased sampling for one element - /// - /// Returns a reference to one element of the slice, sampled according - /// to the provided weights. Returns `None` if and only if `self.is_empty()`. - /// - /// The specified function `weight` maps each item `x` to a relative - /// likelihood `weight(x)`. The probability of each item being selected is - /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`. - /// - /// For slices of length `n`, complexity is `O(n)`. - /// For more information about the underlying algorithm, - /// see the [`WeightedIndex`] distribution. - /// - /// See also [`choose_weighted_mut`]. - /// - /// # Example - /// - /// ``` - /// use rand::prelude::*; - /// - /// let choices = [('a', 2), ('b', 1), ('c', 1), ('d', 0)]; - /// let mut rng = rand::rng(); - /// // 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`]: IndexedRandom::choose - /// [`choose_weighted_mut`]: IndexedMutRandom::choose_weighted_mut - /// [`WeightedIndex`]: crate::distr::weighted::WeightedIndex - #[cfg(feature = "alloc")] - fn choose_weighted( - &self, - rng: &mut R, - weight: F, - ) -> Result<&Self::Output, WeightError> - where - R: Rng + ?Sized, - F: Fn(&Self::Output) -> B, - B: SampleBorrow, - X: SampleUniform + Weight + PartialOrd, - { - use crate::distr::weighted::WeightedIndex; - let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; - Ok(&self[rng.sample(distr)]) - } - - /// Biased sampling with replacement - /// - /// Returns an iterator which samples elements from `self` according to the - /// given weights with replacement (i.e. elements may be repeated). - /// Returns `None` if and only if `self.is_empty()`. - /// - /// See also doc for [`Self::choose_weighted`]. - #[cfg(feature = "alloc")] - fn choose_weighted_iter( - &self, - rng: &mut R, - weight: F, - ) -> Result, WeightError> - where - R: Rng + ?Sized, - F: Fn(&Self::Output) -> B, - B: SampleBorrow, - X: SampleUniform + Weight + PartialOrd, - { - use crate::distr::weighted::WeightedIndex; - let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; - Ok(rng.sample_iter(distr).map(|i| &self[i])) - } - - /// Biased sampling of `amount` distinct elements - /// - /// Similar to [`sample`], but where the likelihood of each - /// element's inclusion in the output may be specified. Zero-weighted - /// elements are never returned; the result may therefore contain fewer - /// elements than `amount` even when `self.len() >= amount`. The elements - /// are returned in an arbitrary, unspecified order. - /// - /// The specified function `weight` maps each item `x` to a relative - /// likelihood `weight(x)`. The probability of each item being selected is - /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`. - /// - /// This implementation uses `O(length + amount)` space and `O(length)` time. - /// See [`index::sample_weighted`] for details. - /// - /// # Example - /// - /// ``` - /// use rand::prelude::*; - /// - /// let choices = [('a', 2), ('b', 1), ('c', 1)]; - /// let mut rng = rand::rng(); - /// // First Draw * Second Draw = total odds - /// // ----------------------- - /// // (50% * 50%) + (25% * 67%) = 41.7% chance that the output is `['a', 'b']` in some order. - /// // (50% * 50%) + (25% * 67%) = 41.7% chance that the output is `['a', 'c']` in some order. - /// // (25% * 33%) + (25% * 33%) = 16.6% chance that the output is `['b', 'c']` in some order. - /// println!("{:?}", choices.sample_weighted(&mut rng, 2, |item| item.1).unwrap().collect::>()); - /// ``` - /// [`sample`]: IndexedRandom::sample - // Note: this is feature-gated on std due to usage of f64::powf. - // If necessary, we may use alloc+libm as an alternative (see PR #1089). - #[cfg(feature = "std")] - fn sample_weighted( - &self, - rng: &mut R, - amount: usize, - weight: F, - ) -> Result, WeightError> - where - Self::Output: Sized, - R: Rng + ?Sized, - F: Fn(&Self::Output) -> X, - X: Into, - { - let amount = core::cmp::min(amount, self.len()); - Ok(IndexedSamples { - slice: self, - _phantom: Default::default(), - indices: index::sample_weighted( - rng, - self.len(), - |idx| weight(&self[idx]).into(), - amount, - )? - .into_iter(), - }) - } - - /// Deprecated: use [`Self::sample`] instead - #[cfg(feature = "alloc")] - #[deprecated(since = "0.10.0", note = "Renamed to `sample`")] - fn choose_multiple( - &self, - rng: &mut R, - amount: usize, - ) -> IndexedSamples<'_, Self, Self::Output> - where - Self::Output: Sized, - R: Rng + ?Sized, - { - self.sample(rng, amount) - } - - /// Deprecated: use [`Self::sample_array`] instead - #[deprecated(since = "0.10.0", note = "Renamed to `sample_array`")] - fn choose_multiple_array(&self, rng: &mut R) -> Option<[Self::Output; N]> - where - Self::Output: Clone + Sized, - R: Rng + ?Sized, - { - self.sample_array(rng) - } - - /// Deprecated: use [`Self::sample_weighted`] instead - #[cfg(feature = "std")] - #[deprecated(since = "0.10.0", note = "Renamed to `sample_weighted`")] - fn choose_multiple_weighted( - &self, - rng: &mut R, - amount: usize, - weight: F, - ) -> Result, WeightError> - where - Self::Output: Sized, - R: Rng + ?Sized, - F: Fn(&Self::Output) -> X, - X: Into, - { - self.sample_weighted(rng, amount, weight) - } -} - -/// Extension trait on indexable lists, providing random sampling methods. -/// -/// This trait is implemented automatically for every type implementing -/// [`IndexedRandom`] and [`std::ops::IndexMut`]. -pub trait IndexedMutRandom: IndexedRandom + IndexMut { - /// Uniformly sample one element (mut) - /// - /// Returns a mutable reference to one uniformly-sampled random element of - /// the slice, or `None` if the slice is empty. - /// - /// For slices, complexity is `O(1)`. - fn choose_mut(&mut self, rng: &mut R) -> Option<&mut Self::Output> - where - R: Rng + ?Sized, - { - if self.is_empty() { - None - } else { - let len = self.len(); - Some(&mut self[rng.random_range(..len)]) - } - } - - /// Biased sampling for one element (mut) - /// - /// Returns a mutable reference to one element of the slice, sampled according - /// to the provided weights. Returns `None` only if the slice is empty. - /// - /// The specified function `weight` maps each item `x` to a relative - /// likelihood `weight(x)`. The probability of each item being selected is - /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`. - /// - /// For slices of length `n`, complexity is `O(n)`. - /// For more information about the underlying algorithm, - /// see the [`WeightedIndex`] distribution. - /// - /// See also [`choose_weighted`]. - /// - /// [`choose_mut`]: IndexedMutRandom::choose_mut - /// [`choose_weighted`]: IndexedRandom::choose_weighted - /// [`WeightedIndex`]: crate::distr::weighted::WeightedIndex - #[cfg(feature = "alloc")] - fn choose_weighted_mut( - &mut self, - rng: &mut R, - weight: F, - ) -> Result<&mut Self::Output, WeightError> - where - R: Rng + ?Sized, - F: Fn(&Self::Output) -> B, - B: SampleBorrow, - X: SampleUniform + Weight + PartialOrd, - { - use crate::distr::{Distribution, weighted::WeightedIndex}; - let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?; - let index = distr.sample(rng); - Ok(&mut self[index]) - } -} - -/// Extension trait on slices, providing shuffling methods. -/// -/// This trait is implemented on all `[T]` slice types, providing several -/// methods for choosing and shuffling elements. You must `use` this trait: -/// -/// ``` -/// use rand::seq::SliceRandom; -/// -/// let mut rng = rand::rng(); -/// let mut bytes = "Hello, random!".to_string().into_bytes(); -/// bytes.shuffle(&mut rng); -/// let str = String::from_utf8(bytes).unwrap(); -/// println!("{}", str); -/// ``` -/// Example output (non-deterministic): -/// ```none -/// l,nmroHado !le -/// ``` -pub trait SliceRandom: IndexedMutRandom { - /// 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 - /// - /// ``` - /// use rand::seq::SliceRandom; - /// - /// let mut rng = rand::rng(); - /// let mut y = [1, 2, 3, 4, 5]; - /// println!("Unshuffled: {:?}", y); - /// y.shuffle(&mut rng); - /// println!("Shuffled: {:?}", y); - /// ``` - fn shuffle(&mut self, rng: &mut R) - where - R: Rng + ?Sized; - - /// Shuffle a slice in place, but exit early. - /// - /// Returns two mutable slices from the source slice. The first contains - /// `amount` elements randomly permuted. The second has the remaining - /// elements that are not fully shuffled. - /// - /// This is an efficient method to select `amount` elements at random from - /// the slice, provided the slice may be mutated. - /// - /// If you only need to choose elements randomly and `amount > self.len()/2` - /// then you may improve performance by taking - /// `amount = self.len() - amount` and using only the second slice. - /// - /// If `amount` is greater than the number of elements in the slice, this - /// will perform a full shuffle. - /// - /// For slices, complexity is `O(m)` where `m = amount`. - fn partial_shuffle( - &mut self, - rng: &mut R, - amount: usize, - ) -> (&mut [Self::Output], &mut [Self::Output]) - where - Self::Output: Sized, - R: Rng + ?Sized; -} - -impl IndexedRandom for [T] { - fn len(&self) -> usize { - self.len() - } -} - -impl + ?Sized> IndexedMutRandom for IR {} - -impl SliceRandom for [T] { - fn shuffle(&mut self, rng: &mut R) - 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(&mut self, rng: &mut R, amount: usize) -> (&mut [T], &mut [T]) - 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. - // It ensures that the last `amount` elements of the slice - // are randomly selected from the whole slice. - - // `IncreasingUniform::next_index()` is faster than `Rng::random_range` - // 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 = rng.random_range(..i + 1); - self.swap(i, index); - } - } - let r = self.split_at_mut(m); - (r.1, r.0) - } -} - -/// An iterator over multiple slice elements. -/// -/// This struct is created by -/// [`IndexedRandom::sample`](trait.IndexedRandom.html#tymethod.sample). -#[cfg(feature = "alloc")] -#[derive(Debug)] -pub struct IndexedSamples<'a, S: ?Sized + 'a, T: 'a> { - slice: &'a S, - _phantom: core::marker::PhantomData, - indices: index::IndexVecIntoIter, -} - -#[cfg(feature = "alloc")] -impl<'a, S: Index + ?Sized + 'a, T: 'a> Iterator for IndexedSamples<'a, S, T> { - type Item = &'a T; - - fn next(&mut self) -> Option { - // TODO: investigate using SliceIndex::get_unchecked when stable - self.indices.next().map(|i| &self.slice[i]) - } - - fn size_hint(&self) -> (usize, Option) { - (self.indices.len(), Some(self.indices.len())) - } -} - -#[cfg(feature = "alloc")] -impl<'a, S: Index + ?Sized + 'a, T: 'a> ExactSizeIterator - for IndexedSamples<'a, S, T> -{ - fn len(&self) -> usize { - self.indices.len() - } -} - -/// Deprecated: renamed to [`IndexedSamples`] -#[cfg(feature = "alloc")] -#[deprecated(since = "0.10.0", note = "Renamed to `IndexedSamples`")] -pub type SliceChooseIter<'a, S, T> = IndexedSamples<'a, S, T>; - -#[cfg(test)] -mod test { - use super::*; - #[cfg(feature = "alloc")] - use alloc::vec::Vec; - - #[test] - fn test_slice_choose() { - let mut r = crate::test::rng(107); - let chars = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - ]; - let mut chosen = [0i32; 14]; - // The below all use a binomial distribution with n=1000, p=1/14. - // binocdf(40, 1000, 1/14) ~= 2e-5; 1-binocdf(106, ..) ~= 2e-5 - for _ in 0..1000 { - let picked = *chars.choose(&mut r).unwrap(); - chosen[(picked as usize) - ('a' as usize)] += 1; - } - for count in chosen.iter() { - assert!(40 < *count && *count < 106); - } - - chosen.iter_mut().for_each(|x| *x = 0); - for _ in 0..1000 { - *chosen.choose_mut(&mut r).unwrap() += 1; - } - for count in chosen.iter() { - assert!(40 < *count && *count < 106); - } - - let mut v: [isize; 0] = []; - assert_eq!(v.choose(&mut r), None); - assert_eq!(v.choose_mut(&mut r), None); - } - - #[test] - fn value_stability_slice() { - let mut r = crate::test::rng(413); - let chars = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - ]; - let mut nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; - - assert_eq!(chars.choose(&mut r), Some(&'l')); - assert_eq!(nums.choose_mut(&mut r), Some(&mut 3)); - - assert_eq!( - &chars.sample_array(&mut r), - &Some(['f', 'i', 'd', 'b', 'c', 'm', 'j', 'k']) - ); - - #[cfg(feature = "alloc")] - assert_eq!( - &chars.sample(&mut r, 8).cloned().collect::>(), - &['h', 'm', 'd', 'b', 'c', 'e', 'n', 'f'] - ); - - #[cfg(feature = "alloc")] - assert_eq!(chars.choose_weighted(&mut r, |_| 1), Ok(&'i')); - #[cfg(feature = "alloc")] - assert_eq!(nums.choose_weighted_mut(&mut r, |_| 1), Ok(&mut 2)); - - let mut r = crate::test::rng(414); - nums.shuffle(&mut r); - 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, 12, 6, 8, 1, 9]); - assert_eq!(res.1, &mut [0, 11, 2, 3, 4, 5, 10]); - } - - #[test] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_shuffle() { - let mut r = crate::test::rng(108); - let empty: &mut [isize] = &mut []; - empty.shuffle(&mut r); - let mut one = [1]; - one.shuffle(&mut r); - let b: &[_] = &[1]; - assert_eq!(one, b); - - let mut two = [1, 2]; - two.shuffle(&mut r); - assert!(two == [1, 2] || two == [2, 1]); - - fn move_last(slice: &mut [usize], pos: usize) { - // use slice[pos..].rotate_left(1); once we can use that - let last_val = slice[pos]; - for i in pos..slice.len() - 1 { - slice[i] = slice[i + 1]; - } - *slice.last_mut().unwrap() = last_val; - } - let mut counts = [0i32; 24]; - for _ in 0..10000 { - let mut arr: [usize; 4] = [0, 1, 2, 3]; - arr.shuffle(&mut r); - let mut permutation = 0usize; - let mut pos_value = counts.len(); - for i in 0..4 { - pos_value /= 4 - i; - let pos = arr.iter().position(|&x| x == i).unwrap(); - assert!(pos < (4 - i)); - permutation += pos * pos_value; - move_last(&mut arr, pos); - assert_eq!(arr[3], i); - } - for (i, &a) in arr.iter().enumerate() { - assert_eq!(a, i); - } - counts[permutation] += 1; - } - for count in counts.iter() { - // Binomial(10000, 1/24) with average 416.667 - // Octave: binocdf(n, 10000, 1/24) - // 99.9% chance samples lie within this range: - assert!(352 <= *count && *count <= 483, "count: {}", count); - } - } - - #[test] - fn test_partial_shuffle() { - let mut r = crate::test::rng(118); - - let mut empty: [u32; 0] = []; - let res = empty.partial_shuffle(&mut r, 10); - assert_eq!((res.0.len(), res.1.len()), (0, 0)); - - let mut v = [1, 2, 3, 4, 5]; - let res = v.partial_shuffle(&mut r, 2); - assert_eq!((res.0.len(), res.1.len()), (2, 3)); - assert!(res.0[0] != res.0[1]); - // First elements are only modified if selected, so at least one isn't modified: - assert!(res.1[0] == 1 || res.1[1] == 2 || res.1[2] == 3); - } - - #[test] - #[cfg(feature = "alloc")] - #[cfg_attr(miri, ignore)] // Miri is too slow - fn test_weighted() { - let mut r = crate::test::rng(406); - const N_REPS: u32 = 3000; - let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; - let total_weight = weights.iter().sum::() as f32; - - let verify = |result: [i32; 14]| { - for (i, count) in result.iter().enumerate() { - let exp = (weights[i] * N_REPS) as f32 / total_weight; - let mut err = (*count as f32 - exp).abs(); - if err != 0.0 { - err /= exp; - } - assert!(err <= 0.25); - } - }; - - // choose_weighted - fn get_weight(item: &(u32, T)) -> u32 { - item.0 - } - let mut chosen = [0i32; 14]; - let mut items = [(0u32, 0usize); 14]; // (weight, index) - for (i, item) in items.iter_mut().enumerate() { - *item = (weights[i], i); - } - for _ in 0..N_REPS { - let item = items.choose_weighted(&mut r, get_weight).unwrap(); - chosen[item.1] += 1; - } - verify(chosen); - - // choose_weighted_mut - let mut items = [(0u32, 0i32); 14]; // (weight, count) - for (i, item) in items.iter_mut().enumerate() { - *item = (weights[i], 0); - } - for _ in 0..N_REPS { - items.choose_weighted_mut(&mut r, get_weight).unwrap().1 += 1; - } - for (ch, item) in chosen.iter_mut().zip(items.iter()) { - *ch = item.1; - } - verify(chosen); - - // Check error cases - let empty_slice = &mut [10][0..0]; - assert_eq!( - empty_slice.choose_weighted(&mut r, |_| 1), - Err(WeightError::InvalidInput) - ); - assert_eq!( - empty_slice.choose_weighted_mut(&mut r, |_| 1), - Err(WeightError::InvalidInput) - ); - assert_eq!( - ['x'].choose_weighted_mut(&mut r, |_| 0), - Err(WeightError::InsufficientNonZero) - ); - assert_eq!( - [0, -1].choose_weighted_mut(&mut r, |x| *x), - Err(WeightError::InvalidWeight) - ); - assert_eq!( - [-1, 0].choose_weighted_mut(&mut r, |x| *x), - Err(WeightError::InvalidWeight) - ); - } - - #[test] - #[cfg(feature = "std")] - fn test_multiple_weighted_edge_cases() { - use super::*; - - let mut rng = crate::test::rng(413); - - // Case 1: One of the weights is 0 - let choices = [('a', 2), ('b', 1), ('c', 0)]; - for _ in 0..100 { - let result = choices - .sample_weighted(&mut rng, 2, |item| item.1) - .unwrap() - .collect::>(); - - assert_eq!(result.len(), 2); - assert!(!result.iter().any(|val| val.0 == 'c')); - } - - // Case 2: All of the weights are 0 - let choices = [('a', 0), ('b', 0), ('c', 0)]; - let r = choices.sample_weighted(&mut rng, 2, |item| item.1); - assert_eq!(r.unwrap().len(), 0); - - // Case 3: Negative weights - let choices = [('a', -1), ('b', 1), ('c', 1)]; - let r = choices.sample_weighted(&mut rng, 2, |item| item.1); - assert_eq!(r.unwrap_err(), WeightError::InvalidWeight); - - // Case 4: Empty list - let choices = []; - let r = choices.sample_weighted(&mut rng, 0, |_: &()| 0); - assert_eq!(r.unwrap().count(), 0); - - // Case 5: NaN weights - let choices = [('a', f64::NAN), ('b', 1.0), ('c', 1.0)]; - let r = choices.sample_weighted(&mut rng, 2, |item| item.1); - assert_eq!(r.unwrap_err(), WeightError::InvalidWeight); - - // Case 6: +infinity weights - let choices = [('a', f64::INFINITY), ('b', 1.0), ('c', 1.0)]; - for _ in 0..100 { - let result = choices - .sample_weighted(&mut rng, 2, |item| item.1) - .unwrap() - .collect::>(); - assert_eq!(result.len(), 2); - assert!(result.iter().any(|val| val.0 == 'a')); - } - - // Case 7: -infinity weights - let choices = [('a', f64::NEG_INFINITY), ('b', 1.0), ('c', 1.0)]; - let r = choices.sample_weighted(&mut rng, 2, |item| item.1); - assert_eq!(r.unwrap_err(), WeightError::InvalidWeight); - - // Case 8: -0 weights - let choices = [('a', -0.0), ('b', 1.0), ('c', 1.0)]; - let r = choices.sample_weighted(&mut rng, 2, |item| item.1); - assert!(r.is_ok()); - } - - #[test] - #[cfg(feature = "std")] - fn test_multiple_weighted_distributions() { - use super::*; - - // The theoretical probabilities of the different outcomes are: - // AB: 0.5 * 0.667 = 0.3333 - // AC: 0.5 * 0.333 = 0.1667 - // BA: 0.333 * 0.75 = 0.25 - // BC: 0.333 * 0.25 = 0.0833 - // CA: 0.167 * 0.6 = 0.1 - // CB: 0.167 * 0.4 = 0.0667 - let choices = [('a', 3), ('b', 2), ('c', 1)]; - let mut rng = crate::test::rng(414); - - let mut results = [0i32; 3]; - let expected_results = [5833, 2667, 1500]; - for _ in 0..10000 { - let result = choices - .sample_weighted(&mut rng, 2, |item| item.1) - .unwrap() - .collect::>(); - - assert_eq!(result.len(), 2); - - match (result[0].0, result[1].0) { - ('a', 'b') | ('b', 'a') => { - results[0] += 1; - } - ('a', 'c') | ('c', 'a') => { - results[1] += 1; - } - ('b', 'c') | ('c', 'b') => { - results[2] += 1; - } - (_, _) => panic!("unexpected result"), - } - } - - let mut diffs = results - .iter() - .zip(&expected_results) - .map(|(a, b)| (a - b).abs()); - assert!(!diffs.any(|deviation| deviation > 100)); - } -} diff --git a/tests/fill.rs b/tests/fill.rs deleted file mode 100644 index 5fb401de4d9..00000000000 --- a/tests/fill.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(unused)] - -use rand::{Fill, Rng, RngExt}; - -// Test that Fill may be implemented for externally-defined types -struct MyInt(i32); -impl Fill for MyInt { - fn fill_slice(this: &mut [Self], rng: &mut R) { - todo!() - } -} diff --git a/utils/redirect.html b/utils/redirect.html deleted file mode 100644 index 4f361b9f971..00000000000 --- a/utils/redirect.html +++ /dev/null @@ -1 +0,0 @@ -