mapFolding 0.4.3__py3-none-any.whl → 0.5.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 (41) hide show
  1. mapFolding/__init__.py +93 -58
  2. mapFolding/basecamp.py +9 -11
  3. mapFolding/beDRY.py +24 -23
  4. mapFolding/oeis.py +47 -45
  5. mapFolding/theDao.py +48 -48
  6. mapFolding/theSSOT.py +22 -20
  7. mapFolding/theSSOTdatatypes.py +20 -32
  8. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/METADATA +3 -1
  9. mapFolding-0.5.1.dist-info/RECORD +14 -0
  10. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/top_level.txt +0 -1
  11. mapFolding/reference/flattened.py +0 -377
  12. mapFolding/reference/hunterNumba.py +0 -132
  13. mapFolding/reference/irvineJavaPort.py +0 -120
  14. mapFolding/reference/jax.py +0 -208
  15. mapFolding/reference/lunnan.py +0 -153
  16. mapFolding/reference/lunnanNumpy.py +0 -123
  17. mapFolding/reference/lunnanWhile.py +0 -121
  18. mapFolding/reference/rotatedEntryPoint.py +0 -240
  19. mapFolding/reference/total_countPlus1vsPlusN.py +0 -211
  20. mapFolding/someAssemblyRequired/__init__.py +0 -5
  21. mapFolding/someAssemblyRequired/getLLVMforNoReason.py +0 -19
  22. mapFolding/someAssemblyRequired/makeJob.py +0 -55
  23. mapFolding/someAssemblyRequired/synthesizeModuleJAX.py +0 -29
  24. mapFolding/someAssemblyRequired/synthesizeNumba.py +0 -340
  25. mapFolding/someAssemblyRequired/synthesizeNumbaGeneralized.py +0 -396
  26. mapFolding/someAssemblyRequired/synthesizeNumbaJob.py +0 -162
  27. mapFolding/someAssemblyRequired/synthesizeNumbaModules.py +0 -129
  28. mapFolding/syntheticModules/numbaCount.py +0 -158
  29. mapFolding/syntheticModules/numba_doTheNeedful.py +0 -13
  30. mapFolding-0.4.3.dist-info/RECORD +0 -40
  31. tests/__init__.py +0 -1
  32. tests/conftest.py +0 -306
  33. tests/test_computations.py +0 -43
  34. tests/test_oeis.py +0 -129
  35. tests/test_other.py +0 -171
  36. tests/test_tasks.py +0 -40
  37. tests/test_types.py +0 -5
  38. /mapFolding/{syntheticModules/__init__.py → py.typed} +0 -0
  39. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/LICENSE +0 -0
  40. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/WHEEL +0 -0
  41. {mapFolding-0.4.3.dist-info → mapFolding-0.5.1.dist-info}/entry_points.txt +0 -0
mapFolding/__init__.py CHANGED
@@ -1,69 +1,104 @@
1
+ from collections import defaultdict
2
+ from types import ModuleType
3
+ import importlib
4
+
5
+ _dictionaryListsImportFrom: dict[str, list[str]] = defaultdict(list)
6
+
7
+ def __getattr__(name: str):
8
+ if name not in _mapSymbolToModule:
9
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
10
+
11
+ try:
12
+ moduleAsStr: str = _mapSymbolToModule[name]
13
+ module: ModuleType = importlib.import_module(moduleAsStr)
14
+ blankSymbol = getattr(module, name)
15
+ except (ImportError, ModuleNotFoundError, AttributeError):
16
+ raise
17
+
18
+ # The need to inject into globals tells us that the symbol has not actually been imported
19
+ globals()[name] = blankSymbol
20
+ return blankSymbol
21
+
22
+ _dictionaryListsImportFrom['mapFolding.basecamp'].extend([
23
+ 'countFolds',
24
+ ])
25
+
26
+ _dictionaryListsImportFrom['mapFolding.beDRY'].extend([
27
+ 'getFilenameFoldsTotal',
28
+ 'getPathFilenameFoldsTotal',
29
+ 'outfitCountFolds',
30
+ 'saveFoldsTotal',
31
+ ])
32
+
33
+ _dictionaryListsImportFrom['mapFolding.oeis'].extend([
34
+ 'clearOEIScache',
35
+ 'getOEISids',
36
+ 'oeisIDfor_n',
37
+ ])
38
+
1
39
  # fundamentals
2
- from mapFolding.theSSOT import (
3
- computationState,
4
- EnumIndices,
5
- getDispatcherCallable,
6
- getPathPackage,
7
- indexMy,
8
- indexTrack,
9
- myPackageNameIs,
10
- )
40
+ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
41
+ 'computationState',
42
+ 'EnumIndices',
43
+ 'getDispatcherCallable',
44
+ 'getPathPackage',
45
+ 'indexMy',
46
+ 'indexTrack',
47
+ 'myPackageNameIs',
48
+ ])
11
49
 
12
50
  # Datatype management
13
- from mapFolding.theSSOT import (
14
- getDatatypeModule,
15
- hackSSOTdatatype,
16
- hackSSOTdtype,
17
- setDatatypeElephino,
18
- setDatatypeFoldsTotal,
19
- setDatatypeLeavesTotal,
20
- setDatatypeModule,
21
- )
51
+ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
52
+ 'getDatatypeModule',
53
+ 'hackSSOTdatatype',
54
+ 'hackSSOTdtype',
55
+ 'setDatatypeElephino',
56
+ 'setDatatypeFoldsTotal',
57
+ 'setDatatypeLeavesTotal',
58
+ 'setDatatypeModule',
59
+ ])
22
60
 
23
61
  # Synthesize modules
24
- from mapFolding.theSSOT import (
25
- formatFilenameModuleDEFAULT,
26
- getAlgorithmDispatcher,
27
- getAlgorithmSource,
28
- getPathJobRootDEFAULT,
29
- getPathSyntheticModules,
30
- moduleOfSyntheticModules,
31
- Z0Z_getDatatypeModuleScalar,
32
- Z0Z_getDecoratorCallable,
33
- Z0Z_setDatatypeModuleScalar,
34
- Z0Z_setDecoratorCallable,
35
- Z0Z_identifierCountFolds,
36
- )
62
+ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
63
+ 'formatFilenameModuleDEFAULT',
64
+ 'getAlgorithmDispatcher',
65
+ 'getAlgorithmSource',
66
+ 'getPathJobRootDEFAULT',
67
+ 'getPathSyntheticModules',
68
+ 'moduleOfSyntheticModules',
69
+ 'Z0Z_getDatatypeModuleScalar',
70
+ 'Z0Z_getDecoratorCallable',
71
+ 'Z0Z_setDatatypeModuleScalar',
72
+ 'Z0Z_setDecoratorCallable',
73
+ 'Z0Z_identifierCountFolds',
74
+ ])
37
75
 
38
76
  # Parameters for the prima donna
39
- from mapFolding.theSSOT import (
40
- ParametersNumba,
41
- parametersNumbaDEFAULT,
42
- parametersNumbaFailEarly,
43
- parametersNumbaMinimum,
44
- parametersNumbaParallelDEFAULT,
45
- parametersNumbaSuperJit,
46
- parametersNumbaSuperJitParallel,
47
- )
77
+ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
78
+ 'ParametersNumba',
79
+ 'parametersNumbaDEFAULT',
80
+ 'parametersNumbaFailEarly',
81
+ 'parametersNumbaMinimum',
82
+ 'parametersNumbaParallelDEFAULT',
83
+ 'parametersNumbaSuperJit',
84
+ 'parametersNumbaSuperJitParallel',
85
+ ])
48
86
 
49
87
  # Coping
50
- from mapFolding.theSSOT import (
51
- FREAKOUT,
52
- )
53
-
54
- from mapFolding.beDRY import (
55
- getFilenameFoldsTotal,
56
- getPathFilenameFoldsTotal,
57
- outfitCountFolds,
58
- saveFoldsTotal,
59
- )
88
+ _dictionaryListsImportFrom['mapFolding.theSSOT'].extend([
89
+ 'FREAKOUT',
90
+ ])
60
91
 
61
- from mapFolding.basecamp import countFolds
62
- from mapFolding.oeis import clearOEIScache, getOEISids, oeisIDfor_n
92
+ _mapSymbolToModule: dict[str, str] = {}
93
+ for moduleAsStr, listSymbolsAsStr in _dictionaryListsImportFrom.items():
94
+ for symbolAsStr in listSymbolsAsStr:
95
+ _mapSymbolToModule[symbolAsStr] = moduleAsStr
63
96
 
64
- __all__ = [
65
- 'clearOEIScache',
66
- 'countFolds',
67
- 'getOEISids',
68
- 'oeisIDfor_n',
69
- ]
97
+ from typing import TYPE_CHECKING
98
+ if TYPE_CHECKING:
99
+ from basecamp import *
100
+ from beDRY import *
101
+ from oeis import *
102
+ from theDao import *
103
+ from theSSOT import *
104
+ from theSSOTdatatypes import *
mapFolding/basecamp.py CHANGED
@@ -1,12 +1,13 @@
1
+ from collections.abc import Sequence
1
2
  from mapFolding import computationState, getDispatcherCallable, getPathFilenameFoldsTotal, outfitCountFolds, saveFoldsTotal
2
- from typing import Optional, Sequence, Union
3
- import os
3
+ from os import PathLike
4
+ from pathlib import Path
4
5
 
5
6
  def countFolds(listDimensions: Sequence[int]
6
- , pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None
7
- , computationDivisions: Optional[Union[int, str]] = None
8
- , CPUlimit: Optional[Union[int, float, bool]] = None
9
- , **keywordArguments: Optional[Union[str, bool]]
7
+ , pathLikeWriteFoldsTotal: str | PathLike[str] | None = None
8
+ , computationDivisions: int | str | None = None
9
+ , CPUlimit: int | float | bool | None = None
10
+ , **keywordArguments: str | bool
10
11
  ) -> int:
11
12
  """Count the total number of possible foldings for a given map dimensions.
12
13
 
@@ -40,16 +41,13 @@ def countFolds(listDimensions: Sequence[int]
40
41
  """
41
42
  stateUniversal: computationState = outfitCountFolds(listDimensions, computationDivisions=computationDivisions, CPUlimit=CPUlimit, **keywordArguments)
42
43
 
43
- pathFilenameFoldsTotal = None
44
- if pathLikeWriteFoldsTotal is not None:
45
- pathFilenameFoldsTotal = getPathFilenameFoldsTotal(stateUniversal['mapShape'], pathLikeWriteFoldsTotal)
46
-
47
44
  dispatcher = getDispatcherCallable()
48
45
  dispatcher(**stateUniversal)
49
46
 
50
47
  foldsTotal = int(stateUniversal['foldGroups'][0:-1].sum() * stateUniversal['foldGroups'][-1])
51
48
 
52
- if pathFilenameFoldsTotal is not None:
49
+ if pathLikeWriteFoldsTotal is not None:
50
+ pathFilenameFoldsTotal: Path = getPathFilenameFoldsTotal(stateUniversal['mapShape'], pathLikeWriteFoldsTotal)
53
51
  saveFoldsTotal(pathFilenameFoldsTotal, foldsTotal)
54
52
 
55
53
  return foldsTotal
mapFolding/beDRY.py CHANGED
@@ -12,17 +12,18 @@ from mapFolding import (
12
12
  setDatatypeLeavesTotal,
13
13
  setDatatypeModule,
14
14
  )
15
+ from collections.abc import Sequence
16
+ from numba import get_num_threads, set_num_threads # type: ignore
15
17
  from numpy import dtype, integer, ndarray
16
18
  from numpy.typing import DTypeLike, NDArray
17
- from typing import Any, List, Optional, Sequence, Tuple, Union
19
+ from pathlib import Path
20
+ from sys import maxsize as sysMaxsize
21
+ from typing import Any
18
22
  from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
19
- import numba
20
23
  import numpy
21
24
  import os
22
- import pathlib
23
- import sys
24
25
 
25
- def getFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]]) -> str:
26
+ def getFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[integer[Any]]]) -> str:
26
27
  """Imagine your computer has been counting folds for 70 hours, and when it tries to save your newly discovered value,
27
28
  the filename is invalid. I bet you think this function is more important after that thought experiment.
28
29
 
@@ -66,13 +67,13 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
66
67
  else:
67
68
  productDimensions = 1
68
69
  for dimension in listPositive:
69
- if dimension > sys.maxsize // productDimensions:
70
+ if dimension > sysMaxsize // productDimensions:
70
71
  raise OverflowError(f"I received {dimension=} in {listDimensions=}, but the product of the dimensions exceeds the maximum size of an integer on this system.")
71
72
  productDimensions *= dimension
72
73
 
73
74
  return productDimensions
74
75
 
75
- def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]], pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
76
+ def getPathFilenameFoldsTotal(mapShape: Sequence[int] | ndarray[tuple[int], dtype[integer[Any]]], pathLikeWriteFoldsTotal: str | os.PathLike[str] | None = None) -> Path:
76
77
  """Get a standardized path and filename for the computed value `foldsTotal`.
77
78
 
78
79
  If you provide a directory, the function will append a standardized filename. If you provide a filename
@@ -86,7 +87,7 @@ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int],
86
87
  Returns:
87
88
  pathFilenameFoldsTotal: Absolute path and filename.
88
89
  """
89
- pathLikeSherpa = pathlib.Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
90
+ pathLikeSherpa = Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
90
91
  if not pathLikeSherpa:
91
92
  pathLikeSherpa = getPathJobRootDEFAULT()
92
93
  if pathLikeSherpa.is_dir():
@@ -99,7 +100,7 @@ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int],
99
100
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
100
101
  return pathFilenameFoldsTotal
101
102
 
102
- def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]) -> int:
103
+ def getTaskDivisions(computationDivisions: int | str | None, concurrencyLimit: int, CPUlimit: bool | float | int | None, listDimensions: Sequence[int]) -> int:
103
104
  """
104
105
  Determines whether to divide the computation into tasks and how many divisions.
105
106
 
@@ -138,7 +139,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
138
139
  pass
139
140
  elif isinstance(computationDivisions, int):
140
141
  taskDivisions = computationDivisions
141
- elif isinstance(computationDivisions, str):
142
+ elif isinstance(computationDivisions, str): # type: ignore 'Unnecessary isinstance call; "str" is always an instance of "str", so sayeth Pylance'. Yeah, well "User is not always an instance of "correct input" so sayeth the programmer.
142
143
  computationDivisions = computationDivisions.lower()
143
144
  if computationDivisions == 'maximum':
144
145
  taskDivisions = leavesTotal
@@ -152,7 +153,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
152
153
 
153
154
  return taskDivisions
154
155
 
155
- def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[str]) -> ndarray[Tuple[int, int, int], dtype[integer[Any]]]:
156
+ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: str | None) -> ndarray[tuple[int, int, int], dtype[integer[Any]]]:
156
157
  """
157
158
  Constructs a multi-dimensional connection graph representing the connections between the leaves of a map with the given dimensions.
158
159
  Also called a Cartesian product decomposition or dimensional product mapping.
@@ -179,7 +180,7 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
179
180
  for leaf1ndex in range(1, leavesTotal + 1):
180
181
  coordinateSystem[indexDimension, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1 )
181
182
 
182
- connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
183
+ connectionGraph: ndarray[tuple[int, int, int], numpy.dtype[integer[Any]]] = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
183
184
  for indexDimension in range(dimensionsTotal):
184
185
  for activeLeaf1ndex in range(1, leavesTotal + 1):
185
186
  for connectee1ndex in range(1, activeLeaf1ndex + 1):
@@ -197,7 +198,7 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
197
198
 
198
199
  return connectionGraph
199
200
 
200
- def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTypeLike] = None) -> NDArray[integer[Any]]:
201
+ def makeDataContainer(shape: int | tuple[int, ...], datatype: DTypeLike | None = None) -> NDArray[integer[Any]]:
201
202
  """Create a zeroed-out `ndarray` with the given shape and datatype.
202
203
 
203
204
  Parameters:
@@ -219,7 +220,7 @@ def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTy
219
220
  else:
220
221
  raise NotImplementedError("Somebody done broke it.")
221
222
 
222
- def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Optional[Union[int, str]] = None, CPUlimit: Optional[Union[bool, float, int]] = None, **keywordArguments: Optional[Union[str, bool]]) -> computationState:
223
+ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: int | str | None = None, CPUlimit: bool | float | int | None = None, **keywordArguments: str | bool | None) -> computationState:
223
224
  """
224
225
  Initializes and configures the computation state for map folding computations.
225
226
 
@@ -284,7 +285,7 @@ def outfitCountFolds(listDimensions: Sequence[int], computationDivisions: Option
284
285
 
285
286
  return stateInitialized
286
287
 
287
- def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> List[int]:
288
+ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> list[int]:
288
289
  """
289
290
  Parse and validate the dimensions are non-negative integers.
290
291
 
@@ -297,8 +298,8 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensi
297
298
  ValueError: If any dimension is negative or if the list is empty.
298
299
  TypeError: If any element cannot be converted to integer (raised by `intInnit`).
299
300
  """
300
- listValidated = intInnit(dimensions, parameterName)
301
- listNonNegative = []
301
+ listValidated: list[int] = intInnit(dimensions, parameterName)
302
+ listNonNegative: list[int] = []
302
303
  for dimension in listValidated:
303
304
  if dimension < 0:
304
305
  raise ValueError(f"Dimension {dimension} must be non-negative")
@@ -306,7 +307,7 @@ def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensi
306
307
 
307
308
  return listNonNegative
308
309
 
309
- def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int) -> None:
310
+ def saveFoldsTotal(pathFilename: str | os.PathLike[str], foldsTotal: int) -> None:
310
311
  """
311
312
  Save foldsTotal with multiple fallback mechanisms.
312
313
 
@@ -315,7 +316,7 @@ def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int)
315
316
  foldsTotal: Critical computed value to save
316
317
  """
317
318
  try:
318
- pathFilenameFoldsTotal = pathlib.Path(pathFilename)
319
+ pathFilenameFoldsTotal = Path(pathFilename)
319
320
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
320
321
  pathFilenameFoldsTotal.write_text(str(foldsTotal))
321
322
  except Exception as ERRORmessage:
@@ -333,7 +334,7 @@ def saveFoldsTotal(pathFilename: Union[str, os.PathLike[str]], foldsTotal: int)
333
334
  except Exception:
334
335
  print(foldsTotal)
335
336
 
336
- def setCPUlimit(CPUlimit: Optional[Any]) -> int:
337
+ def setCPUlimit(CPUlimit: Any | None) -> int:
337
338
  """Sets CPU limit for Numba concurrent operations. Note that it can only affect Numba-jitted functions that have not yet been imported.
338
339
 
339
340
  Parameters:
@@ -355,12 +356,12 @@ def setCPUlimit(CPUlimit: Optional[Any]) -> int:
355
356
  CPUlimit = oopsieKwargsie(CPUlimit)
356
357
 
357
358
  concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
358
- numba.set_num_threads(concurrencyLimit)
359
- concurrencyLimit = numba.get_num_threads()
359
+ set_num_threads(concurrencyLimit)
360
+ concurrencyLimit: int = get_num_threads()
360
361
 
361
362
  return concurrencyLimit
362
363
 
363
- def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
364
+ def validateListDimensions(listDimensions: Sequence[int]) -> list[int]:
364
365
  """
365
366
  Validates and sorts a sequence of at least two positive dimensions.
366
367
 
mapFolding/oeis.py CHANGED
@@ -1,10 +1,11 @@
1
- """Everything implementing the The Online Encyclopedia of Integer Sequences (OEIS);
2
- _only_ things that implement _only_ the OEIS."""
1
+ """Everything implementing the The Online Encyclopedia of Integer Sequences (OEIS); _only_ things that implement _only_ the OEIS."""
2
+ from collections.abc import Callable
3
3
  from datetime import datetime, timedelta
4
4
  from mapFolding import countFolds, getPathPackage
5
- from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
5
+ from typing import Any, Final, TYPE_CHECKING
6
6
  import argparse
7
7
  import pathlib
8
+ from pathlib import Path
8
9
  import random
9
10
  import sys
10
11
  import time
@@ -17,22 +18,24 @@ if TYPE_CHECKING:
17
18
  else:
18
19
  TypedDict = dict
19
20
 
21
+ cacheDays = 7
22
+
20
23
  """
21
24
  Section: make `settingsOEIS`"""
22
25
 
23
- _pathCache = getPathPackage() / ".cache"
26
+ _pathCache: Path = getPathPackage() / ".cache"
24
27
 
25
28
  class SettingsOEIS(TypedDict):
26
29
  description: str
27
- getMapShape: Callable[[int], List[int]]
30
+ getMapShape: Callable[[int], list[int]]
28
31
  offset: int
29
- valuesBenchmark: List[int]
30
- valuesKnown: Dict[int, int]
31
- valuesTestParallelization: List[int]
32
- valuesTestValidation: List[int]
32
+ valuesBenchmark: list[int]
33
+ valuesKnown: dict[int, int]
34
+ valuesTestParallelization: list[int]
35
+ valuesTestValidation: list[int]
33
36
  valueUnknown: int
34
37
 
35
- settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
38
+ settingsOEIShardcodedValues: dict[str, dict[str, Any]] = {
36
39
  'A001415': {
37
40
  'getMapShape': lambda n: sorted([2, n]),
38
41
  'valuesBenchmark': [14],
@@ -65,7 +68,7 @@ settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
65
68
  },
66
69
  }
67
70
 
68
- oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
71
+ oeisIDsImplemented: Final[list[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
69
72
  """Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
70
73
 
71
74
  def validateOEISid(oeisIDcandidate: str) -> str:
@@ -89,7 +92,7 @@ def validateOEISid(oeisIDcandidate: str) -> str:
89
92
  if oeisIDcandidate in oeisIDsImplemented:
90
93
  return oeisIDcandidate
91
94
  else:
92
- oeisIDcleaned = str(oeisIDcandidate).upper().strip()
95
+ oeisIDcleaned: str = str(oeisIDcandidate).upper().strip()
93
96
  if oeisIDcleaned in oeisIDsImplemented:
94
97
  return oeisIDcleaned
95
98
  else:
@@ -102,7 +105,7 @@ def getFilenameOEISbFile(oeisID: str) -> str:
102
105
  oeisID = validateOEISid(oeisID)
103
106
  return f"b{oeisID[1:]}.txt"
104
107
 
105
- def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
108
+ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> dict[int, int]:
106
109
  """
107
110
  Parses the content of an OEIS b-file for a given sequence ID.
108
111
 
@@ -121,12 +124,12 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
121
124
  ValueError: If the first line of the file does not indicate the expected
122
125
  sequence ID or if the content format is invalid.
123
126
  """
124
- bFileLines = OEISbFile.strip().splitlines()
127
+ bFileLines: list[str] = OEISbFile.strip().splitlines()
125
128
  if not bFileLines.pop(0).startswith(f"# {oeisID}"):
126
129
  warnings.warn(f"Content does not match sequence {oeisID}")
127
130
  return {-1: -1}
128
131
 
129
- OEISsequence = {}
132
+ OEISsequence: dict[int, int] = {}
130
133
  for line in bFileLines:
131
134
  if line.startswith('#'):
132
135
  continue
@@ -135,17 +138,16 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
135
138
  return OEISsequence
136
139
 
137
140
  def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
138
- cacheDays = 7
139
141
  tryCache = False
140
142
  if pathFilenameCache.exists():
141
- fileAge = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
142
- tryCache = fileAge < timedelta(days=cacheDays)
143
+ fileAge: timedelta = datetime.now() - datetime.fromtimestamp(pathFilenameCache.stat().st_mtime)
144
+ tryCache: bool = fileAge < timedelta(days=cacheDays)
143
145
 
144
- oeisInformation = None
146
+ oeisInformation: str | None = None
145
147
  if tryCache:
146
148
  try:
147
149
  oeisInformation = pathFilenameCache.read_text()
148
- except IOError:
150
+ except OSError:
149
151
  tryCache = False
150
152
 
151
153
  if not tryCache:
@@ -159,7 +161,7 @@ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
159
161
 
160
162
  return oeisInformation
161
163
 
162
- def getOEISidValues(oeisID: str) -> Dict[int, int]:
164
+ def getOEISidValues(oeisID: str) -> dict[int, int]:
163
165
  """
164
166
  Retrieves the specified OEIS sequence as a dictionary mapping integer indices
165
167
  to their corresponding values.
@@ -178,26 +180,26 @@ def getOEISidValues(oeisID: str) -> Dict[int, int]:
178
180
  IOError: If there is an error reading from or writing to the local cache.
179
181
  """
180
182
 
181
- pathFilenameCache = _pathCache / getFilenameOEISbFile(oeisID)
182
- url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
183
+ pathFilenameCache: Path = _pathCache / getFilenameOEISbFile(oeisID)
184
+ url: str = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
183
185
 
184
- oeisInformation = getOEISofficial(pathFilenameCache, url)
186
+ oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
185
187
 
186
188
  if oeisInformation:
187
189
  return _parseBFileOEIS(oeisInformation, oeisID)
188
190
  return {-1: -1}
189
191
 
190
- def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
192
+ def getOEISidInformation(oeisID: str) -> tuple[str, int]:
191
193
  oeisID = validateOEISid(oeisID)
192
- pathFilenameCache = _pathCache / f"{oeisID}.txt"
193
- url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
194
+ pathFilenameCache: Path = _pathCache / f"{oeisID}.txt"
195
+ url: str = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
194
196
 
195
- oeisInformation = getOEISofficial(pathFilenameCache, url)
197
+ oeisInformation: None | str = getOEISofficial(pathFilenameCache, url)
196
198
 
197
199
  if not oeisInformation:
198
200
  return "Not found", -1
199
201
 
200
- description_parts = []
202
+ description_parts: list[str] = []
201
203
  offset = None
202
204
  for line in oeisInformation.splitlines():
203
205
  if line.startswith('%N'):
@@ -206,9 +208,9 @@ def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
206
208
  desc_part = ' '.join(parts[2:])
207
209
  description_parts.append(desc_part)
208
210
  elif line.startswith('%O'):
209
- parts = line.split()
211
+ parts: list[str] = line.split()
210
212
  if parts[1] == oeisID:
211
- offset_str = parts[2].split(',')[0]
213
+ offset_str: str = parts[2].split(',')[0]
212
214
  offset = int(offset_str)
213
215
  if not description_parts:
214
216
  warnings.warn(f"No description found for {oeisID}")
@@ -216,13 +218,13 @@ def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
216
218
  if offset is None:
217
219
  warnings.warn(f"No offset found for {oeisID}")
218
220
  offset = -1
219
- description = ' '.join(description_parts)
221
+ description: str = ' '.join(description_parts)
220
222
  return description, offset
221
223
 
222
- def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
223
- settingsTarget = {}
224
+ def makeSettingsOEIS() -> dict[str, SettingsOEIS]:
225
+ settingsTarget: dict[str, SettingsOEIS] = {}
224
226
  for oeisID in oeisIDsImplemented:
225
- valuesKnownSherpa = getOEISidValues(oeisID)
227
+ valuesKnownSherpa: dict[int, int] = getOEISidValues(oeisID)
226
228
  descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
227
229
  settingsTarget[oeisID] = SettingsOEIS(
228
230
  description=descriptionSherpa,
@@ -236,7 +238,7 @@ def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
236
238
  )
237
239
  return settingsTarget
238
240
 
239
- settingsOEIS: Dict[str, SettingsOEIS] = makeSettingsOEIS()
241
+ settingsOEIS: dict[str, SettingsOEIS] = makeSettingsOEIS()
240
242
  """All values and settings for `oeisIDsImplemented`."""
241
243
 
242
244
  """
@@ -244,8 +246,8 @@ Section: private functions"""
244
246
 
245
247
  def _formatHelpText() -> str:
246
248
  """Format standardized help text for both CLI and interactive use."""
247
- exampleOEISid = oeisIDsImplemented[0]
248
- exampleN = settingsOEIS[exampleOEISid]['valuesTestValidation'][-1]
249
+ exampleOEISid: str = oeisIDsImplemented[0]
250
+ exampleN: int = settingsOEIS[exampleOEISid]['valuesTestValidation'][-1]
249
251
 
250
252
  return (
251
253
  "\nAvailable OEIS sequences:\n"
@@ -268,7 +270,7 @@ def _formatOEISsequenceInfo() -> str:
268
270
  """
269
271
  Section: public functions"""
270
272
 
271
- def oeisIDfor_n(oeisID: str, n: int) -> int:
273
+ def oeisIDfor_n(oeisID: str, n: int | Any) -> int:
272
274
  """
273
275
  Calculate a(n) of a sequence from "The On-Line Encyclopedia of Integer Sequences" (OEIS).
274
276
 
@@ -288,13 +290,13 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
288
290
  if not isinstance(n, int) or n < 0:
289
291
  raise ValueError("`n` must be non-negative integer.")
290
292
 
291
- listDimensions = settingsOEIS[oeisID]['getMapShape'](n)
293
+ listDimensions: list[int] = settingsOEIS[oeisID]['getMapShape'](n)
292
294
 
293
295
  if n <= 1 or len(listDimensions) < 2:
294
- offset = settingsOEIS[oeisID]['offset']
296
+ offset: int = settingsOEIS[oeisID]['offset']
295
297
  if n < offset:
296
298
  raise ArithmeticError(f"OEIS sequence {oeisID} is not defined at n={n}.")
297
- foldsTotal = settingsOEIS[oeisID]['valuesKnown'][n]
299
+ foldsTotal: int = settingsOEIS[oeisID]['valuesKnown'][n]
298
300
  return foldsTotal
299
301
 
300
302
  return countFolds(listDimensions)
@@ -309,9 +311,9 @@ def OEIS_for_n() -> None:
309
311
  parserCLI.add_argument('oeisID', help="OEIS sequence identifier")
310
312
  parserCLI.add_argument('n', type=int, help="Calculate a(n) for this n")
311
313
 
312
- argumentsCLI = parserCLI.parse_args()
314
+ argumentsCLI: argparse.Namespace = parserCLI.parse_args()
313
315
 
314
- timeStart = time.perf_counter()
316
+ timeStart: float = time.perf_counter()
315
317
 
316
318
  try:
317
319
  print(oeisIDfor_n(argumentsCLI.oeisID, argumentsCLI.n), "distinct folding patterns.")
@@ -319,7 +321,7 @@ def OEIS_for_n() -> None:
319
321
  print(f"Error: {ERRORmessage}", file=sys.stderr)
320
322
  sys.exit(1)
321
323
 
322
- timeElapsed = time.perf_counter() - timeStart
324
+ timeElapsed: float = time.perf_counter() - timeStart
323
325
  print(f"Time elapsed: {timeElapsed:.3f} seconds")
324
326
 
325
327
  def clearOEIScache() -> None: