Current task: —

E-019-zeroflow-a12.py

Experiments: E-019 · script updated 08-18-2026

E-019-zeroflow-a12.csv — 55.3 KB, updated 08-18-2026

page 1 of 5 · 500/page · 2129 rows next →
# seriesajvalue
2 zero 10 1 0.4495970125
3 zero 10 2 0.7729656445
4 zero 10 3 1.0910341734
5 zero 10 4 1.4074225952
6 zero 10 5 1.7230599232
7 zero 10 6 2.0382961524
8 zero 10 7 2.3532937979
9 zero 10 8 2.6681366685
10 zero 10 9 2.9828746207
11 zero 10 10 3.2975393066
12 zero 10 11 3.6121505336
13 zero 10 12 3.9267190506
14 zero 10 13 4.2412588597
15 zero 10 14 4.5557702720
16 zero 10 15 4.8702669562
17 zero 10 16 5.1847439267
18 zero 10 17 5.4992177540
19 zero 10 18 5.8136727234
20 zero 10 19 6.1281307224
21 zero 10 20 6.4425807469
22 zero 10 21 6.7570263981
23 zero 10 22 7.0714694880
24 zero 10 23 7.3859156282
25 zero 10 24 7.7003618946
26 zero 10 25 8.0148178004
27 zero 10 26 8.3292707069
28 zero 10 27 8.6437307844
29 zero 10 28 8.9582101672
30 zero 10 29 9.2726777935
31 zero 10 30 9.5871893371
32 zero 10 31 9.9016972850
33 zero 10 32 10.2162424022
34 zero 10 33 10.5308208778
35 zero 10 34 10.8454234374
36 zero 10 35 11.1600951105
37 zero 10 36 11.4748303143
38 zero 10 37 11.7896703075
39 zero 10 38 12.1046582143
40 zero 10 39 12.4198659343
41 zero 10 40 12.7354576696
42 zero 10 41 13.0517765082
43 zero 10 42 13.3696805996
44 zero 10 43 13.6925880403
45 zero 10 44 14.1078445663
46 zero 10 45 14.1872365812
47 zero 10 46 14.5943806442
48 zero 10 47 14.9172691749
49 zero 10 48 15.2351773613
50 zero 10 49 15.5515728816
51 zero 10 50 15.8671813633
52 zero 10 51 16.1824781538
53 zero 10 52 16.4975647459
54 zero 10 53 16.8125184595
55 zero 10 54 17.1273666550
56 zero 10 55 17.4422004368
57 zero 10 56 17.7570000558
58 zero 10 57 18.0718411247
59 zero 10 58 18.3866748954
60 zero 10 59 18.7015609277
61 zero 10 60 19.0166231741
62 zero 10 61 19.3318840805
63 zero 10 62 19.6474130799
64 zero 10 63 19.9635468221
65 zero 10 64 20.2812249229
66 zero 10 65 20.6033226727
67 zero 10 66 21.0100201742
68 zero 10 67 21.1321487731
69 zero 10 68 21.5096707783
70 zero 10 69 21.8315316227
71 zero 10 70 22.1490832222
72 zero 10 71 22.4652509355
73 zero 10 72 22.7808021472
74 zero 10 73 23.0960257053
75 zero 10 74 23.4110286524
76 zero 10 75 23.7259378006
77 zero 10 76 24.0408814213
78 zero 10 77 24.3558645529
79 zero 10 78 24.6713844859
80 zero 10 79 25.0095528736
81 zero 10 80 25.2962561267
82 zero 10 81 25.6122241117
83 zero 10 82 25.9272221868
84 zero 10 83 26.2420008851
85 zero 10 84 26.5567194991
86 zero 10 85 26.8714334062
87 zero 10 86 27.1860506924
88 zero 10 87 27.5009127798
89 zero 10 88 27.8155265209
90 zero 10 89 28.1304583962
91 zero 10 90 28.4453526958
92 zero 10 91 28.7604533721
93 zero 10 92 29.0758996727
94 zero 10 93 29.3916006437
95 zero 10 94 29.7086795016
96 zero 10 95 30.0291928667
97 zero 10 96 30.4183746509
98 zero 10 97 30.5985483598
99 zero 10 98 30.9459403240
100 zero 10 99 31.2661190612
101 zero 10 100 31.5835352593
102 zero 10 101 31.9005808665
103 zero 10 102 32.2182758146
104 zero 10 103 32.5391825999
105 zero 10 104 32.9285568449
106 zero 10 105 33.1086257447
107 zero 10 106 33.4558132858
108 zero 10 107 33.7756824489
109 zero 10 108 34.0925478517
110 zero 10 109 34.4084669806
111 zero 10 110 34.7236853784
112 zero 10 111 35.0389929244
113 zero 10 112 35.3538337276
114 zero 10 113 35.6688775957
115 zero 10 114 35.9836858129
116 zero 10 115 36.2983990184
117 zero 10 116 36.6134249908
118 zero 10 117 36.9283431533
119 zero 10 118 37.2442770258
120 zero 10 119 37.5847393265
121 zero 10 120 37.8684705345
122 zero 10 121 38.1848406638
123 zero 10 122 38.5000715295
124 zero 10 123 38.8153220951
125 zero 10 124 39.1306288402
126 zero 10 125 39.4461621894
127 zero 10 126 39.7621362910
128 zero 10 127 40.0791056837
129 zero 10 128 40.3991638010
130 zero 10 129 40.7475188898
131 zero 10 130 40.9253845967
132 zero 10 131 41.3155933225
133 zero 10 132 41.6369812575
134 zero 10 133 41.9548415439
135 zero 10 134 42.2722933901
136 zero 10 135 42.5904886954
137 zero 10 136 42.9129760979
138 zero 10 137 43.3167102107
139 zero 10 138 43.4518714981
140 zero 10 139 43.8220962380
141 zero 10 140 44.1439703220
142 zero 10 141 44.4616308564
143 zero 10 142 44.7777008838
144 zero 10 143 45.0935453962
145 zero 10 144 45.4086510526
146 zero 10 145 45.7242681142
147 zero 10 146 46.0393026369
148 zero 10 147 46.3548270696
149 zero 10 148 46.6699955317
150 zero 10 149 46.9854738758
151 zero 10 150 47.3022069574
152 zero 10 151 47.6216183857
153 zero 10 152 48.0003136822
154 zero 10 153 48.2091483289
155 zero 10 154 48.5439938634
156 zero 10 155 48.8628820911
157 zero 10 156 49.1799230710
158 zero 10 157 49.4978750274
159 zero 10 158 49.7755482895
160 zero 10 159 50.1203070102
161 zero 10 160 50.4364651251
162 zero 10 161 50.7514965645
163 zero 10 162 51.0667156156
164 zero 10 163 51.3809952930
165 zero 10 164 51.6960272119
166 zero 10 165 52.0106744300
167 zero 10 166 52.3256456649
168 zero 10 167 52.6404227139
169 zero 10 168 52.9695486041
170 zero 10 169 53.2678343537
171 zero 10 170 53.5829081669
172 zero 10 171 53.8974832078
173 zero 10 172 54.2126100883
174 zero 10 173 54.5271796773
175 zero 10 174 54.8415353568
176 zero 10 175 55.1564503157
177 zero 10 176 55.4711099131
178 zero 10 177 55.7860115066
179 zero 10 178 56.1019114392
180 zero 10 179 56.4446630783
181 zero 10 180 56.7249421329
182 zero 10 181 57.0418236096
183 zero 10 182 57.3573662002
184 zero 10 183 57.6725461613
185 zero 10 184 57.9881360941
186 zero 10 185 58.3037886740
187 zero 10 186 58.6219796780
188 zero 10 187 58.9428048222
189 zero 10 188 59.3390063259
190 zero 10 189 59.4974232893
191 zero 10 190 59.8549703979
192 zero 10 191 60.1759079564
193 zero 10 192 60.4939088054
194 zero 10 193 60.8306355329
195 zero 10 194 61.1219077941
196 zero 10 195 61.4381568551
197 zero 10 196 61.7534568145
198 zero 10 197 62.0685659453
199 zero 10 198 62.3835689540
200 zero 10 199 62.6983107578
201 zero 10 200 63.0135074131
202 zero 10 201 63.3289421192
203 zero 10 202 63.6445572786
204 zero 10 203 63.9599664847
205 zero 10 204 64.2763999095
206 zero 10 205 64.5973271312
207 zero 10 206 64.9485616832
208 zero 10 207 65.1196307299
209 zero 10 208 65.5119866190
210 zero 10 209 65.8325168580
211 zero 10 210 66.1501076726
212 zero 10 211 66.4657567160
213 zero 10 212 66.7819936528
214 zero 10 213 67.0806184024
215 zero 10 214 67.4108247553
216 zero 10 215 67.7258079400
217 zero 10 216 68.0415440271
218 zero 10 217 68.3571044034
219 zero 10 218 68.6729588718
220 zero 10 219 68.9903961368
221 zero 10 220 69.3173718776
222 zero 10 221 69.5501010682
223 zero 10 222 69.9189986117
224 zero 10 223 70.2375132161
225 zero 10 224 70.5542266145
226 zero 10 225 70.8685779852
227 zero 10 226 71.1853407584
228 zero 10 227 71.5023545680
229 zero 10 228 71.8271332607
230 zero 10 229 72.0703451487
231 zero 10 230 72.4348877442
232 zero 10 231 72.7514326656
233 zero 10 232 73.0678031732
234 zero 10 233 73.3836603266
235 zero 10 234 73.6989771506
236 zero 10 235 74.0145393763
237 zero 10 236 74.3307032260
238 zero 10 237 74.6463036691
239 zero 10 238 74.9642854564
240 zero 10 239 75.2869285891
241 zero 10 240 75.6930575450
242 zero 10 241 75.8179408862
243 zero 10 242 76.1946148247
244 zero 10 243 76.5156125166
245 zero 10 244 76.8328834503
246 zero 10 245 77.1450498491
247 zero 10 246 77.4648349752
248 zero 10 247 77.7804110327
249 zero 10 248 78.0951643484
250 zero 10 249 78.4103466527
251 zero 10 250 78.7261047626
252 zero 10 251 79.0410402063
253 zero 10 252 79.3382284664
254 zero 10 253 79.6686775071
255 zero 10 254 79.9841054982
256 zero 10 255 80.3000380405
257 zero 10 256 80.6129810794
258 zero 10 257 80.9288600364
259 zero 10 258 81.2447776383
260 zero 10 259 81.5595004026
261 zero 10 260 81.8756967371
262 zero 10 261 82.1930361838
263 zero 10 262 82.5153102982
264 zero 10 263 82.9035706768
265 zero 10 264 83.0799543682
266 zero 10 265 83.4295086496
267 zero 10 266 83.7496354452
268 zero 10 267 84.0674178655
269 zero 10 268 84.3850470969
270 zero 10 269 84.7335956891
271 zero 10 270 85.0066638888
272 zero 10 271 85.3255130356
273 zero 10 272 85.6422987043
274 zero 10 273 85.9575067449
275 zero 10 274 86.2742834810
276 zero 10 275 86.5904303135
277 zero 10 276 86.9125948675
278 zero 10 277 87.2664193366
279 zero 10 278 87.4328055123
280 zero 10 279 87.8256075971
281 zero 10 280 88.1474448776
282 zero 10 281 88.4655369075
283 zero 10 282 88.8076777969
284 zero 10 283 89.0937844099
285 zero 10 284 89.4105516188
286 zero 10 285 89.7261798477
287 zero 10 286 90.0395318408
288 zero 10 287 90.3563067178
289 zero 10 288 90.6718917616
290 zero 10 289 90.9866091313
291 zero 10 290 91.3016446677
292 zero 10 291 91.6189813872
293 zero 10 292 91.9367104254
294 zero 10 293 92.2661588879
295 zero 10 294 92.4958608788
296 zero 10 295 92.8651572033
297 zero 10 296 93.1854565862
298 zero 10 297 93.5032652506
299 zero 10 298 93.8236233704
300 zero 10 299 94.1436100635
301 zero 10 300 94.5084021174
302 zero 10 301 94.6602033344
303 zero 10 302 95.0631255369
304 zero 10 303 95.3940645807
305 zero 10 304 95.7863293055
306 zero 10 305 95.8874882696
307 zero 10 306 96.3013577282
308 zero 10 307 96.6257782221
309 zero 10 308 96.9439454359
310 zero 10 309 97.2617111548
311 zero 10 310 97.5744575811
312 zero 10 311 97.8925785477
313 zero 10 312 98.2080781422
314 zero 10 313 98.5237091997
315 zero 10 314 98.8315296658
316 zero 10 315 99.1521608467
317 zero 10 316 99.4683898421
318 zero 10 317 99.7851789345
319 zero 10 318 100.0970212580
320 zero 10 319 100.4132965126
321 zero 10 320 100.7295101700
322 zero 10 321 101.0492952197
323 zero 10 322 101.3198677172
324 zero 10 323 101.6696467511
325 zero 10 324 101.9868724584
326 zero 10 325 102.3040153991
327 zero 10 326 102.6198193421
328 zero 10 327 102.9392665901
329 zero 10 328 103.2612686003
330 zero 10 329 103.6671353048
331 zero 10 330 103.7498508188
332 zero 10 331 104.1660865450
333 zero 10 332 104.4874921057
334 zero 10 333 104.8076265370
335 zero 10 334 105.1248650880
336 zero 10 335 105.4464016648
337 zero 10 336 105.7606666049
338 zero 10 337 106.0750319279
339 zero 10 338 106.3952473250
340 zero 10 339 106.7193372031
341 zero 10 340 107.1319502090
342 zero 10 341 107.2079290238
343 zero 10 342 107.6215598669
344 zero 10 343 107.9447391101
345 zero 10 344 108.2620541900
346 zero 10 345 108.5804800191
347 zero 10 346 108.8973528973
348 zero 10 347 109.2134268815
349 zero 10 348 109.5295906937
350 zero 10 349 109.8474698101
351 zero 10 350 110.1629318706
352 zero 10 351 110.4827793819
353 zero 10 352 110.8173884801
354 zero 10 353 111.0343700123
355 zero 10 354 111.4224770495
356 zero 10 355 111.8427052733
357 zero 10 356 111.9206133614
358 zero 10 357 112.3336504714
359 zero 10 358 112.6588439364
360 zero 10 359 112.9738137272
361 zero 10 360 113.2939917833
362 zero 10 361 113.6131503375
363 zero 10 362 113.9334555814
364 zero 10 363 114.3152318126
365 zero 10 364 114.5272293013
366 zero 10 365 114.8587815080
367 zero 10 366 115.1809915813
368 zero 10 367 115.5020807733
369 zero 10 368 115.8199469638
370 zero 10 369 116.2187777729
371 zero 10 370 116.3815462804
372 zero 10 371 116.7399044172
373 zero 10 372 117.0610414881
374 zero 10 373 117.3799008958
375 zero 10 374 117.6984490784
376 zero 10 375 118.0199529463
377 zero 10 376 118.3420206849
378 zero 10 377 118.7585985558
379 zero 10 378 118.8356185365
380 zero 10 379 119.2482568241
381 zero 10 380 119.5673477367
382 zero 10 381 119.8899124579
383 zero 10 382 120.2083946324
384 zero 10 383 120.5280932870
385 zero 10 384 120.8505995512
386 zero 10 385 121.1988322739
387 zero 10 386 121.3770325471
388 zero 10 387 121.7719202856
389 zero 10 388 122.0916043255
390 zero 10 389 122.4122781143
391 zero 10 390 122.7556119442
392 zero 10 391 122.9523016695
393 zero 10 392 123.3366544237
394 zero 10 393 123.6632360162
395 zero 10 394 123.9741045608
396 zero 10 395 124.2584280882
397 zero 10 396 124.6017448769
398 zero 10 397 124.9203520512
399 zero 10 398 125.2370755526
400 zero 10 399 125.5539997339
401 zero 10 400 125.8729213334
402 zero 10 401 126.1773291996
403 zero 10 402 126.5017654152
404 zero 10 403 126.8093033907
405 zero 10 404 127.1340196131
406 zero 10 405 127.5120603794
407 zero 10 406 127.7275188655
408 zero 10 407 128.0578894943
409 zero 10 408 128.3816299480
410 zero 10 409 128.6991680483
411 zero 10 410 129.0186681536
412 zero 10 411 129.3497890137
413 zero 10 412 129.5828834544
414 zero 10 413 129.9458068529
415 zero 10 414 130.2716564364
416 zero 10 415 130.5951116878
417 zero 10 416 130.9744168536
418 zero 10 417 131.0996646132
419 zero 10 418 131.5083009852
420 zero 10 419 131.8361427019
421 zero 10 420 132.1478851385
422 zero 10 421 132.4683128556
423 zero 10 422 132.7894932854
424 zero 10 423 133.1009540824
425 zero 10 424 133.4917146961
426 zero 10 425 133.6837912520
427 zero 10 426 134.0309435942
428 zero 10 427 134.3577502596
429 zero 10 428 134.7496162120
430 zero 10 429 134.9332845458
431 zero 10 430 135.2746186495
432 zero 10 431 135.5978124013
433 zero 10 432 135.9165225586
434 zero 10 433 136.2319598194
435 zero 10 434 136.5480742107
436 zero 10 435 136.8690840178
437 zero 10 436 137.1783507379
438 zero 10 437 137.4947912083
439 zero 10 438 137.8083502532
440 zero 10 439 138.1164366062
441 zero 10 440 138.4434376572
442 zero 10 441 138.7488056683
443 zero 10 442 139.0676447940
444 zero 10 443 139.3856691818
445 zero 10 444 139.7346714676
446 zero 10 445 140.0054244614
447 zero 10 446 140.3324504989
448 zero 10 447 140.6570830505
449 zero 10 448 141.0633146440
450 zero 10 449 141.1476179381
451 zero 10 450 141.5654238637
452 zero 10 451 141.8749283648
453 zero 10 452 142.2016650453
454 zero 10 453 142.5233868629
455 zero 10 454 142.8334689648
456 zero 10 455 143.1137196423
457 zero 10 456 143.4642498292
458 zero 10 457 143.7790819452
459 zero 10 458 144.0969722601
460 zero 10 459 144.4196999446
461 zero 10 460 144.7258446169
462 zero 10 461 145.0459547523
463 zero 10 462 145.3662183206
464 zero 10 463 145.6678507479
465 zero 10 464 146.0003230769
466 zero 10 465 146.3080811744
467 zero 10 466 146.6119188746
468 zero 10 467 146.9385784421
469 zero 10 468 147.3321569811
470 zero 10 469 147.4383934796
471 zero 10 470 147.8498623624
472 zero 10 471 148.1739357270
473 zero 10 472 148.4916285398
474 zero 10 473 148.8110627722
475 zero 10 474 149.1256140861
476 zero 10 475 149.4345899821
477 zero 10 476 149.7631883096
478 zero 10 477 150.0551788823
479 zero 10 478 150.3799984627
480 zero 10 479 150.7317925398
481 zero 10 480 150.9309151593
482 zero 10 481 151.3179802177
483 zero 10 482 151.6257170855
484 zero 10 483 151.9545027247
485 zero 10 484 152.2773569730
486 zero 10 485 152.6113263867
487 zero 10 486 153.0065750350
488 zero 10 487 153.1073726408
489 zero 10 488 153.5015978519
490 zero 10 489 153.8264239676
491 zero 10 490 154.1440050957
492 zero 10 491 154.4672867749
493 zero 10 492 154.7802087049
494 zero 10 493 155.1016181313
495 zero 10 494 155.4274225019
496 zero 10 495 155.7272555110
497 zero 10 496 156.1079211896
498 zero 10 497 156.3217277930
499 zero 10 498 156.6569413117
500 zero 10 499 156.9777004648
501 zero 10 500 157.3005569206

E-019-zeroflow-a12.json — 2.4 KB, updated 08-18-2026

JSON sidecar (click to expand)
{
  "experiment": "E-019",
  "task": "2.8(ii): backend v2 validation + zero-flow at a=12",
  "script": "research/numerics/scripts/E-019-zeroflow-a12.py",
  "parameters": {
    "a_val": 10.0,
    "a_new": 12.0,
    "h": 0.01,
    "lambda": -1.0,
    "z_max": 300.0
  },
  "checks": {
    "VA1_backend_matches": true,
    "VA2_pipeline_a10": true,
    "B3_no_new_complex_zeros": true
  },
  "report": {
    "validation": {
      "VA1_max_dev": 2.2246968001127243e-07,
      "VA2_Q_dev": 4.3871651103644105e-08,
      "VA2_z1_dev": 6.942222297023903e-09,
      "VA2_count_diff": 0
    },
    "B1_zeroflow": {
      "a10": {
        "n": 954,
        "z1": 0.44959701245436284,
        "z1_times_a": 4.4959701245436285,
        "count_vs_lattice": 954.9296585513721,
        "gap_mean": 0.31421194329970964,
        "gap_cv": 0.19770812588191275
      },
      "a12": {
        "n": 1145,
        "z1": 0.3746087478472408,
        "z1_times_a": 4.495304974166889,
        "count_vs_lattice": 1145.9155902616465,
        "gap_mean": 0.26188092266824087,
        "gap_cv": 0.18590651886845952
      }
    },
    "B2_gamma_locking": {
      "a10": {
        "min": 0.00020978025152373903,
        "zero_idx": 245,
        "gamma_idx": 20,
        "zero": 77.14504984912632,
        "gamma": 77.1448400688748,
        "median": 48.899737666799254
      },
      "a12": {
        "min": 5.2439703061679666e-05,
        "zero_idx": 95,
        "gamma_idx": 3,
        "zero": 25.010805140442628,
        "gamma": 25.01085758014569,
        "median": 48.79267672793192
      }
    },
    "B3_strip_winding": {
      "a10": {
        "1.0": {
          "winding": 253.99999999999946,
          "real_inside": 252,
          "excess": 1.99999999999946
        },
        "0.1": {
          "winding": 253.99999999999946,
          "real_inside": 252,
          "excess": 1.99999999999946
        }
      },
      "a12": {
        "1.0": {
          "winding": 305.9999999999994,
          "real_inside": 304,
          "excess": 1.9999999999993747
        },
        "0.1": {
          "winding": 305.9999999999993,
          "real_inside": 304,
          "excess": 1.9999999999993179
        }
      }
    },
    "B4_envelope": {
      "median_M_over_R0_y0.3": 0.006526107920803935
    }
  },
  "elapsed_seconds": 672.2,
  "timestamp_utc": "2026-08-18T19:32:51.149654+00:00"
}

Source code

Python script (click to expand)
"""E-019 — task 2.8(ii): backend v2 validation (A) + zero-flow at a=12 (B).

Sub-stage A (validation gate, all must pass before a=12 is trusted):
  V-A1. prime_backend_v2 vs E-016 in-memory cumsums: max |dev| on 1e4
        random t in (0,20] < 1e-6 abs (float64 summation-reordering
        noise floor ~2e-7 measured; both backends carry the same
        rounding level, so agreement at this level = exact).
  V-A2. full pipeline at a=10 with the v2 backend: stiffness matrix Q
        vs E16.build(10) entrywise max dev < 1e-8; located zeros at
        a=10 match the E-016 family (z1 dev < 1e-6; count equal).

Sub-stage B (measurements at a=12, theta=pi):
  B1. zero-flow: located real zeros (0,300]: count vs aT/pi, z1*a law,
      gap statistics vs a=10 (inflow continues? any bounce?).
  B2. gamma-locking metric: min |z_j - gamma_k| over first 30 Riemann
      zeros (mpmath zetazero, safe per STATE) vs a=10 value.
  B3. strip winding counts: W-zeros in |Re z| <= 40, |Im z| <= y0 for
      y0 in {1, 0.1} (argument principle; detects any non-real
      deviation beyond the known origin pair).
  B4. envelope spot check: median |M|/|R_0| at y0 = 0.3 (E-018 family).

Outputs: research/numerics/data/E-019-zeroflow-a12.csv (+ .json)
"""
import importlib.util
import json
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

import mpmath as mp
import numpy as np
import scipy.optimize

REPO = Path(__file__).resolve().parents[3]
DATA = REPO / "research" / "numerics" / "data"
CSV = DATA / "E-019-zeroflow-a12.csv"
JSON = DATA / "E-019-zeroflow-a12.json"

_spec = importlib.util.spec_from_file_location(
    "E16", Path(__file__).with_name("E-016-herglotz-fine-structure.py"))
E16 = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(E16)                     # runs the e^20 sieve

sys.path.insert(0, str(Path(__file__).parent))
from prime_backend_v2 import PrimeBackendV2       # noqa: E402

H = E16.H_MAIN
LAM = E16.LAM_SHIFT
A_VAL = 10.0
A_NEW = 12.0
Z_MAX = 300.0
N_GRID = 40000
mp.mp.dps = 40

XN, XW = E16.XN, E16.XW


def collect_t_points(a, h):
    """Replicates E-016 S_full/quad segment structure (no g evaluation)
    to enumerate every quadrature abscissa t in (0, 2a)."""
    n_elem = int(round(2 * a / h))
    pts = []

    def add_quad(lo, hi, peak):
        n_lo = int(np.ceil(np.exp(lo)))
        n_hi = int(np.floor(np.exp(hi)))
        if n_hi - n_lo > 64:
            for aa, bb in ((lo, peak), (peak, hi)):
                if bb - aa > 0:
                    mid, half = 0.5 * (aa + bb), 0.5 * (bb - aa)
                    pts.extend(mid + half * XN)
            return
        segs = sorted({lo, peak, hi} |
                      {float(np.log(n))
                       for n in range(max(n_lo, 2), n_hi + 1)
                       if lo < np.log(n) < hi})
        for p0, p1 in zip(segs, segs[1:]):
            if p1 - p0 > 0:
                mid, half = 0.5 * (p0 + p1), 0.5 * (p1 - p0)
                pts.extend(mid + half * XN)

    for k in range(n_elem):
        if k == 0:
            add_quad(0.0, h, 0.0)
        else:
            add_quad((k - 1) * h, (k + 1) * h, k * h)
    return np.array(pts)


def locate_zeros(cp, x, h, z_max=Z_MAX, n_grid=N_GRID):
    zg = np.linspace(1e-3, z_max, n_grid)
    Vpr, _ = E16.V_and_dV(zg, cp, x, h)
    g = zg * Vpr.imag - Vpr.real

    def g_direct(zz):
        V = E16.hat_FT(np.array([zz]), h)[0] * complex(
            np.exp(1j * zz * x) @ cp)
        return zz * V.imag - V.real

    idx = np.flatnonzero(np.sign(g[:-1]) * np.sign(g[1:]) < 0)
    zeros = []
    for i in idx:
        try:
            zeros.append(scipy.optimize.brentq(g_direct, zg[i], zg[i + 1],
                                               xtol=1e-13, rtol=1e-13))
        except ValueError:
            pass
    return np.array(zeros)


def winding_rect(cp, cm, x, h, half_re, half_im, n_per_edge=4000):
    """Zeros of W(theta=pi) inside |Re z|<=half_re, |Im z|<=half_im via
    the argument principle (direct evaluation, e^{+-a*y0} range OK at
    y0 <= 1, a <= 12)."""
    edges = []
    xr = np.linspace(-half_re, half_re, n_per_edge)
    yr = np.linspace(-half_im, half_im, max(8, n_per_edge // 10))
    edges.append(xr - 1j * half_im)          # bottom, left->right
    edges.append(half_re + 1j * yr)          # right, bottom->top
    edges.append(xr[::-1] + 1j * half_im)    # top, right->left
    edges.append(-half_re + 1j * yr[::-1])   # left, top->bottom
    total = 0.0
    for z in edges:
        Vp, _ = E16.V_and_dV(z, cp, x, h)
        Vm, _ = E16.V_and_dV(z, cm, x, h)
        W = (z - 1j) * Vp - (z + 1j) * Vm
        total += np.diff(np.unwrap(np.angle(W))).sum()
    return total / (2 * np.pi)


def solve_ab(a, h):
    x, hh, Q, M, b = E16.build(a, h)
    n = len(x)
    cp, T = E16.solve_v(Q, M, b[+1.0], LAM, n)
    cm, _ = E16.solve_v(Q, M, b[-1.0], LAM, n)
    np_ = float(cp @ T @ cp)
    nm = float(cm @ T @ cm)
    return x, hh, cp, cm * np.sqrt(np_ / nm), Q


def main() -> int:
    if CSV.exists() and "--force" not in sys.argv:
        print(f"cached: {CSV} (use --force to recompute)")
        return 0
    t0 = time.time()
    report = {}

    # ---------------- backend v2: collect queries & build -------------
    print("collecting quadrature t-points (a=10 & a=12) ...", flush=True)
    rng = np.random.default_rng(19)
    t_rand = np.sort(rng.uniform(0.01, 20.0, 10000))
    tq = np.concatenate([collect_t_points(A_VAL, H),
                         collect_t_points(A_NEW, H), t_rand])
    print(f"  {tq.size} points; building backend to e^24 ...", flush=True)
    CACHE = DATA / "E-019-backend-cache.npz"
    be = PrimeBackendV2(tq)
    if CACHE.exists() and "--force" not in sys.argv:
        be.load_cache(CACHE)
        print("  backend loaded from cache", flush=True)
    else:
        be.build(verbose=True)
        be.save_cache(CACHE)

    # ---------------- A: validation -----------------------------------
    print("V-A1: backend vs E-016 in-memory cumsums ...", flush=True)
    p_ref = E16.prime_sum_vec(t_rand)
    p_new = be.prime_sum(t_rand)
    va1 = float(np.max(np.abs(p_ref - p_new)))
    print(f"  V-A1 max |dev|: {va1:.2e}", flush=True)

    print("V-A2: full pipeline at a=10 (v2 backend) ...", flush=True)
    orig_psv = E16.prime_sum_vec
    # reference build with the ORIGINAL backend first
    x10r, hh10r, cp10r, cm10r, Q_ref = solve_ab(A_VAL, H)
    zeros10r = locate_zeros(cp10r, x10r, hh10r)
    # rebuild with v2 backend (cache must be cleared — keyed on (a,h))
    E16._BUILD.clear()
    E16.prime_sum_vec = be.prime_sum
    x10, hh10, cp10, cm10, Q_new = solve_ab(A_VAL, H)
    E16.prime_sum_vec = orig_psv
    va2_q = float(np.max(np.abs(Q_new - Q_ref)))
    zeros10 = locate_zeros(cp10, x10, hh10)
    z1_10 = float(zeros10[0])
    va2_z1 = abs(z1_10 - float(zeros10r[0]))
    va2_cnt = abs(len(zeros10) - len(zeros10r))
    print(f"  V-A2 Q dev {va2_q:.2e}; z1 dev {va2_z1:.2e}; "
          f"count diff {va2_cnt}", flush=True)
    report["validation"] = {"VA1_max_dev": va1, "VA2_Q_dev": va2_q,
                            "VA2_z1_dev": va2_z1, "VA2_count_diff": va2_cnt}

    # ---------------- B: a=12 measurements ----------------------------
    print("B: a=12 build + zeros + winding ...", flush=True)
    E16.prime_sum_vec = be.prime_sum
    x12, hh12, cp12, cm12, _ = solve_ab(A_NEW, H)
    E16.prime_sum_vec = orig_psv
    zeros12 = locate_zeros(cp12, x12, hh12)

    # B1 zero-flow
    gaps10 = np.diff(zeros10)
    gaps12 = np.diff(zeros12)
    b1 = {
        "a10": {"n": int(len(zeros10)), "z1": z1_10,
                "z1_times_a": z1_10 * A_VAL,
                "count_vs_lattice": A_VAL * Z_MAX / np.pi,
                "gap_mean": float(gaps10.mean()),
                "gap_cv": float(gaps10.std() / gaps10.mean())},
        "a12": {"n": int(len(zeros12)), "z1": float(zeros12[0]),
                "z1_times_a": float(zeros12[0]) * A_NEW,
                "count_vs_lattice": A_NEW * Z_MAX / np.pi,
                "gap_mean": float(gaps12.mean()),
                "gap_cv": float(gaps12.std() / gaps12.mean())},
    }
    print(f"  B1 a=12: n={b1['a12']['n']} (lattice "
          f"{b1['a12']['count_vs_lattice']:.0f}) z1*a="
          f"{b1['a12']['z1_times_a']:.4f} gap_cv={b1['a12']['gap_cv']:.4f}",
          flush=True)

    # B2 gamma-locking
    gammas = np.array([float(mp.zetazero(k).imag) for k in range(1, 31)])
    def lock_metric(zeros):
        d = np.abs(zeros[:, None] - gammas[None, :])
        j, k = np.unravel_index(np.argmin(d), d.shape)
        dmin = np.min(d, axis=1)
        return (float(d[j, k]), int(j + 1), int(k + 1),
                float(zeros[j]), float(gammas[k]),
                float(np.median(dmin)))
    lm10 = lock_metric(zeros10)
    lm12 = lock_metric(zeros12)
    b2 = {"a10": {"min": lm10[0], "zero_idx": lm10[1], "gamma_idx": lm10[2],
                  "zero": lm10[3], "gamma": lm10[4], "median": lm10[5]},
          "a12": {"min": lm12[0], "zero_idx": lm12[1], "gamma_idx": lm12[2],
                  "zero": lm12[3], "gamma": lm12[4], "median": lm12[5]}}
    print(f"  B2 lock: a=10 min {lm10[0]:.5f} (z_{lm10[1]}={lm10[3]:.4f} "
          f"vs g_{lm10[2]}={lm10[4]:.4f}); a=12 min {lm12[0]:.5f} "
          f"(z_{lm12[1]}={lm12[3]:.4f} vs g_{lm12[2]}={lm12[4]:.4f})",
          flush=True)

    # B3 strip winding (a=12 and a=10 for comparison)
    b3 = {}
    for tag, cp, cm, x, hh in (("a10", cp10, cm10, x10, hh10),
                               ("a12", cp12, cm12, x12, hh12)):
        real_in = {}
        for y0 in (1.0, 0.1):
            w = winding_rect(cp, cm, x, hh, 40.0, y0)
            zz = zeros10 if tag == "a10" else zeros12
            n_real = int(2 * np.sum(zz <= 40.0))
            real_in[str(y0)] = {"winding": w, "real_inside": n_real,
                                "excess": w - n_real}
        b3[tag] = real_in
    print(f"  B3: {b3}", flush=True)

    # B4 envelope spot check at y0 = 0.3 (E-018 family), a=12
    def R0_exact(a, th, z):
        z = np.asarray(z, dtype=complex)
        t1 = -2j * (np.sinh(a * (1 + 1j * z))
                    + np.exp(1j * th) * np.sinh(a * (1j * z - 1)))
        t2 = (2j * np.sin(a * z) / z) * ((z - 1j) - np.exp(1j * th)
                                         * (z + 1j))
        return t1 + t2
    xg = np.linspace(-20, 20, 41) + 0.3j
    xm = x12.min()
    u = x12 - xm
    E = np.exp(1j * np.outer(xg, u))
    hat = E16.hat_FT(xg, hh12)
    W = np.exp(1j * xm * xg) * ((xg - 1j) * (hat * (E @ cp12))
                                - (xg + 1j) * (hat * (E @ cm12)))
    Fv = np.array([])
    def F_complex(z):
        s = mp.mpc(0.5 + z.imag, -z.real)
        val = 0.5 * s * (s - 1) * mp.pi ** (-s / 2) * mp.gamma(s / 2) \
            * mp.zeta(s)
        dval = mp.diff(lambda t: 0.5 * t * (t - 1) * mp.pi ** (-t / 2)
                       * mp.gamma(t / 2) * mp.zeta(t), s)
        return complex(z**2 * val / dval)
    Fv = np.array([F_complex(z) for z in xg])
    ratio = np.abs(W / Fv) / np.abs(R0_exact(A_NEW, np.pi, xg))
    b4 = {"median_M_over_R0_y0.3": float(np.median(ratio))}
    print(f"  B4 median |M/R0| at y0=0.3: {b4['median_M_over_R0_y0.3']:.3e}",
          flush=True)

    report.update({"B1_zeroflow": b1, "B2_gamma_locking": b2,
                   "B3_strip_winding": b3, "B4_envelope": b4})

    checks = {
        "VA1_backend_matches": va1 < 1e-6,
        "VA2_pipeline_a10": va2_q < 1e-6 and va2_z1 < 1e-6
        and va2_cnt == 0,
        "B3_no_new_complex_zeros": all(
            abs(b3[t][y]["excess"]) < 2.5 for t in b3 for y in b3[t]),
    }
    with CSV.open("w", encoding="utf-8", newline="") as f:
        f.write("series,a,j,value\n")
        for j, z in enumerate(zeros10):
            f.write(f"zero,10,{j+1},{z:.10f}\n")
        for j, z in enumerate(zeros12):
            f.write(f"zero,12,{j+1},{z:.10f}\n")
        for k, g in enumerate(gammas):
            f.write(f"gamma,-,{k+1},{g:.10f}\n")
    meta = {
        "experiment": "E-019",
        "task": "2.8(ii): backend v2 validation + zero-flow at a=12",
        "script": "research/numerics/scripts/E-019-zeroflow-a12.py",
        "parameters": {"a_val": A_VAL, "a_new": A_NEW, "h": H,
                       "lambda": LAM, "z_max": Z_MAX},
        "checks": checks,
        "report": report,
        "elapsed_seconds": round(time.time() - t0, 1),
        "timestamp_utc": datetime.now(timezone.utc).isoformat(),
    }
    JSON.write_text(json.dumps(meta, indent=2), encoding="utf-8")
    ok = all(checks.values())
    print(f"wrote {CSV.name}, {JSON.name} — "
          f"{'ALL CHECKS PASS' if ok else 'CHECK FAILURE: ' + str(checks)}")
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())