metrale_bench/benchmarks/kat_equality/
report.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: How an equality run is presented: functions of the
4//! [`super::compare`] results, with no I/O.
5//!
6//! Owner: bench, kat_equality.
7//! Invariants: none beyond the types.
8
9use std::collections::BTreeMap;
10
11use super::compare::{OrderRun, SampleVerdict, Score, verdict_for};
12use crate::result::{Cell, CellStyle, Column, ResultTable, Stat};
13
14pub(super) fn summary(s: &Score) -> Vec<Stat> {
15    vec![
16        Stat::new("orders", s.orders.to_string(), ""),
17        Stat::new("samples", s.samples.to_string(), ""),
18        Stat::new("identical", s.equal.to_string(), ""),
19        Stat::new("DIVERGED", s.diverged.to_string(), ""),
20    ]
21}
22
23/// 2026-09-26: One row per sample that is not equal, up to 40 and then a row
24/// counting the rest; when everything agreed, a single row saying so. Equal
25/// samples get no row, so they cannot bury the ones that differ.
26pub(super) fn table(s: &Score, runs: &[OrderRun]) -> ResultTable {
27    let mut t = ResultTable::new(
28        "SAMPLES THAT CHANGED WITH THE ORDER",
29        vec![
30            Column::left("Sample", 34),
31            Column::left("Result", 12),
32            Column::left("Detail", 46),
33        ],
34    );
35    let Some(reference) = runs.first() else {
36        return t;
37    };
38    let mut shown = 0usize;
39    for obs in &reference.observations {
40        let v = verdict_for(&obs.sample_id, reference, &runs[1..]);
41        let (what, style, detail) = match v {
42            SampleVerdict::Equal => continue,
43            SampleVerdict::Diverged {
44                other_order,
45                common_prefix,
46            } => (
47                "DIVERGED",
48                CellStyle::Bad,
49                format!("differs from `{other_order}` after {common_prefix} identical bytes"),
50            ),
51            SampleVerdict::Unmeasured(reason) => ("unmeasured", CellStyle::Bad, reason),
52        };
53        t.push(vec![
54            Cell::new(obs.sample_id.clone()),
55            Cell::styled(what.to_string(), style),
56            Cell::new(detail),
57        ]);
58        shown += 1;
59        if shown >= 40 {
60            let left = s.diverged + s.unmeasured - shown;
61            if left > 0 {
62                t.push(vec![
63                    Cell::new(format!("… {left} more")),
64                    Cell::new(String::new()),
65                    Cell::new("see the per-sample metrics".to_string()),
66                ]);
67            }
68            break;
69        }
70    }
71    if shown == 0 {
72        t.push(vec![
73            Cell::styled("all samples".to_string(), CellStyle::Good),
74            Cell::styled("identical".to_string(), CellStyle::Good),
75            Cell::new(format!("byte-for-byte across {} request orders", s.orders)),
76        ]);
77    }
78    t
79}
80
81/// 2026-09-26: Raw gate numbers. Every class is a key even at zero: a missing
82/// key and a zero must stay distinguishable to whatever compares records.
83pub(super) fn metrics(s: &Score) -> BTreeMap<String, f64> {
84    [
85        ("orders", s.orders),
86        ("samples", s.samples),
87        ("identical", s.equal),
88        ("diverged", s.diverged),
89        ("unmeasured", s.unmeasured),
90        // 2026-09-26: The vacuity count, which a BENCH.toml
91        // `[benchmarks.metrics.empty_replies]` bound can refuse: an empty reply
92        // agrees perfectly and has measured nothing.
93        ("empty_replies", s.empty_replies),
94    ]
95    .into_iter()
96    .map(|(k, v)| (k.to_string(), v as f64))
97    .collect()
98}
99
100#[cfg(test)]
101#[path = "report_tests.rs"]
102mod report_tests;