bitbelay_cli/commands/
correlation.rs1use std::hash::BuildHasher;
4use std::num::NonZeroUsize;
5
6use anyhow::anyhow;
7use anyhow::bail;
8use anyhow::Context;
9use bitbelay_providers::Provider;
10use bitbelay_report::Config;
11use bitbelay_suites::r#trait::Suite;
12use bitbelay_tests::correlation::bitwise;
13use clap::ArgAction;
14use colored::Colorize as _;
15
16const CORRELATION_MATRIX_STOP_ONE: f64 = 0.05;
18
19const CORRELATION_MATRIX_STOP_TWO: f64 = 0.25;
21
22const CORRELATION_MATRIX_STOP_THREE: f64 = 0.50;
24
25#[derive(clap::Args, Debug)]
27pub struct Args {
28 #[arg(short, long, default_value_t = 1 << 16)]
30 iterations: usize,
31
32 #[arg(long, default_value_t = 0.05)]
35 threshold: f64,
36
37 #[clap(long, action = ArgAction::SetTrue)]
39 correlation_matrix: bool,
40
41 #[clap(long, default_value_t = 2)]
43 correlation_matrix_cell_width: usize,
44}
45
46pub fn main<H: BuildHasher, const N: usize>(
48 args: Args,
49 build_hasher: H,
50 provider: Box<dyn Provider>,
51) -> anyhow::Result<()> {
52 tracing::info!("Starting correlation test suite.");
53
54 let iterations = NonZeroUsize::try_from(args.iterations)
55 .map_err(|_| anyhow!("--iterations per experiment must be non-zero!"))?;
56
57 let threshold = if (0.0..=1.0).contains(&args.threshold) {
58 args.threshold
59 } else {
60 bail!("--threshold must be between 0.0 and 1.0!");
61 };
62
63 let correlation_matrix_cell_width = NonZeroUsize::try_from(args.correlation_matrix_cell_width)
64 .map_err(|_| anyhow!("--correlation-matrix-cell-width must be non-zero!"))
65 .and_then(|cell_size| {
66 if cell_size.get() >= 2 {
67 Ok(cell_size)
68 } else {
69 Err(anyhow!(
70 "--correlation-matrix-cell-width must be 2 or greater!"
71 ))
72 }
73 })?;
74
75 let mut suite = bitbelay_suites::correlation::suite::Builder::<H>::default()
76 .build_hasher(&build_hasher)?
77 .try_build::<N>()?;
78
79 suite
80 .run_bitwise_test(provider, iterations, threshold)
81 .with_context(|| "running bitwise test")?;
82
83 suite
84 .report()
85 .write_to(&mut std::io::stderr(), &Config::default())?;
86
87 if args.correlation_matrix {
88 let mut bitwise_tests = suite
90 .tests()
91 .iter()
92 .filter_map(|test| test.as_bitwise_test())
93 .collect::<Vec<_>>();
94
95 match bitwise_tests.len() {
96 0 => bail!(
97 "there should be at least one bitwise test! This is an issue and should be looked \
98 at by the developers (please report this issue!)"
99 ),
100 1 => print_correlation_table::<N>(
101 correlation_matrix_cell_width.get(),
102 bitwise_tests.pop().unwrap().results().unwrap(),
108 ),
109 v => bail!(
110 "there are {} bitwise tests, and it's not clear what correlation matrix to print \
111 (please report this issue!)",
112 v
113 ),
114 }
115 }
116 Ok(())
117}
118
119fn print_correlation_table<const N: usize>(width: usize, correlations: bitwise::Results) {
121 if width == 0 {
122 panic!("width of correlation table entries cannot be 0!");
123 }
124
125 println!("{}", "Legend".bold().underline());
127
128 println!(
129 "{} => a correlation value in the range [0.0, {}]",
130 " ".on_black(),
131 CORRELATION_MATRIX_STOP_ONE
132 );
133 println!(
134 "{} => a correlation value in the range ({}, {}]",
135 " ".on_red(),
136 CORRELATION_MATRIX_STOP_ONE,
137 CORRELATION_MATRIX_STOP_TWO
138 );
139 println!(
140 "{} => a correlation value in the range ({}, {}]",
141 " ".on_yellow(),
142 CORRELATION_MATRIX_STOP_TWO,
143 CORRELATION_MATRIX_STOP_THREE
144 );
145
146 println!(
147 "{} => a correlation value in the range [{}, 1.0]",
148 " ".on_green(),
149 CORRELATION_MATRIX_STOP_THREE,
150 );
151
152 println!();
153
154 for i in 1..=N {
156 if i < 10 || i % 10 == 0 {
157 print!("{:^width$}", i.to_string().bold().underline())
158 } else {
159 print!("{:^width$}", i % 10, width = width);
160 };
161 }
162
163 println!();
164
165 for i in 0..N {
167 for j in 0..N {
168 let value = *correlations.get(&(i, j)).unwrap();
172 let cell = " ".repeat(width);
173
174 if let Some(value) = value {
175 if value > CORRELATION_MATRIX_STOP_THREE {
177 print!("{}", cell.on_green());
178 } else if value > CORRELATION_MATRIX_STOP_TWO {
179 print!("{}", cell.on_yellow());
180 } else if value > CORRELATION_MATRIX_STOP_ONE {
181 print!("{}", cell.on_red());
182 } else {
183 print!("{}", cell.on_black());
184 };
185 } else {
186 print!("{}", cell.on_bright_purple());
188 }
189 }
190
191 println!();
192 }
193}