mapFolding 0.16.4__py3-none-any.whl → 0.17.1__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 (69) hide show
  1. easyRun/NOTcountingFolds.py +28 -11
  2. easyRun/__init__.py +1 -0
  3. easyRun/countFolds.py +16 -2
  4. easyRun/eliminateFolds.py +60 -0
  5. easyRun/meanders.py +3 -3
  6. mapFolding/__init__.py +2 -1
  7. mapFolding/_theTypes.py +0 -1
  8. mapFolding/algorithms/A086345.py +8 -3
  9. mapFolding/algorithms/__init__.py +1 -1
  10. mapFolding/algorithms/constraintPropagation.py +184 -0
  11. mapFolding/algorithms/elimination.py +131 -0
  12. mapFolding/algorithms/eliminationCount.py +26 -0
  13. mapFolding/algorithms/eliminationPinned.py +35 -0
  14. mapFolding/algorithms/iff.py +206 -0
  15. mapFolding/algorithms/matrixMeanders.py +59 -18
  16. mapFolding/algorithms/matrixMeandersNumPyndas.py +841 -0
  17. mapFolding/algorithms/patternFinder.py +280 -0
  18. mapFolding/algorithms/pinning2Dn.py +345 -0
  19. mapFolding/algorithms/pinning2DnAnnex.py +43 -0
  20. mapFolding/algorithms/symmetricFolds.py +24 -25
  21. mapFolding/basecamp.py +84 -14
  22. mapFolding/beDRY.py +14 -1
  23. mapFolding/dataBaskets.py +86 -71
  24. mapFolding/reference/irvineJavaPort.py +3 -3
  25. mapFolding/reference/meandersDumpingGround/matrixMeandersNumPyV1finalForm.py +1 -1
  26. mapFolding/someAssemblyRequired/A007822/_asynchronousAnnex.py +1 -1
  27. mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +5 -3
  28. mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +22 -6
  29. mapFolding/someAssemblyRequired/RecipeJob.py +14 -24
  30. mapFolding/someAssemblyRequired/__init__.py +1 -0
  31. mapFolding/someAssemblyRequired/_toolkitContainers.py +6 -4
  32. mapFolding/someAssemblyRequired/infoBooth.py +2 -1
  33. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +75 -20
  34. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +9 -10
  35. mapFolding/someAssemblyRequired/makingModules_count.py +20 -22
  36. mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +9 -9
  37. mapFolding/someAssemblyRequired/mapFoldingModules/makeMapFoldingModules.py +6 -5
  38. mapFolding/someAssemblyRequired/meanders/makeMeandersModules.py +6 -6
  39. mapFolding/someAssemblyRequired/toolkitMakeModules.py +3 -29
  40. mapFolding/someAssemblyRequired/toolkitNumba.py +2 -1
  41. mapFolding/someAssemblyRequired/transformationTools.py +2 -3
  42. mapFolding/syntheticModules/A007822/algorithm.py +8 -8
  43. mapFolding/syntheticModules/A007822/asynchronous.py +12 -13
  44. mapFolding/syntheticModules/A007822/initializeState.py +10 -8
  45. mapFolding/syntheticModules/A007822/theorem2.py +10 -8
  46. mapFolding/syntheticModules/A007822/theorem2Numba.py +20 -16
  47. mapFolding/syntheticModules/A007822/theorem2Trimmed.py +10 -8
  48. mapFolding/syntheticModules/countParallelNumba.py +5 -2
  49. mapFolding/syntheticModules/daoOfMapFoldingNumba.py +4 -2
  50. mapFolding/syntheticModules/initializeState.py +1 -1
  51. mapFolding/syntheticModules/meanders/bigInt.py +52 -15
  52. mapFolding/syntheticModules/theorem2.py +1 -1
  53. mapFolding/syntheticModules/theorem2Numba.py +4 -2
  54. mapFolding/syntheticModules/theorem2Trimmed.py +1 -1
  55. mapFolding/tests/conftest.py +1 -1
  56. mapFolding/tests/test_computations.py +21 -4
  57. mapFolding/tests/verify.py +323 -0
  58. {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/METADATA +14 -11
  59. mapfolding-0.17.1.dist-info/RECORD +112 -0
  60. easyRun/A000682.py +0 -25
  61. easyRun/A005316.py +0 -20
  62. mapFolding/algorithms/matrixMeandersBeDry.py +0 -182
  63. mapFolding/algorithms/matrixMeandersNumPy.py +0 -333
  64. mapFolding/algorithms/matrixMeandersPandas.py +0 -334
  65. mapfolding-0.16.4.dist-info/RECORD +0 -106
  66. {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/WHEEL +0 -0
  67. {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/entry_points.txt +0 -0
  68. {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/licenses/LICENSE +0 -0
  69. {mapfolding-0.16.4.dist-info → mapfolding-0.17.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,206 @@
1
+ """Verify that a folding sequence is possible.
2
+
3
+ Notes
4
+ -----
5
+ Eight forbidden inequalities of matching parity k and r *à la* Koehler (1968), indices of:
6
+ [k < r < k+1 < r+1] [r < k+1 < r+1 < k] [k+1 < r+1 < k < r] [r+1 < k < r < k+1]
7
+ [r < k < r+1 < k+1] [k < r+1 < k+1 < r] [r+1 < k+1 < r < k] [k+1 < r < k < r+1]
8
+
9
+ Four forbidden inequalities of matching parity k and r *à la* Legendre (2014), indices of:
10
+ [k < r < k+1 < r+1] [k+1 < r+1 < k < r] [r+1 < k < r < k+1] [k < r+1 < k+1 < r]
11
+
12
+ Citations
13
+ ---------
14
+ - John E. Koehler, Folding a strip of stamps, Journal of Combinatorial Theory, Volume 5, Issue 2, 1968, Pages 135-152, ISSN
15
+ 0021-9800, https://doi.org/10.1016/S0021-9800(68)80048-1.
16
+ - Stéphane Legendre, Foldings and meanders, The Australasian Journal of Combinatorics, Volume 58, Part 2, 2014, Pages 275-291,
17
+ ISSN 2202-3518, https://ajc.maths.uq.edu.au/pdf/58/ajc_v58_p275.pdf.
18
+
19
+ See Also
20
+ --------
21
+ - "[Annotated, corrected, scanned copy]" of Koehler (1968) at https://oeis.org/A001011.
22
+ - Citations in BibTeX format "mapFolding/citations".
23
+ """
24
+ from collections.abc import Callable
25
+ from cytoolz.curried import get
26
+ from cytoolz.functoolz import curry as syntacticCurry
27
+ from functools import cache
28
+ from itertools import combinations, filterfalse, product as CartesianProduct
29
+ from mapFolding import getLeavesTotal
30
+ from mapFolding.dataBaskets import EliminationState
31
+ from math import prod
32
+ from operator import indexOf
33
+
34
+ def thisIsAViolation(column: int, columnComparand: int, getLeafNextCrease: Callable[[], int | None], getComparandNextCrease: Callable[[], int | None], columnOf: Callable[[int], int | None]) -> bool: # noqa: PLR0911
35
+ """Validate.
36
+
37
+ Mathematical reasons for the design of this function
38
+ ----------------------------------------------------
39
+
40
+ 1. To confirm that a multidimensional folding is valid, confirm that each of the constituent one-dimensional¹ foldings is valid.
41
+ 2. To confirm that a one-dimensional folding is valid, check that all creases that might cross do not cross.
42
+
43
+ A "crease" is a convenient lie: it is a shorthand description of two leaves that are physically connected to each other.
44
+ Leaves in a one-dimensional folding are physically connected to at most two other leaves: the prior leaf and the next leaf.
45
+ When talking about a one-dimensional section of a multidimensional folding, we ignore the other dimension and still
46
+ reference the prior and next leaves. To check whether two creases cross, we must compare the four leaves of the two creases.
47
+
48
+ ¹ A so-called one-dimensional folding, map, or strip of stamps has two dimensions, but one of the dimensions has a width of 1.
49
+
50
+ Idiosyncratic reasons for the design of this function
51
+ -----------------------------------------------------
52
+
53
+ I name the first leaf of the first crease `leaf`. I name the leaf to which I am comparing it `comparand`. A crease² is a leaf
54
+ and the next leaf, therefore, the crease of `leaf` connects it to `leafNextCrease`, and the crease of `comparand` connects it
55
+ to `comparandNextCrease`. Nearly everyone else uses letters for names, such as k, k+1, r, and r+1. (Which stand for Kahlo and
56
+ Rivera, of course.)
57
+
58
+ ² "increase" from Latin *in-* "in" + *crescere* "to grow" (from PIE root ⋆ker- "to grow"). https://www.etymonline.com/word/increase
59
+
60
+ Computational reasons for the design of this function
61
+ -----------------------------------------------------
62
+
63
+ If `leaf` and `comparand` do not have matching parity in the dimension, then their creases cannot cross. To call this
64
+ function, you need `leaf` and `comparand`, and because determining parity-by-dimension is easiest when you first select `leaf`
65
+ and `comparand`, this function will not check the parity of `leaf` and `comparand`.
66
+
67
+ Computing the next leaf is not expensive, but 100,000,000 unnecessary but cheap computations is expensive. Therefore, instead of
68
+ passing `leafNextCrease` and `comparandNextCrease`, pass the functions by which those values may be computed on demand.
69
+
70
+ Finally, we need to compare the relative positions of the leaves, so pass a function that returns the position of the "next" leaf.
71
+
72
+ """
73
+ if column < columnComparand:
74
+
75
+ comparandNextCrease: int | None = getComparandNextCrease()
76
+ if comparandNextCrease is None:
77
+ return False
78
+
79
+ leafNextCrease: int | None = getLeafNextCrease()
80
+ if leafNextCrease is None:
81
+ return False
82
+
83
+ columnComparandNextCrease: int | None = columnOf(comparandNextCrease)
84
+ if columnComparandNextCrease is None:
85
+ return False
86
+ columnLeafNextCrease: int | None = columnOf(leafNextCrease)
87
+ if columnLeafNextCrease is None:
88
+ return False
89
+
90
+ if columnComparandNextCrease < column:
91
+ if columnLeafNextCrease < columnComparandNextCrease: # [k+1 < r+1 < k < r]
92
+ return True
93
+ return columnComparand < columnLeafNextCrease # [r+1 < k < r < k+1]
94
+
95
+ if columnComparand < columnLeafNextCrease:
96
+ if columnLeafNextCrease < columnComparandNextCrease: # [k < r < k+1 < r+1]
97
+ return True
98
+ elif column < columnComparandNextCrease < columnLeafNextCrease < columnComparand: # [k < r+1 < k+1 < r]
99
+ return True
100
+ return False
101
+
102
+ # ------- ad hoc computations -----------------------------
103
+ # @cache
104
+ def _dimensionsTotal(mapShape: tuple[int, ...]) -> int:
105
+ return len(mapShape)
106
+
107
+ @cache
108
+ def _leavesTotal(mapShape: tuple[int, ...]) -> int:
109
+ return getLeavesTotal(mapShape)
110
+
111
+ # @cache
112
+ def productOfDimensions(mapShape: tuple[int, ...], dimension: int) -> int:
113
+ return prod(mapShape[0:dimension])
114
+
115
+ # ------- Functions for 'leaf', named 1, 2, ... n, not for 'indexLeaf' -------------
116
+
117
+ @cache
118
+ def ImaOddLeaf(mapShape: tuple[int, ...], leaf: int, dimension: int) -> int:
119
+ # NOTE `leaf-1` because `leaf` is not zero-based indexing.
120
+ return (((leaf-1) // productOfDimensions(mapShape, dimension)) % mapShape[dimension]) & 1
121
+
122
+ def _matchingParityLeaf(mapShape: tuple[int, ...], leaf: int, comparand: int, dimension: int) -> bool:
123
+ return ImaOddLeaf(mapShape, leaf, dimension) == ImaOddLeaf(mapShape, comparand, dimension)
124
+
125
+ @syntacticCurry
126
+ def matchingParityLeaf(mapShape: tuple[int, ...]) -> Callable[[tuple[tuple[tuple[int, int], tuple[int, int]], int]], bool]:
127
+ def repack(aCartesianProduct: tuple[tuple[tuple[int, int], tuple[int, int]], int]) -> bool:
128
+ ((_column, leaf), (_columnComparand, comparand)), dimension = aCartesianProduct
129
+ return _matchingParityLeaf(mapShape, leaf, comparand, dimension)
130
+ return repack
131
+
132
+ @cache
133
+ def nextCreaseLeaf(mapShape: tuple[int, ...], leaf: int, dimension: int) -> int | None:
134
+ leafNext: int | None = None
135
+ if (((leaf-1) // productOfDimensions(mapShape, dimension)) % mapShape[dimension]) + 1 < mapShape[dimension]:
136
+ leafNext = leaf + productOfDimensions(mapShape, dimension)
137
+ return leafNext
138
+
139
+ inThis_pileOf = syntacticCurry(indexOf)
140
+
141
+ def howToGetNextCreaseLeaf(mapShape: tuple[int, ...], leaf: int, dimension: int) -> Callable[[], int | None]:
142
+ return lambda: nextCreaseLeaf(mapShape, leaf, dimension)
143
+
144
+ def thisLeafFoldingIsValid(folding: tuple[int, ...], mapShape: tuple[int, ...]) -> bool:
145
+ """Return `True` if the folding is valid."""
146
+ foldingFiltered: filterfalse[tuple[int, int]] = filterfalse(lambda columnLeaf: columnLeaf[1] == _leavesTotal(mapShape), enumerate(folding)) # leafNPlus1 does not exist.
147
+ leafAndComparand: combinations[tuple[tuple[int, int], tuple[int, int]]] = combinations(foldingFiltered, 2)
148
+
149
+ leafAndComparandAcrossDimensions: CartesianProduct[tuple[tuple[tuple[int, int], tuple[int, int]], int]] = CartesianProduct(leafAndComparand, range(_dimensionsTotal(mapShape)))
150
+ parityInThisDimension: Callable[[tuple[tuple[tuple[int, int], tuple[int, int]], int]], bool] = matchingParityLeaf(mapShape)
151
+ leafAndComparandAcrossDimensionsFiltered: filter[tuple[tuple[tuple[int, int], tuple[int, int]], int]] = filter(parityInThisDimension, leafAndComparandAcrossDimensions)
152
+
153
+ return all(not thisIsAViolation(column, columnComparand, howToGetNextCreaseLeaf(mapShape, leaf, aDimension), howToGetNextCreaseLeaf(mapShape, comparand, aDimension), inThis_pileOf(folding))
154
+ for ((column, leaf), (columnComparand, comparand)), aDimension in leafAndComparandAcrossDimensionsFiltered)
155
+
156
+ # ------- Functions for `indexLeaf`, named 0, 1, ... n-1, not for `leaf` -------------
157
+
158
+ @cache
159
+ def ImaOddIndexLeaf(mapShape: tuple[int, ...], indexLeaf: int, dimension: int) -> int:
160
+ return ((indexLeaf // productOfDimensions(mapShape, dimension)) % mapShape[dimension]) & 1
161
+
162
+ def _matchingParityIndexLeaf(mapShape: tuple[int, ...], indexLeaf: int, comparand: int, dimension: int) -> bool:
163
+ return ImaOddIndexLeaf(mapShape, indexLeaf, dimension) == ImaOddIndexLeaf(mapShape, comparand, dimension)
164
+
165
+ @syntacticCurry
166
+ def matchingParityIndexLeaf(mapShape: tuple[int, ...]) -> Callable[[tuple[tuple[tuple[int, int], tuple[int, int]], int]], bool]:
167
+ def repack(aCartesianProduct: tuple[tuple[tuple[int, int], tuple[int, int]], int]) -> bool:
168
+ ((_pile, indexLeaf), (_pileComparand, comparand)), dimension = aCartesianProduct
169
+ return _matchingParityIndexLeaf(mapShape, indexLeaf, comparand, dimension)
170
+ return repack
171
+
172
+ @cache
173
+ def nextCreaseIndexLeaf(mapShape: tuple[int, ...], indexLeaf: int, dimension: int) -> int | None:
174
+ indexLeafNext: int | None = None
175
+ if ((indexLeaf // productOfDimensions(mapShape, dimension)) % mapShape[dimension]) + 1 < mapShape[dimension]:
176
+ indexLeafNext = indexLeaf + productOfDimensions(mapShape, dimension)
177
+ return indexLeafNext
178
+
179
+ inThis_pileOf = syntacticCurry(indexOf)
180
+
181
+ def getNextCreaseIndexLeaf(mapShape: tuple[int, ...], indexLeaf: int, dimension: int) -> Callable[[], int | None]:
182
+ return lambda: nextCreaseIndexLeaf(mapShape, indexLeaf, dimension)
183
+
184
+ def thisIndexLeafFoldingIsValid(folding: tuple[int, ...], mapShape: tuple[int, ...]) -> bool:
185
+ """Return `True` if the folding is valid."""
186
+ foldingFiltered: filterfalse[tuple[int, int]] = filterfalse(lambda pileIndexLeaf: pileIndexLeaf[1] == _leavesTotal(mapShape) - 1, enumerate(folding)) # indexLeafNPlus1 does not exist.
187
+ indexLeafAndComparand: combinations[tuple[tuple[int, int], tuple[int, int]]] = combinations(foldingFiltered, 2)
188
+
189
+ indexLeafAndComparandAcrossDimensions: CartesianProduct[tuple[tuple[tuple[int, int], tuple[int, int]], int]] = CartesianProduct(indexLeafAndComparand, range(_dimensionsTotal(mapShape)))
190
+ parityInThisDimension: Callable[[tuple[tuple[tuple[int, int], tuple[int, int]], int]], bool] = matchingParityIndexLeaf(mapShape)
191
+ indexLeafAndComparandAcrossDimensionsFiltered: filter[tuple[tuple[tuple[int, int], tuple[int, int]], int]] = filter(parityInThisDimension, indexLeafAndComparandAcrossDimensions)
192
+
193
+ return all(not thisIsAViolation(pile, pileComparand, getNextCreaseIndexLeaf(mapShape, indexLeaf, aDimension), getNextCreaseIndexLeaf(mapShape, comparand, aDimension), inThis_pileOf(folding))
194
+ for ((pile, indexLeaf), (pileComparand, comparand)), aDimension in indexLeafAndComparandAcrossDimensionsFiltered)
195
+
196
+ # ------- Functions for `indexLeaf` in `pinnedLeaves` dictionary, not for `leaf` in `folding` -------------
197
+
198
+ def pinnedLeavesHasAViolation(state: EliminationState, indexLeaf: int) -> bool:
199
+ """Return `True` if `state.pinnedLeaves` or the addition of `indexLeaf` at `state.pile` has a violation."""
200
+ pinnedLeaves: dict[int, int] = state.pinnedLeaves.copy()
201
+ pinnedLeaves[state.pile] = indexLeaf
202
+ indexLeaf2pile: dict[int, int] = {indexLeaf: pile for pile, indexLeaf in pinnedLeaves.items()}
203
+ pinnedLeavesFiltered: filterfalse[tuple[int, int]] = filterfalse(lambda pileIndexLeaf: pileIndexLeaf[1] == state.leavesTotal, pinnedLeaves.items()) # indexLeafNPlus1 does not exist.
204
+ indexLeafAndComparandAcrossDimensions = filter(matchingParityIndexLeaf(state.mapShape), CartesianProduct(combinations(pinnedLeavesFiltered, 2), range(state.dimensionsTotal)))
205
+ return any(thisIsAViolation(pile, pileComparand, getNextCreaseIndexLeaf(state.mapShape, indexLeaf, aDimension), getNextCreaseIndexLeaf(state.mapShape, comparand, aDimension), get(seq=indexLeaf2pile, default=None))
206
+ for ((pile, indexLeaf), (pileComparand, comparand)), aDimension in indexLeafAndComparandAcrossDimensions)
@@ -1,6 +1,44 @@
1
- from mapFolding.algorithms.matrixMeandersBeDry import walkDyckPath
1
+ from functools import cache
2
2
  from mapFolding.dataBaskets import MatrixMeandersState
3
3
 
4
+ @cache
5
+ def walkDyckPath(intWithExtra_0b1: int) -> int:
6
+ """Find the bit position for flipping paired curve endpoints in meander transfer matrices.
7
+
8
+ Parameters
9
+ ----------
10
+ intWithExtra_0b1 : int
11
+ Binary representation of curve locations with an extra bit encoding parity information.
12
+
13
+ Returns
14
+ -------
15
+ flipExtra_0b1_Here : int
16
+ Bit mask indicating the position where the balance condition fails, formatted as 2^(2k).
17
+
18
+ 3L33T H@X0R
19
+ ------------
20
+ Binary search for first negative balance in shifted bit pairs. Returns 2^(2k) mask for
21
+ bit position k where cumulative balance counter transitions from non-negative to negative.
22
+
23
+ Mathematics
24
+ -----------
25
+ Implements the Dyck path balance verification algorithm from Jensen's transfer matrix
26
+ enumeration. Computes the position where ∑(i=0 to k) (-1)^b_i < 0 for the first time,
27
+ where b_i are the bits of the input at positions 2i.
28
+
29
+ """
30
+ findTheExtra_0b1: int = 0
31
+ flipExtra_0b1_Here: int = 1
32
+ while True:
33
+ flipExtra_0b1_Here <<= 2
34
+ if intWithExtra_0b1 & flipExtra_0b1_Here == 0:
35
+ findTheExtra_0b1 += 1
36
+ else:
37
+ findTheExtra_0b1 -= 1
38
+ if findTheExtra_0b1 < 0:
39
+ break
40
+ return flipExtra_0b1_Here
41
+
4
42
  def count(state: MatrixMeandersState) -> MatrixMeandersState:
5
43
  """Count meanders with matrix transfer algorithm using Python `int` (*int*eger) contained in a Python `dict` (*dict*ionary).
6
44
 
@@ -11,52 +49,53 @@ def count(state: MatrixMeandersState) -> MatrixMeandersState:
11
49
 
12
50
  Notes
13
51
  -----
14
- The matrix transfer algorithm is sophisticated, but this implementation is straightforward: compute each index one at a time,
15
- compute each `arcCode` one at a time, and compute each type of analysis one at a time.
52
+ The matrix transfer algorithm is sophisticated, but this implementation is straightforward: compute each `boundary` one at a
53
+ time, compute each `arcCode` one at a time, and compute each type of analysis one at a time.
16
54
  """
17
55
  dictionaryArcCodeToCrossings: dict[int, int] = {}
18
56
 
19
57
  while state.boundary > 0:
20
- state.boundary -= 1
21
- state.bitWidth = max(state.dictionaryMeanders.keys()).bit_length()
58
+ state.reduceBoundary()
22
59
 
23
60
  dictionaryArcCodeToCrossings = state.dictionaryMeanders.copy()
24
61
  state.dictionaryMeanders = {}
25
62
 
26
- for arcCode, crossings in dictionaryArcCodeToCrossings.items():
27
- bitsAlpha: int = arcCode & state.locatorBits
28
- bitsZulu: int = (arcCode >> 1) & state.locatorBits
63
+ def analyzeArcCode(arcCode: int, crossings: int) -> None:
64
+ bitsAlpha: int = arcCode & state.bitsLocator
29
65
  bitsAlphaHasArcs: bool = bitsAlpha > 1
30
- bitsZuluHasArcs: bool = bitsZulu > 1
31
66
  bitsAlphaIsEven: int = bitsAlpha & 1 ^ 1
67
+
68
+ bitsZulu: int = arcCode >> 1 & state.bitsLocator
69
+ bitsZuluHasArcs: bool = bitsZulu > 1
32
70
  bitsZuluIsEven: int = bitsZulu & 1 ^ 1
33
71
 
34
- arcCodeAnalysis = ((bitsAlpha | (bitsZulu << 1)) << 2) | 3
35
- # simple
72
+ arcCodeAnalysis: int = (bitsZulu << 1 | bitsAlpha) << 2 | 3 # Evaluate formula step-wise left to right: (parentheses) override precedence.
36
73
  if arcCodeAnalysis < state.MAXIMUMarcCode:
37
74
  state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
38
75
 
39
76
  if bitsAlphaHasArcs:
40
- arcCodeAnalysis = (bitsAlpha >> 2) | (bitsZulu << 3) | (bitsAlphaIsEven << 1)
77
+ arcCodeAnalysis = bitsAlphaIsEven << 1 | bitsAlpha >> 2 | bitsZulu << 3
41
78
  if arcCodeAnalysis < state.MAXIMUMarcCode:
42
79
  state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
43
80
 
44
81
  if bitsZuluHasArcs:
45
- arcCodeAnalysis = (bitsZulu >> 1) | (bitsAlpha << 2) | bitsZuluIsEven
82
+ arcCodeAnalysis = bitsZuluIsEven | bitsAlpha << 2 | bitsZulu >> 1
46
83
  if arcCodeAnalysis < state.MAXIMUMarcCode:
47
84
  state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
48
85
 
49
86
  if bitsAlphaHasArcs and bitsZuluHasArcs and (bitsAlphaIsEven or bitsZuluIsEven):
50
- # aligned
87
+ # NOTE This analysis might modify `bitsAlpha` or `bitsZulu`, so it should be last.
51
88
  if bitsAlphaIsEven and not bitsZuluIsEven:
52
89
  bitsAlpha ^= walkDyckPath(bitsAlpha)
53
90
  elif bitsZuluIsEven and not bitsAlphaIsEven:
54
91
  bitsZulu ^= walkDyckPath(bitsZulu)
55
92
 
56
- arcCodeAnalysis: int = ((bitsZulu >> 2) << 1) | (bitsAlpha >> 2)
93
+ arcCodeAnalysis = (bitsZulu >> 2 << 3 | bitsAlpha) >> 2 # Evaluate formula step-wise left to right: (parentheses) override precedence.
57
94
  if arcCodeAnalysis < state.MAXIMUMarcCode:
58
95
  state.dictionaryMeanders[arcCodeAnalysis] = state.dictionaryMeanders.get(arcCodeAnalysis, 0) + crossings
59
96
 
97
+ set(map(analyzeArcCode, dictionaryArcCodeToCrossings.keys(), dictionaryArcCodeToCrossings.values()))
98
+
60
99
  dictionaryArcCodeToCrossings = {}
61
100
 
62
101
  return state
@@ -74,14 +113,16 @@ def doTheNeedful(state: MatrixMeandersState) -> int:
74
113
  crossings : int
75
114
  The computed value of `crossings`.
76
115
 
77
- Notes
78
- -----
79
- Citation: https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bibtex
116
+ Citations
117
+ ---------
118
+ - https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bib
119
+ - https://github.com/hunterhogan/mapFolding/blob/main/citations/Howroyd.bib
80
120
 
81
121
  See Also
82
122
  --------
83
123
  https://oeis.org/A000682
84
124
  https://oeis.org/A005316
125
+ https://github.com/archmageirvine/joeis/blob/5dc2148344bff42182e2128a6c99df78044558c5/src/irvine/oeis/a005/A005316.java
85
126
  """
86
127
  state = count(state)
87
128