mapFolding 0.2.1__py3-none-any.whl → 0.2.3__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.
mapFolding/oeis.py CHANGED
@@ -65,6 +65,40 @@ settingsOEIShardcodedValues = {
65
65
  oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
66
66
  """Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
67
67
 
68
+ def _validateOEISid(oeisIDcandidate: str):
69
+ """
70
+ Validates an OEIS sequence ID against implemented sequences.
71
+
72
+ If the provided ID is recognized within the application's implemented
73
+ OEIS sequences, the function returns the verified ID in uppercase.
74
+ Otherwise, a KeyError is raised indicating that the sequence is not
75
+ directly supported.
76
+
77
+ Parameters:
78
+ oeisIDcandidate: The OEIS sequence identifier to validate.
79
+
80
+ Returns:
81
+ oeisID: The validated and possibly modified OEIS sequence ID, if recognized.
82
+
83
+ Raises:
84
+ KeyError: If the provided sequence ID is not directly implemented.
85
+ """
86
+ if oeisIDcandidate in oeisIDsImplemented:
87
+ return oeisIDcandidate
88
+ else:
89
+ oeisIDcleaned = str(oeisIDcandidate).upper().strip()
90
+ if oeisIDcleaned in oeisIDsImplemented:
91
+ return oeisIDcleaned
92
+ else:
93
+ raise KeyError(
94
+ f"OEIS ID {oeisIDcandidate} is not directly implemented.\n"
95
+ f"Available sequences:\n{_formatOEISsequenceInfo()}"
96
+ )
97
+
98
+ def _getFilenameOEISbFile(oeisID: str) -> str:
99
+ oeisID = _validateOEISid(oeisID)
100
+ return f"b{oeisID[1:]}.txt"
101
+
68
102
  def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
69
103
  """
70
104
  Parses the content of an OEIS b-file for a given sequence ID.
@@ -101,8 +135,6 @@ try:
101
135
  except NameError:
102
136
  _pathCache = pathlib.Path.home() / ".mapFoldingCache"
103
137
 
104
- _formatFilenameCache = "{oeisID}.txt"
105
-
106
138
  def _getOEISidValues(oeisID: str) -> Dict[int, int]:
107
139
  """
108
140
  Retrieves the specified OEIS sequence as a dictionary mapping integer indices
@@ -122,7 +154,7 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
122
154
  IOError: If there is an error reading from or writing to the local cache.
123
155
  """
124
156
 
125
- pathFilenameCache = _pathCache / _formatFilenameCache.format(oeisID=oeisID)
157
+ pathFilenameCache = _pathCache / _getFilenameOEISbFile(oeisID)
126
158
  cacheDays = 7
127
159
 
128
160
  tryCache = False
@@ -137,7 +169,7 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
137
169
  except (ValueError, IOError):
138
170
  tryCache = False
139
171
 
140
- urlOEISbFile = f"https://oeis.org/{oeisID}/b{oeisID[1:]}.txt"
172
+ urlOEISbFile = f"https://oeis.org/{oeisID}/{_getFilenameOEISbFile(oeisID)}"
141
173
  httpResponse: urllib.response.addinfourl = urllib.request.urlopen(urlOEISbFile)
142
174
  OEISbFile = httpResponse.read().decode('utf-8')
143
175
 
@@ -212,36 +244,6 @@ def _formatOEISsequenceInfo() -> str:
212
244
  for oeisID in oeisIDsImplemented
213
245
  )
214
246
 
215
- def _validateOEISid(oeisIDcandidate: str):
216
- """
217
- Validates an OEIS sequence ID against implemented sequences.
218
-
219
- If the provided ID is recognized within the application's implemented
220
- OEIS sequences, the function returns the verified ID in uppercase.
221
- Otherwise, a KeyError is raised indicating that the sequence is not
222
- directly supported.
223
-
224
- Parameters:
225
- oeisIDcandidate: The OEIS sequence identifier to validate.
226
-
227
- Returns:
228
- oeisID: The validated and possibly modified OEIS sequence ID, if recognized.
229
-
230
- Raises:
231
- KeyError: If the provided sequence ID is not directly implemented.
232
- """
233
- if oeisIDcandidate in oeisIDsImplemented:
234
- return oeisIDcandidate
235
- else:
236
- oeisIDcleaned = str(oeisIDcandidate).upper().strip()
237
- if oeisIDcleaned in oeisIDsImplemented:
238
- return oeisIDcleaned
239
- else:
240
- raise KeyError(
241
- f"OEIS ID {oeisIDcandidate} is not directly implemented.\n"
242
- f"Available sequences:\n{_formatOEISsequenceInfo()}"
243
- )
244
-
245
247
  """
246
248
  Section: public functions"""
247
249
 
@@ -308,7 +310,7 @@ def clearOEIScache() -> None:
308
310
  return
309
311
  else:
310
312
  for oeisID in settingsOEIS:
311
- pathFilenameCache = _pathCache / _formatFilenameCache.format(oeisID=oeisID)
313
+ pathFilenameCache = _pathCache / _getFilenameOEISbFile(oeisID)
312
314
  pathFilenameCache.unlink(missing_ok=True)
313
315
 
314
316
  print(f"Cache cleared from {_pathCache}")
@@ -0,0 +1,376 @@
1
+ """The algorithm flattened into semantic sections.
2
+ This version is not maintained, so you may see differences from the current version."""
3
+ from numpy import integer
4
+ from numpy.typing import NDArray
5
+ from typing import List, Any, Final, Optional, Union, Sequence, Tuple, Type, TypedDict
6
+ import enum
7
+ import numpy
8
+ import sys
9
+
10
+ def countFolds(listDimensions: Sequence[int], computationDivisions = None, CPUlimit: Optional[Union[int, float, bool]] = None):
11
+ def doWhile():
12
+
13
+ while activeLeafGreaterThan0Condition():
14
+
15
+ if activeLeafIsTheFirstLeafCondition() or leafBelowSentinelIs1Condition():
16
+
17
+ if activeLeafGreaterThanLeavesTotalCondition():
18
+ foldsSubTotalsIncrement()
19
+
20
+ else:
21
+
22
+ findGapsInitializeVariables()
23
+ while loopingTheDimensions():
24
+
25
+ if dimensionsUnconstrainedCondition():
26
+ dimensionsUnconstrainedIncrement()
27
+
28
+ else:
29
+
30
+ leafConnecteeInitialization()
31
+ while loopingLeavesConnectedToActiveLeaf():
32
+ if thereAreComputationDivisionsYouMightSkip():
33
+ countGaps()
34
+ leafConnecteeUpdate()
35
+
36
+ dimension1ndexIncrement()
37
+
38
+ if allDimensionsAreUnconstrained():
39
+ insertUnconstrainedLeaf()
40
+
41
+ indexMiniGapInitialization()
42
+ while loopingToActiveGapCeiling():
43
+ filterCommonGaps()
44
+ indexMiniGapIncrement()
45
+
46
+ while backtrackCondition():
47
+ backtrack()
48
+
49
+ if placeLeafCondition():
50
+ placeLeaf()
51
+
52
+ def activeGapIncrement():
53
+ my[indexMy.gap1ndex] += 1
54
+
55
+ def activeLeafGreaterThan0Condition():
56
+ return my[indexMy.leaf1ndex] > 0
57
+
58
+ def activeLeafGreaterThanLeavesTotalCondition():
59
+ return my[indexMy.leaf1ndex] > the[indexThe.leavesTotal]
60
+
61
+ def activeLeafIsTheFirstLeafCondition():
62
+ return my[indexMy.leaf1ndex] <= 1
63
+
64
+ def activeLeafNotEqualToTaskDivisionsCondition():
65
+ return my[indexMy.leaf1ndex] != the[indexThe.taskDivisions]
66
+
67
+ def allDimensionsAreUnconstrained():
68
+ return my[indexMy.dimensionsUnconstrained] == the[indexThe.dimensionsTotal]
69
+
70
+ def backtrack():
71
+ my[indexMy.leaf1ndex] -= 1
72
+ track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]
73
+ track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]
74
+
75
+ def backtrackCondition():
76
+ return my[indexMy.leaf1ndex] > 0 and my[indexMy.gap1ndex] == track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
77
+
78
+ def computationDivisionsCondition():
79
+ return the[indexThe.taskDivisions] == int(False)
80
+
81
+ def countGaps():
82
+ gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.leafConnectee]
83
+ if track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] == 0:
84
+ gap1ndexCeilingIncrement()
85
+ track[indexTrack.countDimensionsGapped, my[indexMy.leafConnectee]] += 1
86
+
87
+ def dimension1ndexIncrement():
88
+ my[indexMy.dimension1ndex] += 1
89
+
90
+ def dimensionsUnconstrainedCondition():
91
+ return connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]] == my[indexMy.leaf1ndex]
92
+
93
+ def dimensionsUnconstrainedIncrement():
94
+ my[indexMy.dimensionsUnconstrained] += 1
95
+
96
+ def filterCommonGaps():
97
+ gapsWhere[my[indexMy.gap1ndex]] = gapsWhere[my[indexMy.indexMiniGap]]
98
+ if track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] == the[indexThe.dimensionsTotal] - my[indexMy.dimensionsUnconstrained]:
99
+ activeGapIncrement()
100
+ track[indexTrack.countDimensionsGapped, gapsWhere[my[indexMy.indexMiniGap]]] = 0
101
+
102
+ def findGapsInitializeVariables():
103
+ my[indexMy.dimensionsUnconstrained] = 0
104
+ my[indexMy.gap1ndexCeiling] = track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex] - 1]
105
+ my[indexMy.dimension1ndex] = 1
106
+
107
+ def foldsSubTotalsIncrement():
108
+ foldsSubTotals[my[indexMy.taskIndex]] += the[indexThe.leavesTotal]
109
+
110
+ def gap1ndexCeilingIncrement():
111
+ my[indexMy.gap1ndexCeiling] += 1
112
+
113
+ def indexMiniGapIncrement():
114
+ my[indexMy.indexMiniGap] += 1
115
+
116
+ def indexMiniGapInitialization():
117
+ my[indexMy.indexMiniGap] = my[indexMy.gap1ndex]
118
+
119
+ def insertUnconstrainedLeaf():
120
+ my[indexMy.indexLeaf] = 0
121
+ while my[indexMy.indexLeaf] < my[indexMy.leaf1ndex]:
122
+ gapsWhere[my[indexMy.gap1ndexCeiling]] = my[indexMy.indexLeaf]
123
+ my[indexMy.gap1ndexCeiling] += 1
124
+ my[indexMy.indexLeaf] += 1
125
+
126
+ def leafBelowSentinelIs1Condition():
127
+ return track[indexTrack.leafBelow, 0] == 1
128
+
129
+ def leafConnecteeInitialization():
130
+ my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], my[indexMy.leaf1ndex]]
131
+
132
+ def leafConnecteeUpdate():
133
+ my[indexMy.leafConnectee] = connectionGraph[my[indexMy.dimension1ndex], my[indexMy.leaf1ndex], track[indexTrack.leafBelow, my[indexMy.leafConnectee]]]
134
+
135
+ def loopingLeavesConnectedToActiveLeaf():
136
+ return my[indexMy.leafConnectee] != my[indexMy.leaf1ndex]
137
+
138
+ def loopingTheDimensions():
139
+ return my[indexMy.dimension1ndex] <= the[indexThe.dimensionsTotal]
140
+
141
+ def loopingToActiveGapCeiling():
142
+ return my[indexMy.indexMiniGap] < my[indexMy.gap1ndexCeiling]
143
+
144
+ def placeLeaf():
145
+ my[indexMy.gap1ndex] -= 1
146
+ track[indexTrack.leafAbove, my[indexMy.leaf1ndex]] = gapsWhere[my[indexMy.gap1ndex]]
147
+ track[indexTrack.leafBelow, my[indexMy.leaf1ndex]] = track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]]
148
+ track[indexTrack.leafBelow, track[indexTrack.leafAbove, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
149
+ track[indexTrack.leafAbove, track[indexTrack.leafBelow, my[indexMy.leaf1ndex]]] = my[indexMy.leaf1ndex]
150
+ track[indexTrack.gapRangeStart, my[indexMy.leaf1ndex]] = my[indexMy.gap1ndex]
151
+ my[indexMy.leaf1ndex] += 1
152
+
153
+ def placeLeafCondition():
154
+ return my[indexMy.leaf1ndex] > 0
155
+
156
+ def taskIndexCondition():
157
+ return my[indexMy.leafConnectee] % the[indexThe.taskDivisions] == my[indexMy.taskIndex]
158
+
159
+ def thereAreComputationDivisionsYouMightSkip():
160
+ if computationDivisionsCondition():
161
+ return True
162
+ if activeLeafNotEqualToTaskDivisionsCondition():
163
+ return True
164
+ if taskIndexCondition():
165
+ return True
166
+ return False
167
+
168
+ stateUniversal = outfitFoldings(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit)
169
+ connectionGraph: Final[numpy.ndarray] = stateUniversal['connectionGraph']
170
+ foldsSubTotals = stateUniversal['foldsSubTotals']
171
+ gapsWhere = stateUniversal['gapsWhere']
172
+ my = stateUniversal['my']
173
+ the: Final[numpy.ndarray] = stateUniversal['the']
174
+ track = stateUniversal['track']
175
+
176
+ if the[indexThe.taskDivisions] == int(False):
177
+ doWhile()
178
+ else:
179
+ stateUniversal['my'] = my.copy()
180
+ stateUniversal['gapsWhere'] = gapsWhere.copy()
181
+ stateUniversal['track'] = track.copy()
182
+ for indexSherpa in range(the[indexThe.taskDivisions]):
183
+ my = stateUniversal['my'].copy()
184
+ my[indexMy.taskIndex] = indexSherpa
185
+ gapsWhere = stateUniversal['gapsWhere'].copy()
186
+ track = stateUniversal['track'].copy()
187
+ doWhile()
188
+
189
+ return numpy.sum(foldsSubTotals).item()
190
+
191
+ @enum.verify(enum.CONTINUOUS, enum.UNIQUE) if sys.version_info >= (3, 11) else lambda x: x
192
+ class EnumIndices(enum.IntEnum):
193
+ """Base class for index enums."""
194
+ @staticmethod
195
+ def _generate_next_value_(name, start, count, last_values):
196
+ """0-indexed."""
197
+ return count
198
+
199
+ def __index__(self) -> int:
200
+ """Adapt enum to the ultra-rare event of indexing a NumPy 'ndarray', which is not the
201
+ same as `array.array`. See NumPy.org; I think it will be very popular someday."""
202
+ return self
203
+
204
+ class indexMy(EnumIndices):
205
+ """Indices for dynamic values."""
206
+ dimension1ndex = enum.auto()
207
+ dimensionsUnconstrained = enum.auto()
208
+ gap1ndex = enum.auto()
209
+ gap1ndexCeiling = enum.auto()
210
+ indexLeaf = enum.auto()
211
+ indexMiniGap = enum.auto()
212
+ leaf1ndex = enum.auto()
213
+ leafConnectee = enum.auto()
214
+ taskIndex = enum.auto()
215
+
216
+ class indexThe(EnumIndices):
217
+ """Indices for static values."""
218
+ dimensionsTotal = enum.auto()
219
+ leavesTotal = enum.auto()
220
+ taskDivisions = enum.auto()
221
+
222
+ class indexTrack(EnumIndices):
223
+ """Indices for state tracking array."""
224
+ leafAbove = enum.auto()
225
+ leafBelow = enum.auto()
226
+ countDimensionsGapped = enum.auto()
227
+ gapRangeStart = enum.auto()
228
+
229
+ class computationState(TypedDict):
230
+ connectionGraph: NDArray[integer[Any]]
231
+ foldsSubTotals: NDArray[integer[Any]]
232
+ mapShape: Tuple[int, ...]
233
+ my: NDArray[integer[Any]]
234
+ gapsWhere: NDArray[integer[Any]]
235
+ the: NDArray[integer[Any]]
236
+ track: NDArray[integer[Any]]
237
+
238
+ dtypeLarge = numpy.int64
239
+ dtypeDefault = dtypeLarge
240
+
241
+ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
242
+ """
243
+ How many leaves are in the map.
244
+
245
+ Parameters:
246
+ listDimensions: A list of integers representing dimensions.
247
+
248
+ Returns:
249
+ productDimensions: The product of all positive integer dimensions.
250
+ """
251
+ listNonNegative = parseDimensions(listDimensions, 'listDimensions')
252
+ listPositive = [dimension for dimension in listNonNegative if dimension > 0]
253
+
254
+ if not listPositive:
255
+ return 0
256
+ else:
257
+ productDimensions = 1
258
+ for dimension in listPositive:
259
+ if dimension > sys.maxsize // productDimensions:
260
+ raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
261
+ productDimensions *= dimension
262
+
263
+ return productDimensions
264
+
265
+ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]):
266
+ if not computationDivisions:
267
+ return 0
268
+ else:
269
+ leavesTotal = getLeavesTotal(listDimensions)
270
+ if isinstance(computationDivisions, int):
271
+ taskDivisions = computationDivisions
272
+ elif isinstance(computationDivisions, str):
273
+ computationDivisions = computationDivisions.lower()
274
+ if computationDivisions == "maximum":
275
+ taskDivisions = leavesTotal
276
+ elif computationDivisions == "cpu":
277
+ taskDivisions = min(concurrencyLimit, leavesTotal)
278
+ else:
279
+ raise ValueError("Not my problem.")
280
+
281
+ if taskDivisions > leavesTotal:
282
+ raise ValueError("What are you doing?")
283
+
284
+ return taskDivisions
285
+
286
+ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[Type]) -> NDArray[integer[Any]]:
287
+ datatype = keywordArguments.get('datatype', dtypeDefault)
288
+ mapShape = validateListDimensions(listDimensions)
289
+ leavesTotal = getLeavesTotal(mapShape)
290
+ arrayDimensions = numpy.array(mapShape, dtype=datatype)
291
+ dimensionsTotal = len(arrayDimensions)
292
+
293
+ cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=datatype)
294
+ coordinateSystem = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1), dtype=datatype)
295
+ for dimension1ndex in range(1, dimensionsTotal + 1):
296
+ for leaf1ndex in range(1, leavesTotal + 1):
297
+ coordinateSystem[dimension1ndex, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[dimension1ndex - 1]) % arrayDimensions[dimension1ndex - 1] + 1 )
298
+
299
+ connectionGraph = numpy.zeros((dimensionsTotal + 1, leavesTotal + 1, leavesTotal + 1), dtype=datatype)
300
+ for dimension1ndex in range(1, dimensionsTotal + 1):
301
+ for activeLeaf1ndex in range(1, leavesTotal + 1):
302
+ for connectee1ndex in range(1, activeLeaf1ndex + 1):
303
+ isFirstCoord = coordinateSystem[dimension1ndex, connectee1ndex] == 1
304
+ isLastCoord = coordinateSystem[dimension1ndex, connectee1ndex] == arrayDimensions[dimension1ndex - 1]
305
+ exceedsActive = connectee1ndex + cumulativeProduct[dimension1ndex - 1] > activeLeaf1ndex
306
+ isEvenParity = (coordinateSystem[dimension1ndex, activeLeaf1ndex] & 1) == (coordinateSystem[dimension1ndex, connectee1ndex] & 1)
307
+
308
+ if (isEvenParity and isFirstCoord) or (not isEvenParity and (isLastCoord or exceedsActive)):
309
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
310
+ elif isEvenParity and not isFirstCoord:
311
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex - cumulativeProduct[dimension1ndex - 1]
312
+ elif not isEvenParity and not (isLastCoord or exceedsActive):
313
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex + cumulativeProduct[dimension1ndex - 1]
314
+ else:
315
+ connectionGraph[dimension1ndex, activeLeaf1ndex, connectee1ndex] = connectee1ndex
316
+ return connectionGraph
317
+
318
+ def makeDataContainer(shape, datatype: Optional[Type] = None):
319
+ if datatype is None:
320
+ datatype = dtypeDefault
321
+ return numpy.zeros(shape, dtype=datatype)
322
+
323
+ def outfitFoldings(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Type]) -> computationState:
324
+ datatypeDefault = keywordArguments.get('datatypeDefault', dtypeDefault)
325
+ datatypeLarge = keywordArguments.get('datatypeLarge', dtypeLarge)
326
+
327
+ the = makeDataContainer(len(indexThe), datatypeDefault)
328
+
329
+ mapShape = tuple(sorted(validateListDimensions(listDimensions)))
330
+ the[indexThe.leavesTotal] = getLeavesTotal(mapShape)
331
+ the[indexThe.dimensionsTotal] = len(mapShape)
332
+ concurrencyLimit = setCPUlimit(CPUlimit)
333
+ the[indexThe.taskDivisions] = getTaskDivisions(computationDivisions, concurrencyLimit, CPUlimit, listDimensions)
334
+
335
+ stateInitialized = computationState(
336
+ connectionGraph = makeConnectionGraph(mapShape, datatype=datatypeDefault),
337
+ foldsSubTotals = makeDataContainer(the[indexThe.leavesTotal], datatypeLarge),
338
+ mapShape = mapShape,
339
+ my = makeDataContainer(len(indexMy), datatypeLarge),
340
+ gapsWhere = makeDataContainer(int(the[indexThe.leavesTotal]) * int(the[indexThe.leavesTotal]) + 1, datatypeDefault),
341
+ the = the,
342
+ track = makeDataContainer((len(indexTrack), the[indexThe.leavesTotal] + 1), datatypeLarge)
343
+ )
344
+
345
+ stateInitialized['my'][indexMy.leaf1ndex] = 1
346
+ return stateInitialized
347
+
348
+ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'unnamed parameter') -> List[int]:
349
+ # listValidated = intInnit(dimensions, parameterName)
350
+ listNOTValidated = dimensions if isinstance(dimensions, (list, tuple)) else list(dimensions)
351
+ listNonNegative = []
352
+ for dimension in listNOTValidated:
353
+ if dimension < 0:
354
+ raise ValueError(f"Dimension {dimension} must be non-negative")
355
+ listNonNegative.append(dimension)
356
+ if not listNonNegative:
357
+ raise ValueError("At least one dimension must be non-negative")
358
+ return listNonNegative
359
+
360
+ def setCPUlimit(CPUlimit: Union[bool, float, int, None]) -> int:
361
+ # if not (CPUlimit is None or isinstance(CPUlimit, (bool, int, float))):
362
+ # CPUlimit = oopsieKwargsie(CPUlimit)
363
+ # concurrencyLimit = defineConcurrencyLimit(CPUlimit)
364
+ # numba.set_num_threads(concurrencyLimit)
365
+ concurrencyLimitHARDCODED = 1
366
+ concurrencyLimit = concurrencyLimitHARDCODED
367
+ return concurrencyLimit
368
+
369
+ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
370
+ if not listDimensions:
371
+ raise ValueError(f"listDimensions is a required parameter.")
372
+ listNonNegative = parseDimensions(listDimensions, 'listDimensions')
373
+ dimensionsValid = [dimension for dimension in listNonNegative if dimension > 0]
374
+ if len(dimensionsValid) < 2:
375
+ raise NotImplementedError(f"This function requires listDimensions, {listDimensions}, to have at least two dimensions greater than 0. You may want to look at https://oeis.org/.")
376
+ return sorted(dimensionsValid)
@@ -46,15 +46,15 @@ def countFolds(listDimensions: List[int]) -> int:
46
46
 
47
47
  """Step for... for... for...: fill the connection graph"""
48
48
  for dimension1ndex in range(1, dimensionsTotal + 1):
49
- for activeLeaf1ndex in range(1, leavesTotal + 1):
50
- for leaf1ndexConnectee in range(1, activeLeaf1ndex + 1):
51
- connectionGraph[dimension1ndex, activeLeaf1ndex, leaf1ndexConnectee] = (0 if leaf1ndexConnectee == 0
52
- else ((leaf1ndexConnectee if coordinateSystem[dimension1ndex, leaf1ndexConnectee] == 1
53
- else leaf1ndexConnectee - cumulativeProduct[dimension1ndex - 1])
54
- if (coordinateSystem[dimension1ndex, activeLeaf1ndex] & 1) == (coordinateSystem[dimension1ndex, leaf1ndexConnectee] & 1)
55
- else (leaf1ndexConnectee if coordinateSystem[dimension1ndex, leaf1ndexConnectee] == listDimensions[dimension1ndex-1]
56
- or leaf1ndexConnectee + cumulativeProduct[dimension1ndex - 1] > activeLeaf1ndex
57
- else leaf1ndexConnectee + cumulativeProduct[dimension1ndex - 1])))
49
+ for leaf1ndex in range(1, leavesTotal + 1):
50
+ for leafConnectee in range(1, leaf1ndex + 1):
51
+ connectionGraph[dimension1ndex, leaf1ndex, leafConnectee] = (0 if leafConnectee == 0
52
+ else ((leafConnectee if coordinateSystem[dimension1ndex, leafConnectee] == 1
53
+ else leafConnectee - cumulativeProduct[dimension1ndex - 1])
54
+ if (coordinateSystem[dimension1ndex, leaf1ndex] & 1) == (coordinateSystem[dimension1ndex, leafConnectee] & 1)
55
+ else (leafConnectee if coordinateSystem[dimension1ndex, leafConnectee] == listDimensions[dimension1ndex-1]
56
+ or leafConnectee + cumulativeProduct[dimension1ndex - 1] > leaf1ndex
57
+ else leafConnectee + cumulativeProduct[dimension1ndex - 1])))
58
58
 
59
59
  """Indices of array `track` (to "track" the execution state), which is a collection of one-dimensional arrays each of length `leavesTotal + 1`."""
60
60
  leafAbove = numba.literally(0)
@@ -66,67 +66,67 @@ def countFolds(listDimensions: List[int]) -> int:
66
66
  gapsWhere = numpy.zeros(integerLarge(integerLarge(leavesTotal) * integerLarge(leavesTotal) + 1), dtype=dtypeMaximum)
67
67
 
68
68
  foldsTotal = integerLarge(0)
69
- activeLeaf1ndex = integerSmall(1)
70
- activeGap1ndex = integerSmall(0)
69
+ leaf1ndex = integerSmall(1)
70
+ gap1ndex = integerSmall(0)
71
71
 
72
- while activeLeaf1ndex > 0:
73
- if activeLeaf1ndex <= 1 or track[leafBelow, 0] == 1:
74
- if activeLeaf1ndex > leavesTotal:
72
+ while leaf1ndex > 0:
73
+ if leaf1ndex <= 1 or track[leafBelow, 0] == 1:
74
+ if leaf1ndex > leavesTotal:
75
75
  foldsTotal += leavesTotal
76
76
  else:
77
77
  dimensionsUnconstrained = integerSmall(0)
78
- """Track possible gaps for activeLeaf1ndex in each section"""
79
- gap1ndexCeiling = track[gapRangeStart, activeLeaf1ndex - 1]
78
+ """Track possible gaps for leaf1ndex in each section"""
79
+ gap1ndexCeiling = track[gapRangeStart, leaf1ndex - 1]
80
80
 
81
- """Count possible gaps for activeLeaf1ndex in each section"""
81
+ """Count possible gaps for leaf1ndex in each section"""
82
82
  dimension1ndex = integerSmall(1)
83
83
  while dimension1ndex <= dimensionsTotal:
84
- if connectionGraph[dimension1ndex, activeLeaf1ndex, activeLeaf1ndex] == activeLeaf1ndex:
84
+ if connectionGraph[dimension1ndex, leaf1ndex, leaf1ndex] == leaf1ndex:
85
85
  dimensionsUnconstrained += 1
86
86
  else:
87
- leaf1ndexConnectee = connectionGraph[dimension1ndex, activeLeaf1ndex, activeLeaf1ndex]
88
- while leaf1ndexConnectee != activeLeaf1ndex:
89
- gapsWhere[gap1ndexCeiling] = leaf1ndexConnectee
90
- if track[countDimensionsGapped, leaf1ndexConnectee] == 0:
87
+ leafConnectee = connectionGraph[dimension1ndex, leaf1ndex, leaf1ndex]
88
+ while leafConnectee != leaf1ndex:
89
+ gapsWhere[gap1ndexCeiling] = leafConnectee
90
+ if track[countDimensionsGapped, leafConnectee] == 0:
91
91
  gap1ndexCeiling += 1
92
- track[countDimensionsGapped, leaf1ndexConnectee] += 1
93
- leaf1ndexConnectee = connectionGraph[dimension1ndex, activeLeaf1ndex, track[leafBelow, leaf1ndexConnectee]]
92
+ track[countDimensionsGapped, leafConnectee] += 1
93
+ leafConnectee = connectionGraph[dimension1ndex, leaf1ndex, track[leafBelow, leafConnectee]]
94
94
  dimension1ndex += 1
95
95
 
96
- """If activeLeaf1ndex is unconstrained in all sections, it can be inserted anywhere"""
96
+ """If leaf1ndex is unconstrained in all sections, it can be inserted anywhere"""
97
97
  if dimensionsUnconstrained == dimensionsTotal:
98
- leaf1ndex = integerSmall(0)
99
- while leaf1ndex < activeLeaf1ndex:
100
- gapsWhere[gap1ndexCeiling] = leaf1ndex
98
+ indexLeaf = integerSmall(0)
99
+ while indexLeaf < leaf1ndex:
100
+ gapsWhere[gap1ndexCeiling] = indexLeaf
101
101
  gap1ndexCeiling += 1
102
- leaf1ndex += 1
102
+ indexLeaf += 1
103
103
 
104
104
  """Filter gaps that are common to all sections"""
105
- indexMiniGap = activeGap1ndex
105
+ indexMiniGap = gap1ndex
106
106
  while indexMiniGap < gap1ndexCeiling:
107
- gapsWhere[activeGap1ndex] = gapsWhere[indexMiniGap]
107
+ gapsWhere[gap1ndex] = gapsWhere[indexMiniGap]
108
108
  if track[countDimensionsGapped, gapsWhere[indexMiniGap]] == dimensionsTotal - dimensionsUnconstrained:
109
- activeGap1ndex += 1
109
+ gap1ndex += 1
110
110
  """Reset track[countDimensionsGapped] for next iteration"""
111
111
  track[countDimensionsGapped, gapsWhere[indexMiniGap]] = 0
112
112
  indexMiniGap += 1
113
113
 
114
114
  """Recursive backtracking steps"""
115
- while activeLeaf1ndex > 0 and activeGap1ndex == track[gapRangeStart, activeLeaf1ndex - 1]:
116
- activeLeaf1ndex -= 1
117
- track[leafBelow, track[leafAbove, activeLeaf1ndex]] = track[leafBelow, activeLeaf1ndex]
118
- track[leafAbove, track[leafBelow, activeLeaf1ndex]] = track[leafAbove, activeLeaf1ndex]
115
+ while leaf1ndex > 0 and gap1ndex == track[gapRangeStart, leaf1ndex - 1]:
116
+ leaf1ndex -= 1
117
+ track[leafBelow, track[leafAbove, leaf1ndex]] = track[leafBelow, leaf1ndex]
118
+ track[leafAbove, track[leafBelow, leaf1ndex]] = track[leafAbove, leaf1ndex]
119
119
 
120
120
  """Place leaf in valid position"""
121
- if activeLeaf1ndex > 0:
122
- activeGap1ndex -= 1
123
- track[leafAbove, activeLeaf1ndex] = gapsWhere[activeGap1ndex]
124
- track[leafBelow, activeLeaf1ndex] = track[leafBelow, track[leafAbove, activeLeaf1ndex]]
125
- track[leafBelow, track[leafAbove, activeLeaf1ndex]] = activeLeaf1ndex
126
- track[leafAbove, track[leafBelow, activeLeaf1ndex]] = activeLeaf1ndex
121
+ if leaf1ndex > 0:
122
+ gap1ndex -= 1
123
+ track[leafAbove, leaf1ndex] = gapsWhere[gap1ndex]
124
+ track[leafBelow, leaf1ndex] = track[leafBelow, track[leafAbove, leaf1ndex]]
125
+ track[leafBelow, track[leafAbove, leaf1ndex]] = leaf1ndex
126
+ track[leafAbove, track[leafBelow, leaf1ndex]] = leaf1ndex
127
127
  """Save current gap index"""
128
- track[gapRangeStart, activeLeaf1ndex] = activeGap1ndex
128
+ track[gapRangeStart, leaf1ndex] = gap1ndex
129
129
  """Move to next leaf"""
130
- activeLeaf1ndex += 1
130
+ leaf1ndex += 1
131
131
 
132
132
  return int(foldsTotal)
@@ -9,11 +9,11 @@ def foldings(p, job=None):
9
9
  p.insert(0, None) # NOTE mimics Atlas `array` type
10
10
 
11
11
  if job is None:
12
- global f
13
- f = 0
12
+ global G
13
+ G = 0
14
14
  def job(A, B):
15
- global f
16
- f = f + 1
15
+ global G
16
+ G = G + 1
17
17
  return foldings(p, job)
18
18
  # perform job (A, B) on each folding of a p[1] x ... x p[d] map,
19
19
  # where A and B are the above and below vectors. p[d + 1] < 0 terminates p;
@@ -150,4 +150,4 @@ def foldings(p, job=None):
150
150
  else:
151
151
  break
152
152
 
153
- return f #if job.__closure__ else None
153
+ return G #if job.__closure__ else None
@@ -13,7 +13,7 @@ def foldings(p: List[int]) -> int:
13
13
  p: A list of integers representing the dimensions of the map.
14
14
 
15
15
  Returns:
16
- f: The number of distinct foldings for the given map dimensions.
16
+ G: The number of distinct foldings for the given map dimensions.
17
17
 
18
18
  NOTE If there are fewer than two dimensions, any dimensions are not positive, or any dimensions are not integers, the output will be unreliable.
19
19
  """
@@ -65,14 +65,14 @@ def foldings(p: List[int]) -> int:
65
65
  # P[i] = p[1] x ... x p[i], C[i][m] = i-th co-ordinate of leaf m,
66
66
  # D[i][l][m] = leaf connected to m in section i when inserting l;
67
67
 
68
- f: int = 0
68
+ G: int = 0
69
69
  l: int = 1
70
70
 
71
71
  # kick off with null folding
72
72
  while l > 0:
73
73
  if l <= 1 or B[0] == 1: # NOTE This statement is part of a significant divergence from the 1971 paper. As a result, this version is greater than one order of magnitude faster.
74
74
  if l > n:
75
- f = f + n # NOTE Due to `B[0] == 1`, this implementation increments the counted foldings in batches of `n`-many foldings, rather than immediately incrementing when a folding is found, i.e. `f = f + 1`
75
+ G = G + n # NOTE Due to `B[0] == 1`, this implementation increments the counted foldings in batches of `n`-many foldings, rather than immediately incrementing when a folding is found, i.e. `G = G + 1`
76
76
  else:
77
77
  dd: int = 0
78
78
  gg: int = gapter[l - 1]
@@ -120,4 +120,4 @@ def foldings(p: List[int]) -> int:
120
120
  A[B[l]] = l
121
121
  gapter[l] = g
122
122
  l = l + 1
123
- return f
123
+ return G