mapFolding 0.15.3__py3-none-any.whl → 0.16.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 (92) hide show
  1. mapFolding/__init__.py +22 -20
  2. mapFolding/_theSSOT.py +56 -62
  3. mapFolding/_theTypes.py +66 -4
  4. mapFolding/algorithms/__init__.py +1 -0
  5. mapFolding/{daoOfMapFolding.py → algorithms/daoOfMapFolding.py} +1 -2
  6. mapFolding/algorithms/getBucketsTotal.py +137 -0
  7. mapFolding/algorithms/matrixMeanders.py +519 -0
  8. mapFolding/algorithms/oeisIDbyFormula.py +347 -0
  9. mapFolding/algorithms/zCuzDocStoopidoeisIDbyFormula.py +84 -0
  10. mapFolding/basecamp.py +151 -14
  11. mapFolding/dataBaskets.py +74 -0
  12. mapFolding/oeis.py +43 -56
  13. mapFolding/reference/A000682facts.py +662 -0
  14. mapFolding/reference/A005316facts.py +62 -0
  15. mapFolding/reference/matrixMeandersAnalysis/__init__.py +1 -0
  16. mapFolding/reference/matrixMeandersAnalysis/evenEven.py +144 -0
  17. mapFolding/reference/matrixMeandersAnalysis/oddEven.py +54 -0
  18. mapFolding/{_oeisFormulas/matrixMeanders64.py → reference/meandersDumpingGround/matrixMeanders64retired.py} +37 -29
  19. mapFolding/someAssemblyRequired/A007822/A007822rawMaterials.py +55 -0
  20. mapFolding/someAssemblyRequired/A007822/__init__.py +0 -0
  21. mapFolding/someAssemblyRequired/A007822/makeA007822AsynchronousModules.py +185 -0
  22. mapFolding/someAssemblyRequired/A007822/makeA007822Modules.py +71 -0
  23. mapFolding/someAssemblyRequired/RecipeJob.py +2 -2
  24. mapFolding/someAssemblyRequired/__init__.py +9 -2
  25. mapFolding/someAssemblyRequired/_toolIfThis.py +4 -3
  26. mapFolding/someAssemblyRequired/_toolkitContainers.py +8 -8
  27. mapFolding/someAssemblyRequired/infoBooth.py +27 -30
  28. mapFolding/someAssemblyRequired/makeJobTheorem2Numba.py +1 -1
  29. mapFolding/someAssemblyRequired/makeJobTheorem2codon.py +5 -2
  30. mapFolding/someAssemblyRequired/makingModules_count.py +301 -0
  31. mapFolding/someAssemblyRequired/makingModules_doTheNeedful.py +120 -0
  32. mapFolding/someAssemblyRequired/mapFolding/__init__.py +0 -0
  33. mapFolding/someAssemblyRequired/mapFolding/makeMapFoldingModules.py +220 -0
  34. mapFolding/someAssemblyRequired/toolkitMakeModules.py +152 -0
  35. mapFolding/someAssemblyRequired/toolkitNumba.py +1 -1
  36. mapFolding/someAssemblyRequired/transformationTools.py +1 -0
  37. mapFolding/syntheticModules/A007822/__init__.py +1 -0
  38. mapFolding/syntheticModules/A007822/asynchronous.py +148 -0
  39. mapFolding/syntheticModules/A007822/asynchronousAnnex.py +68 -0
  40. mapFolding/syntheticModules/A007822/asynchronousTheorem2.py +53 -0
  41. mapFolding/syntheticModules/A007822/asynchronousTrimmed.py +47 -0
  42. mapFolding/syntheticModules/dataPackingA007822.py +1 -1
  43. mapFolding/tests/conftest.py +28 -9
  44. mapFolding/tests/test_computations.py +32 -10
  45. mapFolding/tests/test_oeis.py +2 -20
  46. mapFolding/trim_memory.py +62 -0
  47. mapFolding/zCuzDocStoopid/__init__.py +1 -0
  48. mapFolding/zCuzDocStoopid/makeDocstrings.py +63 -0
  49. {mapfolding-0.15.3.dist-info → mapfolding-0.16.0.dist-info}/METADATA +9 -2
  50. mapfolding-0.16.0.dist-info/RECORD +100 -0
  51. {mapfolding-0.15.3.dist-info → mapfolding-0.16.0.dist-info}/entry_points.txt +0 -1
  52. mapFolding/_oeisFormulas/A000136.py +0 -4
  53. mapFolding/_oeisFormulas/A000560.py +0 -4
  54. mapFolding/_oeisFormulas/A000682.py +0 -85
  55. mapFolding/_oeisFormulas/A001010.py +0 -19
  56. mapFolding/_oeisFormulas/A001011.py +0 -5
  57. mapFolding/_oeisFormulas/A005315.py +0 -4
  58. mapFolding/_oeisFormulas/A005316.py +0 -10
  59. mapFolding/_oeisFormulas/A223094.py +0 -7
  60. mapFolding/_oeisFormulas/A259702.py +0 -4
  61. mapFolding/_oeisFormulas/A301620.py +0 -6
  62. mapFolding/_oeisFormulas/Z0Z_aOFn.py +0 -34
  63. mapFolding/_oeisFormulas/Z0Z_notes.py +0 -16
  64. mapFolding/_oeisFormulas/Z0Z_oeisMeanders.py +0 -74
  65. mapFolding/_oeisFormulas/Z0Z_symmetry.py +0 -131
  66. mapFolding/_oeisFormulas/__init__.py +0 -1
  67. mapFolding/_oeisFormulas/matrixMeanders.py +0 -134
  68. mapFolding/_oeisFormulas/matrixMeandersAnnex.py +0 -84
  69. mapFolding/someAssemblyRequired/A007822rawMaterials.py +0 -46
  70. mapFolding/someAssemblyRequired/makeAllModules.py +0 -764
  71. mapfolding-0.15.3.dist-info/RECORD +0 -92
  72. /mapFolding/reference/{A005316JavaPort.py → meandersDumpingGround/A005316JavaPort.py} +0 -0
  73. /mapFolding/reference/{A005316imperative.py → meandersDumpingGround/A005316imperative.py} +0 -0
  74. /mapFolding/reference/{A005316intOptimized.py → meandersDumpingGround/A005316intOptimized.py} +0 -0
  75. /mapFolding/reference/{A005316optimized128bit.py → meandersDumpingGround/A005316optimized128bit.py} +0 -0
  76. /mapFolding/reference/{A005316primitiveOptimized.py → meandersDumpingGround/A005316primitiveOptimized.py} +0 -0
  77. /mapFolding/reference/{A005316redis.py → meandersDumpingGround/A005316redis.py} +0 -0
  78. /mapFolding/reference/{A005316write2disk.py → meandersDumpingGround/A005316write2disk.py} +0 -0
  79. /mapFolding/reference/{matrixMeandersBaseline.py → meandersDumpingGround/matrixMeandersBaseline.py} +0 -0
  80. /mapFolding/reference/{matrixMeandersBaselineAnnex.py → meandersDumpingGround/matrixMeandersBaselineAnnex.py} +0 -0
  81. /mapFolding/reference/{matrixMeandersBaselineV2.py → meandersDumpingGround/matrixMeandersBaselineV2.py} +0 -0
  82. /mapFolding/reference/{matrixMeandersSimpleQueue.py → meandersDumpingGround/matrixMeandersSimpleQueue.py} +0 -0
  83. /mapFolding/reference/{matrixMeandersSlicePop.py → meandersDumpingGround/matrixMeandersSlicePop.py} +0 -0
  84. /mapFolding/syntheticModules/{algorithmA007822.py → A007822/algorithm.py} +0 -0
  85. /mapFolding/syntheticModules/{algorithmA007822Numba.py → A007822/algorithmNumba.py} +0 -0
  86. /mapFolding/syntheticModules/{initializeStateA007822.py → A007822/initializeState.py} +0 -0
  87. /mapFolding/syntheticModules/{theorem2A007822.py → A007822/theorem2.py} +0 -0
  88. /mapFolding/syntheticModules/{theorem2A007822Numba.py → A007822/theorem2Numba.py} +0 -0
  89. /mapFolding/syntheticModules/{theorem2A007822Trimmed.py → A007822/theorem2Trimmed.py} +0 -0
  90. {mapfolding-0.15.3.dist-info → mapfolding-0.16.0.dist-info}/WHEEL +0 -0
  91. {mapfolding-0.15.3.dist-info → mapfolding-0.16.0.dist-info}/licenses/LICENSE +0 -0
  92. {mapfolding-0.15.3.dist-info → mapfolding-0.16.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,519 @@
1
+ """Count meanders with matrix transfer algorithm.
2
+
3
+ Notes
4
+ -----
5
+ - Odd/even of `groupAlpha` == the odd/even of `curveLocations`. Proof: `groupAlphaIsEven = curveLocations & 1 & 1 ^ 1`.
6
+ - Odd/even of `groupZulu` == `curveLocations` second-least significant bit. So `groupZuluIsEven = bool(curveLocations & 2 ^ 2)`.
7
+ """
8
+ from functools import cache
9
+ from gc import collect as goByeBye
10
+ from hunterMakesPy import raiseIfNone
11
+ from mapFolding import MatrixMeandersState
12
+ from mapFolding.algorithms.getBucketsTotal import getBucketsTotal
13
+ from pathlib import Path
14
+ from warnings import warn
15
+ import numpy
16
+ import pandas
17
+
18
+ pathRoot: Path = Path.cwd() / 'curves'
19
+ pathRoot.mkdir(exist_ok=True, parents=True)
20
+
21
+ @cache
22
+ def _flipTheExtra_0b1(intWithExtra_0b1: numpy.uint64) -> numpy.uint64:
23
+ return numpy.uint64(intWithExtra_0b1 ^ walkDyckPath(int(intWithExtra_0b1)))
24
+
25
+ flipTheExtra_0b1AsUfunc = numpy.frompyfunc(_flipTheExtra_0b1, 1, 1)
26
+
27
+ def outfitDictionaryCurveGroups(state: MatrixMeandersState) -> dict[tuple[int, int], int]:
28
+ """Outfit `dictionaryCurveGroups` so it may manage the computations for one iteration of the transfer matrix.
29
+
30
+ Parameters
31
+ ----------
32
+ state : MatrixMeandersState
33
+ The current state of the computation, including `dictionaryCurveLocations`.
34
+
35
+ Returns
36
+ -------
37
+ dictionaryCurveGroups : dict[tuple[int, int], int]
38
+ A dictionary of `(groupAlpha, groupZulu)` to `distinctCrossings`.
39
+ """
40
+ state.bitWidth = max(state.dictionaryCurveLocations.keys()).bit_length()
41
+ return {(curveLocations & state.locatorGroupAlpha, (curveLocations & state.locatorGroupZulu) >> 1): distinctCrossings
42
+ for curveLocations, distinctCrossings in state.dictionaryCurveLocations.items()}
43
+
44
+ @cache
45
+ def walkDyckPath(intWithExtra_0b1: int) -> int:
46
+ """Find the bit position for flipping paired curve endpoints in meander transfer matrices.
47
+
48
+ Parameters
49
+ ----------
50
+ intWithExtra_0b1 : int
51
+ Binary representation of curve locations with an extra bit encoding parity information.
52
+
53
+ Returns
54
+ -------
55
+ flipExtra_0b1_Here : int
56
+ Bit mask indicating the position where the balance condition fails, formatted as 2^(2k).
57
+
58
+ 3L33T H@X0R
59
+ ------------
60
+ Binary search for first negative balance in shifted bit pairs. Returns 2^(2k) mask for
61
+ bit position k where cumulative balance counter transitions from non-negative to negative.
62
+
63
+ Mathematics
64
+ -----------
65
+ Implements the Dyck path balance verification algorithm from Jensen's transfer matrix
66
+ enumeration. Computes the position where ∑(i=0 to k) (-1)^b_i < 0 for the first time,
67
+ where b_i are the bits of the input at positions 2i.
68
+
69
+ """
70
+ findTheExtra_0b1: int = 0
71
+ flipExtra_0b1_Here: int = 1
72
+ while True:
73
+ flipExtra_0b1_Here <<= 2
74
+ if (intWithExtra_0b1 & flipExtra_0b1_Here) == 0:
75
+ findTheExtra_0b1 += 1
76
+ else:
77
+ findTheExtra_0b1 -= 1
78
+ if findTheExtra_0b1 < 0:
79
+ break
80
+ return flipExtra_0b1_Here
81
+
82
+ def areIntegersWide(state: MatrixMeandersState, dataframe: pandas.DataFrame | None = None, *, fixedSizeMAXIMUMcurveLocations: bool = False) -> bool:
83
+ """Check if the largest values are wider than the maximum limits.
84
+
85
+ Parameters
86
+ ----------
87
+ state : MatrixMeandersState
88
+ The current state of the computation, including `dictionaryCurveLocations`.
89
+ dataframe : pandas.DataFrame | None = None
90
+ Optional DataFrame containing 'analyzed' and 'distinctCrossings' columns. If provided, use this instead of `state.dictionaryCurveLocations`.
91
+ fixedSizeMAXIMUMcurveLocations : bool = False
92
+ Set this to `True` if you cast `state.MAXIMUMcurveLocations` to the same fixed size integer type as `state.datatypeCurveLocations`.
93
+
94
+ Returns
95
+ -------
96
+ wider : bool
97
+ True if at least one integer is too wide.
98
+
99
+ Notes
100
+ -----
101
+ Casting `state.MAXIMUMcurveLocations` to a fixed-size 64-bit unsigned integer might cause the flow to be a little more
102
+ complicated because `MAXIMUMcurveLocations` is usually 1-bit larger than the `max(curveLocations)` value.
103
+
104
+ If you start the algorithm with very large `curveLocations` in your `dictionaryCurveLocations` (*i.e.,* A000682), then the
105
+ flow will go to a function that does not use fixed size integers. When the integers are below the limits (*e.g.,*
106
+ `bitWidthCurveLocationsMaximum`), the flow will go to a function with fixed size integers. In that case, casting
107
+ `MAXIMUMcurveLocations` to a fixed size merely delays the transition from one function to the other by one iteration.
108
+
109
+ If you start with small values in `dictionaryCurveLocations`, however, then the flow goes to the function with fixed size
110
+ integers and usually stays there until `distinctCrossings` is huge, which is near the end of the computation. If you cast
111
+ `MAXIMUMcurveLocations` into a 64-bit unsigned integer, however, then around `state.kOfMatrix == 28`, the bit width of
112
+ `MAXIMUMcurveLocations` might exceed the limit. That will cause the flow to go to the function that does not have fixed size
113
+ integers for a few iterations before returning to the function with fixed size integers.
114
+ """
115
+ if dataframe is None:
116
+ curveLocationsWidest: int = max(state.dictionaryCurveLocations.keys()).bit_length()
117
+ distinctCrossingsWidest: int = max(state.dictionaryCurveLocations.values()).bit_length()
118
+ else:
119
+ curveLocationsWidest = int(dataframe['analyzed'].max()).bit_length()
120
+ distinctCrossingsWidest = int(dataframe['distinctCrossings'].max()).bit_length()
121
+
122
+ MAXIMUMcurveLocations: int = 0
123
+ if fixedSizeMAXIMUMcurveLocations:
124
+ MAXIMUMcurveLocations = state.MAXIMUMcurveLocations
125
+
126
+ return (curveLocationsWidest > raiseIfNone(state.bitWidthCurveLocationsMaximum)
127
+ or distinctCrossingsWidest > raiseIfNone(state.bitWidthDistinctCrossingsMaximum)
128
+ or MAXIMUMcurveLocations > raiseIfNone(state.bitWidthCurveLocationsMaximum)
129
+ )
130
+
131
+ def countBigInt(state: MatrixMeandersState) -> MatrixMeandersState:
132
+ """Count meanders with matrix transfer algorithm using Python primitive `int` contained in a Python primitive `dict`.
133
+
134
+ Parameters
135
+ ----------
136
+ state : MatrixMeandersState
137
+ The algorithm state containing current `kOfMatrix`, `dictionaryCurveLocations`, and thresholds.
138
+
139
+ Notes
140
+ -----
141
+ The algorithm is sophisticated, but this implementation is straightforward. Compute each index one at a time, compute each
142
+ `curveLocations` one at a time, and compute each type of analysis one at a time.
143
+ """
144
+ dictionaryCurveGroups: dict[tuple[int, int], int] = {}
145
+
146
+ while (state.kOfMatrix > 0 and areIntegersWide(state)):
147
+ state.kOfMatrix -= 1
148
+
149
+ dictionaryCurveGroups = outfitDictionaryCurveGroups(state)
150
+ state.dictionaryCurveLocations.clear()
151
+ goByeBye()
152
+
153
+ for (groupAlpha, groupZulu), distinctCrossings in dictionaryCurveGroups.items():
154
+ groupAlphaCurves: bool = groupAlpha > 1
155
+ groupZuluHasCurves: bool = groupZulu > 1
156
+ groupAlphaIsEven = groupZuluIsEven = 0
157
+
158
+ curveLocationAnalysis = ((groupAlpha | (groupZulu << 1)) << 2) | 3
159
+ # simple
160
+ if curveLocationAnalysis < state.MAXIMUMcurveLocations:
161
+ state.dictionaryCurveLocations[curveLocationAnalysis] = state.dictionaryCurveLocations.get(curveLocationAnalysis, 0) + distinctCrossings
162
+
163
+ if groupAlphaCurves:
164
+ curveLocationAnalysis = (groupAlpha >> 2) | (groupZulu << 3) | ((groupAlphaIsEven := 1 - (groupAlpha & 1)) << 1)
165
+ if curveLocationAnalysis < state.MAXIMUMcurveLocations:
166
+ state.dictionaryCurveLocations[curveLocationAnalysis] = state.dictionaryCurveLocations.get(curveLocationAnalysis, 0) + distinctCrossings
167
+
168
+ if groupZuluHasCurves:
169
+ curveLocationAnalysis = (groupZulu >> 1) | (groupAlpha << 2) | (groupZuluIsEven := 1 - (groupZulu & 1))
170
+ if curveLocationAnalysis < state.MAXIMUMcurveLocations:
171
+ state.dictionaryCurveLocations[curveLocationAnalysis] = state.dictionaryCurveLocations.get(curveLocationAnalysis, 0) + distinctCrossings
172
+
173
+ if groupAlphaCurves and groupZuluHasCurves and (groupAlphaIsEven or groupZuluIsEven):
174
+ # aligned
175
+ if groupAlphaIsEven and not groupZuluIsEven:
176
+ groupAlpha ^= walkDyckPath(groupAlpha) # noqa: PLW2901
177
+ elif groupZuluIsEven and not groupAlphaIsEven:
178
+ groupZulu ^= walkDyckPath(groupZulu) # noqa: PLW2901
179
+
180
+ curveLocationAnalysis: int = ((groupZulu >> 2) << 1) | (groupAlpha >> 2)
181
+ if curveLocationAnalysis < state.MAXIMUMcurveLocations:
182
+ state.dictionaryCurveLocations[curveLocationAnalysis] = state.dictionaryCurveLocations.get(curveLocationAnalysis, 0) + distinctCrossings
183
+
184
+ return state
185
+
186
+ # ruff: noqa: B023
187
+
188
+ def countPandas(state: MatrixMeandersState) -> MatrixMeandersState:
189
+ """Count meanders with matrix transfer algorithm using pandas DataFrame.
190
+
191
+ Parameters
192
+ ----------
193
+ state : MatrixMeandersState
194
+ The algorithm state containing current `kOfMatrix`, `dictionaryCurveLocations`, and thresholds.
195
+
196
+ Returns
197
+ -------
198
+ state : MatrixMeandersState
199
+ Updated state with new `kOfMatrix` and `dictionaryCurveLocations`.
200
+ """
201
+ dataframeAnalyzed = pandas.DataFrame({
202
+ 'analyzed': pandas.Series(name='analyzed', data=state.dictionaryCurveLocations.keys(), copy=False, dtype=state.datatypeCurveLocations)
203
+ , 'distinctCrossings': pandas.Series(name='distinctCrossings', data=state.dictionaryCurveLocations.values(), copy=False, dtype=state.datatypeDistinctCrossings)
204
+ }, dtype=state.datatypeCurveLocations
205
+ )
206
+ state.dictionaryCurveLocations.clear()
207
+
208
+ while (state.kOfMatrix > 0 and not areIntegersWide(state, dataframeAnalyzed)):
209
+
210
+ def aggregateCurveLocations() -> None:
211
+ nonlocal dataframeAnalyzed
212
+ dataframeAnalyzed = dataframeAnalyzed.iloc[0:state.indexStartAnalyzed].groupby('analyzed', sort=False)['distinctCrossings'].aggregate('sum').reset_index()
213
+
214
+ def analyzeCurveLocationsAligned() -> None:
215
+ """Compute `curveLocations` from `groupAlpha` and `groupZulu` if at least one is an even number.
216
+
217
+ Before computing `curveLocations`, some values of `groupAlpha` and `groupZulu` are modified.
218
+
219
+ Warning
220
+ -------
221
+ This function deletes rows from `dataframeCurveLocations`. Always run this analysis last.
222
+
223
+ Formula
224
+ -------
225
+ ```python
226
+ if groupAlpha > 1 and groupZulu > 1 and (groupAlphaIsEven or groupZuluIsEven):
227
+ curveLocations = (groupAlpha >> 2) | ((groupZulu >> 2) << 1)
228
+ ```
229
+ """
230
+ nonlocal dataframeCurveLocations
231
+
232
+ # NOTE Step 1 drop unqualified rows
233
+
234
+ ImaGroupZulpha: pandas.Series = dataframeCurveLocations['curveLocations'].copy() # Ima `groupAlpha`.
235
+ ImaGroupZulpha &= state.locatorGroupAlpha # Ima `groupAlpha`.
236
+
237
+ dataframeCurveLocations = dataframeCurveLocations.loc[(ImaGroupZulpha > 1)] # if groupAlphaHasCurves
238
+
239
+ del ImaGroupZulpha
240
+
241
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
242
+ ImaGroupZulpha &= state.locatorGroupZulu # Ima `groupZulu`.
243
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
244
+
245
+ dataframeCurveLocations = dataframeCurveLocations.loc[(ImaGroupZulpha > 1)] # if groupZuluHasCurves
246
+
247
+ del ImaGroupZulpha
248
+
249
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
250
+ ImaGroupZulpha &= 0b10 # Ima `groupZulu`.
251
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
252
+ ImaGroupZulpha &= 1 # (groupZulu & 1)
253
+ ImaGroupZulpha ^= 1 # (1 - (groupZulu ...))
254
+ dataframeCurveLocations.loc[:, 'analyzed'] = ImaGroupZulpha # selectorGroupZuluAtEven
255
+
256
+ del ImaGroupZulpha
257
+
258
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupAlpha`.
259
+ ImaGroupZulpha &= 1 # (groupAlpha & 1)
260
+ ImaGroupZulpha ^= 1 # (1 - (groupAlpha ...))
261
+ ImaGroupZulpha = ImaGroupZulpha.astype(bool) # selectorGroupAlphaAtODD
262
+
263
+ dataframeCurveLocations = dataframeCurveLocations.loc[(ImaGroupZulpha) | (dataframeCurveLocations.loc[:, 'analyzed'])] # if (groupAlphaIsEven or groupZuluIsEven)
264
+
265
+ del ImaGroupZulpha
266
+
267
+ # NOTE Step 2 modify rows
268
+
269
+ # Make a selector for groupZuluAtEven, so you can modify groupAlpha
270
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
271
+ ImaGroupZulpha &= 0b10 # Ima `groupZulu`.
272
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
273
+ ImaGroupZulpha &= 1 # (groupZulu & 1)
274
+ ImaGroupZulpha ^= 1 # (1 - (groupZulu ...))
275
+ ImaGroupZulpha = ImaGroupZulpha.astype(bool) # selectorGroupZuluAtEven
276
+
277
+ dataframeCurveLocations.loc[:, 'analyzed'] = dataframeCurveLocations['curveLocations'] # Ima `groupAlpha`.
278
+ dataframeCurveLocations.loc[:, 'analyzed'] &= state.locatorGroupAlpha # (groupAlpha)
279
+
280
+ # if groupAlphaIsEven and not groupZuluIsEven, modifyGroupAlphaPairedToOdd
281
+ dataframeCurveLocations.loc[(~ImaGroupZulpha), 'analyzed'] = state.datatypeCurveLocations( # pyright: ignore[reportCallIssue, reportArgumentType]
282
+ flipTheExtra_0b1AsUfunc(dataframeCurveLocations.loc[(~ImaGroupZulpha), 'analyzed']))
283
+
284
+ del ImaGroupZulpha
285
+
286
+ # if groupZuluIsEven and not groupAlphaIsEven, modifyGroupZuluPairedToOdd
287
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
288
+ ImaGroupZulpha &= state.locatorGroupZulu # Ima `groupZulu`.
289
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
290
+
291
+ ImaGroupZulpha.loc[(dataframeCurveLocations.loc[:, 'curveLocations'] & 1).astype(bool)] = state.datatypeCurveLocations( # pyright: ignore[reportArgumentType, reportCallIssue]
292
+ flipTheExtra_0b1AsUfunc(ImaGroupZulpha.loc[(dataframeCurveLocations.loc[:, 'curveLocations'] & 1).astype(bool)])) # pyright: ignore[reportCallIssue, reportUnknownArgumentType, reportArgumentType]
293
+
294
+ # NOTE Step 3 compute curveLocations
295
+
296
+ dataframeCurveLocations.loc[:, 'analyzed'] //= 2**2 # (groupAlpha >> 2)
297
+
298
+ ImaGroupZulpha //= 2**2 # (groupZulu >> 2)
299
+ ImaGroupZulpha *= 2**1 # ((groupZulu ...) << 1)
300
+
301
+ dataframeCurveLocations.loc[:, 'analyzed'] |= ImaGroupZulpha # ... | (groupZulu ...)
302
+
303
+ del ImaGroupZulpha
304
+
305
+ dataframeCurveLocations.loc[dataframeCurveLocations['analyzed'] >= state.MAXIMUMcurveLocations, 'analyzed'] = 0
306
+
307
+ def analyzeCurveLocationsAlpha() -> None:
308
+ """Compute `curveLocations` from `groupAlpha`.
309
+
310
+ Formula
311
+ -------
312
+ ```python
313
+ if groupAlpha > 1:
314
+ curveLocations = ((1 - (groupAlpha & 1)) << 1) | (groupZulu << 3) | (groupAlpha >> 2)
315
+ # `(1 - (groupAlpha & 1)` is an evenness test.
316
+ ```
317
+ """
318
+ nonlocal dataframeCurveLocations
319
+ dataframeCurveLocations['analyzed'] = dataframeCurveLocations['curveLocations']
320
+ dataframeCurveLocations.loc[:, 'analyzed'] &= 1 # (groupAlpha & 1)
321
+ dataframeCurveLocations.loc[:, 'analyzed'] ^= 1 # (1 - (groupAlpha ...))
322
+
323
+ dataframeCurveLocations.loc[:, 'analyzed'] *= 2**1 # ((groupAlpha ...) << 1)
324
+
325
+ ImaGroupZulpha: pandas.Series = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
326
+ ImaGroupZulpha &= state.locatorGroupZulu # Ima `groupZulu`.
327
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
328
+
329
+ ImaGroupZulpha *= 2**3 # (groupZulu << 3)
330
+ dataframeCurveLocations.loc[:, 'analyzed'] |= ImaGroupZulpha # ... | (groupZulu ...)
331
+
332
+ del ImaGroupZulpha
333
+
334
+ dataframeCurveLocations.loc[:, 'analyzed'] *= 2**2 # ... | (groupAlpha >> 2)
335
+
336
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupAlpha`.
337
+ ImaGroupZulpha &= state.locatorGroupAlpha # Ima `groupAlpha`.
338
+
339
+ dataframeCurveLocations.loc[:, 'analyzed'] |= ImaGroupZulpha # ... | (groupAlpha)
340
+ dataframeCurveLocations.loc[:, 'analyzed'] //= 2**2 # (... >> 2)
341
+
342
+ dataframeCurveLocations.loc[(ImaGroupZulpha <= 1), 'analyzed'] = 0 # if groupAlpha > 1
343
+
344
+ del ImaGroupZulpha
345
+
346
+ dataframeCurveLocations.loc[dataframeCurveLocations['analyzed'] >= state.MAXIMUMcurveLocations, 'analyzed'] = 0
347
+
348
+ def analyzeCurveLocationsSimple() -> None:
349
+ """Compute curveLocations with the 'simple' bridges formula.
350
+
351
+ Formula
352
+ -------
353
+ ```python
354
+ curveLocations = ((groupAlpha | (groupZulu << 1)) << 2) | 3
355
+ ```
356
+
357
+ Notes
358
+ -----
359
+ Using `+= 3` instead of `|= 3` is valid in this specific case. Left shift by two means the last bits are '0b00'. '0 + 3'
360
+ is '0b11', and '0b00 | 0b11' is also '0b11'.
361
+
362
+ """
363
+ nonlocal dataframeCurveLocations
364
+ dataframeCurveLocations['analyzed'] = dataframeCurveLocations['curveLocations']
365
+ dataframeCurveLocations.loc[:, 'analyzed'] &= state.locatorGroupAlpha
366
+
367
+ groupZulu: pandas.Series = dataframeCurveLocations['curveLocations'].copy()
368
+ groupZulu &= state.locatorGroupZulu
369
+ groupZulu //= 2**1 # (groupZulu >> 1)
370
+ groupZulu *= 2**1 # (groupZulu << 1)
371
+
372
+ dataframeCurveLocations.loc[:, 'analyzed'] |= groupZulu # ((groupAlpha | (groupZulu ...))
373
+
374
+ del groupZulu
375
+
376
+ dataframeCurveLocations.loc[:, 'analyzed'] *= 2**2 # (... << 2)
377
+ dataframeCurveLocations.loc[:, 'analyzed'] += 3 # (...) | 3
378
+ dataframeCurveLocations.loc[dataframeCurveLocations['analyzed'] >= state.MAXIMUMcurveLocations, 'analyzed'] = 0
379
+
380
+ def analyzeCurveLocationsZulu() -> None:
381
+ """Compute `curveLocations` from `groupZulu`.
382
+
383
+ Formula
384
+ -------
385
+ ```python
386
+ if groupZulu > 1:
387
+ curveLocations = (1 - (groupZulu & 1)) | (groupAlpha << 2) | (groupZulu >> 1)
388
+ ```
389
+ """
390
+ nonlocal dataframeCurveLocations
391
+ dataframeCurveLocations.loc[:, 'analyzed'] = dataframeCurveLocations['curveLocations'] # Ima `groupZulu`.
392
+ dataframeCurveLocations.loc[:, 'analyzed'] &= 0b10 # Ima `groupZulu`.
393
+ dataframeCurveLocations.loc[:, 'analyzed'] //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
394
+ dataframeCurveLocations.loc[:, 'analyzed'] &= 1 # (groupZulu & 1)
395
+ dataframeCurveLocations.loc[:, 'analyzed'] ^= 1 # (1 - (groupZulu ...))
396
+
397
+ ImaGroupZulpha: pandas.Series = dataframeCurveLocations['curveLocations'].copy() # Ima `groupAlpha`.
398
+ ImaGroupZulpha &= state.locatorGroupAlpha # Ima `groupAlpha`.
399
+
400
+ ImaGroupZulpha *= 2**2 # (groupAlpha << 2)
401
+ dataframeCurveLocations.loc[:, 'analyzed'] |= ImaGroupZulpha # ... | (groupAlpha ...)
402
+
403
+ del ImaGroupZulpha
404
+
405
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
406
+ ImaGroupZulpha &= state.locatorGroupZulu # Ima `groupZulu`.
407
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
408
+
409
+ ImaGroupZulpha //= 2**1 # (groupZulu >> 1)
410
+
411
+ dataframeCurveLocations.loc[:, 'analyzed'] |= ImaGroupZulpha # ... | (groupZulu ...)
412
+
413
+ del ImaGroupZulpha
414
+
415
+ ImaGroupZulpha = dataframeCurveLocations['curveLocations'].copy() # Ima `groupZulu`.
416
+ ImaGroupZulpha &= state.locatorGroupZulu # Ima `groupZulu`.
417
+ ImaGroupZulpha //= 2**1 # Ima `groupZulu` (groupZulu >> 1)
418
+
419
+ dataframeCurveLocations.loc[ImaGroupZulpha <= 1, 'analyzed'] = 0 # if groupZulu > 1
420
+
421
+ del ImaGroupZulpha
422
+
423
+ dataframeCurveLocations.loc[dataframeCurveLocations['analyzed'] >= state.MAXIMUMcurveLocations, 'analyzed'] = 0
424
+
425
+ def recordCurveLocations() -> None:
426
+ nonlocal dataframeAnalyzed
427
+
428
+ indexStopAnalyzed: int = state.indexStartAnalyzed + int((dataframeCurveLocations['analyzed'] > 0).sum()) # pyright: ignore[reportUnknownArgumentType, reportUnknownMemberType]
429
+
430
+ if indexStopAnalyzed > state.indexStartAnalyzed:
431
+ if len(dataframeAnalyzed.index) < indexStopAnalyzed:
432
+ warn(f"Lengthened `dataframeAnalyzed` from {len(dataframeAnalyzed.index)} to {indexStopAnalyzed=}; n={state.n}, {state.kOfMatrix=}.", stacklevel=2)
433
+ dataframeAnalyzed = dataframeAnalyzed.reindex(index=pandas.RangeIndex(indexStopAnalyzed), fill_value=0)
434
+
435
+ dataframeAnalyzed.loc[state.indexStartAnalyzed:indexStopAnalyzed - 1, ['analyzed', 'distinctCrossings']] = (
436
+ dataframeCurveLocations.loc[(dataframeCurveLocations['analyzed'] > 0), ['analyzed', 'distinctCrossings']
437
+ ].to_numpy(dtype=state.datatypeCurveLocations, copy=False)
438
+ )
439
+
440
+ state.indexStartAnalyzed = indexStopAnalyzed
441
+
442
+ del indexStopAnalyzed
443
+
444
+ dataframeCurveLocations = pandas.DataFrame({
445
+ 'curveLocations': pandas.Series(name='curveLocations', data=dataframeAnalyzed['analyzed'], copy=False, dtype=state.datatypeCurveLocations)
446
+ , 'analyzed': pandas.Series(name='analyzed', data=0, dtype=state.datatypeCurveLocations)
447
+ , 'distinctCrossings': pandas.Series(name='distinctCrossings', data=dataframeAnalyzed['distinctCrossings'], copy=False, dtype=state.datatypeDistinctCrossings)
448
+ } # pyright: ignore[reportUnknownArgumentType]
449
+ )
450
+
451
+ del dataframeAnalyzed
452
+ goByeBye()
453
+
454
+ state.bitWidth = int(dataframeCurveLocations['curveLocations'].max()).bit_length()
455
+ length: int = getBucketsTotal(state)
456
+ dataframeAnalyzed = pandas.DataFrame({
457
+ 'analyzed': pandas.Series(0, pandas.RangeIndex(length), dtype=state.datatypeCurveLocations, name='analyzed')
458
+ , 'distinctCrossings': pandas.Series(0, pandas.RangeIndex(length), dtype=state.datatypeDistinctCrossings, name='distinctCrossings')
459
+ }, index=pandas.RangeIndex(length), columns=['analyzed', 'distinctCrossings'], dtype=state.datatypeCurveLocations # pyright: ignore[reportUnknownArgumentType]
460
+ )
461
+
462
+ state.kOfMatrix -= 1
463
+
464
+ state.indexStartAnalyzed = 0
465
+
466
+ analyzeCurveLocationsSimple()
467
+ recordCurveLocations()
468
+
469
+ analyzeCurveLocationsAlpha()
470
+ recordCurveLocations()
471
+
472
+ analyzeCurveLocationsZulu()
473
+ recordCurveLocations()
474
+
475
+ analyzeCurveLocationsAligned()
476
+ recordCurveLocations()
477
+ del dataframeCurveLocations
478
+ goByeBye()
479
+
480
+ aggregateCurveLocations()
481
+
482
+ if state.n >= 45: # for data collection
483
+ print(state.n, state.kOfMatrix+1, state.indexStartAnalyzed, sep=',') # noqa: T201
484
+
485
+ state.dictionaryCurveLocations = dataframeAnalyzed.set_index('analyzed')['distinctCrossings'].to_dict()
486
+ return state
487
+
488
+ def doTheNeedful(state: MatrixMeandersState) -> int:
489
+ """Compute a(n) meanders with the transfer matrix algorithm.
490
+
491
+ Parameters
492
+ ----------
493
+ state : MatrixMeandersState
494
+ The algorithm state containing current `kOfMatrix`, `dictionaryCurveLocations`, and thresholds.
495
+
496
+ Returns
497
+ -------
498
+ a(n) : int
499
+ The computed value of a(n).
500
+
501
+ Notes
502
+ -----
503
+ Citation: https://github.com/hunterhogan/mapFolding/blob/main/citations/Jensen.bibtex
504
+
505
+ See Also
506
+ --------
507
+ https://oeis.org/A000682
508
+ https://oeis.org/A005316
509
+ """
510
+ while state.kOfMatrix > 0:
511
+ goByeBye()
512
+
513
+ if areIntegersWide(state):
514
+ state = countBigInt(state)
515
+ else:
516
+ state = countPandas(state)
517
+
518
+ return sum(state.dictionaryCurveLocations.values())
519
+