generated from ProcyOS/rust-template
add runs of ones test
This commit is contained in:
parent
98518f79a0
commit
12581e9e91
5 changed files with 110 additions and 4 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
102
src/tests/longest_runs_of_ones.rs
Normal file
102
src/tests/longest_runs_of_ones.rs
Normal 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");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue