Compare commits
10 commits
f3766124ad
...
e0f244281d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0f244281d | ||
|
|
249a16536a | ||
|
|
800d58ea89 | ||
|
|
cb9c346533 | ||
|
|
8ac9e78057 | ||
|
|
e797669fba | ||
|
|
2d6b61b6cf | ||
|
|
b4c2e5ea9b | ||
|
|
f21804ffaf | ||
|
|
dcc612fc98 |
29 changed files with 1294 additions and 162 deletions
109
Cargo.lock
generated
109
Cargo.lock
generated
|
|
@ -77,6 +77,18 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "kolmogorov_smirnov"
|
||||
version = "1.1.0"
|
||||
|
|
@ -108,6 +120,15 @@ dependencies = [
|
|||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.3"
|
||||
|
|
@ -183,12 +204,19 @@ dependencies = [
|
|||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "onod3000"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"kolmogorov_smirnov",
|
||||
"pyo3",
|
||||
"statrs",
|
||||
]
|
||||
|
||||
|
|
@ -198,6 +226,12 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
|
|
@ -216,6 +250,69 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"indoc",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"portable-atomic",
|
||||
"pyo3-build-config",
|
||||
"pyo3-ffi",
|
||||
"pyo3-macros",
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-build-config"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-ffi"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pyo3-build-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyo3-macros-backend"
|
||||
version = "0.23.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
|
|
@ -363,6 +460,12 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
|
|
@ -375,6 +478,12 @@ version = "1.0.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
|
|
|||
17
Cargo.toml
17
Cargo.toml
|
|
@ -3,7 +3,24 @@ name = "onod3000"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "onod3000"
|
||||
crate-type = ["cdylib", "staticlib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
flate2 = "1.0.35"
|
||||
kolmogorov_smirnov = "1.1.0"
|
||||
pyo3 = {version = "0.23.4", optional = true}
|
||||
statrs = "0.18.0"
|
||||
|
||||
[features]
|
||||
python = ["dep:pyo3"]
|
||||
|
||||
[[bin]]
|
||||
name="onod3000"
|
||||
file="src/bin/onod3000.rs"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
26
LICENSE
Normal file
26
LICENSE
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
Original work is licensed under the terms specified in the respective file header.
|
||||
18
include/onod3000.h
Normal file
18
include/onod3000.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Returns true on success. Writes three doubles: obs, z, p
|
||||
bool onod_run(const char* test,
|
||||
const uint8_t* samples,
|
||||
size_t len,
|
||||
double* result /* length >= 3 */);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
201
readme.md
Normal file
201
readme.md
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
> **Migrated to [code.randogoth.com/randogoth/onod3000](https://code.randogoth.com/randogoth/onod3000)**
|
||||
|
||||
```
|
||||
____
|
||||
|\ ` // /\\ /\\ /\\
|
||||
\\ // || || || || || ||
|
||||
/'\\ \\/\\ /'\\ / \\ \\ || || || || || ||
|
||||
|| || || || || || || || )) || || || || || ||
|
||||
|| || || || || || || || // || || || || || ||
|
||||
\\,/ \\ \\ \\,/ \\/ /' \\/ \\/ \\/
|
||||
|
||||
|
||||
Rust port of Paul Uszak's new randomness testing suite for TRNG Makers
|
||||
generating in the sub 1 MB space. Successor to John Walker’s venerable `ent`.
|
||||
|
||||
Pipe binary entropy data into the `onod3000` command line tool or call it
|
||||
with `-f <filename>` to load entropy from a file.
|
||||
|
||||
```
|
||||
|
||||
`Onod3000` provides multiple randomness tests, each outputting a statistic, a z-score andd a p-value to assess the conformity of the data to random behavior.
|
||||
|
||||
## Testing Methodology
|
||||
|
||||
- **Null Hypothesis**: The data conforms to random behavior.
|
||||
- **P-Value Interpretation**: Low p-values (< 0.01) indicate a significant deviation from randomness.
|
||||
|
||||
All tests have been carefully ported from [Paul Uszak's Java code](http://www.reallyreallyrandom.com/gitbucketlabhub/) (ent3000-0.6.0-beta). Java libraries were substituted with Rust crate equivalents when possible. The suite includes the following tests:
|
||||
|
||||
- **Monobit Test**: Evaluates the balance of 0s and 1s.
|
||||
- **Chi-Square Tests**: Tests the uniformity of bits and bytes.
|
||||
- **MeanByte Test**: Checks if the mean byte value aligns with expected randomness.
|
||||
- **Kolmogorov-Smirnov (KS) Test**: Assesses uniformity of data distribution.
|
||||
- **Pi Test**: Uses Monte Carlo methods to approximate π.
|
||||
- **Shells Test**: Analyzes distances in 3D space.
|
||||
- **Gaps Test**: Measures gaps between occurrences of a specific value.
|
||||
- **Avalanche Test**: Analyzes bit-level changes in data chunks.
|
||||
- **Runs and RunUps Tests**: Checks for sequential patterns in data.
|
||||
- **Prediction Test**: Assesses the predictability of next bits.
|
||||
- **UnCorrelation Test**: Evaluates correlation between shifted data.
|
||||
|
||||
More details on some individual tests [available here](http://www.reallyreallyrandom.com/ent3000/the-tests/index.html)
|
||||
|
||||
### Sample Output
|
||||
```bash
|
||||
cat /dev/random | head -c 259200 | onod3000
|
||||
|
||||
Testing 259200 bytes from stdin.
|
||||
--------------------------------------------------------
|
||||
Randomness Test Value Z-Score P-Value Pass
|
||||
--------------------------------------------------------
|
||||
Shannon 7.999 -0.3669 0.7137 ✅
|
||||
Monobit 0.500 0.9000 0.3681 ✅
|
||||
ChiBit 4.555 -0.8612 0.8039 ✅
|
||||
ChiByte 236.288 -0.8286 0.7939 ✅
|
||||
MeanByte 127.312 -1.2970 0.1946 ✅
|
||||
Compression 1.000 0.0868 0.9308 ✅
|
||||
Kolm.-Smirnov 0.004 2.0722 0.0272 ✅
|
||||
Pi 3.139 -1.3171 0.1878 ✅
|
||||
Shells 33.593 -0.0493 0.4874 ✅
|
||||
Gaps 7.402 -0.3767 0.5954 ✅
|
||||
Avalanche 80.006 0.0009 0.9993 ✅
|
||||
Runs 129288.000 0.8208 0.4118 ✅
|
||||
RunUps 2.647 1.1645 0.1038 ✅
|
||||
Prediction 0.500 -0.0255 0.9796 ✅
|
||||
UnCorrelation -0.001 -0.3725 0.7096 ✅
|
||||
--------------------------------------------------------
|
||||
15/15 tests passed.
|
||||
--------------------------------------------------------
|
||||
```
|
||||
|
||||
## Rust Library
|
||||
|
||||
```rust
|
||||
use onod3000::Onod;
|
||||
|
||||
fn main() {
|
||||
let data = vec![...]; // Your binary data
|
||||
let p_value = Onod::monobit(&data);
|
||||
println!("Monobit Test P-Value: {:.4}", p_value);
|
||||
}
|
||||
```
|
||||
|
||||
## Bindings
|
||||
|
||||
The library ships with FFI and optional Python bindings so it can be used as C or Python library.
|
||||
|
||||
### Python
|
||||
Install using `maturin`:
|
||||
```bash
|
||||
maturin build --release --features python
|
||||
```
|
||||
|
||||
## Comparison
|
||||
|
||||
To test the implementation three files of binary random data have been analyzed using the original Java program (version 0.6.0-beta) and this Rust port, expecting the same p-values. The test files can be found in the test folder.
|
||||
|
||||
### Testing 1024 bytes from test1.bin (/dev/random)
|
||||
|
||||
Test | Java ent3000 | Rust onod3000 | Note |
|
||||
------------- | ------------ | ------------- | ------------- |
|
||||
Shannon | N/A | 0.0000 | |
|
||||
Monobit | 0.6746 | 0.6746 | |
|
||||
ChiBit | 0.9299 | 0.9299 | |
|
||||
ChiByte | 0.9819 | 0.9819 | |
|
||||
MeanByte | 0.7964 | 0.7964 | |
|
||||
Compression | N/A | 0.0146 | |
|
||||
KS | 0.5313 | 0.2476 | Mismatch |
|
||||
Pi | 0.3784 | 0.3784 | |
|
||||
Shells | 0.7559 | 0.7559 | |
|
||||
Gaps | 0.0000 | 0.0000 | |
|
||||
Avalanche | 0.8794 | 0.8794 | |
|
||||
Runs | 0.0850 | 0.0850 | |
|
||||
RunUps | 0.6537 | 0.6537 | |
|
||||
Prediction | 0.6164 | 0.6164 | |
|
||||
UnCorrelation | 0.0921 | 0.0918 | Close enough |
|
||||
|
||||
Feel free to provide more results, and I'll continue updating the table!
|
||||
|
||||
### Testing 259,200 bytes from test2.bin (/dev/random)
|
||||
|
||||
Test | Java ent3000 | Rust onod3000 | Note |
|
||||
------------- | ------------ | ------------- | ------------- |
|
||||
Shannon | N/A | 0.6853 | |
|
||||
Monobit | 0.2103 | 0.2103 | |
|
||||
ChiBit | 0.5590 | 0.5590 | |
|
||||
ChiByte | 0.3748 | 0.3748 | |
|
||||
MeanByte | 0.0183 | 0.0183 | |
|
||||
Compression | N/A | 0.9308 | |
|
||||
KS | 0.2900 | 0.0178 | Mismatch |
|
||||
Pi | 0.9765 | 0.9765 | |
|
||||
Shells | 0.7235 | 0.7235 | |
|
||||
Gaps | 0.6425 | 0.6425 | |
|
||||
Avalanche | 0.9995 | 0.9995 | |
|
||||
Runs | 0.3310 | 0.3310 | |
|
||||
RunUps | 0.9393 | 0.9393 | |
|
||||
Prediction | 0.1519 | 0.1519 | |
|
||||
UnCorrelation | 0.4497 | 0.4497 | |
|
||||
|
||||
### Testing 259,200 bytes from test3.bin (Hardware QRNG)
|
||||
|
||||
Test | Java ent3000 | Rust onod3000 | Note |
|
||||
------------- | ------------ | ------------- | ---- |
|
||||
Shannon | N/A | 0.6978 | |
|
||||
Monobit | 0.4926 | 0.4926 | |
|
||||
ChiBit | 0.8428 | 0.8428 | |
|
||||
ChiByte | 0.5785 | 0.5785 | |
|
||||
MeanByte | 0.9601 | 0.9601 | |
|
||||
Compression | N/A | 0.9308 | |
|
||||
KS | 0.7395 | 0.0441 | Mismatch |
|
||||
Pi | 0.2806 | 0.2806 | |
|
||||
Shells | 0.7711 | 0.7711 | |
|
||||
Gaps | 0.1937 | 0.1937 | |
|
||||
Avalanche | 0.9932 | 0.9932 | |
|
||||
Runs | 0.2500 | 0.2500 | |
|
||||
RunUps | 0.9710 | 0.9710 | |
|
||||
Prediction | 0.7173 | 0.7173 | |
|
||||
UnCorrelation | 0.4674 | 0.4674 | |
|
||||
|
||||
### Conclusions
|
||||
|
||||
Although the implementation of the randomness tests closely follows the original Java logic, differences in the results arise in the Kolmogorov-Smirnov test. The external libraries used for running the test probably differ in their implementation. Also the uniform distribution to test against is provided by the Apache Commons Math library. Since it would be way beyond the scope of this porting project to try to fully match the functionality of the dependencies used we just accept the minor difference. Another issue might be that Java emphasizes predictability and portability, enforcing strict IEEE 754 behavior across platforms. Rust on the other hand prioritizes performance and flexibility, allowing platform-specific optimizations that may deviate slightly from strict IEEE semantics.
|
||||
|
||||
These differences are generally negligible for practical purposes and do not affect the overall functionality or
|
||||
statistical significance of the test.
|
||||
|
||||
### Bonus
|
||||
|
||||
We implemented a Well Equidistributed Long-period Linear pseudo-random number generator that is used with a random seed derived from an epoch timestamp that is being used as uniform distribution for the KS test.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
### License of Original Java Implementation
|
||||
|
||||
```
|
||||
Copyright (c) 2023 Paul Uszak.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
This work was made possible with the invaluable assistance of OpenAI's ChatGPT, which provided guidance, debugging help, and inspiration throughout the development process.
|
||||
|
|
@ -1,3 +1,31 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
// This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original work is licensed under the terms specified in the respective file header.
|
||||
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read};
|
||||
use std::path::Path;
|
||||
|
|
@ -62,7 +90,7 @@ fn main() -> io::Result<()> {
|
|||
("Runs", Onod::runs(&input_data)),
|
||||
("RunUps", Onod::run_ups(&input_data)),
|
||||
("Prediction", Onod::prediction(&input_data)),
|
||||
("UnCorrelation", Onod::un_correlation(&input_data)),
|
||||
("UnCorrelation", Onod::uncorrelation(&input_data)),
|
||||
];
|
||||
|
||||
for (test_name, (observation, z_score, p_value)) in &tests {
|
||||
58
src/chisquaretest.rs
Normal file
58
src/chisquaretest.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
// This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original work is licensed under the terms specified in the respective file header.
|
||||
|
||||
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
pub fn chi_square_test(observed: &[u64], expected: &[f64]) -> f64 {
|
||||
// Preconditions
|
||||
if observed.len() != expected.len() || observed.len() < 2 {
|
||||
panic!("Observed and expected arrays must have the same length and length >= 2.");
|
||||
}
|
||||
if expected.iter().any(|&e| e <= 0.0) {
|
||||
panic!("Expected array must contain only strictly positive values.");
|
||||
}
|
||||
|
||||
// Rescale expected array if necessary
|
||||
let sum_observed: f64 = observed.iter().map(|&o| o as f64).sum();
|
||||
let sum_expected: f64 = expected.iter().sum();
|
||||
let rescaled_expected: Vec<f64> = expected.iter().map(|&e| e * sum_observed / sum_expected).collect();
|
||||
|
||||
// Calculate chi-squared statistic
|
||||
let chi_squared_stat: f64 = observed
|
||||
.iter()
|
||||
.zip(rescaled_expected.iter())
|
||||
.map(|(&o, &e)| (o as f64 - e).powi(2) / e)
|
||||
.sum();
|
||||
|
||||
// Perform chi-squared test
|
||||
let degrees_of_freedom = observed.len() as f64 - 1.0;
|
||||
let chi_squared_dist = ChiSquared::new(degrees_of_freedom).expect("Failed to create ChiSquared distribution");
|
||||
let p_value = 1.0 - chi_squared_dist.cdf(chi_squared_stat);
|
||||
|
||||
p_value
|
||||
}
|
||||
71
src/ffi.rs
Normal file
71
src/ffi.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
// This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original work is licensed under the terms specified in the respective file header.
|
||||
|
||||
|
||||
use std::ffi::{c_char, c_double, CStr};
|
||||
|
||||
use crate::Onod;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn onod_run(
|
||||
test: *const c_char,
|
||||
samples: *const u8,
|
||||
len: usize,
|
||||
result: *mut c_double,
|
||||
) -> bool {
|
||||
// Check for null pointers
|
||||
if test.is_null() || samples.is_null() || result.is_null() {
|
||||
eprintln!("Error: Null pointer passed to onod_run.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert C string to Rust string
|
||||
let test = unsafe {
|
||||
match CStr::from_ptr(test).to_str() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
eprintln!("Error: Invalid UTF-8 string passed to onod_run.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Convert samples to a Rust slice
|
||||
let samples = unsafe { std::slice::from_raw_parts(samples, len as usize) };
|
||||
|
||||
// Call the Rust `run` function
|
||||
let (obs, z, p) = Onod::run(test, samples);
|
||||
|
||||
// Write results to the output buffer
|
||||
unsafe {
|
||||
*result.offset(0) = obs;
|
||||
*result.offset(1) = z;
|
||||
*result.offset(2) = p;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
62
src/lib.rs
62
src/lib.rs
|
|
@ -1,3 +1,63 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
// This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original work is licensed under the terms specified in the respective file header.
|
||||
|
||||
|
||||
pub struct Onod;
|
||||
|
||||
mod uniformity;
|
||||
mod uniformity;
|
||||
pub mod chisquaretest;
|
||||
pub mod well19937c;
|
||||
pub mod ffi;
|
||||
#[cfg(feature = "python")]
|
||||
pub mod python;
|
||||
|
||||
impl Onod {
|
||||
pub fn run(test: &str, samples: &[u8]) -> (f64, f64, f64) {
|
||||
|
||||
match test {
|
||||
"avalanche" => Onod::avalanche(samples),
|
||||
"chi_bit" => Onod::chi_bit(samples),
|
||||
"chi_byte" => Onod::chi_byte(samples),
|
||||
"compression" => Onod::compression(samples),
|
||||
"gaps" => Onod::gaps(samples),
|
||||
"ks" => Onod::ks(samples),
|
||||
"mean_byte" => Onod::mean_byte(samples),
|
||||
"monobit" => Onod::monobit(samples),
|
||||
"pi" => Onod::pi(samples),
|
||||
"prediction" => Onod::prediction(samples),
|
||||
"runs" => Onod::runs(samples),
|
||||
"run_ups" => Onod::run_ups(samples),
|
||||
"shannon" => Onod::shannon(samples),
|
||||
"shells" => Onod::shells(samples),
|
||||
"uncorrelation" => Onod::uncorrelation(samples),
|
||||
_ => {
|
||||
eprintln!("Error: Unknown test '{}'", test);
|
||||
(-1.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/python.rs
Normal file
47
src/python.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
// This project is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original work is licensed under the terms specified in the respective file header.
|
||||
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use super::Onod as O;
|
||||
|
||||
#[pyclass]
|
||||
struct Onod;
|
||||
|
||||
#[pymethods]
|
||||
impl Onod {
|
||||
#[staticmethod]
|
||||
pub fn run(test: &str, samples: Vec<u8>) -> (f64, f64, f64) {
|
||||
O::run(test, &samples)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn onod(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<Onod>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use std::collections::HashMap;
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use flate2::{write::DeflateEncoder, Compression};
|
||||
use std::io::Write;
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,47 +1,101 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use kolmogorov_smirnov::test_f64;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use statrs::distribution::ContinuousCDF;
|
||||
|
||||
use crate::Onod;
|
||||
use crate::well19937c::Well19937c;
|
||||
|
||||
impl Onod {
|
||||
|
||||
/// KS randomness test
|
||||
/// Performs the Kolmogorov-Smirnov test to evaluate uniformity of data distribution and returns a p-value.
|
||||
// Note: This implementation of the Kolmogorov-Smirnov (KS) test differs slightly from the
|
||||
// Java implementation due to differences in library behavior and floating-point handling.
|
||||
// The Java implementation (Apache Commons Math) adds random jitter to handle ties in small
|
||||
// datasets, uses strict inequality for small sample sizes, and applies specific precision
|
||||
// rules based on the IEEE 754 standard for `double`. The Rust implementation, using the
|
||||
// `kolmogorov_smirnov` crate, does not add jitter or handle ties in the same way, and
|
||||
// adheres to the crate's internal handling of floating-point comparisons. These differences
|
||||
// may result in slight variations in p-values or KS statistics between the two versions.
|
||||
/// Performs the Kolmogorov-Smirnov test to evaluate the uniformity of data distribution
|
||||
/// and returns the test statistic (D-statistic), z-score, and p-value.
|
||||
pub fn ks(samples: &[u8]) -> (f64, f64, f64) {
|
||||
|
||||
if samples.is_empty() {
|
||||
return (-1.0, 0.0, 1.0); // empty data
|
||||
return (-1.0, 0.0, 1.0); // Invalid input
|
||||
}
|
||||
|
||||
// Normalize the input samples to [0, 1] range
|
||||
let normalized_samples: Vec<f64> = samples.iter().map(|&x| x as f64 / 255.0).collect();
|
||||
|
||||
|
||||
// Normalize the input samples to [0, 1) range
|
||||
let mut normalized_samples: Vec<f64> = samples.iter().map(|&x| x as f64 / 255.0).collect();
|
||||
|
||||
// Generate a uniform distribution for comparison
|
||||
let uniform_distribution: Vec<f64> = (0..normalized_samples.len())
|
||||
.map(|i| i as f64 / (normalized_samples.len() as f64 - 1.0))
|
||||
.collect();
|
||||
|
||||
let mut uniform_distribution: Vec<f64> = Self::generate_uniform_distribution(normalized_samples.len(), Self::get_timestamp_seed());
|
||||
|
||||
// Sort both distributions
|
||||
normalized_samples.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
uniform_distribution.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
|
||||
let debug = false;
|
||||
// Debugging: Optional print sorted values
|
||||
if debug {
|
||||
println!("Sorted Normalized Samples: {:?}", normalized_samples);
|
||||
println!("Sorted Uniform Distribution: {:?}", uniform_distribution);
|
||||
}
|
||||
|
||||
// Perform the Kolmogorov-Smirnov test
|
||||
let confidence = 0.01; // Significance level
|
||||
let confidence = 0.05; // Significance level
|
||||
let result = test_f64(&normalized_samples, &uniform_distribution, confidence);
|
||||
|
||||
|
||||
// Extract the KS statistic (D-statistic)
|
||||
let ks_statistic = result.statistic;
|
||||
|
||||
|
||||
// Debugging: Print ECDF differences and max difference (D-statistic)
|
||||
if debug {
|
||||
for (i, (&sample, &uniform)) in normalized_samples.iter().zip(&uniform_distribution).enumerate() {
|
||||
let diff = (sample - uniform).abs();
|
||||
println!(
|
||||
"Index: {}, Sample: {:.6}, Uniform: {:.6}, Difference: {:.6}",
|
||||
i, sample, uniform, diff
|
||||
);
|
||||
}
|
||||
println!("D-Statistic: {:.6}", ks_statistic);
|
||||
}
|
||||
|
||||
// Calculate the z-score
|
||||
let sample_size = normalized_samples.len() as f64;
|
||||
let z_score = ks_statistic * sample_size.sqrt();
|
||||
|
||||
// Extract the p-value
|
||||
let p_value = 1.0 - result.reject_probability;
|
||||
|
||||
|
||||
// Calculate the p-value
|
||||
let p_value = 2.0 * (1.0 - statrs::distribution::Normal::new(0.0, 1.0).unwrap().cdf(z_score.abs()));
|
||||
|
||||
(ks_statistic, z_score, p_value)
|
||||
}
|
||||
|
||||
/// Generates a uniform distribution of the same length as the input data.
|
||||
fn generate_uniform_distribution(len: usize, seed: u32) -> Vec<f64> {
|
||||
let mut rng = Well19937c::new(seed);
|
||||
(0..len).map(|_| rng.next_f64()).collect()
|
||||
}
|
||||
|
||||
fn get_timestamp_seed() -> u32 {
|
||||
let duration = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards");
|
||||
// Use seconds or nanoseconds as the seed
|
||||
(duration.as_secs() as u32) ^ (duration.subsec_nanos())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,67 +1,82 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
/// Pi randomness test
|
||||
/// Uses a Monte Carlo simulation to estimate randomness by calculating the approximation of Pi.
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
||||
impl Onod {
|
||||
|
||||
/// Pi randomness test
|
||||
/// Uses a Monte Carlo simulation to estimate randomness by calculating the approximation of Pi.
|
||||
/// This implementation of the Pi randomness test closely follows the logic of the original Java implementation.
|
||||
/// However, minor differences in the results may arise due to the following reasons:
|
||||
///
|
||||
/// 1. **Floating-Point Precision**:
|
||||
/// Rust and Java both use 64-bit floating-point numbers (`double` in Java, `f64` in Rust), but slight differences
|
||||
/// in their implementations (e.g., rounding modes, intermediate representations) can lead to small deviations.
|
||||
///
|
||||
/// 2. **Math Libraries**:
|
||||
/// Java uses Apache Commons Math for statistical computations, which may implement certain calculations
|
||||
/// (e.g., Z-scores and normal distribution CDFs) differently compared to the `statrs` crate used in Rust.
|
||||
///
|
||||
/// 3. **Bit Accuracy**:
|
||||
/// The Java implementation notes the significance of bit accuracy in floating-point computations,
|
||||
/// as defined in the IEEE 754 standard. Differences in handling edge cases (e.g., subnormal values,
|
||||
/// precision limits) could lead to slight variations.
|
||||
///
|
||||
/// These differences are generally negligible for practical purposes and do not affect the overall functionality or
|
||||
/// statistical significance of the test.
|
||||
/// Pi randomness test using nalgebra for vectorized operations.
|
||||
pub fn pi(samples: &[u8]) -> (f64, f64, f64) {
|
||||
if samples.len() < 4 {
|
||||
return (-1.0, 0.0, 1.0); // Not enough data
|
||||
}
|
||||
|
||||
if samples.is_empty() {
|
||||
let normalized_samples: Vec<f32> = get_floats(samples);
|
||||
|
||||
if normalized_samples.is_empty() {
|
||||
return (-1.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
// Normalize samples to [0.0, 1.0)
|
||||
let normalized_samples: Vec<f64> = samples.iter().map(|&x| x as f64 / 255.0).collect();
|
||||
|
||||
// Initialize variables for summary statistics
|
||||
let mut sum_y = 0.0;
|
||||
let mut count = 0.0;
|
||||
let count = normalized_samples.len() as f64;
|
||||
|
||||
// Compute y-values (sqrt(1 - x^2)) and update summary statistics
|
||||
for &x in &normalized_samples {
|
||||
let y = (1.0 - x * x).sqrt();
|
||||
sum_y += y;
|
||||
count += 1.0;
|
||||
let y = (1.0 - x.powi(2)).sqrt();
|
||||
sum_y += y as f64;
|
||||
}
|
||||
|
||||
// Calculate mean of y-values
|
||||
let mean_y = sum_y / count;
|
||||
|
||||
// Calculate the test statistic
|
||||
let test_statistic = 4.0 * mean_y;
|
||||
|
||||
// Calculate variance and standard deviation
|
||||
let variance = (16.0 / count) * ((2.0 / 3.0) - (std::f64::consts::PI / 4.0).powi(2));
|
||||
let variance = compute_variance(count);
|
||||
let std_dev = variance.sqrt();
|
||||
|
||||
// Calculate Z-score
|
||||
let z_score = (test_statistic - std::f64::consts::PI) / std_dev;
|
||||
|
||||
// Use normal distribution to calculate p-value
|
||||
let normal_dist = Normal::new(0.0, 1.0).expect("Failed to create Normal distribution");
|
||||
let p_value = 2.0 * (1.0 - normal_dist.cdf(z_score.abs()));
|
||||
|
||||
(test_statistic, z_score, p_value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn get_floats(samples: &[u8]) -> Vec<f32> {
|
||||
let mut floats = Vec::new();
|
||||
for chunk in samples.chunks_exact(4) {
|
||||
let int_val = i32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
|
||||
let unsigned_val = (int_val as u32) >> 1; // Discard sign bit
|
||||
let normalized = unsigned_val as f32 / i32::MAX as f32;
|
||||
floats.push(normalized);
|
||||
}
|
||||
floats
|
||||
}
|
||||
|
||||
fn compute_variance(n: f64) -> f64 {
|
||||
let term = (2.0 / 3.0) - (std::f64::consts::PI / 4.0).powi(2);
|
||||
(16.0 / n) * term
|
||||
}
|
||||
|
|
@ -1,51 +1,91 @@
|
|||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
/*
|
||||
* Blatantly copied from David Sexton's battery.
|
||||
*
|
||||
* An algorithm is used to predict the value of each byte of the sequence from
|
||||
* the beginning of the sequence to the end. In a random sequence the
|
||||
* probability of success of any such algorithm is 1/256. The number of successes
|
||||
* is counted. A chi-squared statistic is calculated. The degrees-of-freedom is 1.
|
||||
* The algorithm is as follows: the next byte is predicted to be equal to all the
|
||||
* previous bytes bitwise XORed together.
|
||||
*/
|
||||
|
||||
use crate::Onod;
|
||||
|
||||
impl Onod {
|
||||
|
||||
/// Prediction randomness test
|
||||
/// Evaluates the predictability of the next bit based on current data and returns a p-value.
|
||||
/// Evaluates the predictability of the next byte based on XORing the previous bytes
|
||||
/// and returns the total predictions, z-score, and p-value.
|
||||
pub fn prediction(samples: &[u8]) -> (f64, f64, f64) {
|
||||
|
||||
if samples.is_empty() {
|
||||
return (-1.0, 0.0, 1.0);
|
||||
if samples.len() < 3 {
|
||||
return (-1.0, 0.0, 1.0); // Not enough data for meaningful calculation
|
||||
}
|
||||
|
||||
let mut correct_predictions = 0;
|
||||
let mut total_predictions = 0;
|
||||
|
||||
for window in samples.windows(2) {
|
||||
if let [current, next] = window {
|
||||
let predicted = if current & 0x01 == 0 { 0 } else { 1 }; // Predict next bit based on LSB
|
||||
let actual = next & 0x01; // Check LSB of the next byte
|
||||
let mut prediction = samples[0]; // Start with the first byte
|
||||
for i in 2..samples.len() {
|
||||
prediction ^= samples[i - 1]; // XOR all preceding bytes
|
||||
|
||||
if predicted == actual {
|
||||
correct_predictions += 1;
|
||||
}
|
||||
|
||||
total_predictions += 1;
|
||||
if prediction == samples[i] {
|
||||
correct_predictions += 1;
|
||||
}
|
||||
total_predictions += 1;
|
||||
}
|
||||
|
||||
if total_predictions == 0 {
|
||||
return (-1.0, 0.0, 1.0); // No predictions possible
|
||||
}
|
||||
// Calculate expected and observed frequencies
|
||||
let expected = vec![
|
||||
(1.0 / 256.0) * samples.len() as f64, // Probability of correct prediction
|
||||
(255.0 / 256.0) * samples.len() as f64, // Probability of incorrect prediction
|
||||
];
|
||||
let observed = vec![
|
||||
correct_predictions as f64, // Actual correct predictions
|
||||
(samples.len() - correct_predictions) as f64, // Actual incorrect predictions
|
||||
];
|
||||
|
||||
// Calculate observed proportion of correct predictions
|
||||
let observed_proportion = correct_predictions as f64 / total_predictions as f64;
|
||||
// Calculate chi-squared statistic
|
||||
let chi_squared_stat: f64 = observed
|
||||
.iter()
|
||||
.zip(expected.iter())
|
||||
.map(|(o, e)| (o - e).powi(2) / e)
|
||||
.sum();
|
||||
|
||||
// Expected proportion for random data
|
||||
let expected_proportion = 0.5;
|
||||
let std_dev = (0.5 * 0.5 / total_predictions as f64).sqrt(); // Standard deviation for a binomial distribution
|
||||
// Use Chi-Squared distribution to calculate p-value
|
||||
let chi_squared_dist = ChiSquared::new(1.0).unwrap(); // Degrees of freedom = 1
|
||||
let p_value = 1.0 - chi_squared_dist.cdf(chi_squared_stat);
|
||||
|
||||
// Calculate the z-score
|
||||
let z_score = (observed_proportion - expected_proportion) / std_dev;
|
||||
// Calculate z-score (optional, for diagnostics)
|
||||
let mean = 1.0; // Mean of chi-squared distribution
|
||||
let std_dev = (2.0 as f64).sqrt(); // Standard deviation of chi-squared distribution
|
||||
let z_score = (chi_squared_stat - mean) / std_dev;
|
||||
|
||||
// Use normal distribution to calculate p-value
|
||||
let normal_dist = Normal::new(0.0, 1.0).expect("Failed to create Normal distribution");
|
||||
let p_value = 2.0 * (1.0 - normal_dist.cdf(z_score.abs()));
|
||||
|
||||
(observed_proportion, z_score, p_value)
|
||||
(total_predictions as f64, z_score, p_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
|
|||
|
|
@ -1,101 +1,127 @@
|
|||
use statrs::distribution::{ChiSquared, ContinuousCDF};
|
||||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use crate::Onod;
|
||||
use crate::chisquaretest::chi_square_test;
|
||||
|
||||
impl Onod {
|
||||
|
||||
/// Shells randomness test
|
||||
/// Evaluates the uniformity of distances between identical byte values and returns a p-value.
|
||||
/// Evaluates the uniformity of distances in a 3D sphere and returns the chi-squared statistic, z-score, and p-value.
|
||||
pub fn shells(input: &[u8]) -> (f64, f64, f64) {
|
||||
|
||||
|
||||
// Define shell radii (precomputed to ensure equal volumes)
|
||||
const SHELL_RADII: [f64; 35] = [
|
||||
1., 0.990384019787941, 0.980577593308067, 0.970571001281035, 0.960353705642329,
|
||||
0.949914251592996, 0.939240154232372, 0.928317766722556, 0.91713212619864,
|
||||
0.905666772691187, 0.893903535096568, 0.881822276616739, 0.869400589952457,
|
||||
0.856613429672063, 0.843432665301749, 0.829826533366243, 0.815758959214771,
|
||||
0.801188709029197, 0.786068317431936, 0.770342714221672, 0.753947441129154,
|
||||
0.736806299728077, 0.718828193851318, 0.699902804775202, 0.67989452969576,
|
||||
0.65863375600835, 0.635903899768996, 0.61142141746576, 0.584803547642573,
|
||||
0.555513224287824, 0.52275795857471, 0.485285500640517, 0.440911138308369,
|
||||
0.385171357110836, 0.30571070873288
|
||||
1., 0.990384019787941, 0.980577593308067,
|
||||
0.970571001281035, 0.960353705642329, 0.949914251592996,
|
||||
0.939240154232372, 0.928317766722556, 0.91713212619864,
|
||||
0.905666772691187, 0.893903535096568, 0.881822276616739,
|
||||
0.869400589952457, 0.856613429672063, 0.843432665301749,
|
||||
0.829826533366243, 0.815758959214771, 0.801188709029197,
|
||||
0.786068317431936, 0.770342714221672, 0.753947441129154,
|
||||
0.736806299728077, 0.718828193851318, 0.699902804775202,
|
||||
0.67989452969576, 0.65863375600835, 0.635903899768996,
|
||||
0.61142141746576, 0.584803547642573, 0.555513224287824,
|
||||
0.52275795857471, 0.485285500640517, 0.440911138308369,
|
||||
0.385171357110836, 0.30571070873288,
|
||||
];
|
||||
|
||||
let samples = convert_to_3d_points(input);
|
||||
|
||||
if samples.len() < 25000 {
|
||||
// eprintln!("---------------------------------------------------------------");
|
||||
// eprintln!("ERROR: Shells test requires at least 25,000 points for statistical validity. Skipping.");
|
||||
// eprintln!("---------------------------------------------------------------");
|
||||
return (-1.0, 0.0, 1.0); // Skip the test for small datasets
|
||||
}
|
||||
|
||||
let samples = convert_to_3d_points(&input);
|
||||
let sphere_radius = SHELL_RADII[0];
|
||||
let no_shells = SHELL_RADII.len();
|
||||
|
||||
// Calculate sphere and cube volume proportions
|
||||
let num_shells = SHELL_RADII.len();
|
||||
|
||||
// Calculate sphere and cube proportions
|
||||
let cube_side = 2.0 * sphere_radius;
|
||||
let cube_volume = cube_side.powi(3);
|
||||
let sphere_volume = (4.0 / 3.0) * std::f64::consts::PI * sphere_radius.powi(3);
|
||||
let sphere_proportion = sphere_volume / cube_volume; // Theoretical value: π/6
|
||||
|
||||
let no_points = samples.len() as f64;
|
||||
let no_points_per_shell = sphere_proportion * no_points / no_shells as f64;
|
||||
|
||||
let mut observed = vec![0u64; no_shells];
|
||||
let expected: Vec<f64> = vec![no_points_per_shell; no_shells];
|
||||
|
||||
let sphere_proportion = sphere_volume / cube_volume;
|
||||
|
||||
let num_points = samples.len() as f64;
|
||||
let num_points_per_shell = sphere_proportion * num_points / num_shells as f64;
|
||||
|
||||
// Initialize observed and expected frequencies
|
||||
let mut observed = vec![0u64; num_shells];
|
||||
let expected: Vec<f64> = vec![num_points_per_shell; num_shells];
|
||||
|
||||
// Assign points to shells
|
||||
for (x, y, z) in samples {
|
||||
// Compute radius from origin
|
||||
let radius = (x.powi(2) + y.powi(2) + z.powi(2)).sqrt();
|
||||
|
||||
// Ignore points outside the sphere
|
||||
|
||||
// Skip points outside the sphere
|
||||
if radius > sphere_radius {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Assign to the correct shell
|
||||
for j in 1..SHELL_RADII.len() {
|
||||
|
||||
for j in 1..num_shells {
|
||||
if radius > SHELL_RADII[j] {
|
||||
observed[j - 1] += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if radius < SHELL_RADII[no_shells - 1] {
|
||||
observed[no_shells - 1] += 1;
|
||||
|
||||
// Assign to the last shell if radius <= SHELL_RADII[num_shells - 1]
|
||||
if radius < SHELL_RADII[num_shells - 1] {
|
||||
observed[num_shells - 1] += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Perform Chi-Square Test
|
||||
let chi_squared_stat: f64 = observed.iter()
|
||||
|
||||
// Calculate chi-squared statistic
|
||||
let chi_squared_stat: f64 = observed
|
||||
.iter()
|
||||
.zip(expected.iter())
|
||||
.map(|(&o, &e)| (o as f64 - e).powi(2) / e)
|
||||
.sum();
|
||||
|
||||
let degrees_of_freedom = no_shells as f64 - 1.0;
|
||||
let chi_squared_dist = ChiSquared::new(degrees_of_freedom).expect("Failed to create ChiSquared distribution");
|
||||
let p_value = 1.0 - chi_squared_dist.cdf(chi_squared_stat);
|
||||
|
||||
// Z-score calculation (standardization of the chi-squared statistic)
|
||||
let mean = degrees_of_freedom; // Mean of the chi-squared distribution
|
||||
let std_dev = (2.0 * degrees_of_freedom).sqrt(); // Standard deviation of the chi-squared distribution
|
||||
|
||||
// Perform chi-squared test
|
||||
let degrees_of_freedom = num_shells as f64 - 1.0;
|
||||
let p_value = chi_square_test(&observed, &expected);
|
||||
|
||||
// Calculate z-score
|
||||
let mean = degrees_of_freedom;
|
||||
let std_dev = (2.0 * degrees_of_freedom).sqrt();
|
||||
let z_score = (chi_squared_stat - mean) / std_dev;
|
||||
|
||||
// Return the results
|
||||
(chi_squared_stat, z_score, p_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_3d_points(data: &[u8]) -> Vec<(f64, f64, f64)> {
|
||||
let mut points = Vec::new();
|
||||
|
||||
for chunk in data.chunks(3) {
|
||||
if chunk.len() == 3 {
|
||||
// Normalize the bytes to [0.0, 1.0) range
|
||||
let x = chunk[0] as f64 / 255.0;
|
||||
let y = chunk[1] as f64 / 255.0;
|
||||
let z = chunk[2] as f64 / 255.0;
|
||||
points.push((x, y, z));
|
||||
for chunk in data.chunks(12) {
|
||||
if chunk.len() == 12 {
|
||||
let x = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) >> 1;
|
||||
let y = u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]) >> 1;
|
||||
let z = u32::from_be_bytes([chunk[8], chunk[9], chunk[10], chunk[11]]) >> 1;
|
||||
|
||||
points.push((
|
||||
x as f64 / (i32::MAX as f64),
|
||||
y as f64 / (i32::MAX as f64),
|
||||
z as f64 / (i32::MAX as f64),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,28 @@
|
|||
// This file is a Rust port of the original Java implementation by Paul Uszak.
|
||||
// Original Java code:
|
||||
// http://www.reallyreallyrandom.com/gitbucketlabhub/
|
||||
//
|
||||
// Copyright (c) 2023 Paul Uszak. Port (C) 2025 by Tobias Raayoni Last (@randogoth)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
|
||||
use statrs::distribution::{Normal, ContinuousCDF};
|
||||
|
||||
use crate::Onod;
|
||||
|
|
@ -6,7 +31,7 @@ impl Onod {
|
|||
|
||||
/// UnCorrelation randomness test
|
||||
/// Computes the Pearson correlation between the sequence and its shifted version, returning a p-value.
|
||||
pub fn un_correlation(input: &[u8]) -> (f64, f64, f64) {
|
||||
pub fn uncorrelation(input: &[u8]) -> (f64, f64, f64) {
|
||||
|
||||
let samples = input.iter().map(|&x| x as i32).collect::<Vec<i32>>();
|
||||
|
||||
|
|
|
|||
87
src/well19937c.rs
Normal file
87
src/well19937c.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// MIT License
|
||||
|
||||
// Copyright (c) 2025 Tobias Raayoni Last (@randogoth)
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// ---
|
||||
|
||||
|
||||
pub struct Well19937c {
|
||||
state: [u32; 624],
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl Well19937c {
|
||||
/// Creates a new instance of Well19937c with a given seed.
|
||||
pub fn new(seed: u32) -> Self {
|
||||
let mut state = [0u32; 624];
|
||||
state[0] = seed;
|
||||
|
||||
for i in 1..624 {
|
||||
state[i] = 1812433253u32
|
||||
.wrapping_mul(state[i - 1] ^ (state[i - 1] >> 30))
|
||||
.wrapping_add(i as u32);
|
||||
}
|
||||
|
||||
Well19937c { state, index: 0 }
|
||||
}
|
||||
|
||||
/// Updates the internal state.
|
||||
fn twist(&mut self) {
|
||||
const M: usize = 397;
|
||||
const MATRIX_A: u32 = 0x9908b0df; // Constant matrix A
|
||||
const UPPER_MASK: u32 = 0x80000000; // Most significant w-r bits
|
||||
const LOWER_MASK: u32 = 0x7fffffff; // Least significant r bits
|
||||
|
||||
for i in 0..624 {
|
||||
let x = (self.state[i] & UPPER_MASK) + (self.state[(i + 1) % 624] & LOWER_MASK);
|
||||
let mut x_a = x >> 1;
|
||||
|
||||
if x % 2 != 0 {
|
||||
x_a ^= MATRIX_A;
|
||||
}
|
||||
|
||||
self.state[i] = self.state[(i + M) % 624] ^ x_a;
|
||||
}
|
||||
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
/// Generates the next random number in the sequence.
|
||||
pub fn next_u32(&mut self) -> u32 {
|
||||
if self.index == 0 {
|
||||
self.twist();
|
||||
}
|
||||
|
||||
let mut y = self.state[self.index];
|
||||
self.index = (self.index + 1) % 624;
|
||||
|
||||
// Matsumoto-Kurita tempering
|
||||
y ^= (y << 7) & 0xe46e1700;
|
||||
y ^= (y << 15) & 0x9b868000;
|
||||
|
||||
y
|
||||
}
|
||||
|
||||
/// Generates the next random `f64` in [0, 1).
|
||||
pub fn next_f64(&mut self) -> f64 {
|
||||
self.next_u32() as f64 / u32::MAX as f64
|
||||
}
|
||||
}
|
||||
BIN
test/test1.bin
Normal file
BIN
test/test1.bin
Normal file
Binary file not shown.
BIN
test/test2.bin
Normal file
BIN
test/test2.bin
Normal file
Binary file not shown.
BIN
test/test3.bin
Normal file
BIN
test/test3.bin
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue