mapFolding 0.4.1__py3-none-any.whl → 0.4.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/__init__.py CHANGED
@@ -11,6 +11,7 @@ from mapFolding.theSSOT import (
11
11
 
12
12
  # Datatype management
13
13
  from mapFolding.theSSOT import (
14
+ getDatatypeModule,
14
15
  hackSSOTdatatype,
15
16
  hackSSOTdtype,
16
17
  setDatatypeElephino,
@@ -21,7 +22,7 @@ from mapFolding.theSSOT import (
21
22
 
22
23
  # Synthesize modules
23
24
  from mapFolding.theSSOT import (
24
- formatModuleNameDEFAULT,
25
+ formatFilenameModuleDEFAULT,
25
26
  getAlgorithmDispatcher,
26
27
  getAlgorithmSource,
27
28
  getPathJobRootDEFAULT,
mapFolding/basecamp.py CHANGED
@@ -17,7 +17,7 @@ def countFolds(listDimensions: Sequence[int]
17
17
  computationDivisions (None):
18
18
  Whether and how to divide the computational work. See notes for details.
19
19
  CPUlimit (None): This is only relevant if there are `computationDivisions`: whether and how to limit the CPU usage. See notes for details.
20
- **keywordArguments: Datatype management.
20
+ **keywordArguments: Datatype management. See `outfitCountFolds` for details.
21
21
  Returns:
22
22
  foldsTotal: Total number of distinct ways to fold a map of the given dimensions.
23
23
 
mapFolding/beDRY.py CHANGED
@@ -1,17 +1,20 @@
1
1
  """A relatively stable API for oft-needed functionality."""
2
2
  from mapFolding import (
3
3
  computationState,
4
+ getDatatypeModule,
4
5
  getPathJobRootDEFAULT,
6
+ hackSSOTdatatype,
5
7
  hackSSOTdtype,
6
8
  indexMy,
7
9
  indexTrack,
8
10
  setDatatypeElephino,
9
11
  setDatatypeFoldsTotal,
10
12
  setDatatypeLeavesTotal,
13
+ setDatatypeModule,
11
14
  )
12
- from numpy import integer
15
+ from numpy import dtype, integer, ndarray
13
16
  from numpy.typing import DTypeLike, NDArray
14
- from typing import Any, List, Optional, Sequence, Tuple, Type, Union
17
+ from typing import Any, List, Optional, Sequence, Tuple, Union
15
18
  from Z0Z_tools import defineConcurrencyLimit, intInnit, oopsieKwargsie
16
19
  import numba
17
20
  import numpy
@@ -19,24 +22,26 @@ import os
19
22
  import pathlib
20
23
  import sys
21
24
 
22
- def getFilenameFoldsTotal(mapShape: Union[Sequence[int], numpy.ndarray[Tuple[int], numpy.dtype[integer[Any]]]]) -> str:
23
- """Make a standardized filename for the computed value `foldsTotal`.
25
+ def getFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]]) -> str:
26
+ """Imagine your computer has been counting folds for 70 hours, and when it tries to save your newly discovered value,
27
+ the filename is invalid. I bet you think this function is more important after that thought experiment.
28
+
29
+ Make a standardized filename for the computed value `foldsTotal`.
24
30
 
25
31
  The filename takes into account
26
32
  - the dimensions of the map, aka `mapShape`, aka `listDimensions`
27
33
  - no spaces in the filename
28
- - safe filesystem characters across platforms
34
+ - safe filesystem characters
29
35
  - unique extension
30
- - avoiding potential problems when Python is manipulating the filename, including
31
- - treating the file stem as a valid Python identifier, such as
32
- - not starting with a number
33
- - not using reserved words
34
- - no dashes or other special characters
35
- - uh, I can't remember, but I found some frustrating edge limitations
36
- - if 'p' is still the first character, I picked that because it was the original identifier for the map shape in Lunnan's code
36
+ - Python-safe strings:
37
+ - no starting with a number
38
+ - no reserved words
39
+ - no dashes or other special characters
40
+ - uh, I can't remember, but I found some other frustrating limitations
41
+ - if 'p' is still the first character of the filename, I picked that because it was the original identifier for the map shape in Lunnan's code
37
42
 
38
43
  Parameters:
39
- mapShape: A sequence of integers representing the dimensions of the map (e.g., [3, 2] for a 3x2 map)
44
+ mapShape: A sequence of integers representing the dimensions of the map.
40
45
 
41
46
  Returns:
42
47
  filenameFoldsTotal: A filename string in format 'pNxM.foldsTotal' where N,M are sorted dimensions
@@ -67,63 +72,65 @@ def getLeavesTotal(listDimensions: Sequence[int]) -> int:
67
72
 
68
73
  return productDimensions
69
74
 
70
- def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], numpy.ndarray[Tuple[int], numpy.dtype[integer[Any]]]], pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
71
- """Get path for folds total file.
75
+ def getPathFilenameFoldsTotal(mapShape: Union[Sequence[int], ndarray[Tuple[int], dtype[integer[Any]]]], pathLikeWriteFoldsTotal: Optional[Union[str, os.PathLike[str]]] = None) -> pathlib.Path:
76
+ """Get a standardized path and filename for the computed value `foldsTotal`.
72
77
 
73
- This function determines the file path for storing fold totals. If a path is provided,
74
- it will use that path. If the path is a directory, it will append a default filename.
75
- The function ensures the parent directory exists by creating it if necessary.
78
+ If you provide a directory, the function will append a standardized filename. If you provide a filename
79
+ or a relative path and filename, the function will prepend the default path.
76
80
 
77
81
  Parameters:
78
- mapShape (Sequence[int]): List of dimensions for the map folding problem.
79
- pathLikeWriteFoldsTotal (Union[str, os.PathLike[str]], optional): Path where to save
80
- the folds total. Can be a file path or directory path. If None, uses default path.
82
+ mapShape: List of dimensions for the map folding problem.
83
+ pathLikeWriteFoldsTotal (pathJobRootDEFAULT): Path, filename, or relative path and filename. If None, uses default path.
81
84
  Defaults to None.
82
85
 
83
86
  Returns:
84
- pathlib.Path: Complete path to the folds total file.
87
+ pathFilenameFoldsTotal: Absolute path and filename.
85
88
  """
86
- pathFilenameFoldsTotal = pathlib.Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else getPathJobRootDEFAULT()
87
- if pathFilenameFoldsTotal.is_dir():
88
- filenameFoldsTotalDEFAULT = getFilenameFoldsTotal(mapShape)
89
- pathFilenameFoldsTotal = pathFilenameFoldsTotal / filenameFoldsTotalDEFAULT
90
- elif pathlib.Path(pathLikeWriteFoldsTotal).is_absolute(): # type: ignore
91
- pathFilenameFoldsTotal = pathlib.Path(pathLikeWriteFoldsTotal) # type: ignore
89
+ pathLikeSherpa = pathlib.Path(pathLikeWriteFoldsTotal) if pathLikeWriteFoldsTotal is not None else None
90
+ if not pathLikeSherpa:
91
+ pathLikeSherpa = getPathJobRootDEFAULT()
92
+ if pathLikeSherpa.is_dir():
93
+ pathFilenameFoldsTotal = pathLikeSherpa / getFilenameFoldsTotal(mapShape)
94
+ elif pathLikeSherpa.is_absolute():
95
+ pathFilenameFoldsTotal = pathLikeSherpa
92
96
  else:
93
- pathFilenameFoldsTotal = pathlib.Path(getPathJobRootDEFAULT(), pathLikeWriteFoldsTotal) # type: ignore
97
+ pathFilenameFoldsTotal = getPathJobRootDEFAULT() / pathLikeSherpa
94
98
 
95
99
  pathFilenameFoldsTotal.parent.mkdir(parents=True, exist_ok=True)
96
100
  return pathFilenameFoldsTotal
97
101
 
98
102
  def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrencyLimit: int, CPUlimit: Optional[Union[bool, float, int]], listDimensions: Sequence[int]) -> int:
99
103
  """
100
- Determines whether or how to divide the computation into tasks.
104
+ Determines whether to divide the computation into tasks and how many divisions.
101
105
 
102
106
  Parameters
103
107
  ----------
104
- computationDivisions (None):
108
+ computationDivisions (None)
105
109
  Specifies how to divide computations:
106
- - None: no division of the computation into tasks; sets task divisions to 0
107
- - int: direct set the number of task divisions; cannot exceed the map's total leaves
108
- - "maximum": divides into `leavesTotal`-many `taskDivisions`
109
- - "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
110
- concurrencyLimit:
111
- Maximum number of concurrent tasks allowed
112
- CPUlimit: for error reporting
113
- listDimensions: for error reporting
110
+ - `None`: no division of the computation into tasks; sets task divisions to 0.
111
+ - int: direct set the number of task divisions; cannot exceed the map's total leaves.
112
+ - `'maximum'`: divides into `leavesTotal`-many `taskDivisions`.
113
+ - `'cpu'`: limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`.
114
+ concurrencyLimit
115
+ Maximum number of concurrent tasks allowed.
116
+ CPUlimit
117
+ for error reporting.
118
+ listDimensions
119
+ for error reporting.
114
120
 
115
121
  Returns
116
122
  -------
117
- taskDivisions:
123
+ taskDivisions
124
+ How many tasks must finish before the job can compute the total number of folds; `0` means no tasks, only job.
118
125
 
119
126
  Raises
120
127
  ------
121
128
  ValueError
122
- If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves
129
+ If computationDivisions is an unsupported type or if resulting task divisions exceed total leaves.
123
130
 
124
131
  Notes
125
132
  -----
126
- Task divisions cannot exceed total leaves to prevent duplicate counting of folds.
133
+ Task divisions should not exceed total leaves or the folds will be over-counted.
127
134
  """
128
135
  taskDivisions = 0
129
136
  leavesTotal = getLeavesTotal(listDimensions)
@@ -133,9 +140,9 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
133
140
  taskDivisions = computationDivisions
134
141
  elif isinstance(computationDivisions, str):
135
142
  computationDivisions = computationDivisions.lower()
136
- if computationDivisions == "maximum":
143
+ if computationDivisions == 'maximum':
137
144
  taskDivisions = leavesTotal
138
- elif computationDivisions == "cpu":
145
+ elif computationDivisions == 'cpu':
139
146
  taskDivisions = min(concurrencyLimit, leavesTotal)
140
147
  else:
141
148
  raise ValueError(f"I received {computationDivisions} for the parameter, `computationDivisions`, but the so-called programmer didn't implement code for that.")
@@ -145,7 +152,7 @@ def getTaskDivisions(computationDivisions: Optional[Union[int, str]], concurrenc
145
152
 
146
153
  return taskDivisions
147
154
 
148
- def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[Type]) -> numpy.ndarray[Tuple[int, int, int], numpy.dtype[integer[Any]]]:
155
+ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optional[str]) -> ndarray[Tuple[int, int, int], dtype[integer[Any]]]:
149
156
  """
150
157
  Constructs a multi-dimensional connection graph representing the connections between the leaves of a map with the given dimensions.
151
158
  Also called a Cartesian product decomposition or dimensional product mapping.
@@ -157,21 +164,22 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
157
164
  Returns
158
165
  connectionGraph: A 3D numpy array with shape of (dimensionsTotal, leavesTotal + 1, leavesTotal + 1).
159
166
  """
160
- if keywordArguments.get('datatype', None):
161
- setDatatypeLeavesTotal(keywordArguments['datatype']) # type: ignore
162
- datatype = hackSSOTdtype('connectionGraph')
167
+ ImaSetTheDatatype = keywordArguments.get('datatype', None)
168
+ if ImaSetTheDatatype:
169
+ setDatatypeLeavesTotal(ImaSetTheDatatype)
170
+ dtype = hackSSOTdtype('connectionGraph')
163
171
  mapShape = validateListDimensions(listDimensions)
164
172
  leavesTotal = getLeavesTotal(mapShape)
165
- arrayDimensions = numpy.array(mapShape, dtype=datatype)
173
+ arrayDimensions = numpy.array(mapShape, dtype=dtype)
166
174
  dimensionsTotal = len(arrayDimensions)
167
175
 
168
- cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=datatype)
169
- coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=datatype)
176
+ cumulativeProduct = numpy.multiply.accumulate([1] + mapShape, dtype=dtype)
177
+ coordinateSystem = numpy.zeros((dimensionsTotal, leavesTotal + 1), dtype=dtype)
170
178
  for indexDimension in range(dimensionsTotal):
171
179
  for leaf1ndex in range(1, leavesTotal + 1):
172
180
  coordinateSystem[indexDimension, leaf1ndex] = ( ((leaf1ndex - 1) // cumulativeProduct[indexDimension]) % arrayDimensions[indexDimension] + 1 )
173
181
 
174
- connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=datatype)
182
+ connectionGraph = numpy.zeros((dimensionsTotal, leavesTotal + 1, leavesTotal + 1), dtype=dtype)
175
183
  for indexDimension in range(dimensionsTotal):
176
184
  for activeLeaf1ndex in range(1, leavesTotal + 1):
177
185
  for connectee1ndex in range(1, activeLeaf1ndex + 1):
@@ -190,70 +198,68 @@ def makeConnectionGraph(listDimensions: Sequence[int], **keywordArguments: Optio
190
198
  return connectionGraph
191
199
 
192
200
  def makeDataContainer(shape: Union[int, Tuple[int, ...]], datatype: Optional[DTypeLike] = None) -> NDArray[integer[Any]]:
193
- """Create a zeroed-out `numpy.ndarray` with the given shape and datatype.
201
+ """Create a zeroed-out `ndarray` with the given shape and datatype.
194
202
 
195
203
  Parameters:
196
- shape (Union[int, Tuple[int, ...]]): The shape of the array. Can be an integer for 1D arrays
204
+ shape: The shape of the array. Can be an integer for 1D arrays
197
205
  or a tuple of integers for multi-dimensional arrays.
198
- datatype (Optional[DTypeLike], optional): The desired data type for the array.
199
- If None, defaults to dtypeLargeDEFAULT. Defaults to None.
206
+ datatype ('dtypeFoldsTotal'): The desired data type for the array.
207
+ If `None`, defaults to 'dtypeFoldsTotal'. Defaults to None.
200
208
 
201
209
  Returns:
202
- numpy.ndarray: A new array of given shape and type, filled with zeros.
210
+ dataContainer: A new array of given shape and type, filled with zeros.
211
+
212
+ Notes:
213
+ If a version of the algorithm were to use something other than numpy, such as JAX or CUDA, because other
214
+ functions use this function, it would be much easier to change the datatype "ecosystem".
203
215
  """
204
- if datatype is None:
205
- datatype = hackSSOTdtype('dtypeFoldsTotal')
206
- return numpy.zeros(shape, dtype=datatype)
207
-
208
- def outfitCountFolds(listDimensions: Sequence[int]
209
- , computationDivisions: Optional[Union[int, str]] = None
210
- , CPUlimit: Optional[Union[bool, float, int]] = None
211
- , **keywordArguments: Optional[Union[str, bool]]) -> computationState:
216
+ numpyDtype = datatype or hackSSOTdtype('dtypeFoldsTotal')
217
+ if 'numpy' == getDatatypeModule():
218
+ return numpy.zeros(shape, dtype=numpyDtype)
219
+ else:
220
+ raise NotImplementedError("Somebody done broke it.")
221
+
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:
212
223
  """
213
224
  Initializes and configures the computation state for map folding computations.
214
225
 
215
- Parameters
216
- ----------
217
- listDimensions:
218
- The dimensions of the map to be folded
219
- computationDivisions (None):
220
- Specifies how to divide computations:
221
- - None: no division of the computation into tasks; sets task divisions to 0
222
- - int: direct set the number of task divisions; cannot exceed the map's total leaves
223
- - "maximum": divides into `leavesTotal`-many `taskDivisions`
224
- - "cpu": limits the divisions to the number of available CPUs, i.e. `concurrencyLimit`
225
- CPUlimit (None):
226
- Whether and how to limit the CPU usage. See notes for details.
227
- **keywordArguments:
228
- Datatype management.
229
-
230
- Returns
231
- -------
232
- computationState
233
- An initialized computation state containing:
234
- - connectionGraph: Graph representing connections in the map
235
- - foldsSubTotals: Array tracking total folds
236
- - mapShape: Validated and sorted dimensions of the map
237
- - my: Array for internal state tracking
238
- - gapsWhere: Array tracking gap positions
239
- - the: Static settings and metadata
240
- - track: Array for tracking computation progress
226
+ Parameters:
227
+ listDimensions: The dimensions of the map to be folded
228
+ computationDivisions (None): see `getTaskDivisions`
229
+ CPUlimit (None): see `setCPUlimit`
230
+ **keywordArguments: Datatype management, it's complicated: see the code below.
241
231
 
242
- Limits on CPU usage `CPUlimit`:
243
- - `False`, `None`, or `0`: No limits on CPU usage; uses all available CPUs. All other values will potentially limit CPU usage.
244
- - `True`: Yes, limit the CPU usage; limits to 1 CPU.
245
- - Integer `>= 1`: Limits usage to the specified number of CPUs.
246
- - Decimal value (`float`) between 0 and 1: Fraction of total CPUs to use.
247
- - Decimal value (`float`) between -1 and 0: Fraction of CPUs to *not* use.
248
- - Integer `<= -1`: Subtract the absolute value from total CPUs.
232
+ Returns:
233
+ stateInitialized: The initialized computation state
249
234
  """
250
- kwourGrapes = keywordArguments.get('sourGrapes', False)
251
- kwatatype = keywordArguments.get('datatypeElephino', None)
252
- if kwatatype: setDatatypeElephino(kwatatype, sourGrapes=kwourGrapes) # type: ignore
253
- kwatatype = keywordArguments.get('datatypeFoldsTotal', None)
254
- if kwatatype: setDatatypeFoldsTotal(kwatatype, sourGrapes=kwourGrapes) # type: ignore
255
- kwatatype = keywordArguments.get('datatypeLeavesTotal', None)
256
- if kwatatype: setDatatypeLeavesTotal(kwatatype, sourGrapes=kwourGrapes) # type: ignore
235
+ # keywordArguments START
236
+ kwourGrapes = keywordArguments.get('sourGrapes', None)
237
+ if kwourGrapes:
238
+ sourGrapes = True
239
+ else:
240
+ sourGrapes = False
241
+
242
+ ImaSetTheDatatype = keywordArguments.get('datatypeElephino', None)
243
+ if ImaSetTheDatatype:
244
+ ImaSetTheDatatype = str(ImaSetTheDatatype)
245
+ setDatatypeElephino(ImaSetTheDatatype, sourGrapes)
246
+
247
+ ImaSetTheDatatype = keywordArguments.get('datatypeFoldsTotal', None)
248
+ if ImaSetTheDatatype:
249
+ ImaSetTheDatatype = str(ImaSetTheDatatype)
250
+ setDatatypeFoldsTotal(ImaSetTheDatatype, sourGrapes)
251
+
252
+ ImaSetTheDatatype = keywordArguments.get('datatypeLeavesTotal', None)
253
+ if ImaSetTheDatatype:
254
+ ImaSetTheDatatype = str(ImaSetTheDatatype)
255
+ setDatatypeLeavesTotal(ImaSetTheDatatype, sourGrapes)
256
+
257
+ # NOTE well: this might be only hypothetical because as of this writing, `makeDataContainer` only makes numpy.zeros. But it's here in case things change.
258
+ ImaSetTheDatatype = keywordArguments.get('datatypeModule', None)
259
+ if ImaSetTheDatatype:
260
+ ImaSetTheDatatype = str(ImaSetTheDatatype)
261
+ setDatatypeModule(ImaSetTheDatatype, sourGrapes)
262
+ # keywordArguments END
257
263
 
258
264
  my = makeDataContainer(len(indexMy), hackSSOTdtype('my'))
259
265
 
@@ -268,7 +274,7 @@ def outfitCountFolds(listDimensions: Sequence[int]
268
274
  my[indexMy.dimensionsTotal] = len(mapShape)
269
275
  my[indexMy.leaf1ndex] = 1
270
276
  stateInitialized = computationState(
271
- connectionGraph = makeConnectionGraph(mapShape, datatype=hackSSOTdtype('connectionGraph')),
277
+ connectionGraph = makeConnectionGraph(mapShape, datatype=hackSSOTdatatype('connectionGraph')),
272
278
  foldGroups = foldGroups,
273
279
  mapShape = numpy.array(mapShape, dtype=hackSSOTdtype('mapShape')),
274
280
  my = my,
@@ -280,16 +286,16 @@ def outfitCountFolds(listDimensions: Sequence[int]
280
286
 
281
287
  def parseDimensions(dimensions: Sequence[int], parameterName: str = 'listDimensions') -> List[int]:
282
288
  """
283
- Parse and validate dimensions are non-negative integers.
289
+ Parse and validate the dimensions are non-negative integers.
284
290
 
285
291
  Parameters:
286
- dimensions: Sequence of integers representing dimensions
287
- parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'
292
+ dimensions: Sequence of integers representing dimensions.
293
+ parameterName ('listDimensions'): Name of the parameter for error messages. Defaults to 'listDimensions'.
288
294
  Returns:
289
- listNonNegative: List of validated non-negative integers
295
+ listNonNegative: List of validated non-negative integers.
290
296
  Raises:
291
- ValueError: If any dimension is negative or if the list is empty
292
- TypeError: If any element cannot be converted to integer (raised by intInnit)
297
+ ValueError: If any dimension is negative or if the list is empty.
298
+ TypeError: If any element cannot be converted to integer (raised by `intInnit`).
293
299
  """
294
300
  listValidated = intInnit(dimensions, parameterName)
295
301
  listNonNegative = []
@@ -350,6 +356,7 @@ def setCPUlimit(CPUlimit: Optional[Any]) -> int:
350
356
 
351
357
  concurrencyLimit = int(defineConcurrencyLimit(CPUlimit))
352
358
  numba.set_num_threads(concurrencyLimit)
359
+ concurrencyLimit = numba.get_num_threads()
353
360
 
354
361
  return concurrencyLimit
355
362
 
@@ -364,7 +371,7 @@ def validateListDimensions(listDimensions: Sequence[int]) -> List[int]:
364
371
  dimensionsValidSorted: A list, with at least two elements, of only positive integers.
365
372
 
366
373
  Raises:
367
- ValueError: If the input listDimensions is None.
374
+ ValueError: If the input listDimensions is empty.
368
375
  NotImplementedError: If the resulting list of positive dimensions has fewer than two elements.
369
376
  """
370
377
  if not listDimensions:
mapFolding/oeis.py CHANGED
@@ -2,7 +2,7 @@
2
2
  _only_ things that implement _only_ the OEIS."""
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, Union
5
+ from typing import Any, Callable, Dict, Final, List, Tuple, TYPE_CHECKING
6
6
  import argparse
7
7
  import pathlib
8
8
  import random
@@ -24,8 +24,8 @@ _pathCache = getPathPackage() / ".cache"
24
24
 
25
25
  class SettingsOEIS(TypedDict):
26
26
  description: str
27
- offset: int
28
27
  getMapShape: Callable[[int], List[int]]
28
+ offset: int
29
29
  valuesBenchmark: List[int]
30
30
  valuesKnown: Dict[int, int]
31
31
  valuesTestParallelization: List[int]
@@ -68,7 +68,7 @@ settingsOEIShardcodedValues: Dict[str, Dict[str, Any]] = {
68
68
  oeisIDsImplemented: Final[List[str]] = sorted([oeisID.upper().strip() for oeisID in settingsOEIShardcodedValues.keys()])
69
69
  """Directly implemented OEIS IDs; standardized, e.g., 'A001415'."""
70
70
 
71
- def _validateOEISid(oeisIDcandidate: str) -> str:
71
+ def validateOEISid(oeisIDcandidate: str) -> str:
72
72
  """
73
73
  Validates an OEIS sequence ID against implemented sequences.
74
74
 
@@ -98,13 +98,14 @@ def _validateOEISid(oeisIDcandidate: str) -> str:
98
98
  f"Available sequences:\n{_formatOEISsequenceInfo()}"
99
99
  )
100
100
 
101
- def _getFilenameOEISbFile(oeisID: str) -> str:
102
- oeisID = _validateOEISid(oeisID)
101
+ def getFilenameOEISbFile(oeisID: str) -> str:
102
+ oeisID = validateOEISid(oeisID)
103
103
  return f"b{oeisID[1:]}.txt"
104
104
 
105
105
  def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
106
106
  """
107
107
  Parses the content of an OEIS b-file for a given sequence ID.
108
+
108
109
  This function processes a multiline string representing an OEIS b-file and
109
110
  creates a dictionary mapping integer indices to their corresponding sequence
110
111
  values. The first line of the b-file is expected to contain a comment that
@@ -133,7 +134,7 @@ def _parseBFileOEIS(OEISbFile: str, oeisID: str) -> Dict[int, int]:
133
134
  OEISsequence[n] = aOFn
134
135
  return OEISsequence
135
136
 
136
- def _getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
137
+ def getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
137
138
  cacheDays = 7
138
139
  tryCache = False
139
140
  if pathFilenameCache.exists():
@@ -158,7 +159,7 @@ def _getOEISofficial(pathFilenameCache: pathlib.Path, url: str) -> None | str:
158
159
 
159
160
  return oeisInformation
160
161
 
161
- def _getOEISidValues(oeisID: str) -> Dict[int, int]:
162
+ def getOEISidValues(oeisID: str) -> Dict[int, int]:
162
163
  """
163
164
  Retrieves the specified OEIS sequence as a dictionary mapping integer indices
164
165
  to their corresponding values.
@@ -177,21 +178,21 @@ def _getOEISidValues(oeisID: str) -> Dict[int, int]:
177
178
  IOError: If there is an error reading from or writing to the local cache.
178
179
  """
179
180
 
180
- pathFilenameCache = _pathCache / _getFilenameOEISbFile(oeisID)
181
- url = f"https://oeis.org/{oeisID}/{_getFilenameOEISbFile(oeisID)}"
181
+ pathFilenameCache = _pathCache / getFilenameOEISbFile(oeisID)
182
+ url = f"https://oeis.org/{oeisID}/{getFilenameOEISbFile(oeisID)}"
182
183
 
183
- oeisInformation = _getOEISofficial(pathFilenameCache, url)
184
+ oeisInformation = getOEISofficial(pathFilenameCache, url)
184
185
 
185
186
  if oeisInformation:
186
187
  return _parseBFileOEIS(oeisInformation, oeisID)
187
188
  return {-1: -1}
188
189
 
189
- def _getOEISidInformation(oeisID: str) -> Tuple[str, int]:
190
- oeisID = _validateOEISid(oeisID)
190
+ def getOEISidInformation(oeisID: str) -> Tuple[str, int]:
191
+ oeisID = validateOEISid(oeisID)
191
192
  pathFilenameCache = _pathCache / f"{oeisID}.txt"
192
193
  url = f"https://oeis.org/search?q=id:{oeisID}&fmt=text"
193
194
 
194
- oeisInformation = _getOEISofficial(pathFilenameCache, url)
195
+ oeisInformation = getOEISofficial(pathFilenameCache, url)
195
196
 
196
197
  if not oeisInformation:
197
198
  return "Not found", -1
@@ -221,8 +222,8 @@ def _getOEISidInformation(oeisID: str) -> Tuple[str, int]:
221
222
  def makeSettingsOEIS() -> Dict[str, SettingsOEIS]:
222
223
  settingsTarget = {}
223
224
  for oeisID in oeisIDsImplemented:
224
- valuesKnownSherpa = _getOEISidValues(oeisID)
225
- descriptionSherpa, offsetSherpa = _getOEISidInformation(oeisID)
225
+ valuesKnownSherpa = getOEISidValues(oeisID)
226
+ descriptionSherpa, offsetSherpa = getOEISidInformation(oeisID)
226
227
  settingsTarget[oeisID] = SettingsOEIS(
227
228
  description=descriptionSherpa,
228
229
  offset=offsetSherpa,
@@ -282,7 +283,7 @@ def oeisIDfor_n(oeisID: str, n: int) -> int:
282
283
  ValueError: If n is negative.
283
284
  KeyError: If the OEIS sequence ID is not directly implemented.
284
285
  """
285
- oeisID = _validateOEISid(oeisID)
286
+ oeisID = validateOEISid(oeisID)
286
287
 
287
288
  if not isinstance(n, int) or n < 0:
288
289
  raise ValueError("`n` must be non-negative integer.")
@@ -328,7 +329,7 @@ def clearOEIScache() -> None:
328
329
  return
329
330
  for oeisID in settingsOEIS:
330
331
  ( _pathCache / f"{oeisID}.txt" ).unlink(missing_ok=True)
331
- ( _pathCache / _getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
332
+ ( _pathCache / getFilenameOEISbFile(oeisID) ).unlink(missing_ok=True)
332
333
  print(f"Cache cleared from {_pathCache}")
333
334
 
334
335
  def getOEISids() -> None: