Compare commits

..

2 commits

Author SHA1 Message Date
f650a10e67
Merge branch 'main' into add-runs-of-ones
Some checks failed
Hydra x86_64-linux.checks.rand_testsuite Hydra build #6520 of procyos:rand_testsuite-pr8:x86_64-linux.checks.rand_testsuite
Hydra x86_64-linux.packages.rand_testsuite Hydra build #6521 of procyos:rand_testsuite-pr8:x86_64-linux.packages.rand_testsuite
2024-09-17 06:28:31 +00:00
12581e9e91 add runs of ones test 2024-09-10 10:28:28 +02:00
8 changed files with 155 additions and 49 deletions

View file

@ -11,5 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added a simple monobit test.
- Added a block frequency test.
- Added a runs test.
- Added a longest runs of ones test.
[Unreleased]: https://git.chir.rs/ProcyOS/rand_testsuite

20
Cargo.lock generated
View file

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "autocfg"
version = "1.4.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "bitvec"
@ -61,15 +61,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.161"
version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libm"
version = "0.2.10"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00419de735aac21d53b0de5ce2c03bd3627277cf471300f27ebc89f7d828047"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "num-traits"
@ -92,9 +92,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.89"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
@ -158,9 +158,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.85"
version = "2.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
dependencies = [
"proc-macro2",
"quote",

View file

@ -22,7 +22,7 @@ args @ {
workspaceSrc,
ignoreLockHash,
}: let
nixifiedLockHash = "5604c9f2bec13d4ab9a8a5fba2223479ecd3fc6199912d48c278edb02c0d4ab5";
nixifiedLockHash = "a0c35c5dbb7b7f0a97aaa0410b0bcdce042cdade80df9c137817016071bf7fee";
workspaceSrc =
if args.workspaceSrc == null
then ./.
@ -61,13 +61,13 @@ in
workspace = {
rand_testsuite = rustPackages.unknown.rand_testsuite."0.1.0";
};
"registry+https://github.com/rust-lang/crates.io-index".autocfg."1.4.0" = overridableMkRustCrate (profileName: rec {
"registry+https://github.com/rust-lang/crates.io-index".autocfg."1.3.0" = overridableMkRustCrate (profileName: rec {
name = "autocfg";
version = "1.4.0";
version = "1.3.0";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26";
sha256 = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0";
};
});
@ -127,7 +127,7 @@ in
["default"]
];
dependencies = {
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.10" {inherit profileName;}).out;
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.8" {inherit profileName;}).out;
num_traits = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.19" {inherit profileName;}).out;
};
});
@ -160,7 +160,7 @@ in
then "libc"
else null
} =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.161" {inherit profileName;}).out;
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.158" {inherit profileName;}).out;
${
if hostPlatform.parsed.kernel.name == "wasi"
then "wasi"
@ -170,23 +170,23 @@ in
};
});
"registry+https://github.com/rust-lang/crates.io-index".libc."0.2.161" = overridableMkRustCrate (profileName: rec {
"registry+https://github.com/rust-lang/crates.io-index".libc."0.2.158" = overridableMkRustCrate (profileName: rec {
name = "libc";
version = "0.2.161";
version = "0.2.158";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1";
sha256 = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439";
};
});
"registry+https://github.com/rust-lang/crates.io-index".libm."0.2.10" = overridableMkRustCrate (profileName: rec {
"registry+https://github.com/rust-lang/crates.io-index".libm."0.2.8" = overridableMkRustCrate (profileName: rec {
name = "libm";
version = "0.2.10";
version = "0.2.8";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "a00419de735aac21d53b0de5ce2c03bd3627277cf471300f27ebc89f7d828047";
sha256 = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058";
};
features = builtins.concatLists [
["default"]
@ -206,10 +206,10 @@ in
(lib.optional (rootFeatures' ? "rand_testsuite/full" || rootFeatures' ? "rand_testsuite/std") "std")
];
dependencies = {
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.10" {inherit profileName;}).out;
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.8" {inherit profileName;}).out;
};
buildDependencies = {
autocfg = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".autocfg."1.4.0" {profileName = "__noProfile";}).out;
autocfg = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".autocfg."1.3.0" {profileName = "__noProfile";}).out;
};
});
@ -230,13 +230,13 @@ in
};
});
"registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.89" = overridableMkRustCrate (profileName: rec {
"registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.86" = overridableMkRustCrate (profileName: rec {
name = "proc-macro2";
version = "1.0.89";
version = "1.0.86";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e";
sha256 = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77";
};
features = builtins.concatLists [
["default"]
@ -260,7 +260,7 @@ in
["proc-macro"]
];
dependencies = {
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.89" {inherit profileName;}).out;
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.86" {inherit profileName;}).out;
};
});
@ -297,7 +297,7 @@ in
then "libc"
else null
} =
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.161" {inherit profileName;}).out;
(rustPackages."registry+https://github.com/rust-lang/crates.io-index".libc."0.2.158" {inherit profileName;}).out;
rand_chacha = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_chacha."0.3.1" {inherit profileName;}).out;
rand_core = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_core."0.6.4" {inherit profileName;}).out;
};
@ -352,7 +352,7 @@ in
dependencies = {
bitvec = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bitvec."1.0.1" {inherit profileName;}).out;
extra_math = (rustPackages."sparse+https://git.chir.rs/api/packages/ProcyOS/cargo/".extra-math."0.1.1" {inherit profileName;}).out;
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.10" {inherit profileName;}).out;
libm = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".libm."0.2.8" {inherit profileName;}).out;
num_traits = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".num-traits."0.2.19" {inherit profileName;}).out;
${
if rootFeatures' ? "rand_testsuite/full" || rootFeatures' ? "rand_testsuite/rand_core"
@ -367,13 +367,13 @@ in
};
});
"registry+https://github.com/rust-lang/crates.io-index".syn."2.0.85" = overridableMkRustCrate (profileName: rec {
"registry+https://github.com/rust-lang/crates.io-index".syn."2.0.77" = overridableMkRustCrate (profileName: rec {
name = "syn";
version = "2.0.85";
version = "2.0.77";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo {
inherit name version;
sha256 = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56";
sha256 = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed";
};
features = builtins.concatLists [
["clone-impls"]
@ -384,7 +384,7 @@ in
["proc-macro"]
];
dependencies = {
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.89" {inherit profileName;}).out;
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.86" {inherit profileName;}).out;
quote = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".quote."1.0.37" {inherit profileName;}).out;
unicode_ident = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".unicode-ident."1.0.13" {inherit profileName;}).out;
};
@ -463,9 +463,9 @@ in
sha256 = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e";
};
dependencies = {
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.89" {inherit profileName;}).out;
proc_macro2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".proc-macro2."1.0.86" {inherit profileName;}).out;
quote = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".quote."1.0.37" {inherit profileName;}).out;
syn = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".syn."2.0.85" {inherit profileName;}).out;
syn = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".syn."2.0.77" {inherit profileName;}).out;
};
});
}

View file

@ -11,7 +11,7 @@ categories = ["development-tools::testing", "no-std", "no-std::no-alloc"]
[dependencies]
bitvec = { version = "1.0.1", default-features = false }
extra-math = { version = "0.1.0", registry = "procyos" }
extra-math = { version = "0.1.1", registry = "procyos" }
libm = "0.2.8"
num-traits = { version = "0.2.19", default-features = false, features = [
"libm",

View file

@ -63,11 +63,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@ -78,11 +78,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1730098753,
"narHash": "sha256-3If4Hb2XhBho9zlfT4avej2FSaMX+FZK6eoqhi0gXUE=",
"lastModified": 1726552853,
"narHash": "sha256-IiMC/CUatt5uvU5oHfWfqoaCjNs/vqRHj0B8JTZLrTg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "369601464538ac0ddec00538fa2d3b5a8f2986c2",
"rev": "683fddf43ec6cbb85bb61de54349090beb190173",
"type": "github"
},
"original": {
@ -107,11 +107,11 @@
]
},
"locked": {
"lastModified": 1730082698,
"narHash": "sha256-xGP95+G2/esys6FpxrunwwfhirfGsFfPKBJ12MLV1Ps=",
"lastModified": 1726539203,
"narHash": "sha256-u1tAteb4qkH2gGjDY3mN/4Qxa6y798t4G0jNKDyTwv8=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "0d594a39c8f08d81246d06a56e1ccfc04782404f",
"rev": "20c8461785d8f5af32d8d4d5c128589e23d7f033",
"type": "github"
},
"original": {

View file

@ -1,6 +1,7 @@
//! All supported tests are available in this module.
mod block_frequency;
mod longest_runs_of_ones;
mod monobit;
mod runs;
@ -8,6 +9,7 @@ use core::f64;
use bitvec::prelude::{BitOrder, BitSlice, BitStore};
pub use block_frequency::*;
pub use longest_runs_of_ones::*;
pub use monobit::*;
pub use runs::*;
@ -34,12 +36,14 @@ impl RandomTest for CombinedRandomTest {
Monobit::RECOMMENDED_BIT_SIZE,
BlockFrequency::RECOMMENDED_BIT_SIZE,
Runs::RECOMMENDED_BIT_SIZE,
LongestRunOfOnes::RECOMMENDED_BIT_SIZE,
]);
const MINIMUM_BIT_SIZE: usize = max_of([
Monobit::MINIMUM_BIT_SIZE,
BlockFrequency::MINIMUM_BIT_SIZE,
Runs::MINIMUM_BIT_SIZE,
LongestRunOfOnes::MINIMUM_BIT_SIZE,
]);
fn evaluate<T: BitStore, O: BitOrder>(&self, bs: &BitSlice<T, O>) -> Option<f64> {
@ -47,6 +51,7 @@ impl RandomTest for CombinedRandomTest {
worst_test_score = worst_test_score.min(Monobit.evaluate(bs)?);
worst_test_score = worst_test_score.min(BlockFrequency.evaluate(bs)?);
worst_test_score = worst_test_score.min(Runs.evaluate(bs)?);
worst_test_score = worst_test_score.min(LongestRunOfOnes.evaluate(bs)?);
Some(worst_test_score)
}
}

View file

@ -0,0 +1,102 @@
//! Longest runs of ones test
use bitvec::prelude::{BitOrder, BitSlice, BitStore};
use extra_math::gamma::Gamma;
use crate::RandomTest;
const EXPECTED_M8: [f64; 4] = [0.2148, 0.3672, 0.2305, 0.1875];
const EXPECTED_M128: [f64; 6] = [0.1174, 0.2430, 0.2493, 0.1752, 0.1027, 0.1124];
const EXPECTED_M10K: [f64; 7] = [0.0882, 0.2092, 0.2483, 0.1933, 0.1208, 0.0675, 0.0727];
/// Longest runs of ones test
///
/// This test is defined in the [NIST Special Publication 800-22](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-22r1a.pdf).
#[derive(Copy, Clone, Debug)]
pub struct LongestRunOfOnes;
impl RandomTest for LongestRunOfOnes {
const RECOMMENDED_BIT_SIZE: usize = 750_000;
const MINIMUM_BIT_SIZE: usize = 128;
fn evaluate<T: BitStore, O: BitOrder>(&self, bs: &BitSlice<T, O>) -> Option<f64> {
let block_size = match bs.len() {
0..128 => {
return None;
}
128..6272 => 8,
6272..750_000 => 128,
750_000.. => 10_000,
};
let mut occurences = [0usize; 7];
for block in bs.chunks(block_size) {
let mut c = 0usize;
for b in block.into_iter().map(|b| *b).chain([false]) {
if b {
c += 1;
} else if c > 0 {
match (block_size, c) {
(0..=8, 0..=1) | (9..=128, 0..=4) | (129.., 0..=10) => occurences[0] += 1,
(0..=8, 2) | (9..=128, 5) | (129.., 11) => occurences[1] += 1,
(0..=8, 3) | (9..=128, 6) | (129.., 12) => occurences[2] += 1,
(0..=8, 4..) | (9..=128, 7) | (129.., 13) => occurences[3] += 1,
(9..=128, 8) | (129.., 14) => occurences[4] += 1,
(9..=128, 9..) | (129.., 15) => occurences[5] += 1,
(129.., 16..) => occurences[6] += 1,
}
c = 0;
}
}
}
let mut chi_squared = 0.0;
let k = if block_size == 8 {
for i in 0..EXPECTED_M8.len() {
chi_squared +=
(occurences[i] as f64 - 16.0 * EXPECTED_M8[i]) / (16.0 * EXPECTED_M8[i]);
}
EXPECTED_M8.len()
} else if block_size == 128 {
for i in 0..EXPECTED_M128.len() {
chi_squared +=
(occurences[i] as f64 - 49.0 * EXPECTED_M128[i]) / (49.0 * EXPECTED_M128[i]);
}
EXPECTED_M128.len()
} else if block_size == 10_000 {
for i in 0..EXPECTED_M10K.len() {
chi_squared +=
(occurences[i] as f64 - 75.0 * EXPECTED_M10K[i]) / (75.0 * EXPECTED_M10K[i]);
}
EXPECTED_M10K.len()
} else {
unreachable!();
};
Some(Gamma::upper_gamma_regularized(
k as f64 / 2.0,
chi_squared / 2.0,
))
}
}
#[cfg(test)]
mod tests {
use bitvec::prelude::*;
use crate::RandomTest;
use super::LongestRunOfOnes;
#[test]
fn longest_run_of_ones_test_vector() {
let epsilon = bitvec![
1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1,
1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1,
0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1,
1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,
];
let res = LongestRunOfOnes.evaluate(epsilon.as_bitslice());
assert!(res.is_some());
let res = res.unwrap();
assert!(res > 0.01, "Data should be evaluated as random");
}
}

View file

@ -1,10 +1,8 @@
//! Various utility functions
use core::convert::identity;
use bitvec::{order::BitOrder, slice::BitSlice, store::BitStore};
/// Calculates the proportion of ones in a bitslice
pub fn ones_proportion<B: BitStore, O: BitOrder>(bs: &BitSlice<B, O>) -> f64 {
pub(crate) fn ones_proportion<B: BitStore, O: BitOrder>(bs: &BitSlice<B, O>) -> f64 {
bs.into_iter().filter(|b| **b).count() as f64 / bs.len() as f64
}