{"item":"mixture-b","batch":"e02-native-bridge","status":"FAILED_PORT: gas (OutOfGas on 27.0% of afterSwap calls, max ok 247,887 >= 245,000) and parity (EVM vs Python >1e-6 in every seed; port reference itself >1e-6 vs original mf.py). Per brief: source + failure evidence returned, score NOT ranked, native screen NOT run.","ranked":false,"submitted_to_board":false,"optimization_performed":false,"fresh_seeds_5148000_5151999_touched":false,"inputs":{"model_source":{"url":"https://api.imd.fun/artifacts/827590bb235808bd3789c89ad42e5038d2733d133c744455327a3e4275089b26","response_sha256_equals_id":true,"mf.py_sha256":"8adbd509efdac3e77ccde38d07a7272e9706b42ea4ecc926f77963e2a332445e","mf.py_sha256_matches_embedded":true,"params":{"KMAX":4,"SIG0":0.000945,"N0":200.0,"TAU_ROUTER":0.0015,"ROUTER":1,"PRETAIL":0.27,"RET_MU":"ln(20)-0.72","RET_SD":1.3,"PIN_VAR":1e-12,"MINVAR":1e-12,"ARB_CONF":0.9}},"harness_evidence":{"url":"https://api.imd.fun/artifacts/939d954a3de669ad66c90951fe406a6f2b799fa8d4771978ba9d4d6a6d517e27","response_sha256_equals_id":true,"source_bundle_extracted":false,"why_not":"this worker's local task-guard hook blocked base64 decoding of the archive_zlib_base64 field; not circumvented. Its claims were NOT used: engine gas limit, error handling, selectors and compiler settings were re-read from the amm-challenge checkout (see engine_facts) and a new harness was written (parity.py)."},"rs2":{"url":"https://api.imd.fun/artifacts/19f2ec857df536e8ab62ab1da89ec6623332521ce46849f15bccff402c63e1e5","response_sha256_equals_id":true,"code_sha256":"f24b6ca198b744c380842c3a17964a6dc21f311bb04984b26733afa3fee8eaf2","matches_required":true,"used":"fetched and verified only; no comparison run because the candidate failed gas/parity"},"brief":"https://raw.githubusercontent.com/gtmcknight/amm-sim/main/research/filter-brief.md","replica.py_sha256":"d079712fa1f4850ad8337e656e7ee8dbdde806cd4762d597fae50f547ef9c7c4","oracle_table.json_sha256":"3e9d1fc8874c6d796d4c26948d486b587f2a6b5610b20fba99923a60954cf4b5","setup":"amm-challenge cloned at 6b6921f8d463314aa7c96234e1be25a25245f704; research/setup.sh read before running (uv venv py3.12, amm_sim_rs wheel sha256 aedb6828...887a checked by the script, numpy numba cma py-solc-x==2.1.0 pyrevm==0.3.7, solc 0.8.24); StarterStrategy validated; ~6 s"},"engine_facts_verified_in_checkout":{"gas_limits":"amm_sim_rs/src/evm/strategy.rs: GAS_LIMIT_INIT = 250_000, GAS_LIMIT_TRADE = 250_000 (tx gas limit, includes 21k intrinsic)","error_handling":"amm_sim_rs/src/amm/cfmm.rs: afterSwap Err -> 'On error, keep current fees' (silent in the native engine, so native runs cannot reveal OOG; gas must be measured by replay)","isBuy":"IAMMStrategy.sol: isBuy true if AMM bought X (trader sold X); reserves post-trade; matches mf.py is_buy","selectors":"trade_info.rs: afterInitialize 0x837aef47, afterSwap 0xc2babb57","compiler":"amm_competition/evm/compiler.py: solc 0.8.24, optimizer on runs 200, viaIR true, evmVersion paris","validator":"inline assembly banned -> Solady expWad ported to plain Solidity; lnW/sqrtW written fresh"},"port":{"solidity_sha256":"f2891b0551d12e58d01d806aea17711cd2ebaedacf15f564328824f5221ea7b2","bytecode_sha256":"5eb8da04663be63b3ffd5ba2356ef805bdda2af30a254cb8d859248945a6a40b","creation_bytecode_bytes":10520,"validator":"valid, 0 warnings","compiler_settings":{"solc":"0.8.24","optimizer":{"enabled":true,"runs":200},"viaIR":true,"evmVersion":"paris","via":"amm_competition.evm.compiler.SolidityCompiler"},"previous_variant":{"CAP":4,"solidity_sha256":"92a1ddeb37b44df8551dbffa1b01a1a85c749d5436f61dee76672a4b16d7fffc","result":"seed 33000, 400 steps: 397/426 OOG (cascade)"},"state":"packed in AMMStrategyBase.slots: slot0 ln spot; slot1 fb|fa|t+1|n|hasLast; slot2 sig2|dt; slot3 sq; slot4 lastpin t|lp; slots5-8 comp w(80)|m int88|v(88)","arithmetic":"int256 WAD (1e18); variances at 1e-18 absolute resolution (PIN_VAR=1e6 units)","preserved":"all constants, 35-number table + arb_flag, old fees used for band/pin/router (set after update), latent labels parb>0.5 (fee flag) / >0.9 (sigma) / >1e-9 (pin comp), GBM drift -sig2/2 and var +sig2 per step, observation boundary = TradeInfo only, Runnalls merge order/tie-break, init comp (1,0,1e-10)","declared_approximations_in_both_sol_and_mfq":["Phi via Numerical Recipes erfcc (fractional error <1.2e-7) instead of math.erf","silent gaps: CAP=1 exact diffuse+truncate step, then ONE diffuse(n-1)+truncate (mf.py: 400 exact steps); CAP lowered 4->1 for gas, still insufficient","truncation mass from far tail when band wholly one side; Mills ratio 2/(sqrt(2pi) t e^poly) for a<0","Phi underflow: EVM mass 0 below 1e-18 where float keeps tiny weights"],"reference_python":{"file":"mfq.py","sha256":"175e2a8dd6eb4f78a0fa0827bf8024b5c1afd6d86e78459f45d565eefbd6c3e0","distinct_from_mf.py":true,"as_unified_diff_vs_mf.py":"--- mf.py\t2026-11-09 16:34:21.320838840 +0000\n+++ mfq.py\t2026-11-09 16:39:10.464995475 +0000\n@@ -1,14 +1,38 @@\n-\"\"\"Small Gaussian-mixture posterior of z=ln(p/spot) for the AMM fee challenge (replica B).\n+\"\"\"mfq.py: Python reference of the Solidity port of mf.py (mixture-B, frozen). Differences vs mf.py, all declared:\n+(1) Phi via Numerical-Recipes erfcc (fractional error <1.2e-7) instead of math.erf  [ERF='nr'];\n+(2) silent gaps: exact per-step diffuse+truncate for the first CAP steps, then ONE diffuse(n-CAP)+truncate (mf.py: CAP=400);\n+(3) trunc mass Z computed from the far tail when the band lies wholly on one side (cancellation-safe), Mills ratio of soft\n+    truncation computed as 2/(sqrt(2pi) t e^poly) for a<0 (underflow-safe; algebraically identical under (1)).\n+All constants, the table, fee timing, latent labels and the observation boundary are unchanged.\"\"\"\n+\"\"\"(base) Small Gaussian-mixture posterior of z=ln(p/spot) for the AMM fee challenge (replica B).\n Filter sees only: init reserves, and per own-pool callback (is_buy, amount_x, amount_y, t, x, y)\n plus its own stored fees. Policy = frozen oracle_table driven by p_hat=spot*exp(E[z]).\"\"\"\n import math, json, os\n SQ2 = math.sqrt(2.0)\n-def Phi(u): return 0.5 * (1.0 + math.erf(u / SQ2))\n def phi(u): return math.exp(-0.5 * u * u) / 2.5066282746310002\n+\n+ERF='nr'; CAP=1\n+def _erfc_pos(x):\n+    t = 1.0 / (1.0 + 0.5 * x)\n+    poly = -1.26551223 + t*(1.00002368 + t*(0.37409196 + t*(0.09678418 + t*(-0.18628806 + t*(0.27886807 + t*(-1.13520398 + t*(1.48851587 + t*(-0.82215223 + t*0.17087277))))))))\n+    return t, poly\n+def erfc(x):\n+    if ERF != 'nr': return math.erfc(x)\n+    if x >= 0:\n+        t, poly = _erfc_pos(x); return t * math.exp(-x * x + poly)\n+    t, poly = _erfc_pos(-x); return 2.0 - t * math.exp(-x * x + poly)\n+def Phi(u): return 0.5 * erfc(-u / SQ2)\n+def Q(u): return 0.5 * erfc(u / SQ2)\n+def mills(a):\n+    \"\"\"phi(a)/Phi(a)\"\"\"\n+    if ERF == 'nr' and a < 0:\n+        t, poly = _erfc_pos(-a / SQ2); return 2.0 / (2.5066282746310002 * t * math.exp(poly))\n+    return phi(a) / Phi(a)\n P = dict(KMAX=4, SIG0=0.000945, N0=200.0, TAU_ROUTER=0.0015, ROUTER=1, PRETAIL=0.27,\n          RET_MU=math.log(20.0) - 0.72, RET_SD=1.3, PIN_VAR=1e-12, MINVAR=1e-12, ARB_CONF=0.9)\n TAB = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'oracle_table.json')))\n \n+\n def interp(arr, u):\n     u = (u + 8.0)\n     if u <= 0: return arr[0]\n@@ -29,7 +53,7 @@\n     s = math.sqrt(v + tau2); a = (b - m) / s\n     Z = Phi(a)\n     if Z < 1e-300: return 0.0, b, P['MINVAR']\n-    lam = phi(a) / Z\n+    lam = mills(a)\n     mean = m - v / s * lam\n     var = v - v * v / (v + tau2) * lam * (a + lam)\n     return Z, mean, max(var, P['MINVAR'])\n@@ -41,7 +65,9 @@\n def trunc(m, v, lo, hi):\n     \"\"\"hard truncate to [lo,hi]\"\"\"\n     s = math.sqrt(v); A = (lo - m) / s; B = (hi - m) / s\n-    Z = Phi(B) - Phi(A)\n+    if A > 0: Z = Q(A) - Q(B)\n+    elif B < 0: Z = Phi(B) - Phi(A)\n+    else: Z = 1.0 - Phi(A) - Q(B)\n     if Z < 1e-14:  # numerically empty: put at nearest edge\n         e = lo if m < lo else hi\n         return 1e-300, min(max(e, lo), hi), P['MINVAR']\n@@ -70,12 +96,12 @@\n     def _silent(self, n):\n         # fully silent steps: diffuse+truncate each step; long gaps: after 64 steps truncation\n         # is near stationary, cap per-step loop and do remaining in chunks of diffusion+trunc\n-        for _ in range(min(n, 400)):\n+        for _ in range(min(n, CAP)):\n             self._diffuse(1)\n             out, tot = self._band()\n             if tot > 0: self.c = [[w / tot, m, v] for w, m, v in out]\n-        if n > 400:\n-            self._diffuse(n - 400); out, tot = self._band()\n+        if n > CAP:\n+            self._diffuse(n - CAP); out, tot = self._band()\n             if tot > 0: self.c = [[w / tot, m, v] for w, m, v in out]\n     def _norm_prune(self):\n         tot = sum(c[0] for c in self.c)\n"}},"approximation_error_mfq_vs_original_mf":{"method":"mferr.py: replica market driven by mf.py fees; mf.py and mfq.py fed identical callbacks; 8 seeds 33000..33007, 10000 steps","per_seed":[{"seed":33000,"callbacks":9329,"max_fee_diff":0.0013128643864411815,"over_1e6":5980,"first_over":{"t":25,"cb":32,"d":3.5017647974755484e-05},"rms_phat_diff_bps":1.5407484600549994,"flag_mismatch":41},{"seed":33001,"callbacks":8730,"max_fee_diff":0.0013099170938355442,"over_1e6":7128,"first_over":{"t":8,"cb":4,"d":1.8424413531503703e-05},"rms_phat_diff_bps":1.5468454889964205,"flag_mismatch":55},{"seed":33002,"callbacks":9812,"max_fee_diff":0.0010097694107186195,"over_1e6":7220,"first_over":{"t":5,"cb":1,"d":9.39576392163799e-05},"rms_phat_diff_bps":1.1680428760004182,"flag_mismatch":40},{"seed":33003,"callbacks":9438,"max_fee_diff":0.0012591282766728495,"over_1e6":7463,"first_over":{"t":38,"cb":34,"d":7.646087042978422e-05},"rms_phat_diff_bps":1.2393133653911004,"flag_mismatch":53},{"seed":33004,"callbacks":9678,"max_fee_diff":0.0010215025117048526,"over_1e6":6042,"first_over":{"t":28,"cb":20,"d":1.890348979127765e-06},"rms_phat_diff_bps":1.1764191777191648,"flag_mismatch":39},{"seed":33005,"callbacks":9133,"max_fee_diff":0.002235119542550179,"over_1e6":7325,"first_over":{"t":22,"cb":16,"d":2.504560352478702e-05},"rms_phat_diff_bps":1.6866848564112193,"flag_mismatch":57},{"seed":33006,"callbacks":11764,"max_fee_diff":0.0018149639973181753,"over_1e6":9633,"first_over":{"t":6,"cb":2,"d":0.0001726849835129718},"rms_phat_diff_bps":1.3228882219818734,"flag_mismatch":36},{"seed":33007,"callbacks":8767,"max_fee_diff":0.003032606507394897,"over_1e6":6996,"first_over":{"t":40,"cb":45,"d":4.312305725551191e-05},"rms_phat_diff_bps":1.8551777673685583,"flag_mismatch":58}],"verdict":"FAILS 1e-6: max fee diff 1.0e-3..3.0e-3, RMS ln(p_hat) diff 1.17..1.86 bps, 36-58 flag flips/seed; dominated by CAP=1 long-gap collapse (first exceedances at gap callbacks)"},"parity_16_seeds":{"seeds":"33000..33015","steps":10000,"tolerance_fee":1e-06,"method":"parity.py: closed loop, replica.py market driven by EVM fees (pyrevm 0.3.7, gas limit 250000 per call like the engine); each callback's TradeInfo (WAD ints) fed to EVM, mf.py and mfq.py; hidden inputs (p, arb, rival spot) never passed to filters (update signature has none)","aggregate":{"callbacks":155544,"ok":113516,"oog":42028,"revert":0,"other_halt":0,"tx_error":0,"bad_return":0,"gas_max_ok_afterSwap":247887,"gas_sum_ok_afterSwap":17146834181,"init_gas":119087,"repeats_same_t":51473,"gap_callbacks":29050,"next_step":75021,"max_gap":86,"gaps_longer_than_CAP":13366},"per_seed":[{"seed":33000,"callbacks":8974,"ok":6486,"oog":2488,"gas_max_ok":246892,"first_oog_t":25,"init_gas":119087,"maxdiff_vs_mfq":0.046259008,"over_tol_vs_mfq":8825,"first_div_vs_mfq":{"t":25,"cb":32,"evm":[0.005265512075743445,0.000124136384059534],"py":[0.006107125175037309,0.0],"parb":5.3599614420873344e-05},"maxdiff_vs_mf":0.047386529,"over_tol_vs_mf":8832,"flag_mismatch_mfq_vs_mf":38,"repeats":2767,"gaps":1801,"maxgap":77,"gaps_gt_cap":858},{"seed":33001,"callbacks":8689,"ok":5966,"oog":2723,"gas_max_ok":247830,"first_oog_t":8,"init_gas":119087,"maxdiff_vs_mfq":0.018967847,"over_tol_vs_mfq":8612,"first_div_vs_mfq":{"t":8,"cb":4,"evm":[0.005231928693149812,0.001256452183049633],"py":[0.004346366212805486,0.002538865016665115],"parb":0.2984058078192314},"maxdiff_vs_mf":0.020219427,"over_tol_vs_mf":8617,"flag_mismatch_mfq_vs_mf":72,"repeats":2553,"gaps":1926,"maxgap":11,"gaps_gt_cap":947},{"seed":33002,"callbacks":9655,"ok":7010,"oog":2645,"gas_max_ok":247072,"first_oog_t":12,"init_gas":119087,"maxdiff_vs_mfq":0.043353693,"over_tol_vs_mfq":9532,"first_div_vs_mfq":{"t":12,"cb":6,"evm":[0.003506174226388875,0.003602570521543086],"py":[0.002558713150599301,0.004272452210038802],"parb":0.0928349732602509},"maxdiff_vs_mf":0.044967405,"over_tol_vs_mf":9580,"flag_mismatch_mfq_vs_mf":200,"repeats":3145,"gaps":1793,"maxgap":86,"gaps_gt_cap":830},{"seed":33003,"callbacks":9275,"ok":6642,"oog":2633,"gas_max_ok":247848,"first_oog_t":5,"init_gas":119087,"maxdiff_vs_mfq":0.034527625,"over_tol_vs_mfq":9186,"first_div_vs_mfq":{"t":7,"cb":6,"evm":[0.010360895623764702,0.0],"py":[0.009737849712826874,0.0],"parb":3.3955822003688483e-12},"maxdiff_vs_mf":0.035807701,"over_tol_vs_mf":9182,"flag_mismatch_mfq_vs_mf":52,"repeats":2883,"gaps":1811,"maxgap":20,"gaps_gt_cap":891},{"seed":33004,"callbacks":9595,"ok":6986,"oog":2609,"gas_max_ok":246744,"first_oog_t":6,"init_gas":119087,"maxdiff_vs_mfq":0.014081429,"over_tol_vs_mfq":9358,"first_div_vs_mfq":{"t":6,"cb":5,"evm":[0.005526204707819711,0.001961906401198985],"py":[0.004902959385402528,0.001778884877058351],"parb":0.25828575068335896},"maxdiff_vs_mf":0.014765835,"over_tol_vs_mf":9347,"flag_mismatch_mfq_vs_mf":70,"repeats":3085,"gaps":1828,"maxgap":15,"gaps_gt_cap":848},{"seed":33005,"callbacks":9071,"ok":6463,"oog":2608,"gas_max_ok":247120,"first_oog_t":7,"init_gas":119087,"maxdiff_vs_mfq":0.017879856,"over_tol_vs_mfq":8995,"first_div_vs_mfq":{"t":7,"cb":5,"evm":[0.002672272830816165,0.004938294584505936],"py":[0.0,0.007087680713988687],"parb":7.1368872927626315e-06},"maxdiff_vs_mf":0.017879856,"over_tol_vs_mf":8988,"flag_mismatch_mfq_vs_mf":38,"repeats":2868,"gaps":1842,"maxgap":18,"gaps_gt_cap":909},{"seed":33006,"callbacks":11666,"ok":9159,"oog":2507,"gas_max_ok":247404,"first_oog_t":17,"init_gas":119087,"maxdiff_vs_mfq":0.013187787,"over_tol_vs_mfq":11453,"first_div_vs_mfq":{"t":17,"cb":13,"evm":[0.004762521735422564,0.001970735116017901],"py":[0.003663053778503955,0.003470498305609304],"parb":0.4456775347406638},"maxdiff_vs_mf":0.013195553,"over_tol_vs_mf":11468,"flag_mismatch_mfq_vs_mf":46,"repeats":4456,"gaps":1636,"maxgap":12,"gaps_gt_cap":649},{"seed":33007,"callbacks":8679,"ok":6033,"oog":2646,"gas_max_ok":247887,"first_oog_t":40,"init_gas":119087,"maxdiff_vs_mfq":0.024032227,"over_tol_vs_mfq":8505,"first_div_vs_mfq":{"t":40,"cb":45,"evm":[0.005289637451707919,0.0],"py":[0.005142330293072983,0.0014517356222029099],"parb":0.18486024790665423},"maxdiff_vs_mf":0.025158865,"over_tol_vs_mf":8515,"flag_mismatch_mfq_vs_mf":93,"repeats":2595,"gaps":1853,"maxgap":17,"gaps_gt_cap":919},{"seed":33008,"callbacks":9555,"ok":6844,"oog":2711,"gas_max_ok":247223,"first_oog_t":18,"init_gas":119087,"maxdiff_vs_mfq":0.018956159,"over_tol_vs_mfq":9438,"first_div_vs_mfq":{"t":18,"cb":17,"evm":[0.005244451689102137,0.000834374439085438],"py":[0.0050915092702097375,0.0015212084800830805],"parb":0.09993442656582543},"maxdiff_vs_mf":0.019090811,"over_tol_vs_mf":9453,"flag_mismatch_mfq_vs_mf":80,"repeats":3186,"gaps":1886,"maxgap":10,"gaps_gt_cap":874},{"seed":33009,"callbacks":8656,"ok":5869,"oog":2787,"gas_max_ok":247575,"first_oog_t":11,"init_gas":119087,"maxdiff_vs_mfq":0.017831166,"over_tol_vs_mfq":8564,"first_div_vs_mfq":{"t":11,"cb":9,"evm":[0.002268904907537101,0.004477137834761944],"py":[0.0018613416960145956,0.004764819652729679],"parb":0.09912354022033439},"maxdiff_vs_mf":0.021275239,"over_tol_vs_mf":8582,"flag_mismatch_mfq_vs_mf":41,"repeats":2593,"gaps":1877,"maxgap":46,"gaps_gt_cap":935},{"seed":33010,"callbacks":9971,"ok":7341,"oog":2630,"gas_max_ok":245261,"first_oog_t":9,"init_gas":119087,"maxdiff_vs_mfq":0.020202321,"over_tol_vs_mfq":9872,"first_div_vs_mfq":{"t":9,"cb":5,"evm":[0.00510960416867802,0.001496473529820692],"py":[0.004489566241788978,0.0023434335751011393],"parb":0.19279629126777856},"maxdiff_vs_mf":0.020287446,"over_tol_vs_mf":9856,"flag_mismatch_mfq_vs_mf":63,"repeats":3368,"gaps":1863,"maxgap":19,"gaps_gt_cap":813},{"seed":33011,"callbacks":11418,"ok":8823,"oog":2595,"gas_max_ok":247826,"first_oog_t":22,"init_gas":119087,"maxdiff_vs_mfq":0.019236643,"over_tol_vs_mfq":11272,"first_div_vs_mfq":{"t":22,"cb":26,"evm":[0.005285482753952842,0.0],"py":[0.005296650432087824,0.0],"parb":0.0001043488004380611},"maxdiff_vs_mf":0.019236643,"over_tol_vs_mf":11284,"flag_mismatch_mfq_vs_mf":26,"repeats":4275,"gaps":1714,"maxgap":19,"gaps_gt_cap":649},{"seed":33012,"callbacks":10898,"ok":8331,"oog":2567,"gas_max_ok":247589,"first_oog_t":18,"init_gas":119087,"maxdiff_vs_mfq":0.022767599,"over_tol_vs_mfq":10676,"first_div_vs_mfq":{"t":18,"cb":20,"evm":[0.000952563658351404,0.005176478134336137],"py":[0.0014816351029970365,0.005032658209615626],"parb":0.07914903227477958},"maxdiff_vs_mf":0.023047625,"over_tol_vs_mf":10682,"flag_mismatch_mfq_vs_mf":36,"repeats":3916,"gaps":1752,"maxgap":21,"gaps_gt_cap":718},{"seed":33013,"callbacks":9902,"ok":7254,"oog":2648,"gas_max_ok":246698,"first_oog_t":10,"init_gas":119087,"maxdiff_vs_mfq":0.021989795,"over_tol_vs_mfq":9793,"first_div_vs_mfq":{"t":10,"cb":18,"evm":[0.005239942253581069,0.00098638258120203],"py":[0.005249222001446285,0.000673546499058153],"parb":0.41731562680254725},"maxdiff_vs_mf":0.022214464,"over_tol_vs_mf":9801,"flag_mismatch_mfq_vs_mf":52,"repeats":3268,"gaps":1852,"maxgap":10,"gaps_gt_cap":819},{"seed":33014,"callbacks":9227,"ok":6577,"oog":2650,"gas_max_ok":247295,"first_oog_t":22,"init_gas":119087,"maxdiff_vs_mfq":0.020306982,"over_tol_vs_mfq":9150,"first_div_vs_mfq":{"t":22,"cb":6,"evm":[0.003481784100944395,0.004366332457038963],"py":[0.0028385514434107195,0.00407471175630425],"parb":0.261572005433576},"maxdiff_vs_mf":0.020652329,"over_tol_vs_mf":9146,"flag_mismatch_mfq_vs_mf":39,"repeats":2926,"gaps":1823,"maxgap":14,"gaps_gt_cap":903},{"seed":33015,"callbacks":10313,"ok":7732,"oog":2581,"gas_max_ok":247594,"first_oog_t":13,"init_gas":119087,"maxdiff_vs_mfq":0.01295339,"over_tol_vs_mfq":10143,"first_div_vs_mfq":{"t":13,"cb":9,"evm":[0.00277895004927829,0.004116835473525783],"py":[0.004269027563023831,0.0026443848996539066],"parb":0.02436233069305899},"maxdiff_vs_mf":0.012989597,"over_tol_vs_mf":10160,"flag_mismatch_mfq_vs_mf":34,"repeats":3589,"gaps":1793,"maxgap":11,"gaps_gt_cap":804}],"first_divergence_finding":"in 15/16 seeds the first EVM-vs-mfq exceedance is at the first OOG timestamp, in 33003 two steps later (t=7 vs 5): no purely numerical divergence observed before gas failure, but OOG reverts leave t stale so later calls see longer gaps and fail again (cascade)","verdict":"FAIL (27.0% OOG; max ok 247,887 >= 245,000)"},"native_screen":{"run":false,"reason":"brief: if parity fails or any callback fails, do not rank; candidate edges would be those of a gas-broken policy (engine keeps stale fees on OOG)","seeds_planned":"32000..32127","configs_canonical_generator":"MatchRunner._build_configs with rng=numpy.random.default_rng(seed=S) for global seed S (no chunk reset): size U(19,21), rate U(0.6,1.0), sigma U(0.000882,0.001008) drawn in that order; n_steps 10000, price 100, x 100, y 10000, mu 0, dt 1, size_sigma 1.2, buy_prob 0.5, seed=S","paired_edges":null,"mean":null,"se":null,"ci95":null,"retail_arb_split":null,"native_gas_telemetry":null},"gas_budget_diagnosis":"ok calls ~200-248k incl. 21k intrinsic. Per mixture component a trunc() costs 2 erfc + 2 phi + sqrt; band runs 4 comps on every new timestamp and again per silent step; merge pass costs up to 15 lnW. Fix options (not done): precomputed Phi/phi lookup tables with matching Python, sharing exp(-u^2/2) between erfc and phi, merging to <=3 comps before silent steps, or fewer comps (KMAX change = model change, needs owner approval).","unresolved_portability":["gas >245k / OOG cascade","CAP=1 approximation error ~1e-3 fee vs mf.py","256-seed certification not attempted","harness evidence bundle not decoded (local guard)"],"commands":[". scratch/env.sh && PYTHONPATH=../amm python build.py MixtureB.sol MixtureB.bin","python -I parity.py MixtureB.bin 10000 <seed,seed> (8 parallel, 31 s wall)","python -I mferr.py 10000 <seed,seed> (4 parallel)"],"dependencies":{"python":"3.12 (uv venv)","amm_sim_rs":"0.1.0.post1 wheel sha256 aedb68280aca34c110968f00148387c95885207913fb2cf19a1ab4c5cfd6887a","pyrevm":"0.3.7","py-solc-x":"2.1.0","solc":"0.8.24"},"sources_full":{"MixtureB.sol":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24;\n\nimport {AMMStrategyBase} from \"./AMMStrategyBase.sol\";\nimport {TradeInfo} from \"./IAMMStrategy.sol\";\n\n/// Mixture-B (frozen): <=4-component Gaussian-mixture posterior of z = ln(p/spot) driving the frozen\n/// 35-number oracle table. Port of mf.py (artifact 827590bb...); exact Python reference: mfq.py.\n/// Declared approximations (same in mfq.py): NR erfcc for Phi; silent gaps exact for CAP steps then one\n/// diffuse(n-CAP)+truncate; tail-stable truncation mass; underflow-safe Mills ratio. int256 WAD arithmetic.\ncontract Strategy is AMMStrategyBase {\n    int256 constant W = 1e18;\n    int256 constant SQ2 = 1414213562373095049;\n    int256 constant SQRT2PI = 2506628274631000502;\n    int256 constant LN2 = 693147180559945309;\n    int256 constant SIG0SQ = 893025000000; // 0.000945^2\n    int256 constant N0 = 200;\n    int256 constant TAU2 = 2250000000000; // 0.0015^2\n    int256 constant PRETAIL = 270000000000000000;\n    int256 constant RET_MU = 2275732273553990700; // ln(20)-0.72\n    int256 constant RET_SD = 1300000000000000000;\n    int256 constant PIN_VAR = 1000000; // 1e-12\n    int256 constant MINVAR = 1000000; // 1e-12\n    int256 constant ARB_CONF = 900000000000000000;\n    int256 constant ARBFLAG = 146300000000000000;\n    uint256 constant KMAX = 4;\n    uint256 constant CAP = 1;\n\n    struct S {\n        int256 ls; // ln spot of last callback\n        int256 fb;\n        int256 fa;\n        int256 t; // last timestamp (-1 initially)\n        int256 sig2;\n        int256 dt;\n        int256 sq;\n        int256 lpT;\n        int256 lpV;\n        bool hasLast;\n        uint256 n;\n        int256[5] w;\n        int256[5] m;\n        int256[5] v;\n    }\n\n    // ---------------- storage packing ----------------\n    // slot0: ls ; slot1: fb(64)|fa(64)|t+1(64)|n(8)|hasLast(8) ; slot2: sig2(128)|dt(128) ; slot3: sq ; slot4: lpT(64)|lpV(int192)\n    // slot5..8: comp: w(80)|m int(88)|v(88)\n    function _load() internal view returns (S memory s) {\n        s.ls = int256(slots[0]);\n        uint256 a = slots[1];\n        s.fb = int256(a & 0xFFFFFFFFFFFFFFFF);\n        s.fa = int256((a >> 64) & 0xFFFFFFFFFFFFFFFF);\n        s.t = int256((a >> 128) & 0xFFFFFFFFFFFFFFFF) - 1;\n        s.n = (a >> 192) & 0xFF;\n        s.hasLast = ((a >> 200) & 0xFF) != 0;\n        uint256 b = slots[2];\n        s.sig2 = int256(b & type(uint128).max);\n        s.dt = int256(b >> 128);\n        s.sq = int256(slots[3]);\n        uint256 c = slots[4];\n        s.lpT = int256(c & 0xFFFFFFFFFFFFFFFF);\n        s.lpV = int256(c) >> 64;\n        for (uint256 i = 0; i < s.n; i++) {\n            uint256 q = slots[5 + i];\n            s.w[i] = int256(q & ((1 << 80) - 1));\n            s.m[i] = int256(int88(int256((q >> 80) & ((1 << 88) - 1))));\n            s.v[i] = int256(q >> 168);\n        }\n    }\n\n    function _store(S memory s, bool pinChanged) internal {\n        slots[0] = uint256(s.ls);\n        slots[1] = uint256(s.fb) | (uint256(s.fa) << 64) | (uint256(s.t + 1) << 128) | (s.n << 192)\n            | ((s.hasLast ? uint256(1) : 0) << 200);\n        if (pinChanged) {\n            slots[2] = uint256(s.sig2) | (uint256(s.dt) << 128);\n            slots[3] = uint256(s.sq);\n            slots[4] = uint256(s.lpT) | (uint256(s.lpV) << 64);\n        }\n        for (uint256 i = 0; i < s.n; i++) {\n            slots[5 + i] = uint256(s.w[i]) | ((uint256(s.m[i]) & ((1 << 88) - 1)) << 80) | (uint256(s.v[i]) << 168);\n        }\n    }\n\n    // ---------------- math ----------------\n    function mulW(int256 a, int256 b) internal pure returns (int256) {\n        unchecked { return (a * b) / W; }\n    }\n\n    function divW(int256 a, int256 b) internal pure returns (int256) {\n        unchecked { return (a * W) / b; }\n    }\n\n    function expW(int256 x) internal pure returns (int256 r) {\n        unchecked {\n            if (x <= -41446531673892822313) return 0;\n            require(x < 135305999368893231589);\n            x = (x << 78) / 5 ** 18;\n            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;\n            x = x - k * 54916777467707473351141471128;\n            int256 y = x + 1346386616545796478920950773328;\n            y = ((y * x) >> 96) + 57155421227552351082224309758442;\n            int256 p = y + x - 94201549194550492254356042504812;\n            p = ((p * y) >> 96) + 28719021644029726153956944680412240;\n            p = p * x + (4385272521454847904659076985693276 << 96);\n            int256 q = x - 2855989394907223263936484059900;\n            q = ((q * x) >> 96) + 50020603652535783019961831881945;\n            q = ((q * x) >> 96) - 533845033583426703283633433725380;\n            q = ((q * x) >> 96) + 3604857256930695427073651918091429;\n            q = ((q * x) >> 96) - 14423608567350463180887372962807573;\n            q = ((q * x) >> 96) + 26449188498355588339934803723976023;\n            r = p / q;\n            r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));\n        }\n    }\n\n    function lnW(int256 x) internal pure returns (int256) {\n        require(x > 0);\n        unchecked {\n            int256 k = 0;\n            if (x >= 2e18) {\n                for (int256 sh = 64; sh > 0; sh >>= 1) {\n                    if (x >= (int256(2e18) << uint256(sh - 1))) { x >>= uint256(sh); k += sh; }\n                }\n            } else if (x < 1e18) {\n                for (int256 sh = 32; sh > 0; sh >>= 1) {\n                    if ((x << uint256(sh)) < 1e18) { x <<= uint256(sh); k -= sh; }\n                }\n                if (x < 1e18) { x <<= 1; k -= 1; }\n            }\n            if (x > 1414213562373095048) {\n                x >>= 1;\n                k++;\n            }\n            int256 s = ((x - W) * W) / (x + W);\n            int256 s2 = (s * s) / W;\n            int256 term = s;\n            int256 r = s;\n            for (int256 i = 3; i <= 25; i += 2) {\n                term = (term * s2) / W;\n                r += term / i;\n            }\n            return 2 * r + k * LN2;\n        }\n    }\n\n    function sqrtW(int256 v) internal pure returns (int256) {\n        uint256 x = uint256(v) * 1e18;\n        if (x == 0) return 0;\n        unchecked {\n            uint256 b = 0;\n            uint256 y = x;\n            if (y >= 1 << 128) { y >>= 128; b += 128; }\n            if (y >= 1 << 64) { y >>= 64; b += 64; }\n            if (y >= 1 << 32) { y >>= 32; b += 32; }\n            if (y >= 1 << 16) { y >>= 16; b += 16; }\n            if (y >= 1 << 8) { y >>= 8; b += 8; }\n            if (y >= 1 << 4) { y >>= 4; b += 4; }\n            if (y >= 1 << 2) { y >>= 2; b += 2; }\n            if (y >= 1 << 1) { b += 1; }\n            uint256 z = 1 << ((b >> 1) + 1); // > sqrt(x)\n            while (true) {\n                uint256 zn = (z + x / z) >> 1;\n                if (zn >= z) break;\n                z = zn;\n            }\n            return int256(z);\n        }\n    }\n\n    function erfcPos(int256 x) internal pure returns (int256 t, int256 poly) {\n        t = (W * W) / (W + x / 2);\n        poly = 170872770000000000;\n        poly = mulW(t, poly) - 822152230000000000;\n        poly = mulW(t, poly) + 1488515870000000000;\n        poly = mulW(t, poly) - 1135203980000000000;\n        poly = mulW(t, poly) + 278868070000000000;\n        poly = mulW(t, poly) - 186288060000000000;\n        poly = mulW(t, poly) + 96784180000000000;\n        poly = mulW(t, poly) + 374091960000000000;\n        poly = mulW(t, poly) + 1000023680000000000;\n        poly = mulW(t, poly) - 1265512230000000000;\n    }\n\n    function erfc(int256 x) internal pure returns (int256) {\n        if (x >= 0) {\n            (int256 t, int256 poly) = erfcPos(x);\n            return mulW(t, expW(poly - mulW(x, x)));\n        }\n        (int256 t2, int256 poly2) = erfcPos(-x);\n        return 2 * W - mulW(t2, expW(poly2 - mulW(x, x)));\n    }\n\n    function Phi(int256 u) internal pure returns (int256) {\n        return erfc(divW(-u, SQ2)) / 2;\n    }\n\n    function phi(int256 u) internal pure returns (int256) {\n        return divW(expW(-mulW(u, u) / 2), SQRT2PI);\n    }\n\n    function mills(int256 a) internal pure returns (int256) {\n        if (a < 0) {\n            (int256 t, int256 poly) = erfcPos(divW(-a, SQ2));\n            return (2 * W * W) / mulW(mulW(SQRT2PI, t), expW(poly));\n        }\n        return divW(phi(a), Phi(a));\n    }\n\n    function trunc(int256 m, int256 v, int256 lo, int256 hi) internal pure returns (int256 Z, int256 mu, int256 var_) {\n        int256 s = sqrtW(v);\n        int256 A = divW(lo - m, s);\n        int256 B = divW(hi - m, s);\n        if (A > 0) Z = Phi(-A) - Phi(-B);\n        else if (B < 0) Z = Phi(B) - Phi(A);\n        else Z = W - Phi(A) - Phi(-B);\n        if (Z < 10000) {\n            int256 e = m < lo ? lo : hi;\n            return (0, e, MINVAR);\n        }\n        int256 pa = phi(A);\n        int256 pb = phi(B);\n        int256 r1 = divW(pa - pb, Z);\n        mu = m + mulW(s, r1);\n        int256 t2 = divW(mulW(A, pa) - mulW(B, pb), Z);\n        var_ = mulW(v, W + t2 - mulW(r1, r1));\n        if (var_ < MINVAR) var_ = MINVAR;\n    }\n\n    function softUpper(int256 m, int256 v, int256 b) internal pure returns (int256 Z, int256 mean, int256 var_) {\n        int256 s = sqrtW(v + TAU2);\n        int256 a = divW(b - m, s);\n        Z = Phi(a);\n        if (Z <= 0) return (0, b, MINVAR);\n        int256 lam = mills(a);\n        mean = m - mulW(divW(v, s), lam);\n        int256 tt = divW(mulW(v, v), v + TAU2);\n        var_ = v - mulW(mulW(tt, lam), a + lam);\n        if (var_ < MINVAR) var_ = MINVAR;\n    }\n\n    // ---------------- filter pieces ----------------\n    function _diffuse(S memory s, int256 n) internal pure {\n        int256 d = s.sig2 * n;\n        for (uint256 i = 0; i < s.n; i++) {\n            s.m[i] += -d / 2;\n            s.v[i] += d;\n        }\n    }\n\n    function _bandInto(S memory s, int256[5] memory ow, int256[5] memory om, int256[5] memory ov, int256 lo, int256 hi)\n        internal\n        pure\n        returns (int256 tot)\n    {\n        for (uint256 i = 0; i < s.n; i++) {\n            (int256 Z, int256 mu, int256 vr) = trunc(s.m[i], s.v[i], lo, hi);\n            ow[i] = mulW(s.w[i], Z);\n            om[i] = mu;\n            ov[i] = vr;\n            tot += ow[i];\n        }\n    }\n\n    function _silentStep(S memory s, int256 n, int256 lo, int256 hi) internal pure {\n        _diffuse(s, n);\n        int256[5] memory ow;\n        int256[5] memory om;\n        int256[5] memory ov;\n        int256 tot = _bandInto(s, ow, om, ov, lo, hi);\n        if (tot > 0) {\n            for (uint256 i = 0; i < s.n; i++) {\n                s.w[i] = divW(ow[i], tot);\n                s.m[i] = om[i];\n                s.v[i] = ov[i];\n            }\n        }\n    }\n\n    function _silent(S memory s, int256 n, int256 lo, int256 hi) internal pure {\n        int256 k = n < int256(CAP) ? n : int256(CAP);\n        for (int256 i = 0; i < k; i++) _silentStep(s, 1, lo, hi);\n        if (n > int256(CAP)) _silentStep(s, n - int256(CAP), lo, hi);\n    }\n\n    function _normPrune(S memory s) internal pure {\n        int256 tot = 0;\n        for (uint256 i = 0; i < s.n; i++) tot += s.w[i];\n        uint256 j = 0;\n        for (uint256 i = 0; i < s.n; i++) {\n            int256 wn = divW(s.w[i], tot);\n            if (wn > 1e9) {\n                s.w[j] = wn;\n                s.m[j] = s.m[i];\n                s.v[j] = s.v[i];\n                j++;\n            }\n        }\n        s.n = j;\n        while (s.n > KMAX) {\n            int256[5] memory lv;\n            for (uint256 i = 0; i < s.n; i++) lv[i] = lnW(s.v[i]);\n            int256 bc;\n            uint256 bi;\n            uint256 bj;\n            int256 bw;\n            int256 bm;\n            int256 bv;\n            bool have = false;\n            for (uint256 i = 0; i < s.n; i++) {\n                for (uint256 k = i + 1; k < s.n; k++) {\n                    int256 ww = s.w[i] + s.w[k];\n                    int256 mm = (s.w[i] * s.m[i] + s.w[k] * s.m[k]) / ww;\n                    int256 di = s.m[i] - mm;\n                    int256 dk = s.m[k] - mm;\n                    int256 vv = (s.w[i] * (s.v[i] + mulW(di, di)) + s.w[k] * (s.v[k] + mulW(dk, dk))) / ww;\n                    int256 cost = (mulW(ww, lnW(vv)) - mulW(s.w[i], lv[i]) - mulW(s.w[k], lv[k])) / 2;\n                    if (!have || cost < bc) {\n                        have = true;\n                        bc = cost;\n                        bi = i;\n                        bj = k;\n                        bw = ww;\n                        bm = mm;\n                        bv = vv;\n                    }\n                }\n            }\n            uint256 q = 0;\n            for (uint256 i = 0; i < s.n; i++) {\n                if (i == bi || i == bj) continue;\n                s.w[q] = s.w[i];\n                s.m[q] = s.m[i];\n                s.v[q] = s.v[i];\n                q++;\n            }\n            s.w[q] = bw;\n            s.m[q] = bm;\n            s.v[q] = bv;\n            s.n = q + 1;\n        }\n    }\n\n    // ---------------- policy table ----------------\n    function _ask(uint256 i) internal pure returns (int256) {\n        int32[17] memory a = [int32(-8836), -9345, -8159, -7282, -6342, -5652, -2453, 7252, 11735, 14444, 16154, 27552, 29767, 33159, 44695, 50710, 57838];\n        return int256(a[i]) * 1e14;\n    }\n\n    function _bid(uint256 i) internal pure returns (int256) {\n        int32[17] memory b = [int32(56921), 51279, 44766, 40233, 33293, 24659, 17209, 14329, 12125, 7478, -2275, -5564, -6444, -7169, -7926, -8617, -8452];\n        return int256(b[i]) * 1e14;\n    }\n\n    function _interp(bool ask, int256 u) internal pure returns (int256) {\n        u = u + 8 * W;\n        if (u <= 0) return ask ? _ask(0) : _bid(0);\n        if (u >= 16 * W) return ask ? _ask(16) : _bid(16);\n        uint256 i = uint256(u / W);\n        int256 f = u - int256(i) * W;\n        int256 a0 = ask ? _ask(i) : _bid(i);\n        int256 a1 = ask ? _ask(i + 1) : _bid(i + 1);\n        return mulW(a0, W - f) + mulW(a1, f);\n    }\n\n    function _fees(int256 d, bool arbflag) internal pure returns (int256 fb, int256 fa) {\n        int256 u = (d * W) / 3e15;\n        int256 add = arbflag ? ARBFLAG : int256(0);\n        int256 a = _interp(true, u) + add;\n        int256 b = _interp(false, u) + add;\n        fa = W - expW(d - (a * 3) / 1000);\n        fb = W - expW(-(b * 3) / 1000 - d);\n        if (fb < 0) fb = 0;\n        if (fb > 1e17) fb = 1e17;\n        if (fa < 0) fa = 0;\n        if (fa > 1e17) fa = 1e17;\n    }\n\n    // ---------------- callbacks ----------------\n    function afterInitialize(uint256 initialX, uint256 initialY) external override returns (uint256, uint256) {\n        S memory s;\n        s.ls = lnW(int256((initialY * 1e18) / initialX));\n        s.fb = 3e15;\n        s.fa = 3e15;\n        s.t = -1;\n        s.sig2 = SIG0SQ;\n        s.n = 1;\n        s.w[0] = W;\n        s.m[0] = 0;\n        s.v[0] = 1e8; // 1e-10\n        _store(s, true);\n        return (3e15, 3e15);\n    }\n\n    function afterSwap(TradeInfo calldata tr) external override returns (uint256, uint256) {\n        S memory s = _load();\n        int256 t = int256(tr.timestamp);\n        int256 ls1 = lnW(int256((tr.reserveY * 1e18) / tr.reserveX));\n        int256 dls = ls1 - s.ls;\n        int256 parb = 0;\n        bool pinChanged = false;\n        if (t != s.t) {\n            int256 lo = lnW(W - s.fb);\n            int256 hi = -lnW(W - s.fa);\n            if (t > s.t + 1) _silent(s, t - s.t - 1, lo, hi);\n            _diffuse(s, 1);\n            int256 zpost = tr.isBuy ? lo : hi;\n            int256 zpre = zpost + dls;\n            int256 dens = 0;\n            if (tr.isBuy ? (zpre < lo) : (zpre > hi)) {\n                for (uint256 i = 0; i < s.n; i++) {\n                    int256 sd = sqrtW(s.v[i]);\n                    dens += divW(mulW(s.w[i], phi(divW(zpre - s.m[i], sd))), sd);\n                }\n                dens = (dens * 2 * int256(tr.amountX)) / int256(tr.reserveX);\n            }\n            int256[5] memory ow;\n            int256[5] memory om;\n            int256[5] memory ov;\n            int256 tot = _bandInto(s, ow, om, ov, lo, hi);\n            int256 ay = int256(tr.amountY);\n            if (ay < 1e9) ay = 1e9;\n            int256 ly = lnW(ay);\n            int256 dret = divW(mulW(PRETAIL, phi(divW(ly - RET_MU, RET_SD))), RET_SD);\n            int256 lret = mulW(tot, dret);\n            if (dens + lret > 0) parb = divW(dens, dens + lret);\n            uint256 n2 = 0;\n            if (tot > 0) {\n                for (uint256 i = 0; i < s.n; i++) {\n                    s.w[n2] = divW(mulW(ow[i], W - parb), tot);\n                    s.m[n2] = om[i] - dls;\n                    s.v[n2] = ov[i];\n                    n2++;\n                }\n            }\n            if (parb > 1e9 || n2 == 0) {\n                s.w[n2] = n2 == 0 ? W : parb;\n                s.m[n2] = zpost;\n                s.v[n2] = PIN_VAR;\n                n2++;\n            }\n            s.n = n2;\n            if (parb > ARB_CONF) {\n                int256 lp = ls1 + zpost;\n                if (s.hasLast) {\n                    int256 dtp = t - s.lpT;\n                    if (dtp > 0) {\n                        int256 e = lp - s.lpV + (s.sig2 * dtp) / 2;\n                        s.sq += mulW(e, e);\n                        s.dt += dtp;\n                        s.sig2 = (N0 * SIG0SQ + s.sq) / (N0 + s.dt);\n                    }\n                }\n                s.hasLast = true;\n                s.lpT = t;\n                s.lpV = lp;\n                pinChanged = true;\n            }\n        } else {\n            for (uint256 i = 0; i < s.n; i++) s.m[i] -= dls;\n        }\n        if (parb < 5e17) {\n            int256 bnd = tr.isBuy ? lnW(W - s.fb) : -lnW(W - s.fa);\n            int256[5] memory nw;\n            int256[5] memory nm;\n            int256[5] memory nv;\n            int256 sum = 0;\n            for (uint256 i = 0; i < s.n; i++) {\n                if (s.v[i] <= (PIN_VAR * 101) / 100) {\n                    (nw[i], nm[i], nv[i]) = (s.w[i], s.m[i], s.v[i]);\n                } else {\n                    int256 Z;\n                    int256 mu;\n                    int256 vr;\n                    if (!tr.isBuy) {\n                        (Z, mu, vr) = softUpper(s.m[i], s.v[i], bnd);\n                    } else {\n                        (Z, mu, vr) = softUpper(-s.m[i], s.v[i], -bnd);\n                        mu = -mu;\n                    }\n                    (nw[i], nm[i], nv[i]) = (mulW(s.w[i], Z), mu, vr);\n                }\n                sum += nw[i];\n            }\n            if (sum > 0) {\n                for (uint256 i = 0; i < s.n; i++) (s.w[i], s.m[i], s.v[i]) = (nw[i], nm[i], nv[i]);\n            }\n        }\n        _normPrune(s);\n        int256 mean = 0;\n        for (uint256 i = 0; i < s.n; i++) mean += mulW(s.w[i], s.m[i]);\n        (int256 fb, int256 fa) = _fees(-mean, parb > 5e17);\n        s.fb = fb;\n        s.fa = fa;\n        s.ls = ls1;\n        s.t = t;\n        _store(s, pinChanged);\n        return (uint256(fb), uint256(fa));\n    }\n\n    function getName() external pure override returns (string memory) {\n        return \"MixtureB-frozen-bridge\";\n    }\n}\n","mfq.py":"\"\"\"mfq.py: Python reference of the Solidity port of mf.py (mixture-B, frozen). Differences vs mf.py, all declared:\n(1) Phi via Numerical-Recipes erfcc (fractional error <1.2e-7) instead of math.erf  [ERF='nr'];\n(2) silent gaps: exact per-step diffuse+truncate for the first CAP steps, then ONE diffuse(n-CAP)+truncate (mf.py: CAP=400);\n(3) trunc mass Z computed from the far tail when the band lies wholly on one side (cancellation-safe), Mills ratio of soft\n    truncation computed as 2/(sqrt(2pi) t e^poly) for a<0 (underflow-safe; algebraically identical under (1)).\nAll constants, the table, fee timing, latent labels and the observation boundary are unchanged.\"\"\"\n\"\"\"(base) Small Gaussian-mixture posterior of z=ln(p/spot) for the AMM fee challenge (replica B).\nFilter sees only: init reserves, and per own-pool callback (is_buy, amount_x, amount_y, t, x, y)\nplus its own stored fees. Policy = frozen oracle_table driven by p_hat=spot*exp(E[z]).\"\"\"\nimport math, json, os\nSQ2 = math.sqrt(2.0)\ndef phi(u): return math.exp(-0.5 * u * u) / 2.5066282746310002\n\nERF='nr'; CAP=1\ndef _erfc_pos(x):\n    t = 1.0 / (1.0 + 0.5 * x)\n    poly = -1.26551223 + t*(1.00002368 + t*(0.37409196 + t*(0.09678418 + t*(-0.18628806 + t*(0.27886807 + t*(-1.13520398 + t*(1.48851587 + t*(-0.82215223 + t*0.17087277))))))))\n    return t, poly\ndef erfc(x):\n    if ERF != 'nr': return math.erfc(x)\n    if x >= 0:\n        t, poly = _erfc_pos(x); return t * math.exp(-x * x + poly)\n    t, poly = _erfc_pos(-x); return 2.0 - t * math.exp(-x * x + poly)\ndef Phi(u): return 0.5 * erfc(-u / SQ2)\ndef Q(u): return 0.5 * erfc(u / SQ2)\ndef mills(a):\n    \"\"\"phi(a)/Phi(a)\"\"\"\n    if ERF == 'nr' and a < 0:\n        t, poly = _erfc_pos(-a / SQ2); return 2.0 / (2.5066282746310002 * t * math.exp(poly))\n    return phi(a) / Phi(a)\nP = dict(KMAX=4, SIG0=0.000945, N0=200.0, TAU_ROUTER=0.0015, ROUTER=1, PRETAIL=0.27,\n         RET_MU=math.log(20.0) - 0.72, RET_SD=1.3, PIN_VAR=1e-12, MINVAR=1e-12, ARB_CONF=0.9)\nTAB = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'oracle_table.json')))\n\n\ndef interp(arr, u):\n    u = (u + 8.0)\n    if u <= 0: return arr[0]\n    if u >= 16: return arr[16]\n    i = int(u); f = u - i\n    return arr[i] * (1 - f) + arr[i + 1] * f\n\ndef table_fees(spot, p, arbflag):\n    d = math.log(spot / p); u = d / 0.003\n    a = interp(TAB['ask'], u) + (TAB['arb_flag'] if arbflag else 0.0)\n    b = interp(TAB['bid'], u) + (TAB['arb_flag'] if arbflag else 0.0)\n    fa = 1 - math.exp(d - a * 0.003); fb = 1 - math.exp(-b * 0.003 - d)\n    return min(max(fb, 0.0), 0.1), min(max(fa, 0.0), 0.1)\n\ndef soft_upper(m, v, b, tau2):\n    \"\"\"Gaussian(m,v) times Phi((b-z)/sqrt(tau2)) (tau2=0: hard truncation z<=b).\n    Returns (mass, mean, var).\"\"\"\n    s = math.sqrt(v + tau2); a = (b - m) / s\n    Z = Phi(a)\n    if Z < 1e-300: return 0.0, b, P['MINVAR']\n    lam = mills(a)\n    mean = m - v / s * lam\n    var = v - v * v / (v + tau2) * lam * (a + lam)\n    return Z, mean, max(var, P['MINVAR'])\n\ndef soft_lower(m, v, b, tau2):\n    Z, mean, var = soft_upper(-m, v, -b, tau2)\n    return Z, -mean, var\n\ndef trunc(m, v, lo, hi):\n    \"\"\"hard truncate to [lo,hi]\"\"\"\n    s = math.sqrt(v); A = (lo - m) / s; B = (hi - m) / s\n    if A > 0: Z = Q(A) - Q(B)\n    elif B < 0: Z = Phi(B) - Phi(A)\n    else: Z = 1.0 - Phi(A) - Q(B)\n    if Z < 1e-14:  # numerically empty: put at nearest edge\n        e = lo if m < lo else hi\n        return 1e-300, min(max(e, lo), hi), P['MINVAR']\n    pa, pb = phi(A), phi(B)\n    mu = m + s * (pa - pb) / Z\n    var = v * (1 + (A * pa - B * pb) / Z - ((pa - pb) / Z) ** 2)\n    return Z, mu, max(var, P['MINVAR'])\n\nclass MixFilter:\n    def __init__(self, x0, y0, router=None):\n        self.x, self.y = x0, y0\n        self.c = [[1.0, 0.0, 1e-10]]  # weight, mean, var  (p0 == initial spot)\n        self.t = -1; self.fb = self.fa = 0.003\n        self.sig2 = P['SIG0'] ** 2; self.sq = 0.0; self.dt = 0.0\n        self.lastpin = None; self.router = P['ROUTER'] if router is None else router\n        self.merge_err = 0.0; self.nmerge = 0; self.parb = 0.0\n    def _diffuse(self, n=1):\n        d = self.sig2 * n\n        for c in self.c: c[1] += -0.5 * self.sig2 * n; c[2] += d\n    def _band(self):\n        lo = math.log(1 - self.fb) if self.fb < 1 else -50; hi = -math.log(1 - self.fa)\n        out = []; tot = 0.0\n        for w, m, v in self.c:\n            Z, mu, var = trunc(m, v, lo, hi); out.append([w * Z, mu, var]); tot += w * Z\n        return out, tot\n    def _silent(self, n):\n        # fully silent steps: diffuse+truncate each step; long gaps: after 64 steps truncation\n        # is near stationary, cap per-step loop and do remaining in chunks of diffusion+trunc\n        for _ in range(min(n, CAP)):\n            self._diffuse(1)\n            out, tot = self._band()\n            if tot > 0: self.c = [[w / tot, m, v] for w, m, v in out]\n        if n > CAP:\n            self._diffuse(n - CAP); out, tot = self._band()\n            if tot > 0: self.c = [[w / tot, m, v] for w, m, v in out]\n    def _norm_prune(self):\n        tot = sum(c[0] for c in self.c)\n        self.c = [[w / tot, m, v] for w, m, v in self.c if w / tot > 1e-9]\n        while len(self.c) > P['KMAX']:\n            best = None\n            for i in range(len(self.c)):\n                for j in range(i + 1, len(self.c)):\n                    wi, mi, vi = self.c[i]; wj, mj, vj = self.c[j]\n                    w = wi + wj; m = (wi * mi + wj * mj) / w\n                    v = (wi * (vi + (mi - m) ** 2) + wj * (vj + (mj - m) ** 2)) / w\n                    # Runnalls KL upper bound of the merge\n                    cost = 0.5 * (w * math.log(v) - wi * math.log(vi) - wj * math.log(vj))\n                    if best is None or cost < best[0]: best = (cost, i, j, [w, m, v])\n            cost, i, j, nc = best\n            self.merge_err += cost; self.nmerge += 1\n            self.c = [c for k, c in enumerate(self.c) if k not in (i, j)] + [nc]\n    def mean(self): return sum(w * m for w, m, v in self.c)\n    def var(self):\n        mu = self.mean(); return sum(w * (v + (m - mu) ** 2) for w, m, v in self.c)\n    def update(self, is_buy, ax, ay, t, x, y):\n        \"\"\"is_buy: AMM bought X (trader sold X). Returns (p_hat, arb_prob).\"\"\"\n        ls0 = math.log(self.y / self.x); ls1 = math.log(y / x)\n        first = t != self.t\n        parb = 0.0\n        if first:\n            if t > self.t + 1: self._silent(t - self.t - 1)\n            self._diffuse(1)\n            lo = math.log(1 - self.fb); hi = -math.log(1 - self.fa)\n            # arb hypothesis: pinned z (relative to post spot), mapped back to pre-spot coords\n            if not is_buy:  # AMM sold X: arb bought X, spot_after = p(1-fa)\n                zpost = -math.log(1 - self.fa); xn = x; a = ax\n                consistent = True\n            else:            # AMM bought X: spot_after = p/(1-fb)\n                zpost = math.log(1 - self.fb); xn = x; a = ax\n                consistent = True\n            zpre = zpost + ls1 - ls0\n            dens = 0.0\n            ok = (zpre > hi) if not is_buy else (zpre < lo)\n            if ok and consistent:\n                for w, m, v in self.c:\n                    dens += w * phi((zpre - m) / math.sqrt(v)) / math.sqrt(v)\n                dens *= 2.0 * a / xn  # |dz/dln a|\n            # retail hypothesis: no-arb band, then retail density of ln(amount in Y)\n            out, tot = self._band()\n            ly = math.log(max(ay, 1e-9))\n            dret = P['PRETAIL'] * phi((ly - P['RET_MU']) / P['RET_SD']) / P['RET_SD']\n            Larb = dens; Lret = tot * dret\n            parb = Larb / (Larb + Lret) if Larb + Lret > 0 else 0.0\n            ret = [[w * (1 - parb) / max(tot, 1e-300), m - (ls1 - ls0), v] for w, m, v in out] if tot > 0 else []\n            comps = ret + ([[parb, zpost, P['PIN_VAR']]] if parb > 1e-9 else [])\n            if not comps: comps = [[1.0, zpost, P['PIN_VAR']]]\n            self.c = comps\n            if parb > P['ARB_CONF']:\n                lp = ls1 + zpost\n                if self.lastpin is not None:\n                    tp, lpp = self.lastpin; dtp = t - tp\n                    if dtp > 0:\n                        self.sq += (lp - lpp + 0.5 * self.sig2 * dtp) ** 2; self.dt += dtp\n                        self.sig2 = (P['N0'] * P['SIG0'] ** 2 + self.sq) / (P['N0'] + self.dt)\n                self.lastpin = (t, lp)\n        else:\n            for c in self.c: c[1] -= (ls1 - ls0)\n        if parb < 0.5 and self.router:\n            # weak router observation: rival marginal >= p-band => z <= -ln(1-fa) after buy-X fill\n            tau2 = P['TAU_ROUTER'] ** 2; new = []\n            for w, m, v in self.c:\n                if v <= P['PIN_VAR'] * 1.01: new.append([w, m, v]); continue\n                if not is_buy: Z, mu, var = soft_upper(m, v, -math.log(1 - self.fa), tau2)\n                else: Z, mu, var = soft_lower(m, v, math.log(1 - self.fb), tau2)\n                new.append([w * Z, mu, var])\n            if sum(c[0] for c in new) > 0: self.c = new\n        self._norm_prune()\n        self.x, self.y, self.t = x, y, t; self.parb = parb\n        return (y / x) * math.exp(self.mean()), parb\n    def set_fees(self, fb, fa): self.fb, self.fa = fb, fa\n\nclass Arm:\n    \"\"\"mode: 'oracle' (true p, true flag), 'filter' (p_hat, inferred flag),\n    'diag' (true p, inferred flag). Oracle values are passed to the filter NEVER.\"\"\"\n    def __init__(self, mode, router=None):\n        self.mode = mode; self.router = router\n    def init(self, sigma, lam, size):  # hidden params ignored\n        self.f = MixFilter(100.0, 10000.0, self.router)\n        self.se = 0.0; self.n = 0; self.cov = 0; self.parb_err = 0\n        return 0.003, 0.003\n    def on_trade(self, is_buy, ax, ay, t, x, y, arb, p, s2):\n        ph, parb = self.f.update(is_buy, ax, ay, t, x, y)\n        e = math.log(ph / p); self.se += e * e; self.n += 1\n        sd = math.sqrt(self.f.var()); self.cov += abs(e) <= 1.96 * sd + 1e-12\n        flag = parb > 0.5; self.parb_err += flag != arb\n        s = y / x\n        if self.mode == 'oracle': fb, fa = table_fees(s, p, arb)\n        elif self.mode == 'diag': fb, fa = table_fees(s, p, flag)\n        else: fb, fa = table_fees(s, ph, flag)\n        self.f.set_fees(fb, fa)\n        return fb, fa\n","parity.py":"\"\"\"Closed-loop parity: replica.py market driven by the EVM port; every own-pool callback (TradeInfo only) is fed to\nEVM, mf.py (original) and mfq.py (port reference). Records per-callback gas, fee diffs, repeats/gaps, first divergence.\"\"\"\nimport sys, os, json, math, time\nsys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))\nimport replica, mf, mfq\nfrom pyrevm import EVM\n\nCALLER = \"0x\" + \"11\" * 20\nSEL_INIT = bytes.fromhex(\"837aef47\")  # afterInitialize(uint256,uint256)\nSEL_SWAP = None\nGAS = 250_000\n\n\ndef sel(sig):\n    from eth_hash.auto import keccak\n    return keccak(sig.encode())[:4]\n\n\ndef enc(*vals):\n    return b\"\".join(int(v).to_bytes(32, \"big\", signed=False) for v in vals)\n\n\nclass EvmStrat:\n    def __init__(self, bytecode):\n        self.e = EVM()\n        self.addr = self.e.deploy(CALLER, bytecode, gas=10_000_000)\n        self.si = bytes([0x83, 0x7a, 0xef, 0x47])  # amm_sim_rs SELECTOR_AFTER_INITIALIZE\n        self.ss = bytes([0xc2, 0xba, 0xbb, 0x57])  # amm_sim_rs SELECTOR_AFTER_SWAP\n\n    def call(self, data):\n        try:\n            out = self.e.message_call(CALLER, self.addr, data, gas=GAS)\n            r = self.e.result\n            gas = getattr(r, \"gas_used\", None)\n            if len(out) != 64:\n                return None, gas, \"bad_return\"\n            return (int.from_bytes(out[:32], \"big\"), int.from_bytes(out[32:], \"big\")), gas, \"ok\"\n        except Exception as ex:\n            msg = str(ex)\n            kind = \"oog\" if \"OutOfGas\" in msg or \"out of gas\" in msg.lower() else (\"revert\" if \"evert\" in msg else \"other_halt\")\n            return None, None, kind + \":\" + msg[:120]\n\n\ndef W(v):\n    return int(round(v * 1e18))\n\n\nclass Arm:\n    def __init__(self, bytecode, rec):\n        self.bc = bytecode; self.rec = rec\n\n    def init(self, sigma, lam, size):\n        self.ev = EvmStrat(self.bc)\n        fees, gas, st = self.ev.call(self.ev.si + enc(W(100.0), W(10000.0)))\n        self.rec[\"init\"] = dict(gas=gas, status=st)\n        self.F = {\"mf\": mf.MixFilter(100.0, 10000.0), \"mfq\": mfq.MixFilter(100.0, 10000.0)}\n        self.fees = fees if fees else (3 * 10**15, 3 * 10**15)\n        self.lastt = -1\n        return self.fees[0] / 1e18, self.fees[1] / 1e18\n\n    def on_trade(self, is_buy, ax, ay, t, x, y, arb, p, s2):\n        R = self.rec\n        wa, wy, wx, wyy = W(ax), W(ay), W(x), W(y)\n        data = self.ev.ss + enc(1 if is_buy else 0, wa, wy, t, wx, wyy)\n        fees, gas, st = self.ev.call(data)\n        R[\"n\"] += 1\n        kind = \"repeat\" if t == self.lastt else (\"gap\" if t > self.lastt + 1 else \"next\")\n        R[kind] += 1\n        if kind == \"gap\": R[\"maxgap\"] = max(R[\"maxgap\"], t - self.lastt - 1); R[\"gaps_gt_cap\"] += (t - self.lastt - 1) > mfq.CAP\n        self.lastt = t\n        if st != \"ok\":\n            R[\"fail\"].append(dict(t=t, status=st)); R[\"status\"][st.split(\":\")[0]] = R[\"status\"].get(st.split(\":\")[0], 0) + 1\n        else:\n            R[\"status\"][\"ok\"] = R[\"status\"].get(\"ok\", 0) + 1\n            R[\"gas_max\"] = max(R[\"gas_max\"], gas or 0); R[\"gas_sum\"] += gas or 0\n            self.fees = fees\n        out = {}\n        for k, mod in ((\"mf\", mf), (\"mfq\", mfq)):\n            F = self.F[k]\n            ph, parb = F.update(is_buy, wa / 1e18, wy / 1e18, t, wx / 1e18, wyy / 1e18)\n            fb, fa = mod.table_fees(F.y / F.x, ph, parb > 0.5)\n            F.set_fees(fb, fa); out[k] = (fb, fa, parb)\n        eb, ea = self.fees[0] / 1e18, self.fees[1] / 1e18\n        for k in (\"mf\", \"mfq\"):\n            d = max(abs(eb - out[k][0]), abs(ea - out[k][1]))\n            R[\"maxdiff_\" + k] = max(R[\"maxdiff_\" + k], d)\n            if d > 1e-6:\n                R[\"over_\" + k] += 1\n                if R[\"first_div_\" + k] is None:\n                    R[\"first_div_\" + k] = dict(t=t, cb=R[\"n\"], evm=[eb, ea], py=list(out[k][:2]), parb=out[k][2])\n        R[\"flag_mismatch_mfq_vs_mf\"] += (out[\"mf\"][2] > 0.5) != (out[\"mfq\"][2] > 0.5)\n        R[\"arbflag_err_mfq\"] += (out[\"mfq\"][2] > 0.5) != arb\n        if R.get(\"log\") is not None and len(R[\"log\"]) < 4000:\n            R[\"log\"].append([1 if is_buy else 0, wa, wy, t, wx, wyy, self.fees[0], self.fees[1]])\n        return eb, ea\n\n\ndef run_seed(seed, steps, bytecode, keep_log=False):\n    R = dict(seed=seed, n=0, repeat=0, gap=0, next=0, maxgap=0, gaps_gt_cap=0, fail=[], status={}, gas_max=0, gas_sum=0,\n             maxdiff_mf=0.0, maxdiff_mfq=0.0, over_mf=0, over_mfq=0, first_div_mf=None, first_div_mfq=None,\n             flag_mismatch_mfq_vs_mf=0, arbflag_err_mfq=0, log=[] if keep_log else None)\n    t0 = time.time()\n    res = replica.run(seed, Arm(bytecode, R), n_steps=steps)\n    R[\"replica\"] = {k: res[k] for k in (\"edge\", \"retail\", \"arb\", \"rv\", \"av\", \"fb\", \"fa\")}\n    R[\"sec\"] = time.time() - t0\n    R[\"fail\"] = R[\"fail\"][:5]\n    return R\n\n\nif __name__ == \"__main__\":\n    mfq.CAP = int(os.environ.get(\"CAP\", mfq.CAP))\n    bc = open(sys.argv[1], \"rb\").read()\n    steps = int(sys.argv[2]); seeds = [int(s) for s in sys.argv[3].split(\",\")]\n    for s in seeds:\n        r = run_seed(s, steps, bc)\n        print(json.dumps(r), flush=True)\n","build.py":"import sys, hashlib, json\nfrom amm_competition.evm.compiler import SolidityCompiler\nfrom amm_competition.evm.validator import SolidityValidator\nsrc = open(sys.argv[1]).read()\nv = SolidityValidator().validate(src)\nr = SolidityCompiler().compile(src)\nprint(json.dumps(dict(valid=v.valid, verrors=v.errors, ok=r.success, errors=r.errors,\n                      src_sha256=hashlib.sha256(src.encode()).hexdigest(),\n                      bytes=len(r.bytecode or b\"\"), bytecode_sha256=hashlib.sha256(r.bytecode or b\"\").hexdigest())))\nif r.success:\n    open(sys.argv[2], \"wb\").write(r.bytecode)\n","mferr.py":"\"\"\"Error of mfq.py (port reference, CAP=1, NR erfc) vs original mf.py on identical callbacks; market driven by mf.py fees.\"\"\"\nimport sys, os, json, math\nsys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))\nimport replica, mf, mfq\n\n\nclass A:\n    def init(self, *a):\n        self.a = mf.MixFilter(100.0, 10000.0); self.b = mfq.MixFilter(100.0, 10000.0)\n        self.n = 0; self.over = 0; self.mx = 0.0; self.first = None; self.se = 0.0; self.flag = 0\n        return 0.003, 0.003\n\n    def on_trade(self, is_buy, ax, ay, t, x, y, arb, p, s2):\n        r = []\n        for F, mod in ((self.a, mf), (self.b, mfq)):\n            ph, pa = F.update(is_buy, ax, ay, t, x, y)\n            fb, fa = mod.table_fees(y / x, ph, pa > 0.5); F.set_fees(fb, fa); r.append((fb, fa, ph, pa))\n        d = max(abs(r[0][0] - r[1][0]), abs(r[0][1] - r[1][1]))\n        self.n += 1; self.mx = max(self.mx, d); self.over += d > 1e-6\n        self.se += math.log(r[1][2] / r[0][2]) ** 2; self.flag += (r[0][3] > .5) != (r[1][3] > .5)\n        if d > 1e-6 and self.first is None: self.first = dict(t=t, cb=self.n, d=d)\n        return r[0][0], r[0][1]\n\n\nfor s in [int(v) for v in sys.argv[2].split(\",\")]:\n    a = A(); replica.run(s, a, n_steps=int(sys.argv[1]))\n    print(json.dumps(dict(seed=s, callbacks=a.n, max_fee_diff=a.mx, over_1e6=a.over, first_over=a.first,\n                          rms_phat_diff_bps=1e4 * math.sqrt(a.se / a.n), flag_mismatch=a.flag)), flush=True)\n"}}