"""DATA FRAUD detection — statistical fingerprints of fabricated numbers. Text plagiarism tools ignore this entirely. BENFORD'S LAW : in genuine natural datasets the leading digit follows P(d) = log10(1 + 1/d) (1 appears ~30%, 9 ~4.6%). Fabricated/manually-invented numbers are too uniform -> chi-square + MAD divergence from Benford flags them. GRIM TEST : a reported mean of integer-valued items over N items must be one of only N possible fractions. A mean that is arithmetically impossible for its sample size is a red flag for fabricated or mistyped summary statistics. Pure numpy, no dependencies. Returns evidence, never a hard verdict — these are leads for a human reviewer (consistent with the recall-first philosophy). """ import re import numpy as np BENFORD = np.array([np.log10(1 + 1 / d) for d in range(1, 10)]) # numbers like 12, 3.4, 1,234.5, 67% — capture the numeric token _NUM = re.compile(r"(? 15.51 and mad >= 0.012), } def grim_test(text): """Check reported means against GRIM-possible values for their N.""" means = [(m.group(1), m.start()) for m in _MEAN_CTX.finditer(text)] ns = [(int(m.group(1)), m.start()) for m in _N_CTX.finditer(text)] if not means or not ns: return None flagged = [] for mean_str, mpos in means: decimals = len(mean_str.split(".")[1]) if decimals == 0 or decimals > 3: continue mean_val = float(mean_str) # nearest reported N (same sentence-ish window) n = min(ns, key=lambda x: abs(x[1] - mpos))[0] if not 1 <= n <= 10000: continue # GRIM: mean*N should be (close to) an integer prod = mean_val * n frac = abs(prod - round(prod)) tol = 0.5 / n + 1e-9 # rounding tolerance for given N if frac > tol and min(frac, 1 - frac) > tol: flagged.append({"reported_mean": mean_val, "n": n, "implied_total": round(prod, 3), "off_by": round(min(frac, 1 - frac), 4)}) if not flagged: return None return {"inconsistent_means": flagged[:10], "count": len(flagged)} def detect_fraud(text): """Returns {benford, grim, flags} or None when there's too little data.""" benford = benford_analysis(text) grim = grim_test(text) if benford is None and grim is None: return None flags = [] if benford and benford["suspicious"]: flags.append(f"leading-digit distribution diverges from Benford's law " f"(MAD {benford['mad']}, chi2 {benford['chi_square']}) — " f"numbers may be fabricated or hand-invented") if grim: flags.append(f"{grim['count']} reported mean(s) arithmetically " f"impossible for the stated sample size (GRIM test)") return {"benford": benford, "grim": grim, "flags": flags, "suspicious": bool(flags)}