minerva 0.2.0

Causal ordering for distributed systems
//! The measurements report: deterministic counts for the go/no-go questions.
//!
//! Every number here is a deterministic count (dot-counts, byte lengths from the
//! shipped `to_bytes`, structural sizes), never a wall-clock reading, so the
//! table is reproducible. The crate is `#![no_std]`, so the table is printed
//! only under `--features std` (guarded `std::println!`); under default features
//! the same numbers are asserted into named variables, and the slice's returned
//! report records them. The single `#[test] fn measurements_report` assembles a
//! realistic three-replica session and reads every quantity off it.

use self::probes::assert_c8_index_costs;
use self::session::run_session;

mod output;
mod probes;
mod readings;
mod scale;
mod session;

// Ignored under Miri: a deliberate-scale measurement workload (6,000
// keystrokes with periodic whole-state gossip), pure safe code whose paths
// the small example tests cover; interpreting it buys minutes, not coverage
// (the cfg(miri) volume discipline, S86; the S181 scale probe is gated the
// same way).
#[cfg_attr(miri, ignore)]
#[test]
fn measurements_report() {
    // Scale: the slice names a 10,000-char session but requires the test stay
    // debug-fast (< ~30s). Periodic whole-state gossip is O(sweeps x frame) and
    // the growing skeleton makes the whole session slightly super-linear, so 10k
    // keystrokes overruns the budget in a debug build. We measure at 6,000 typed
    // / 1,800 deleted, which runs in ~10s, and note the scaling: the per-dot and
    // per-delta shapes (the go/no-go numbers) are scale-invariant, and the
    // context frame stays tiny at every scale (contexts are gap-free floors, so
    // the frame is a per-station floor pair, not per-dot); only the absolute
    // skeleton size grows linearly with the char count (at 10k it is ~10k).
    let typed_target = 6_000usize;
    let delete_target = 1_800usize;
    let r = run_session(typed_target, delete_target);

    // order() per keystroke: one call, whose WALK visits every woven element
    // (O(n), unchanged by S116; emitting the whole order is inherently O(n)).
    // What S116 (C8) removed is the per-call child-index RECONSTRUCTION (see
    // `assert_c8_index_costs`): the pre-S116 order() regrouped the whole
    // skeleton every call (quadratic over a session); the maintained index
    // moves that to an O(log bucket) incremental insert per weave.
    let order_calls_per_keystroke = 1usize;
    let order_node_visits = r.skeleton_len; // one full walk per call (unchanged)

    // Assert the numbers so they are recorded even under no_std (no println).
    assert!(
        r.visible_len <= r.skeleton_len,
        "visible never exceeds skeleton"
    );
    assert!(
        r.skeleton_len >= r.typed - r.deleted,
        "skeleton retains every live element"
    );
    assert_eq!(
        r.per_keystroke_dots, 1,
        "a single keystroke ships exactly one store dot"
    );
    // Contract C2, the floored-delta framing win: the whole-state context frame
    // drops from 37 bytes (empty-seeded, the `delta_for` novelty framing, where
    // the lone dot above the peer's floor parks as a 16-byte exception run) to
    // 21 bytes (`delta_for_since`, floored at the witnessed peer cut, the
    // contiguous prefix folded into one floor). The 16-byte drop is exactly the
    // reclaimed run. `delta_for_since` is a whole-state read: it buys the
    // context frame, not a store trim (see its rustdoc; a floored context must
    // carry the whole survivor store to stay sound), so this asserts the frame,
    // the quantity the contract names.
    assert_eq!(
        r.per_keystroke_context_bytes, 37,
        "the empty-seeded per-keystroke context frame parks the novel dot as a run"
    );
    assert_eq!(
        r.per_keystroke_floored_context_bytes, 21,
        "the floored per-keystroke context frame folds the novel dot into the floor"
    );
    assert!(
        r.batch_dots >= 50,
        "a 50-keystroke batch ships at least 50 store dots"
    );
    assert!(
        r.per_keystroke_bytes > 0 && r.batch_bytes > 0,
        "both deltas carry positive measured cost",
    );
    // Steady-state whole-state anti-entropy keeps the reorder window empty.
    assert!(
        r.exceptions.iter().all(|&e| e == 0),
        "whole-state deltas leave no exceptions"
    );
    assert!(
        r.holes.iter().all(|&h| h == 0),
        "whole-state deltas leave no holes"
    );
    // The reads still track a genuine out-of-order receipt: dots 5 and 6 park
    // above the gap 2..=4, so 2 exceptions (in one run) and 3 holes.
    assert_eq!(r.demo_exceptions, 2, "two parked dots above the gap");
    assert_eq!(
        r.demo_holes, 3,
        "three missing dots (2, 3, 4) below the high water"
    );

    // C8 (S116): the per-keystroke child-index work dropped (asserted in a
    // helper to keep this report function within the line budget).
    assert_c8_index_costs(&r);

    // The per-dot amortized byte cost of the two framings: the go/no-go for a
    // batched delta layer. If the per-keystroke delta's bytes-per-dot dwarfs the
    // batch's, per-char deltas waste the frame overhead and a batch layer pays.
    let per_keystroke_bytes_per_dot = r.per_keystroke_bytes / r.per_keystroke_dots.max(1);
    let batch_bytes_per_dot = r.batch_bytes / r.batch_dots.max(1);

    // Print the table only under std (the crate is no_std; println needs std).
    #[cfg(feature = "std")]
    output::print_report(
        &r,
        order_calls_per_keystroke,
        order_node_visits,
        per_keystroke_bytes_per_dot,
        batch_bytes_per_dot,
    );

    // Silence unused warnings under no_std (the values are the deliverable, read
    // off in the returned report).
    output::touch_report_values(
        &r,
        order_calls_per_keystroke,
        order_node_visits,
        per_keystroke_bytes_per_dot,
        batch_bytes_per_dot,
    );
}