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

One million doubles of very different magnitudes, summed in five random
orders with a plain left-to-right loop (Python 3.12's built-in sum()
compensates, so it would hide the effect), then with math.fsum, which is
exactly rounded.
"""
import argparse, json, math, random

p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument("--n", type=int, default=1_000_000, help="Numbers.")
p.add_argument("--orders", type=int, default=5, help="Random orders to sum in.")
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)

xs = [random.uniform(-1, 1) * 10 ** random.randint(0, 12) for _ in range(args.n)]
def plain(v):
    s = 0.0
    for x in v: s += x                                                  # what a simple engine or a reducer does
    return s
rows = []
for i in range(args.orders):
    random.shuffle(xs); rows.append({"order": i + 1, "plain_sum": plain(xs), "fsum": math.fsum(xs)})
exact = math.fsum(xs)
for r in rows: r["plain_minus_exact"] = r["plain_sum"] - exact
summary = {"distinct_plain": len({r["plain_sum"] for r in rows}), "distinct_fsum": len({r["fsum"] for r in rows}),
           "exact": exact, "max_abs_error": max(abs(r["plain_minus_exact"]) for r in rows)}
for r in rows: print(f"order {r['order']}: plain loop {r['plain_sum']!r:<24} fsum {r['fsum']!r}")
print(f"{summary['distinct_plain']} different plain sums, {summary['distinct_fsum']} distinct fsum value(s); largest error {summary['max_abs_error']:.4g}")

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