# /// script
# requires-python = ">=3.10"
# ///
# Copyright (c) 2026 Aayush Sharma · SPDX-License-Identifier: MIT
# https://thedataengineering.com/lab-code/LICENSE.txt
"""R + W > N is not recency.

A toy N = 3 replicated register (the chapter's simulation option). A write
with W = 2 reaches only one replica before the others are paused, so it
fails; reads with R = 2 and a random coordinator then go back and forth.
Repeated with read repair, and with reads at ALL.
"""
import argparse, json, random

p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument("--trials", type=int, default=10_000, help="Pairs of back-to-back reads per variant.")
p.add_argument("--seed", type=int, default=0, help="Random seed.")
p.add_argument("--json", help="Also write the results to this JSON file.")
args = p.parse_args()
random.seed(args.seed)


def setup():
    # version 1 is on all three replicas; the write of version 2 reached only replica 0, then 1 and 2 were paused:
    # fewer than W = 2 acks, so the client was told the write failed. Replica 0 keeps version 2.
    return [2, 1, 1]

def read(reps, r, repair):
    chosen = random.sample(range(3), r)
    newest = max(reps[i] for i in chosen)
    if repair:
        for i in chosen: reps[i] = newest              # read repair: bring the stale replicas up to date
    return newest

rows = []
for label, r, repair in (("R = 2", 2, False), ("R = 2 with read repair", 2, True), ("R = ALL", 3, False)):
    backwards, failed_seen = 0, 0
    for _ in range(args.trials):
        reps = setup()
        a = read(reps, r, repair); b = read(reps, r, repair)
        backwards += b < a
        failed_seen += 2 in (a, b)
    rows.append({"variant": label, "backwards_pct": backwards / args.trials * 100,
                 "failed_write_seen_pct": failed_seen / args.trials * 100})
    print(f"{label:<24} second read older than the first: {backwards / args.trials:6.1%}   "
          f"the failed write seen: {failed_seen / args.trials:6.1%}")

if args.json:
    with open(args.json, "w") as f:
        json.dump({"tables": {"variants": rows}}, f)
