mapFolding 0.17.1__py3-none-any.whl → 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. easyRun/NOTcountingFolds.py +7 -11
  2. easyRun/countFolds.py +11 -10
  3. easyRun/meanders.py +6 -8
  4. mapFolding/__init__.py +24 -36
  5. mapFolding/_e/Z0Z_analysisPython/SORTZ0Z_hypothesis.py +189 -0
  6. mapFolding/_e/Z0Z_analysisPython/SORTZ0Z_p2d6.py +143 -0
  7. mapFolding/_e/Z0Z_analysisPython/__init__.py +4 -0
  8. mapFolding/_e/Z0Z_analysisPython/exclusionData/__init__.py +0 -0
  9. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200.py +369 -0
  10. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/2001.py +694 -0
  11. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/270/211.py +514 -0
  12. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/270/2111.py +480 -0
  13. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/272/214.py +511 -0
  14. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/272/2141.py +515 -0
  15. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/272/214/344/270/211.py +485 -0
  16. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/200/344/272/214/344/270/2111.py +442 -0
  17. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/211.py +313 -0
  18. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/270/2111.py +343 -0
  19. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/272/214.py +400 -0
  20. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/272/2141.py +497 -0
  21. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/272/214/344/270/211.py +463 -0
  22. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/344/272/214/344/270/2111.py +441 -0
  23. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266.py +35 -0
  24. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/2661.py +35 -0
  25. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200.py +382 -0
  26. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/2001.py +630 -0
  27. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/270/211.py +488 -0
  28. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/270/2111.py +475 -0
  29. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/272/214.py +473 -0
  30. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/272/2141.py +500 -0
  31. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/272/214/344/270/211.py +465 -0
  32. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/200/344/272/214/344/270/2111.py +439 -0
  33. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/211.py +599 -0
  34. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/270/2111.py +536 -0
  35. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/272/214.py +506 -0
  36. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/272/2141.py +533 -0
  37. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/272/214/344/270/211.py +489 -0
  38. mapFolding/_e/Z0Z_analysisPython/exclusionData/aggregated/351/246/226/351/233/266/344/272/214/344/270/2111.py +474 -0
  39. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200.py +1186 -0
  40. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/2001.py +2158 -0
  41. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/2001Negative.py +2158 -0
  42. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200Negative.py +1186 -0
  43. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/270/211.py +1397 -0
  44. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/270/2111.py +1291 -0
  45. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/270/2111Negative.py +1291 -0
  46. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/270/211Negative.py +1397 -0
  47. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214.py +1240 -0
  48. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/2141.py +1420 -0
  49. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/2141Negative.py +1420 -0
  50. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214Negative.py +1240 -0
  51. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214/344/270/211.py +1366 -0
  52. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214/344/270/2111.py +1274 -0
  53. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214/344/270/2111Negative.py +1274 -0
  54. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/200/344/272/214/344/270/211Negative.py +1366 -0
  55. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/211.py +1186 -0
  56. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/2111.py +1186 -0
  57. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/2111Negative.py +1186 -0
  58. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/270/211Negative.py +1186 -0
  59. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214.py +1102 -0
  60. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/2141.py +1422 -0
  61. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/2141Negative.py +1422 -0
  62. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214Negative.py +1102 -0
  63. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214/344/270/211.py +1240 -0
  64. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214/344/270/2111.py +1228 -0
  65. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214/344/270/2111Negative.py +1228 -0
  66. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/344/272/214/344/270/211Negative.py +1240 -0
  67. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266.py +32 -0
  68. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/2661.py +1162 -0
  69. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/2661Negative.py +1162 -0
  70. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266Negative.py +32 -0
  71. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200.py +1186 -0
  72. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/2001.py +1926 -0
  73. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/2001Negative.py +1926 -0
  74. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200Negative.py +1186 -0
  75. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/270/211.py +1291 -0
  76. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/270/2111.py +1176 -0
  77. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/270/2111Negative.py +1176 -0
  78. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/270/211Negative.py +1291 -0
  79. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214.py +1228 -0
  80. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/2141.py +1324 -0
  81. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/2141Negative.py +1324 -0
  82. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214Negative.py +1228 -0
  83. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214/344/270/211.py +1274 -0
  84. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214/344/270/2111.py +1038 -0
  85. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214/344/270/2111Negative.py +1038 -0
  86. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/200/344/272/214/344/270/211Negative.py +1274 -0
  87. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/211.py +2158 -0
  88. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/2111.py +1926 -0
  89. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/2111Negative.py +1926 -0
  90. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/270/211Negative.py +2158 -0
  91. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214.py +1422 -0
  92. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/2141.py +1364 -0
  93. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/2141Negative.py +1364 -0
  94. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214Negative.py +1422 -0
  95. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214/344/270/211.py +1420 -0
  96. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214/344/270/2111.py +1324 -0
  97. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214/344/270/2111Negative.py +1324 -0
  98. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d5/351/246/226/351/233/266/344/272/214/344/270/211Negative.py +1420 -0
  99. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200.py +3133 -0
  100. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/2001.py +6039 -0
  101. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/2001Negative.py +6039 -0
  102. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200Negative.py +3133 -0
  103. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/270/211.py +3527 -0
  104. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/270/2111.py +2300 -0
  105. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/270/2111Negative.py +2300 -0
  106. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/270/211Negative.py +3527 -0
  107. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214.py +3597 -0
  108. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/2141.py +3317 -0
  109. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/2141Negative.py +3317 -0
  110. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214Negative.py +3597 -0
  111. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214/344/270/211.py +3161 -0
  112. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214/344/270/2111.py +2877 -0
  113. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214/344/270/2111Negative.py +2877 -0
  114. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/200/344/272/214/344/270/211Negative.py +3161 -0
  115. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/211.py +2981 -0
  116. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/2111.py +3055 -0
  117. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/2111Negative.py +3055 -0
  118. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/270/211Negative.py +2981 -0
  119. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214.py +3221 -0
  120. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/2141.py +3988 -0
  121. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/2141Negative.py +3988 -0
  122. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214Negative.py +3221 -0
  123. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214/344/270/211.py +3652 -0
  124. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214/344/270/2111.py +2863 -0
  125. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214/344/270/2111Negative.py +2863 -0
  126. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/344/272/214/344/270/211Negative.py +3652 -0
  127. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200.py +2485 -0
  128. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/2001.py +4566 -0
  129. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/2001Negative.py +4566 -0
  130. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200Negative.py +2485 -0
  131. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/270/211.py +3006 -0
  132. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/270/2111.py +2485 -0
  133. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/270/2111Negative.py +2485 -0
  134. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/270/211Negative.py +3006 -0
  135. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214.py +3304 -0
  136. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/2141.py +3015 -0
  137. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/2141Negative.py +3015 -0
  138. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214Negative.py +3304 -0
  139. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214/344/270/211.py +2939 -0
  140. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214/344/270/2111.py +2589 -0
  141. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214/344/270/2111Negative.py +2589 -0
  142. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/200/344/272/214/344/270/211Negative.py +2939 -0
  143. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/211.py +3899 -0
  144. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/2111.py +2996 -0
  145. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/2111Negative.py +2996 -0
  146. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/270/211Negative.py +3899 -0
  147. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214.py +3223 -0
  148. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/2141.py +3020 -0
  149. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/2141Negative.py +3020 -0
  150. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214Negative.py +3223 -0
  151. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214/344/270/211.py +3250 -0
  152. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214/344/270/2111.py +2667 -0
  153. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214/344/270/2111Negative.py +2667 -0
  154. mapFolding/_e/Z0Z_analysisPython/exclusionData/collatedp2d6/351/246/226/351/233/266/344/272/214/344/270/211Negative.py +3250 -0
  155. mapFolding/_e/Z0Z_analysisPython/measure.py +162 -0
  156. mapFolding/_e/Z0Z_analysisPython/positionAnalysis.py +403 -0
  157. mapFolding/_e/Z0Z_analysisPython/positionAnalysisPileRanges2d6.py +110 -0
  158. mapFolding/_e/Z0Z_analysisPython/theExcluderBeast.py +640 -0
  159. mapFolding/_e/Z0Z_analysisPython/toolkit.py +166 -0
  160. mapFolding/_e/Z0Z_analysisPython/toolkitCSVsequences.py +188 -0
  161. mapFolding/_e/Z0Z_analysisPython/workBenchPatternFinder.py +284 -0
  162. mapFolding/_e/Z0Z_notes/__init__.py +0 -0
  163. mapFolding/_e/Z0Z_notes/knowledgeDump.py +214 -0
  164. mapFolding/_e/__init__.py +45 -0
  165. mapFolding/_e/_beDRY.py +547 -0
  166. mapFolding/_e/_dataDynamic.py +1164 -0
  167. mapFolding/_e/_measure.py +579 -0
  168. mapFolding/_e/_semiotics.py +363 -0
  169. mapFolding/_e/_theTypes.py +31 -0
  170. mapFolding/_e/algorithms/__init__.py +1 -0
  171. mapFolding/_e/algorithms/constraintPropagation.py +158 -0
  172. mapFolding/_e/algorithms/elimination.py +118 -0
  173. mapFolding/_e/algorithms/eliminationCrease.py +66 -0
  174. mapFolding/_e/algorithms/iff.py +584 -0
  175. mapFolding/_e/basecamp.py +89 -0
  176. mapFolding/_e/dataBaskets.py +123 -0
  177. mapFolding/_e/dataRaw/__init__.py +0 -0
  178. mapFolding/_e/easyRun/__init__.py +0 -0
  179. mapFolding/_e/easyRun/eliminateFolds.py +72 -0
  180. mapFolding/_e/easyRun/pinning.py +62 -0
  181. mapFolding/_e/filters.py +384 -0
  182. mapFolding/_e/pin2/344/270/212nDimensions.py +882 -0
  183. mapFolding/_e/pin2/344/270/212nDimensionsAnnex.py +551 -0
  184. mapFolding/_e/pin2/344/270/212nDimensionsByCrease.py +190 -0
  185. mapFolding/_e/pin2/344/270/212nDimensionsByDomain.py +459 -0
  186. mapFolding/_e/pinIt.py +436 -0
  187. mapFolding/_semiotics.py +42 -0
  188. mapFolding/_theSSOT.py +11 -56
  189. mapFolding/_theTypes.py +52 -67
  190. mapFolding/algorithms/matrixMeandersNumPyndas.py +18 -18
  191. mapFolding/algorithms/oeisIDbyFormula.py +4 -4
  192. mapFolding/algorithms/zCuzDocStoopidoeisIDbyFormula.py +3 -3
  193. mapFolding/basecamp.py +11 -80
  194. mapFolding/beDRY.py +107 -111
  195. mapFolding/dataBaskets.py +0 -56
  196. mapFolding/filesystemToolkit.py +15 -11
  197. mapFolding/oeis.py +17 -16
  198. mapFolding/reference/matrixMeandersAnalysis/prefixNotationNotes.py +2 -2
  199. mapFolding/reference/meandersDumpingGround/matrixMeandersBaselineV2.py +0 -1
  200. mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +8 -10
  201. mapFolding/someAssemblyRequired/RecipeJob.py +5 -5
  202. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +5 -2
  203. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +9 -11
  204. mapFolding/someAssemblyRequired/mapFoldingModules/makeMapFoldingModules.py +2 -1
  205. mapFolding/someAssemblyRequired/transformationTools.py +1 -1
  206. mapFolding/tests/Z0Z_test_e_excluder.py +155 -0
  207. mapFolding/tests/conftest.py +193 -314
  208. mapFolding/tests/dataSamples/A001417.py +455 -0
  209. mapFolding/tests/dataSamples/__init__.py +1 -0
  210. mapFolding/tests/dataSamples/measurementData.py +1818 -0
  211. mapFolding/tests/dataSamples/p2DnDomain3_2_/351/246/226/344/270/200_/351/246/226/351/233/266/344/270/200.py +17 -0
  212. mapFolding/tests/dataSamples/p2DnDomain3_/351/246/226/344/270/200.py +17 -0
  213. mapFolding/tests/dataSamples/p2DnDomain5_4.py +17 -0
  214. mapFolding/tests/dataSamples/p2DnDomain6_5.py +17 -0
  215. mapFolding/tests/dataSamples/p2DnDomain6_7_5_4.py +17 -0
  216. mapFolding/tests/dataSamples/p2DnDomain7_6.py +17 -0
  217. mapFolding/tests/dataSamples/p2DnDomain/351/246/226/344/272/214_/351/246/226/351/233/266/344/270/200/344/272/214.py +17 -0
  218. mapFolding/tests/dataSamples/p2DnDomain/351/246/226/344/272/214_/351/246/226/351/233/266/344/272/214_/351/246/226/351/233/266/344/270/200/344/272/214_/351/246/226/344/270/200/344/272/214.py +17 -0
  219. mapFolding/tests/dataSamples/p2DnDomain/351/246/226/351/233/266/344/270/200/344/272/214_/351/246/226/344/270/200/344/272/214.py +15 -0
  220. mapFolding/tests/dataSamples/p2DnDomain/351/246/226/351/233/266/344/272/214_/351/246/226/344/272/214.py +15 -0
  221. mapFolding/tests/dataSamples/semioticsData.py +135 -0
  222. mapFolding/tests/test_computations.py +134 -80
  223. mapFolding/tests/test_e_computations.py +42 -0
  224. mapFolding/tests/test_e_dataDynamic.py +189 -0
  225. mapFolding/tests/test_e_measurements.py +257 -0
  226. mapFolding/tests/test_e_pinning.py +61 -0
  227. mapFolding/tests/test_e_semiotics.py +128 -0
  228. mapFolding/tests/test_filesystem.py +39 -17
  229. mapFolding/tests/{test_other.py → test_parameterValidation.py} +3 -3
  230. mapFolding/tests/{test_tasks.py → test_taskDivisions.py} +42 -23
  231. mapFolding/zCuzDocStoopid/makeDocstrings.py +3 -2
  232. {mapfolding-0.17.1.dist-info → mapfolding-0.18.0.dist-info}/METADATA +11 -8
  233. mapfolding-0.18.0.dist-info/RECORD +305 -0
  234. {mapfolding-0.17.1.dist-info → mapfolding-0.18.0.dist-info}/WHEEL +1 -1
  235. easyRun/eliminateFolds.py +0 -60
  236. mapFolding/algorithms/constraintPropagation.py +0 -184
  237. mapFolding/algorithms/elimination.py +0 -131
  238. mapFolding/algorithms/eliminationCount.py +0 -26
  239. mapFolding/algorithms/eliminationPinned.py +0 -35
  240. mapFolding/algorithms/iff.py +0 -206
  241. mapFolding/algorithms/patternFinder.py +0 -280
  242. mapFolding/algorithms/pinning2Dn.py +0 -345
  243. mapFolding/algorithms/pinning2DnAnnex.py +0 -43
  244. mapFolding/tests/verify.py +0 -323
  245. mapfolding-0.17.1.dist-info/RECORD +0 -112
  246. {mapfolding-0.17.1.dist-info → mapfolding-0.18.0.dist-info}/entry_points.txt +0 -0
  247. {mapfolding-0.17.1.dist-info → mapfolding-0.18.0.dist-info}/licenses/LICENSE +0 -0
  248. {mapfolding-0.17.1.dist-info → mapfolding-0.18.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,640 @@
1
+ # NOTE to AI assistants: this module is not representative of my coding style. Most of it is AI generated, but because it's temporary code, I didn't strictly enforce my usual standards. Do not emulate it.
2
+ from collections.abc import Sequence
3
+ from fractions import Fraction
4
+ from functools import cache, reduce
5
+ from gmpy2 import bit_flip
6
+ from hunterMakesPy import CallableFunction, raiseIfNone
7
+ from hunterMakesPy.dataStructures import updateExtendPolishDictionaryLists
8
+ from hunterMakesPy.filesystemToolkit import importPathFilename2Identifier, writePython
9
+ from itertools import product as CartesianProduct, repeat
10
+ from mapFolding import ansiColorReset, ansiColors, inclusive, packageSettings
11
+ from mapFolding._e import (
12
+ getDictionaryLeafDomains, getLeafDomain, getPileRange, PermutationSpace, 首一, 首一三, 首一二, 首一二三, 首三, 首二, 首二三, 首零, 首零一,
13
+ 首零一三, 首零一二, 首零一二三, 首零三, 首零二, 首零二三)
14
+ from mapFolding._e._dataDynamic import getDataFrameFoldings
15
+ from mapFolding._e.dataBaskets import EliminationState
16
+ from mapFolding._e.filters import between, exclude
17
+ from mapFolding._e.pin2上nDimensions import pinPilesAtEnds
18
+ from mapFolding._e.pinIt import deconstructPermutationSpaceAtPile, deconstructPermutationSpaceByDomainOfLeaf
19
+ from mapFolding._e.Z0Z_analysisPython.toolkit import detectPermutationSpaceErrors, PermutationSpaceStatus
20
+ from more_itertools import consecutive_groups
21
+ from operator import indexOf, neg, pos
22
+ from pathlib import Path, PurePath
23
+ from pprint import pformat
24
+ from typing import TYPE_CHECKING
25
+ import numpy
26
+ import sys
27
+
28
+ if TYPE_CHECKING:
29
+ import pandas
30
+
31
+ def 首一1(dd: int, /) -> int: return 首一(dd) + 1
32
+ def 首一三1(dd: int, /) -> int: return 首一三(dd) + 1
33
+ def 首一二1(dd: int, /) -> int: return 首一二(dd) + 1
34
+ def 首一二三1(dd: int, /) -> int: return 首一二三(dd) + 1
35
+ def 首三1(dd: int, /) -> int: return 首三(dd) + 1
36
+ def 首二1(dd: int, /) -> int: return 首二(dd) + 1
37
+ def 首二三1(dd: int, /) -> int: return 首二三(dd) + 1
38
+ def 首零1(dd: int, /) -> int: return 首零(dd) + 1
39
+ def 首零一1(dd: int, /) -> int: return 首零一(dd) + 1
40
+ def 首零一三1(dd: int, /) -> int: return 首零一三(dd) + 1
41
+ def 首零一二1(dd: int, /) -> int: return 首零一二(dd) + 1
42
+ def 首零一二三1(dd: int, /) -> int: return 首零一二三(dd) + 1
43
+ def 首零三1(dd: int, /) -> int: return 首零三(dd) + 1
44
+ def 首零二1(dd: int, /) -> int: return 首零二(dd) + 1
45
+ def 首零二三1(dd: int, /) -> int: return 首零二三(dd) + 1
46
+
47
+ type Addend = int
48
+ type FractionAddend = tuple[Fraction, Addend]
49
+ type IndexPilesTotal = int
50
+ type Leaf = int
51
+ type MapKind = str
52
+ type Pile = int
53
+ type strLeafExcluded = str
54
+ type strLeafExcluder = str
55
+ type strPileExcluded = str
56
+ type strPileExcluder = str
57
+ type ExclusionData = dict[MapKind, dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]]
58
+
59
+ pathExclusionData: Path = Path(f"{packageSettings.pathPackage}/_e/Z0Z_analysisPython/exclusionData")
60
+ pathExclusionData.mkdir(parents=True, exist_ok=True)
61
+
62
+ functionsHeadDimensions: list[CallableFunction[[int], int]] = [
63
+ 首一, 首一三, 首一二, 首一二三, 首三, 首二, 首二三, 首零, 首零一, 首零一三, 首零一二, 首零一二三, 首零三, 首零二, 首零二三,
64
+ 首一1, 首一三1, 首一二1, 首一二三1, 首三1, 首二1, 首二三1, 首零1, 首零一1, 首零一三1, 首零一二1, 首零一二三1, 首零三1, 首零二1, 首零二三1]
65
+ dictionaryFunctionsByName: dict[str, CallableFunction[[int], int]] = {function.__name__: function for function in functionsHeadDimensions}
66
+
67
+ #======== Collate exclusion data =======
68
+
69
+ def writeExclusionDataCollated(listDimensions: Sequence[int] = (5, 6)) -> list[PurePath]:
70
+ """{mapShape: {leafExcluder: {pileExcluder: {leafExcluded: listIndicesExcluded}}}}."""
71
+ dictionaryIndices: dict[MapKind, dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]] = {}
72
+ dictionaryIndicesNegative: dict[MapKind, dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]] = {}
73
+
74
+ # TODO Use the docstring to explain this computation, and change the computation to a simpler statement.
75
+ listsAreAlwaysLessThanHalfLeavesTotal = 1
76
+ integerDivisionIsSillyIfTheNumeratorIsLessThanTwiceTheDenominator = 1
77
+ qq = min(listDimensions) - listsAreAlwaysLessThanHalfLeavesTotal - integerDivisionIsSillyIfTheNumeratorIsLessThanTwiceTheDenominator
78
+ denominatorsValid: tuple[int, ...] = tuple(int(bit_flip(0, ww)) for ww in range(1, qq))
79
+
80
+ for dimensionsTotal in listDimensions:
81
+ state: EliminationState = EliminationState((2,) * dimensionsTotal)
82
+ dataframeFoldings: pandas.DataFrame = raiseIfNone(getDataFrameFoldings(state))
83
+
84
+ dictionaryLeafDomains: dict[Leaf, range] = getDictionaryLeafDomains(state)
85
+
86
+ mapKind: MapKind = f"p2d{dimensionsTotal}"
87
+ dictionaryIndices[mapKind] = {}
88
+ dictionaryIndicesNegative[mapKind] = {}
89
+
90
+ for leafExcluder, pileExcluder, leafExcluded in CartesianProduct(functionsHeadDimensions, functionsHeadDimensions, functionsHeadDimensions):
91
+ if pileExcluder(dimensionsTotal) not in dictionaryLeafDomains.get(leafExcluder(dimensionsTotal), []):
92
+ continue
93
+ pilesInTheDataframe: pandas.Series = dataframeFoldings.loc[dataframeFoldings[pileExcluder(dimensionsTotal)] == leafExcluder(dimensionsTotal)].eq(leafExcluded(dimensionsTotal)).any()
94
+ leafExcludedValue = leafExcluded(dimensionsTotal)
95
+ if leafExcludedValue not in dictionaryLeafDomains:
96
+ continue
97
+ listOfPiles: list[Pile] = list(dictionaryLeafDomains[leafExcludedValue])
98
+ listPilesExcluded: set[Pile] = set(listOfPiles).difference(pilesInTheDataframe[pilesInTheDataframe].index.tolist())
99
+ listIndicesExcluded: list[IndexPilesTotal] = sorted(map(indexOf, repeat(listOfPiles), listPilesExcluded))
100
+
101
+ pilesTotal = len(listOfPiles)
102
+ denominators: list[int] = list(filter(between(0, pilesTotal), denominatorsValid))
103
+ dictionaryIndices[mapKind].setdefault(leafExcluder.__name__, {}).setdefault(pileExcluder.__name__, {})[leafExcluded.__name__] = [
104
+ expressIndexAsFractionAddend(index, pilesTotal, tuple(denominators)) for index in listIndicesExcluded]
105
+ dictionaryIndicesNegative[mapKind].setdefault(leafExcluder.__name__, {}).setdefault(pileExcluder.__name__, {})[leafExcluded.__name__] = [
106
+ expressIndexAsFractionAddend(index - pilesTotal, pilesTotal, tuple(denominators)) for index in listIndicesExcluded]
107
+
108
+ listPathFilenames: list[PurePath] = []
109
+
110
+ for mapKind in dictionaryIndices:
111
+ for leafExcluderName, dictionary in dictionaryIndices[mapKind].items():
112
+ pythonSource: str = f"from fractions import Fraction\n\nleafExcluderData: dict[str, dict[str, list[tuple[Fraction, int]]]] = {pformat(dictionary, indent=0, width=160)}"
113
+ pathFilename: Path = pathExclusionData / f"collated{mapKind}{leafExcluderName}.py"
114
+ writePython(pythonSource, pathFilename)
115
+ listPathFilenames.append(PurePath(pathFilename))
116
+
117
+ for mapKind in dictionaryIndicesNegative:
118
+ for leafExcluderName, dictionary in dictionaryIndicesNegative[mapKind].items():
119
+ pythonSource: str = f"from fractions import Fraction\n\nleafExcluderData: dict[str, dict[str, list[tuple[Fraction, int]]]] = {pformat(dictionary, indent=0, width=160)}"
120
+ pathFilename: Path = pathExclusionData / f"collated{mapKind}{leafExcluderName}Negative.py"
121
+ writePython(pythonSource, pathFilename)
122
+ listPathFilenames.append(PurePath(pathFilename))
123
+
124
+ return listPathFilenames
125
+
126
+ @cache
127
+ def expressIndexAsFractionAddend(index: IndexPilesTotal, pilesTotal: int, denominators: tuple[int, ...]) -> FractionAddend:
128
+ indexAsFractionAndAddend: FractionAddend = (Fraction(0, 1), index)
129
+ direction = pos if index >= 0 else neg
130
+
131
+ if denominators:
132
+ addendsMagnitude: int = pilesTotal // max(denominators)
133
+ distanceBest: float = 9000.1
134
+
135
+ for denominator, addend in CartesianProduct(denominators, range(-(addendsMagnitude), addendsMagnitude + inclusive)):
136
+ for numerator in range(1, denominator):
137
+ if ((numerator / denominator).is_integer()) and (numerator // denominator in denominators):
138
+ continue
139
+ numerator = direction(numerator)
140
+ if index == (((numerator * pilesTotal) // denominator) + addend):
141
+ distance: float = abs(index - (((numerator * pilesTotal) / denominator) + addend))
142
+ if distance < distanceBest:
143
+ indexAsFractionAndAddend = (Fraction(numerator, denominator), addend)
144
+ distanceBest = distance
145
+
146
+ return indexAsFractionAndAddend
147
+
148
+ #======== Analyze exclusion data =======
149
+
150
+ def loadCollatedIndices(*, negative: bool = False) -> ExclusionData:
151
+ collatedIndices: ExclusionData = {}
152
+ stringGlob: str = 'collated*'
153
+ stringGlob += 'Negative' if negative else ''
154
+ for pathFilename in pathExclusionData.glob(stringGlob + ".py"):
155
+ stem: str = pathFilename.stem.removeprefix("collated").removesuffix("Negative")
156
+ mapKind, leafExcluderName = stem[0:4], stem[4:]
157
+ collatedIndices.setdefault(mapKind, {})[leafExcluderName] = importPathFilename2Identifier(pathFilename, "leafExcluderData")
158
+ return collatedIndices
159
+
160
+ @cache
161
+ def _dictionaryLeafDomainsByMapKind(mapKind: MapKind) -> dict[Leaf, range]:
162
+ return getDictionaryLeafDomains(EliminationState((2,) * _dimensionsTotalFromMapKind(mapKind)))
163
+
164
+ def _dimensionsTotalFromMapKind(mapKind: MapKind) -> int:
165
+ return int(mapKind.removeprefix("p2d"))
166
+
167
+ @cache
168
+ def _fractionAddendToIndex(fractionAddend: FractionAddend, pilesTotal: int) -> int:
169
+ fraction, addend = fractionAddend
170
+ index: int = ((fraction.numerator * pilesTotal) // fraction.denominator) + addend
171
+ if index < 0:
172
+ return pilesTotal + index
173
+ return index
174
+
175
+ def _listFractionAddendsToIndices(mapKind: MapKind, leafExcludedName: strLeafExcluded, listFractionAddends: list[FractionAddend]) -> list[int]:
176
+ pilesTotal: int = _pilesTotalOfLeafExcluded(mapKind, leafExcludedName)
177
+ return [_fractionAddendToIndex(fractionAddend, pilesTotal) for fractionAddend in listFractionAddends]
178
+
179
+ @cache
180
+ def _pilesTotalOfLeafExcluded(mapKind: MapKind, leafExcludedName: strLeafExcluded) -> int:
181
+ leaf: Leaf = dictionaryFunctionsByName[leafExcludedName](_dimensionsTotalFromMapKind(mapKind))
182
+ return len(_dictionaryLeafDomainsByMapKind(mapKind)[leaf])
183
+
184
+ def _fractionAddendsForIndexSubset(listFractionAddends: list[FractionAddend], listResolvedIndices: list[int], indicesSubset: list[int]) -> set[FractionAddend]:
185
+ if not indicesSubset:
186
+ return set()
187
+ indicesSet: set[int] = set(indicesSubset)
188
+ return {fractionAddend for fractionAddend, resolvedIndex in zip(listFractionAddends, listResolvedIndices, strict=True) if resolvedIndex in indicesSet}
189
+
190
+ def _fractionAddendFromIndex(index: IndexPilesTotal) -> FractionAddend:
191
+ return (Fraction(0, 1), index)
192
+
193
+ def _getContiguousFromStart(listIndices: list[IndexPilesTotal]) -> list[IndexPilesTotal]:
194
+ """Return the first contiguous group starting at index 0, if it has at least 2 elements."""
195
+ listContiguous: list[IndexPilesTotal] = []
196
+ if listIndices and listIndices[0] == 0:
197
+ listContiguous = list(next(consecutive_groups(listIndices)))
198
+ if len(listContiguous) < 2:
199
+ listContiguous = []
200
+ return listContiguous
201
+
202
+ def _getContiguousEndingAtNegativeOne(listRelativeIndices: list[int]) -> list[int]:
203
+ """Return the last contiguous group ending at -1, if it has at least 2 elements."""
204
+ listContiguous: list[int] = []
205
+ listRelativeIndicesSorted: list[int] = sorted(listRelativeIndices)
206
+ if listRelativeIndicesSorted and listRelativeIndicesSorted[-1] == -1:
207
+ for group in consecutive_groups(listRelativeIndicesSorted):
208
+ listContiguous = list(group)
209
+ if (len(listContiguous) < 2) or (listContiguous[-1] != -1):
210
+ listContiguous = []
211
+ return listContiguous
212
+
213
+ def analyzeContiguousStartAbsolute(dataset: ExclusionData) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
214
+ """Find common contiguous indices starting from 0 across all map shapes, expressed as absolute indices."""
215
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
216
+ listMapKinds: list[MapKind] = list(dataset.keys())
217
+ mapKind0: MapKind = listMapKinds[0]
218
+
219
+ for leafExcluderName in dataset[mapKind0]:
220
+ if any(leafExcluderName not in dataset[mapKind] for mapKind in listMapKinds):
221
+ continue
222
+ aggregatedExclusions[leafExcluderName] = {}
223
+
224
+ for pileExcluderName in dataset[mapKind0][leafExcluderName]:
225
+ if any(pileExcluderName not in dataset[mapKind][leafExcluderName] for mapKind in listMapKinds):
226
+ continue
227
+ aggregatedExclusions[leafExcluderName][pileExcluderName] = {}
228
+
229
+ for leafExcludedName in dataset[mapKind0][leafExcluderName][pileExcluderName]:
230
+ if any(leafExcludedName not in dataset[mapKind][leafExcluderName][pileExcluderName] for mapKind in listMapKinds):
231
+ continue
232
+
233
+ listContiguousLengths: list[int] = []
234
+
235
+ for mapKind in listMapKinds:
236
+ listFractionAddends: list[FractionAddend] = dataset[mapKind][leafExcluderName][pileExcluderName][leafExcludedName]
237
+ resolvedIndices: list[int] = _listFractionAddendsToIndices(mapKind, leafExcludedName, listFractionAddends)
238
+ listContiguousLengths.append(len(_getContiguousFromStart(resolvedIndices)))
239
+
240
+ commonLength: int = min(listContiguousLengths) if listContiguousLengths else 0
241
+ listFractionAddendsCommon: list[FractionAddend] = [_fractionAddendFromIndex(indexValue) for indexValue in range(commonLength)] if commonLength >= 2 else []
242
+ aggregatedExclusions[leafExcluderName][pileExcluderName][leafExcludedName] = listFractionAddendsCommon
243
+
244
+ return aggregatedExclusions
245
+
246
+ def analyzeContiguousEndAbsolute(dataset: ExclusionData) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
247
+ """Find common contiguous indices ending at pilesTotal-1 across all map shapes, expressed as negative absolute indices."""
248
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
249
+ listMapKinds: list[MapKind] = list(dataset.keys())
250
+ mapKind0: MapKind = listMapKinds[0]
251
+
252
+ for leafExcluderName in dataset[mapKind0]:
253
+ if any(leafExcluderName not in dataset[mapKind] for mapKind in listMapKinds):
254
+ continue
255
+ aggregatedExclusions[leafExcluderName] = {}
256
+
257
+ for pileExcluderName in dataset[mapKind0][leafExcluderName]:
258
+ if any(pileExcluderName not in dataset[mapKind][leafExcluderName] for mapKind in listMapKinds):
259
+ continue
260
+ aggregatedExclusions[leafExcluderName][pileExcluderName] = {}
261
+
262
+ for leafExcludedName in dataset[mapKind0][leafExcluderName][pileExcluderName]:
263
+ if any(leafExcludedName not in dataset[mapKind][leafExcluderName][pileExcluderName] for mapKind in listMapKinds):
264
+ continue
265
+
266
+ listContiguousLengths: list[int] = []
267
+
268
+ for mapKind in listMapKinds:
269
+ listFractionAddends: list[FractionAddend] = dataset[mapKind][leafExcluderName][pileExcluderName][leafExcludedName]
270
+ listRelativeIndices: list[int] = _listFractionAddendsToIndices(mapKind, leafExcludedName, listFractionAddends)
271
+ listContiguousLengths.append(len(_getContiguousEndingAtNegativeOne(listRelativeIndices)))
272
+
273
+ commonLength: int = min(listContiguousLengths) if listContiguousLengths else 0
274
+ rangeIndices: range = range(-commonLength, 0) if commonLength >= 2 else range(0)
275
+ aggregatedExclusions[leafExcluderName][pileExcluderName][leafExcludedName] = [_fractionAddendFromIndex(indexValue) for indexValue in rangeIndices]
276
+
277
+ return aggregatedExclusions
278
+
279
+ def analyzeContiguousStartRelative(dataset: ExclusionData) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
280
+ """Find common contiguous indices starting from 0 across all map shapes, expressed as fractions of pilesTotal."""
281
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
282
+ listMapKinds: list[MapKind] = list(dataset.keys())
283
+ mapKind0: MapKind = listMapKinds[0]
284
+
285
+ for leafExcluderName in dataset[mapKind0]:
286
+ if any(leafExcluderName not in dataset[mapKind] for mapKind in listMapKinds):
287
+ continue
288
+ aggregatedExclusions[leafExcluderName] = {}
289
+
290
+ for pileExcluderName in dataset[mapKind0][leafExcluderName]:
291
+ if any(pileExcluderName not in dataset[mapKind][leafExcluderName] for mapKind in listMapKinds):
292
+ continue
293
+ aggregatedExclusions[leafExcluderName][pileExcluderName] = {}
294
+
295
+ for leafExcludedName in dataset[mapKind0][leafExcluderName][pileExcluderName]:
296
+ if any(leafExcludedName not in dataset[mapKind][leafExcluderName][pileExcluderName] for mapKind in listMapKinds):
297
+ continue
298
+
299
+ listSetsFractionAddends: list[set[FractionAddend]] = []
300
+
301
+ for mapKind in listMapKinds:
302
+ listFractionAddends: list[FractionAddend] = dataset[mapKind][leafExcluderName][pileExcluderName][leafExcludedName]
303
+ resolvedIndices: list[int] = _listFractionAddendsToIndices(mapKind, leafExcludedName, listFractionAddends)
304
+ contiguousIndices: list[int] = _getContiguousFromStart(resolvedIndices)
305
+ setFractionAddends: set[FractionAddend] = _fractionAddendsForIndexSubset(listFractionAddends, resolvedIndices, contiguousIndices)
306
+ listSetsFractionAddends.append(setFractionAddends)
307
+
308
+ commonFractionAddends: set[FractionAddend] = reduce(set[FractionAddend].intersection, listSetsFractionAddends)
309
+ aggregatedExclusions[leafExcluderName][pileExcluderName][leafExcludedName] = list(commonFractionAddends)
310
+
311
+ return aggregatedExclusions
312
+
313
+ def analyzeContiguousEndRelative(dataset: ExclusionData) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
314
+ """Find common contiguous indices ending at pilesTotal-1 across all map shapes, expressed as fractions of pilesTotal."""
315
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
316
+ listMapKinds: list[MapKind] = list(dataset.keys())
317
+ mapKind0: MapKind = listMapKinds[0]
318
+
319
+ for leafExcluderName in dataset[mapKind0]:
320
+ if any(leafExcluderName not in dataset[mapKind] for mapKind in listMapKinds):
321
+ continue
322
+ aggregatedExclusions[leafExcluderName] = {}
323
+
324
+ for pileExcluderName in dataset[mapKind0][leafExcluderName]:
325
+ if any(pileExcluderName not in dataset[mapKind][leafExcluderName] for mapKind in listMapKinds):
326
+ continue
327
+ aggregatedExclusions[leafExcluderName][pileExcluderName] = {}
328
+
329
+ for leafExcludedName in dataset[mapKind0][leafExcluderName][pileExcluderName]:
330
+ if any(leafExcludedName not in dataset[mapKind][leafExcluderName][pileExcluderName] for mapKind in listMapKinds):
331
+ continue
332
+
333
+ listSetsFractionAddends: list[set[FractionAddend]] = []
334
+
335
+ for mapKind in listMapKinds:
336
+ listFractionAddends: list[FractionAddend] = dataset[mapKind][leafExcluderName][pileExcluderName][leafExcludedName]
337
+ listRelativeIndices: list[int] = _listFractionAddendsToIndices(mapKind, leafExcludedName, listFractionAddends)
338
+ contiguousRelativeIndices: list[int] = _getContiguousEndingAtNegativeOne(listRelativeIndices)
339
+ setFractionAddends: set[FractionAddend] = _fractionAddendsForIndexSubset(listFractionAddends, listRelativeIndices, contiguousRelativeIndices)
340
+ listSetsFractionAddends.append(setFractionAddends)
341
+
342
+ commonFractionAddends: set[FractionAddend] = reduce(set[FractionAddend].intersection, listSetsFractionAddends)
343
+ aggregatedExclusions[leafExcluderName][pileExcluderName][leafExcludedName] = list(commonFractionAddends)
344
+
345
+ return aggregatedExclusions
346
+
347
+ def analyzeNonContiguousIndicesRelative(dataset: ExclusionData) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
348
+ """Find common indices across all map shapes (contiguous or not), expressed as fractions of pilesTotal."""
349
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
350
+ listMapKinds: list[MapKind] = list(dataset.keys())
351
+ mapKind0: MapKind = listMapKinds[0]
352
+
353
+ for leafExcluderName in dataset[mapKind0]:
354
+ if any(leafExcluderName not in dataset[mapKind] for mapKind in listMapKinds):
355
+ continue
356
+
357
+ for pileExcluderName in dataset[mapKind0][leafExcluderName]:
358
+ if any(pileExcluderName not in dataset[mapKind][leafExcluderName] for mapKind in listMapKinds):
359
+ continue
360
+
361
+ for leafExcludedName in dataset[mapKind0][leafExcluderName][pileExcluderName]:
362
+ if any(leafExcludedName not in dataset[mapKind][leafExcluderName][pileExcluderName] for mapKind in listMapKinds):
363
+ continue
364
+
365
+ listSetsFractionAddends: list[set[FractionAddend]] = []
366
+
367
+ for mapKind in listMapKinds:
368
+ listFractionAddends: list[FractionAddend] = dataset[mapKind][leafExcluderName][pileExcluderName][leafExcludedName]
369
+ setFractionAddends: set[FractionAddend] = set(listFractionAddends)
370
+ listSetsFractionAddends.append(setFractionAddends)
371
+
372
+ aggregatedExclusions.setdefault(leafExcluderName, {}).setdefault(pileExcluderName, {})[leafExcludedName] = list(reduce(set[FractionAddend].intersection, listSetsFractionAddends))
373
+
374
+ return aggregatedExclusions
375
+
376
+ #======== Aggregate exclusion data =======
377
+
378
+ def writeAggregatedExclusions(pathWrite: Path | None = None) -> list[PurePath]:
379
+ """{leafExcluder: {pileExcluder: {leafExcluded: listIndicesAsFractionAddends}}}."""
380
+ if pathWrite is None:
381
+ pathWrite = pathExclusionData
382
+ listPathFilenames: list[PurePath] = []
383
+ collatedIndices: ExclusionData = loadCollatedIndices()
384
+ collatedIndicesNegative: ExclusionData = loadCollatedIndices(negative=True)
385
+
386
+ listExclusions: list[dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]] = [
387
+ analyzeContiguousEndAbsolute(collatedIndicesNegative),
388
+ analyzeContiguousEndRelative(collatedIndicesNegative),
389
+ analyzeContiguousStartAbsolute(collatedIndices),
390
+ analyzeContiguousStartRelative(collatedIndices),
391
+ analyzeNonContiguousIndicesRelative(collatedIndices),
392
+ analyzeNonContiguousIndicesRelative(collatedIndicesNegative),
393
+ ]
394
+
395
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
396
+
397
+ for leafExcluder, pileExcluder in CartesianProduct(functionsHeadDimensions, functionsHeadDimensions):
398
+ dictionaryMerged: dict[strLeafExcluded, list[FractionAddend]] = updateExtendPolishDictionaryLists(
399
+ *[dictionaryExclusions.get(leafExcluder.__name__, {}).get(pileExcluder.__name__, {}) for dictionaryExclusions in listExclusions]
400
+ , destroyDuplicates=True, reorderLists=True)
401
+ aggregatedExclusions.setdefault(leafExcluder.__name__, {})[pileExcluder.__name__] = dictionaryMerged
402
+
403
+ for leafExcluderName, pileExcluderData in aggregatedExclusions.items():
404
+ pythonSource: str = "from fractions import Fraction\n\n"
405
+ pythonSource += "type FractionAddend = tuple[Fraction, int]\n\n"
406
+ dataFormatted: str = pformat(pileExcluderData, indent=0, width=160, compact=True)
407
+ pythonSource += f"dictionaryExclusions: dict[str, dict[str, list[FractionAddend]]] = {dataFormatted}\n"
408
+ pathFilename: Path = pathWrite / f"aggregated{leafExcluderName}.py"
409
+ writePython(pythonSource, pathFilename)
410
+ listPathFilenames.append(PurePath(pathFilename))
411
+
412
+ return listPathFilenames
413
+
414
+ #======== Create exclusion dictionaries for elimination tools =======
415
+
416
+ def loadAggregatedExclusions() -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
417
+ aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = {}
418
+ for pathFilename in pathExclusionData.glob("aggregated*.py"):
419
+ leafExcluderName: str = pathFilename.stem.removeprefix("aggregated")
420
+ aggregatedExclusions[leafExcluderName] = importPathFilename2Identifier(pathFilename, "dictionaryExclusions")
421
+ return aggregatedExclusions
422
+
423
+ def restructureAggregatedExclusionsForMapShape(dimensionsTotal: int, aggregatedExclusions: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]) -> tuple[dict[Leaf, dict[Pile, dict[Pile, list[Leaf]]]], dict[Pile, dict[Leaf, dict[Pile, list[Leaf]]]]]:
424
+ leafDomains: dict[Leaf, range] = getDictionaryLeafDomains(EliminationState(mapShape=(2,) * dimensionsTotal))
425
+
426
+ dictionaryLeafExcludedAtPileByPile: dict[Leaf, dict[Pile, dict[Pile, list[Leaf]]]] = {}
427
+ dictionaryAtPileLeafExcludedByPile: dict[Pile, dict[Leaf, dict[Pile, list[Leaf]]]] = {}
428
+
429
+ for leafExcluderName, pileExcluderData in aggregatedExclusions.items():
430
+ for pileExcluderName, leafExcludedData in pileExcluderData.items():
431
+ for leafExcludedName, listFractionAddends in leafExcludedData.items():
432
+ leafExcluded: Leaf = dictionaryFunctionsByName[leafExcludedName](dimensionsTotal)
433
+ leafExcluderValue: Leaf = dictionaryFunctionsByName[leafExcluderName](dimensionsTotal)
434
+ pileExcluderValue: Pile = dictionaryFunctionsByName[pileExcluderName](dimensionsTotal)
435
+
436
+ domainOfLeafExcluded: list[Pile] = list(leafDomains[leafExcluded])
437
+ pilesTotal: int = len(domainOfLeafExcluded)
438
+
439
+ for fractionAddend in listFractionAddends:
440
+ indexResolved: int = _fractionAddendToIndex(fractionAddend, pilesTotal)
441
+ pileExcluded: Pile = domainOfLeafExcluded[indexResolved]
442
+
443
+ dictionaryLeafExcludedAtPileByPile.setdefault(leafExcluded, {}
444
+ ).setdefault(pileExcluded, {}
445
+ ).setdefault(pileExcluderValue, []
446
+ ).append(leafExcluderValue)
447
+ dictionaryAtPileLeafExcludedByPile.setdefault(pileExcluded, {}
448
+ ).setdefault(leafExcluded, {}
449
+ ).setdefault(pileExcluderValue, []
450
+ ).append(leafExcluderValue)
451
+
452
+ for leafExcluded in dictionaryLeafExcludedAtPileByPile:
453
+ for pileExcluded in dictionaryLeafExcludedAtPileByPile[leafExcluded]:
454
+ for pileExcluder in dictionaryLeafExcludedAtPileByPile[leafExcluded][pileExcluded]:
455
+ dictionaryLeafExcludedAtPileByPile[leafExcluded][pileExcluded][pileExcluder] = sorted(set(dictionaryLeafExcludedAtPileByPile[leafExcluded][pileExcluded][pileExcluder]))
456
+
457
+ for pileExcluded in dictionaryAtPileLeafExcludedByPile:
458
+ for leafExcluded in dictionaryAtPileLeafExcludedByPile[pileExcluded]:
459
+ for pileExcluder in dictionaryAtPileLeafExcludedByPile[pileExcluded][leafExcluded]:
460
+ dictionaryAtPileLeafExcludedByPile[pileExcluded][leafExcluded][pileExcluder] = sorted(set(dictionaryAtPileLeafExcludedByPile[pileExcluded][leafExcluded][pileExcluder]))
461
+
462
+ return (dictionaryLeafExcludedAtPileByPile, dictionaryAtPileLeafExcludedByPile)
463
+
464
+ def writeExclusionDictionaries(pathExclusionsFile: PurePath | None = None) -> PurePath:
465
+ listDimensions: list[int] = [5, 6]
466
+ listLines: list[str] = ['"""Static exclusion dictionaries for elimination tools."""', ""]
467
+
468
+ for dimensionsTotal in listDimensions:
469
+ dictionaryLeafExcludedAtPileByPile, dictionaryAtPileLeafExcludedByPile = restructureAggregatedExclusionsForMapShape(dimensionsTotal, loadAggregatedExclusions())
470
+
471
+ mapKind: MapKind = f"2d{dimensionsTotal}"
472
+
473
+ listLines.append(f"dictionary{mapKind}LeafExcludedAtPileByPile: dict[int, dict[int, dict[int, list[int]]]] = {pformat(dictionaryLeafExcludedAtPileByPile, indent=4, width=160, compact=True)}")
474
+ listLines.append("")
475
+ listLines.append(f"dictionary{mapKind}AtPileLeafExcludedByPile: dict[int, dict[int, dict[int, list[int]]]] = {pformat(dictionaryAtPileLeafExcludedByPile, indent=4, width=160, compact=True)}")
476
+ listLines.append("")
477
+
478
+ pathFilename: Path = Path(pathExclusionsFile) if pathExclusionsFile is not None else Path(f"{packageSettings.pathPackage}/_e/_exclusions.py")
479
+ pythonSource: str = "\n".join(listLines)
480
+ writePython(pythonSource, pathFilename)
481
+
482
+ return PurePath(pathFilename)
483
+
484
+ #======== Validation functions =======
485
+
486
+ @cache
487
+ def _getArrayFoldingsByDimensions(dimensionsTotal: int) -> numpy.ndarray:
488
+ return raiseIfNone(getDataFrameFoldings(EliminationState((2,) * dimensionsTotal))).to_numpy(dtype=numpy.uint8, copy=False)
489
+
490
+ def validateExclusionDictionaries(dictionaryLeafExcludedAtPileByPile: dict[Leaf, dict[Pile, dict[Pile, list[Leaf]]]], dictionaryAtPileLeafExcludedByPile: dict[Pile, dict[Leaf, dict[Pile, list[Leaf]]]], mapKind: MapKind) -> tuple[bool, list[str]]:
491
+ dimensions: int = int(mapKind[3:])
492
+ listValidationErrors: list[str] = []
493
+ arrayFoldings = _getArrayFoldingsByDimensions(dimensions)
494
+
495
+ for pileExcluded, leafExcludedData in dictionaryAtPileLeafExcludedByPile.items():
496
+ for leafExcluded, pileExcluderData in leafExcludedData.items():
497
+ for pileExcluder, listLeafExcluders in pileExcluderData.items():
498
+ for leafExcluder in listLeafExcluders:
499
+ mask = (arrayFoldings[:, pileExcluder] == leafExcluder)
500
+ if (arrayFoldings[mask, pileExcluded] == leafExcluded).any():
501
+ listValidationErrors.append(
502
+ f"Invalid exclusion in dictionaryAtPile...: If pile {pileExcluder} has leaf {leafExcluder}, "
503
+ f"pile {pileExcluded} cannot have leaf {leafExcluded}. Found counter-example."
504
+ )
505
+
506
+ for leafExcluded, pileExcludedData in dictionaryLeafExcludedAtPileByPile.items():
507
+ for pileExcluded, pileExcluderData in pileExcludedData.items():
508
+ for pileExcluder, listLeafExcluders in pileExcluderData.items():
509
+ for leafExcluder in listLeafExcluders:
510
+ mask = (arrayFoldings[:, pileExcluder] == leafExcluder)
511
+ if (arrayFoldings[mask, pileExcluded] == leafExcluded).any():
512
+ listValidationErrors.append(
513
+ f"Invalid exclusion in dictionaryLeaf...: If pile {pileExcluder} has leaf {leafExcluder}, "
514
+ f"pile {pileExcluded} cannot have leaf {leafExcluded}. Found counter-example."
515
+ )
516
+
517
+ return len(listValidationErrors) == 0, listValidationErrors
518
+
519
+ def validateAnalysisMethodForMapShape(exclusionsFromAnalysisMethod: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]], mapKind: MapKind) -> tuple[bool, list[str]]:
520
+ dimensions: int = int(mapKind[3:])
521
+ listValidationErrors: list[str] = []
522
+ arrayFoldings = _getArrayFoldingsByDimensions(dimensions)
523
+ rowsTotal: int = int(arrayFoldings.shape[0])
524
+
525
+ for leafExcluderName in exclusionsFromAnalysisMethod:
526
+ leafExcluderFunction: CallableFunction[[int], int] = dictionaryFunctionsByName[leafExcluderName]
527
+ leafExcluder: int = leafExcluderFunction(dimensions)
528
+
529
+ for pileExcluderName in exclusionsFromAnalysisMethod[leafExcluderName]:
530
+ pileExcluderFunction: CallableFunction[[int], int] = dictionaryFunctionsByName[pileExcluderName]
531
+ pileExcluder: int = pileExcluderFunction(dimensions)
532
+
533
+ for leafExcludedName in exclusionsFromAnalysisMethod[leafExcluderName][pileExcluderName]:
534
+ leafExcludedFunction: CallableFunction[[int], int] = dictionaryFunctionsByName[leafExcludedName]
535
+ leafExcluded: int = leafExcludedFunction(dimensions)
536
+
537
+ listFractionAddends: list[FractionAddend] = exclusionsFromAnalysisMethod[leafExcluderName][pileExcluderName][leafExcludedName]
538
+
539
+ if not listFractionAddends:
540
+ continue
541
+
542
+ stateValidation: EliminationState = EliminationState(mapShape=(2,) * dimensions)
543
+ pilesTotalCurrent: int = len(list(getLeafDomain(stateValidation, dictionaryFunctionsByName[leafExcludedName](dimensions))))
544
+
545
+ listIndicesExcluded: list[int] = []
546
+ for fractionAddend in listFractionAddends:
547
+ indexComputed: int = _fractionAddendToIndex(fractionAddend, pilesTotalCurrent)
548
+ listIndicesExcluded.append(indexComputed)
549
+
550
+ if not listIndicesExcluded:
551
+ continue
552
+
553
+ stateValidation = pinPilesAtEnds(stateValidation, 1)
554
+
555
+ pileRange: list[int] = list(getPileRange(stateValidation, pileExcluder))
556
+ dictionaryDeconstructed: dict[int, PermutationSpace] = deconstructPermutationSpaceAtPile(stateValidation.listPermutationSpace[0], pileExcluder, pileRange)
557
+
558
+ permutationSpaceWithExcluder: PermutationSpace | None = dictionaryDeconstructed.get(leafExcluder)
559
+ if permutationSpaceWithExcluder is None:
560
+ continue
561
+
562
+ listPermutationSpaceOther: list[PermutationSpace] = [permutationSpace for leaf, permutationSpace in dictionaryDeconstructed.items() if leaf != leafExcluder]
563
+
564
+ domainOfLeafExcluded: list[int] = list(getLeafDomain(stateValidation, leafExcluded))
565
+ domainReduced: list[int] = list(exclude(domainOfLeafExcluded, listIndicesExcluded))
566
+
567
+ listPermutationSpaceFromExcluder: list[PermutationSpace] = deconstructPermutationSpaceByDomainOfLeaf(permutationSpaceWithExcluder, leafExcluded, domainReduced)
568
+
569
+ stateValidation.listPermutationSpace = listPermutationSpaceOther + listPermutationSpaceFromExcluder
570
+
571
+ pinningCoverage: PermutationSpaceStatus = detectPermutationSpaceErrors(arrayFoldings, stateValidation.listPermutationSpace)
572
+
573
+ if pinningCoverage.rowsRequired < rowsTotal:
574
+ listValidationErrors.append(
575
+ f"{mapKind} {leafExcluderName}->{pileExcluderName}->{leafExcludedName}: {pinningCoverage.rowsRequired = }/{rowsTotal}")
576
+
577
+ overlappingRowCount: int = int(pinningCoverage.indicesOverlappingRows.size)
578
+ if overlappingRowCount > 0:
579
+ listValidationErrors.append(
580
+ f"{mapKind} {leafExcluderName}->{pileExcluderName}->{leafExcludedName}: {overlappingRowCount} overlapping rows")
581
+
582
+ isValid: bool = len(listValidationErrors) == 0
583
+ return (isValid, listValidationErrors)
584
+
585
+ def validateAnalysisMethod(analysisMethodCallable: CallableFunction[[ExclusionData], dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]]) -> dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]]:
586
+ collatedIndices: ExclusionData = loadCollatedIndices()
587
+ listMapShapeNames: list[str] = list(collatedIndices.keys())
588
+ exclusionsFromMethod: dict[strLeafExcluder, dict[strPileExcluder, dict[strLeafExcluded, list[FractionAddend]]]] = analysisMethodCallable(collatedIndices)
589
+ errorsByMapShape: dict[str, list[str]] = {}
590
+
591
+ for mapShapeName in listMapShapeNames:
592
+ isValid, listValidationErrors = validateAnalysisMethodForMapShape(exclusionsFromMethod, mapShapeName)
593
+ if not isValid:
594
+ errorsByMapShape.setdefault(mapShapeName, []).extend(listValidationErrors)
595
+
596
+ dimensions: int = int(mapShapeName[3:])
597
+ dictionaryLeafExcludedAtPileByPile, dictionaryAtPileLeafExcludedByPile = restructureAggregatedExclusionsForMapShape(dimensions, exclusionsFromMethod)
598
+ isValidDictionaries, listDictionaryErrors = validateExclusionDictionaries(dictionaryLeafExcludedAtPileByPile, dictionaryAtPileLeafExcludedByPile, mapShapeName)
599
+ if not isValidDictionaries:
600
+ errorsByMapShape.setdefault(mapShapeName, []).extend(listDictionaryErrors)
601
+
602
+ if not errorsByMapShape:
603
+ colorSuccess = ansiColors.BlackOnCyan
604
+ sys.stdout.write(f"{colorSuccess}{analysisMethodCallable.__name__} validated across {len(listMapShapeNames)} mapShapes{ansiColorReset}\n")
605
+ else:
606
+ colorFailure = ansiColors.WhiteOnMagenta
607
+ sys.stdout.write(f"{colorFailure}{analysisMethodCallable.__name__} validation failed for {len(errorsByMapShape)} mapShapes{ansiColorReset}\n")
608
+ for mapShapeName, listErrors in errorsByMapShape.items():
609
+ sys.stdout.write(f"{colorFailure}{mapShapeName}: {len(listErrors)} issues{ansiColorReset}\n")
610
+ for error in listErrors[0:3]:
611
+ sys.stdout.write(f"{colorFailure} {error}{ansiColorReset}\n")
612
+
613
+ return exclusionsFromMethod
614
+
615
+ def runGenerators() -> None:
616
+ sys.stdout.write(f"{writeExclusionDataCollated() = }\n")
617
+ sys.stdout.write(f"{writeAggregatedExclusions() = }\n")
618
+ sys.stdout.write(f"{writeExclusionDictionaries() = }\n")
619
+
620
+ def runValidators() -> None:
621
+ collatedIndices: ExclusionData = loadCollatedIndices()
622
+ if not collatedIndices:
623
+ sys.stdout.write("No collated indices found. Run 'generate' mode first to create exclusion data.\\n")
624
+ return
625
+
626
+ listAnalysisMethods = [
627
+ analyzeNonContiguousIndicesRelative,
628
+ analyzeContiguousStartAbsolute,
629
+ analyzeContiguousEndAbsolute,
630
+ analyzeContiguousStartRelative,
631
+ analyzeContiguousEndRelative,
632
+ ]
633
+
634
+ for analysisMethod in listAnalysisMethods:
635
+ validateAnalysisMethod(analysisMethod)
636
+
637
+ if __name__ == '__main__':
638
+ runGenerators()
639
+ runValidators()
640
+