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

← prev page 2 of 5 · 500/page · 2129 rows next →
# seriesajvalue
502 zero 10 501 157.5976474783
503 zero 10 502 157.9228169545
504 zero 10 503 158.2382406795
505 zero 10 504 158.5588718402
506 zero 10 505 158.8503129861
507 zero 10 506 159.1804449152
508 zero 10 507 159.5004446038
509 zero 10 508 159.8176350323
510 zero 10 509 160.1351210195
511 zero 10 510 160.4528960390
512 zero 10 511 160.7925559672
513 zero 10 512 161.1769480039
514 zero 10 513 161.3073827627
515 zero 10 514 161.6963023387
516 zero 10 515 162.0053277549
517 zero 10 516 162.3270310697
518 zero 10 517 162.6461719165
519 zero 10 518 163.0258808217
520 zero 10 519 163.2412058404
521 zero 10 520 163.5752928872
522 zero 10 521 163.8966807467
523 zero 10 522 164.2205484512
524 zero 10 523 164.5301493408
525 zero 10 524 164.8469797406
526 zero 10 525 165.1671195075
527 zero 10 526 165.5336178787
528 zero 10 527 165.7809814416
529 zero 10 528 166.1201871166
530 zero 10 529 166.4062905404
531 zero 10 530 166.7440667745
532 zero 10 531 167.1622035743
533 zero 10 532 167.2507597316
534 zero 10 533 167.6591816194
535 zero 10 534 167.9850626959
536 zero 10 535 168.2900138169
537 zero 10 536 168.6419943796
538 zero 10 537 169.0027994922
539 zero 10 538 169.1125370902
540 zero 10 539 169.5365285173
541 zero 10 540 169.9088429139
542 zero 10 541 170.1629496742
543 zero 10 542 170.4665842036
544 zero 10 543 170.7991466285
545 zero 10 544 171.1220351744
546 zero 10 545 171.4370233859
547 zero 10 546 171.7591888828
548 zero 10 547 172.0501866745
549 zero 10 548 172.3814476913
550 zero 10 549 172.7020522367
551 zero 10 550 173.0279268482
552 zero 10 551 173.4042229917
553 zero 10 552 173.6051606804
554 zero 10 553 173.9497512241
555 zero 10 554 174.2950969637
556 zero 10 555 174.6772141550
557 zero 10 556 174.7739523648
558 zero 10 557 175.1862152478
559 zero 10 558 175.5147024623
560 zero 10 559 175.8340875217
561 zero 10 560 176.1545925119
562 zero 10 561 176.4429417565
563 zero 10 562 176.8032698241
564 zero 10 563 177.0796078001
565 zero 10 564 177.4085116115
566 zero 10 565 177.7267515384
567 zero 10 566 178.0646192059
568 zero 10 567 178.3747337813
569 zero 10 568 178.6591413799
570 zero 10 569 178.9925017838
571 zero 10 570 179.2647470698
572 zero 10 571 179.6058313684
573 zero 10 572 179.9166517041
574 zero 10 573 180.2386684299
575 zero 10 574 180.5518211747
576 zero 10 575 180.8713354315
577 zero 10 576 181.1901739531
578 zero 10 577 181.4896621188
579 zero 10 578 181.8206422720
580 zero 10 579 182.2021466736
581 zero 10 580 182.4146205718
582 zero 10 581 182.7538847820
583 zero 10 582 183.0672726818
584 zero 10 583 183.3827623242
585 zero 10 584 183.7060352080
586 zero 10 585 184.0149797042
587 zero 10 586 184.3507166841
588 zero 10 587 184.6902308585
589 zero 10 588 184.8812851344
590 zero 10 589 185.2941407059
591 zero 10 590 185.5962672457
592 zero 10 591 185.8828721485
593 zero 10 592 186.2108861440
594 zero 10 593 186.5567920082
595 zero 10 594 186.8282191992
596 zero 10 595 187.2240491477
597 zero 10 596 187.4351427205
598 zero 10 597 187.7755337992
599 zero 10 598 188.0946466624
600 zero 10 599 188.4081609379
601 zero 10 600 188.7290102757
602 zero 10 601 189.0520944431
603 zero 10 602 189.4127511459
604 zero 10 603 189.6748959152
605 zero 10 604 189.9859786191
606 zero 10 605 190.3090962116
607 zero 10 606 190.6514953373
608 zero 10 607 190.9170575592
609 zero 10 608 191.2461356218
610 zero 10 609 191.5764611104
611 zero 10 610 191.9942496506
612 zero 10 611 192.0831277326
613 zero 10 612 192.4784925674
614 zero 10 613 192.8092877271
615 zero 10 614 193.0820431158
616 zero 10 615 193.4339959115
617 zero 10 616 193.7575367561
618 zero 10 617 194.0756857416
619 zero 10 618 194.4049013042
620 zero 10 619 194.7058535351
621 zero 10 620 195.0396549132
622 zero 10 621 195.2696330124
623 zero 10 622 195.6443071385
624 zero 10 623 195.9830815771
625 zero 10 624 196.2463369025
626 zero 10 625 196.6031733719
627 zero 10 626 196.8813533062
628 zero 10 627 197.1860814076
629 zero 10 628 197.5405705178
630 zero 10 629 197.9323111997
631 zero 10 630 198.0328166118
632 zero 10 631 198.4501931198
633 zero 10 632 198.7732962425
634 zero 10 633 199.0936009086
635 zero 10 634 199.4146228826
636 zero 10 635 199.7328187857
637 zero 10 636 200.0787337541
638 zero 10 637 200.3204427762
639 zero 10 638 200.6731389805
640 zero 10 639 201.0240069512
641 zero 10 640 201.2647116969
642 zero 10 641 201.6098224975
643 zero 10 642 201.9286383895
644 zero 10 643 202.2618611903
645 zero 10 644 202.4976206675
646 zero 10 645 202.8679407234
647 zero 10 646 203.2149717395
648 zero 10 647 203.4898349083
649 zero 10 648 203.8225214673
650 zero 10 649 204.1867152257
651 zero 10 650 204.4688003361
652 zero 10 651 204.7094469541
653 zero 10 652 205.0606475623
654 zero 10 653 205.3940016758
655 zero 10 654 205.7027944350
656 zero 10 655 206.0297865953
657 zero 10 656 206.2951130926
658 zero 10 657 206.6381735409
659 zero 10 658 206.9619468690
660 zero 10 659 207.2833412943
661 zero 10 660 207.6319050349
662 zero 10 661 207.9058465815
663 zero 10 662 208.2348736095
664 zero 10 663 208.4992067093
665 zero 10 664 208.8537668561
666 zero 10 665 209.1730920994
667 zero 10 666 209.5694313315
668 zero 10 667 209.7883557333
669 zero 10 668 210.0403944245
670 zero 10 669 210.4013417905
671 zero 10 670 210.7236441785
672 zero 10 671 211.0478473319
673 zero 10 672 211.3548680890
674 zero 10 673 211.6898989618
675 zero 10 674 211.9919273696
676 zero 10 675 212.3116110830
677 zero 10 676 212.6521195948
678 zero 10 677 212.9088697376
679 zero 10 678 213.3358582504
680 zero 10 679 213.5008086564
681 zero 10 680 213.8584386464
682 zero 10 681 214.1818407919
683 zero 10 682 214.5448078002
684 zero 10 683 214.8275737786
685 zero 10 684 215.0905340267
686 zero 10 685 215.4475323460
687 zero 10 686 215.7760533983
688 zero 10 687 216.1626698257
689 zero 10 688 216.3497907544
690 zero 10 689 216.6973710253
691 zero 10 690 217.0209283308
692 zero 10 691 217.3442742813
693 zero 10 692 217.6980325216
694 zero 10 693 217.9163413274
695 zero 10 694 218.2749427521
696 zero 10 695 218.6087849319
697 zero 10 696 219.0413161447
698 zero 10 697 219.1472912917
699 zero 10 698 219.4600229048
700 zero 10 699 219.8137372288
701 zero 10 700 220.1502731862
702 zero 10 701 220.4820707642
703 zero 10 702 220.7205030403
704 zero 10 703 221.0864668377
705 zero 10 704 221.4298915553
706 zero 10 705 221.7263877893
707 zero 10 706 222.0445674917
708 zero 10 707 222.3595345310
709 zero 10 708 222.6927282833
710 zero 10 709 223.0339941094
711 zero 10 710 223.2517257109
712 zero 10 711 223.6239246827
713 zero 10 712 224.0049923962
714 zero 10 713 224.1967360648
715 zero 10 714 224.5640259369
716 zero 10 715 224.9693139593
717 zero 10 716 225.0999203661
718 zero 10 717 225.4659628898
719 zero 10 718 225.8004301414
720 zero 10 719 226.1264214028
721 zero 10 720 226.4635149336
722 zero 10 721 226.7596718934
723 zero 10 722 227.0662111212
724 zero 10 723 227.4195653245
725 zero 10 724 227.7008434551
726 zero 10 725 228.0250748390
727 zero 10 726 228.3338517650
728 zero 10 727 228.6508057017
729 zero 10 728 228.9773632094
730 zero 10 729 229.3338263134
731 zero 10 730 229.5875538822
732 zero 10 731 229.9138504333
733 zero 10 732 230.2815020989
734 zero 10 733 230.4842177980
735 zero 10 734 230.8542964183
736 zero 10 735 231.2435500878
737 zero 10 736 231.4499951057
738 zero 10 737 231.8525456094
739 zero 10 738 231.9946125895
740 zero 10 739 232.3993008956
741 zero 10 740 232.7310271861
742 zero 10 741 233.0333105430
743 zero 10 742 233.3553532763
744 zero 10 743 233.6922328939
745 zero 10 744 233.9838395489
746 zero 10 745 234.3091097304
747 zero 10 746 234.6338593264
748 zero 10 747 234.9075881526
749 zero 10 748 235.2500083566
750 zero 10 749 235.5809111523
751 zero 10 750 235.9235332434
752 zero 10 751 236.1278374841
753 zero 10 752 236.5226410567
754 zero 10 753 236.8448722604
755 zero 10 754 237.1304979399
756 zero 10 755 237.4426847767
757 zero 10 756 237.7691877454
758 zero 10 757 238.0805806467
759 zero 10 758 238.4006250890
760 zero 10 759 238.7173839990
761 zero 10 760 239.0408630014
762 zero 10 761 239.4173617421
763 zero 10 762 239.5616875737
764 zero 10 763 239.9579795836
765 zero 10 764 240.2862723718
766 zero 10 765 240.6077109019
767 zero 10 766 241.0245717338
768 zero 10 767 241.1117812045
769 zero 10 768 241.5153833687
770 zero 10 769 241.8478461716
771 zero 10 770 242.1601969052
772 zero 10 771 242.4655300813
773 zero 10 772 242.8219276175
774 zero 10 773 243.1220819348
775 zero 10 774 243.3853679848
776 zero 10 775 243.7382631341
777 zero 10 776 244.0700882241
778 zero 10 777 244.3716928641
779 zero 10 778 244.7325745773
780 zero 10 779 244.9238007915
781 zero 10 780 245.2986076684
782 zero 10 781 245.6157361034
783 zero 10 782 245.9485977207
784 zero 10 783 246.2629079559
785 zero 10 784 246.5890904784
786 zero 10 785 246.8549826473
787 zero 10 786 247.1412600750
788 zero 10 787 247.5149822017
789 zero 10 788 247.8339684355
790 zero 10 789 248.1053362641
791 zero 10 790 248.4483424198
792 zero 10 791 248.7756680317
793 zero 10 792 249.1114662127
794 zero 10 793 249.5327742977
795 zero 10 794 249.6338250646
796 zero 10 795 249.9269519183
797 zero 10 796 250.3200406469
798 zero 10 797 250.6490957468
799 zero 10 798 251.0116238462
800 zero 10 799 251.2697279253
801 zero 10 800 251.5982334914
802 zero 10 801 251.9713201470
803 zero 10 802 252.2132471584
804 zero 10 803 252.5061499985
805 zero 10 804 252.8914088678
806 zero 10 805 253.0782380355
807 zero 10 806 253.4622144728
808 zero 10 807 253.7913520423
809 zero 10 808 254.1168432233
810 zero 10 809 254.4475662188
811 zero 10 810 254.7497393238
812 zero 10 811 255.1098370142
813 zero 10 812 255.3043420845
814 zero 10 813 255.6872856065
815 zero 10 814 255.9905998794
816 zero 10 815 256.3757440262
817 zero 10 816 256.6072035961
818 zero 10 817 256.9382778262
819 zero 10 818 257.2585122043
820 zero 10 819 257.5847832600
821 zero 10 820 257.8999353566
822 zero 10 821 258.2570660994
823 zero 10 822 258.6030125206
824 zero 10 823 258.8189540265
825 zero 10 824 259.1995274424
826 zero 10 825 259.3927592602
827 zero 10 826 259.8643643301
828 zero 10 827 260.0577056919
829 zero 10 828 260.3434054542
830 zero 10 829 260.7940142137
831 zero 10 830 260.9633421767
832 zero 10 831 261.3333271419
833 zero 10 832 261.6557882357
834 zero 10 833 262.0474079115
835 zero 10 834 262.2079498434
836 zero 10 835 262.5743502488
837 zero 10 836 262.9065914673
838 zero 10 837 263.2305165518
839 zero 10 838 263.5738995848
840 zero 10 839 263.8626824897
841 zero 10 840 264.1723344931
842 zero 10 841 264.5046562926
843 zero 10 842 264.8827206376
844 zero 10 843 265.0478407009
845 zero 10 844 265.5269497469
846 zero 10 845 265.6249135203
847 zero 10 846 266.0362747892
848 zero 10 847 266.3823513276
849 zero 10 848 266.6192342032
850 zero 10 849 267.0214406411
851 zero 10 850 267.3034515530
852 zero 10 851 267.6244878325
853 zero 10 852 267.9231742773
854 zero 10 853 268.2615823112
855 zero 10 854 268.5753780088
856 zero 10 855 268.8981677772
857 zero 10 856 269.2182102870
858 zero 10 857 269.5493238789
859 zero 10 858 269.9555981790
860 zero 10 859 270.1157579672
861 zero 10 860 270.3893995920
862 zero 10 861 270.7764652942
863 zero 10 862 271.1514244056
864 zero 10 863 271.4807950945
865 zero 10 864 271.6450956432
866 zero 10 865 272.0181295258
867 zero 10 866 272.3088270246
868 zero 10 867 272.6523040987
869 zero 10 868 272.9897160703
870 zero 10 869 273.3879721449
871 zero 10 870 273.4866006981
872 zero 10 871 273.9030474413
873 zero 10 872 274.2264400557
874 zero 10 873 274.5482785409
875 zero 10 874 274.8348826529
876 zero 10 875 275.1938456959
877 zero 10 876 275.5766429505
878 zero 10 877 275.7532701038
879 zero 10 878 276.0991530872
880 zero 10 879 276.4474821262
881 zero 10 880 276.7392780158
882 zero 10 881 277.0750689210
883 zero 10 882 277.3944285116
884 zero 10 883 277.7480283507
885 zero 10 884 277.9357250379
886 zero 10 885 278.2552624237
887 zero 10 886 278.6333682642
888 zero 10 887 278.9943123045
889 zero 10 888 279.2323794649
890 zero 10 889 279.5793721333
891 zero 10 890 279.9044910855
892 zero 10 891 280.2169765548
893 zero 10 892 280.5351384814
894 zero 10 893 280.9028906175
895 zero 10 894 281.0748198678
896 zero 10 895 281.4761646559
897 zero 10 896 281.8552960563
898 zero 10 897 282.0110946430
899 zero 10 898 282.4595191849
900 zero 10 899 282.7276152989
901 zero 10 900 283.1150967503
902 zero 10 901 283.2360815766
903 zero 10 902 283.6543163000
904 zero 10 903 283.9733575396
905 zero 10 904 284.3041513223
906 zero 10 905 284.6275604520
907 zero 10 906 284.8435779502
908 zero 10 907 285.2337119599
909 zero 10 908 285.5565546836
910 zero 10 909 285.8862359835
911 zero 10 910 286.2549309742
912 zero 10 911 286.5974960956
913 zero 10 912 286.7054152988
914 zero 10 913 287.2092398759
915 zero 10 914 287.3450871401
916 zero 10 915 287.8335199417
917 zero 10 916 287.9453222184
918 zero 10 917 288.4349563225
919 zero 10 918 288.6163609671
920 zero 10 919 288.9805443290
921 zero 10 920 289.3071444789
922 zero 10 921 289.5826640130
923 zero 10 922 289.9726748841
924 zero 10 923 290.2517307513
925 zero 10 924 290.5685738886
926 zero 10 925 290.8933166029
927 zero 10 926 291.2046614787
928 zero 10 927 291.4833286821
929 zero 10 928 291.8455732179
930 zero 10 929 292.1504607095
931 zero 10 930 292.4695006488
932 zero 10 931 292.7955446917
933 zero 10 932 293.1151938086
934 zero 10 933 293.5276413449
935 zero 10 934 293.6365977846
936 zero 10 935 294.0941717901
937 zero 10 936 294.3127492620
938 zero 10 937 294.6706344505
939 zero 10 938 294.9661714362
940 zero 10 939 295.2993595030
941 zero 10 940 295.5764486535
942 zero 10 941 295.9326345910
943 zero 10 942 296.2509018025
944 zero 10 943 296.5577358360
945 zero 10 944 296.8489798585
946 zero 10 945 297.1998446371
947 zero 10 946 297.5231362200
948 zero 10 947 297.9475321531
949 zero 10 948 298.0689385446
950 zero 10 949 298.3749337520
951 zero 10 950 298.7657975554
952 zero 10 951 299.1479341303
953 zero 10 952 299.3128626329
954 zero 10 953 299.7857748646
955 zero 10 954 299.8935789771
956 zero 12 1 0.3746087478
957 zero 12 2 0.6440426323
958 zero 12 3 0.9090603224
959 zero 12 4 1.1726778220
960 zero 12 5 1.4356693206
961 zero 12 6 1.6983265593
962 zero 12 7 1.9607842028
963 zero 12 8 2.2231130497
964 zero 12 9 2.4853539513
965 zero 12 10 2.7475331517
966 zero 12 11 3.0096647376
967 zero 12 12 3.2717631642
968 zero 12 13 3.5338345807
969 zero 12 14 3.7958840322
970 zero 12 15 4.0579160588
971 zero 12 16 4.3199391528
972 zero 12 17 4.5819464797
973 zero 12 18 4.8439469166
974 zero 12 19 5.1059379917
975 zero 12 20 5.3679280500
976 zero 12 21 5.6299081893
977 zero 12 22 5.8918877503
978 zero 12 23 6.1538619067
979 zero 12 24 6.4158362256
980 zero 12 25 6.6778064471
981 zero 12 26 6.9397767620
982 zero 12 27 7.2017460381
983 zero 12 28 7.4637184455
984 zero 12 29 7.7256849125
985 zero 12 30 7.9876623666
986 zero 12 31 8.2496336046
987 zero 12 32 8.5116115158
988 zero 12 33 8.7735910174
989 zero 12 34 9.0355878936
990 zero 12 35 9.2975640099
991 zero 12 36 9.5595728286
992 zero 12 37 9.8215761564
993 zero 12 38 10.0836047443
994 zero 12 39 10.3456306371
995 zero 12 40 10.6076857493
996 zero 12 41 10.8697443413
997 zero 12 42 11.1318436142
998 zero 12 43 11.3939762917
999 zero 12 44 11.6561402767
1000 zero 12 45 11.9183839651
1001 zero 12 46 12.1806924587

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())