Skip to content

Commit 2982163

Browse files
author
Release Manager
committed
sagemathgh-41013: make use of the b-file fixes sagemath#41005 When requesting more terms, we check whether the b-file of the oeis provides them. URL: sagemath#41013 Reported by: Martin Rubey Reviewer(s):
2 parents 10351f6 + 9ed0b41 commit 2982163

File tree

1 file changed

+116
-15
lines changed

1 file changed

+116
-15
lines changed

src/sage/databases/oeis.py

Lines changed: 116 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -147,14 +147,16 @@
147147

148148
from sage.version import version
149149
from sage.cpython.string import bytes_to_str
150-
from sage.misc.cachefunc import cached_method
151150
from sage.misc.flatten import flatten
152151
from sage.misc.html import HtmlFragment
153152
from sage.misc.temporary_file import tmp_filename
154153
from sage.misc.unknown import Unknown
155154
from sage.misc.verbose import verbose
156155
from sage.repl.preparse import preparse
157156
from sage.rings.integer import Integer
157+
from sage.rings.integer_ring import ZZ
158+
from sage.rings.infinity import infinity
159+
from sage.structure.global_options import GlobalOptions
158160
from sage.structure.sage_object import SageObject
159161
from sage.structure.unique_representation import UniqueRepresentation
160162

@@ -246,7 +248,7 @@ class OEIS:
246248
- a list representing a sequence of integers.
247249
- a string, representing a text search.
248250
249-
- ``max_results`` -- integer (default: 30); the maximum number of
251+
- ``max_results`` -- integer (default: 3); the maximum number of
250252
results to return, they are sorted according to their relevance. In
251253
any cases, the OEIS website will never provide more than 100 results.
252254
@@ -255,6 +257,8 @@ class OEIS:
255257
This is useful if you are looking for a sequence that may appear
256258
after the 100 first found sequences.
257259
260+
``max_results`` can also be set using :meth:`options`.
261+
258262
OUTPUT:
259263
260264
- if ``query`` is an integer or an OEIS ID (e.g. 'A000045'), returns
@@ -361,8 +365,7 @@ class OEIS:
361365
sage: oeis('A000045') # optional -- internet
362366
A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1.
363367
"""
364-
365-
def __call__(self, query, max_results=3, first_result=0):
368+
def __call__(self, query, max_results=None, first_result=0):
366369
r"""
367370
See the documentation of :class:`OEIS`.
368371
@@ -383,6 +386,46 @@ def __call__(self, query, max_results=3, first_result=0):
383386
elif isinstance(query, (list, tuple)):
384387
return self.find_by_subsequence(query, max_results, first_result)
385388

389+
class options(GlobalOptions):
390+
r"""
391+
Set and display the options for the OEIS.
392+
393+
If no parameters are set, then the function returns a copy of
394+
the options dictionary.
395+
396+
The ``options`` can be accessed as using
397+
:class:`oeis.options`.
398+
399+
@OPTIONS@
400+
401+
EXAMPLES::
402+
403+
sage: oeis.options
404+
Current options for OEIS
405+
- fetch_b_file: False
406+
- max_results: 3
407+
408+
sage: oeis.options.max_results = 5
409+
sage: oeis('beaver') # optional -- internet
410+
0: A...: ...eaver...
411+
1: A...: ...eaver...
412+
2: A...: ...eaver...
413+
3: A...: ...eaver...
414+
4: A...: ...eaver...
415+
416+
sage: oeis.options._reset()
417+
sage: oeis.options.max_results
418+
3
419+
"""
420+
NAME = 'OEIS'
421+
module = 'sage.databases.oeis'
422+
max_results = dict(default=3,
423+
description='the maximum number of results to return',
424+
checker=lambda x: x in ZZ and x > 0)
425+
fetch_b_file = dict(default=False,
426+
description='whether to fetch terms from the b-file by default',
427+
checker=lambda x: isinstance(x, bool))
428+
386429
def __repr__(self) -> str:
387430
r"""
388431
Return the representation of ``self``.
@@ -440,7 +483,7 @@ def find_by_entry(self, entry):
440483
sequence._raw = entry
441484
return sequence
442485

443-
def find_by_description(self, description, max_results=3, first_result=0):
486+
def find_by_description(self, description, max_results=None, first_result=0):
444487
r"""
445488
Search for OEIS sequences corresponding to the description.
446489
@@ -483,7 +526,19 @@ def find_by_description(self, description, max_results=3, first_result=0):
483526
1: A...: ...eaver...
484527
2: A...: ...eaver...
485528
3: A...: ...eaver...
529+
530+
Alternatively, we can also set the global option `max_results`::
531+
532+
sage: oeis.options.max_results = 5
533+
sage: oeis('beaver') # optional -- internet
534+
0: A...: ...eaver...
535+
1: A...: ...eaver...
536+
2: A...: ...eaver...
537+
3: A...: ...eaver...
538+
4: A...: ...eaver...
486539
"""
540+
if max_results is None:
541+
max_results = self.options['max_results']
487542
options = {'q': description,
488543
'n': str(max_results),
489544
'fmt': 'text',
@@ -493,7 +548,7 @@ def find_by_description(self, description, max_results=3, first_result=0):
493548
T = [self.find_by_entry(entry=s) for s in sequence_list]
494549
return FancyTuple([s for s in T if not s.is_dead()])
495550

496-
def find_by_subsequence(self, subsequence, max_results=3, first_result=0):
551+
def find_by_subsequence(self, subsequence, max_results=None, first_result=0):
497552
r"""
498553
Search for OEIS sequences containing the given subsequence.
499554
@@ -689,7 +744,7 @@ def __init__(self, ident):
689744

690745
def online_update(self):
691746
r"""
692-
Fetch the online OEIS to update the informations about this sequence.
747+
Fetch the sequence from the OEIS.
693748
694749
TESTS::
695750
@@ -1205,15 +1260,17 @@ def is_full(self):
12051260
else:
12061261
return Unknown
12071262

1208-
@cached_method
12091263
def first_terms(self, number=None):
12101264
r"""
1265+
Return the first few terms of the sequence.
12111266
12121267
INPUT:
12131268
1214-
- ``number`` -- integer or ``None`` (default); the number of
1215-
terms returned (if less than the number of available terms). When set
1216-
to ``None``, returns all the known terms.
1269+
- ``number`` -- integer, ``infinity`` or ``None`` (default);
1270+
the number of terms returned. When set to ``None``,
1271+
returns all the known terms. When set to an integer larger
1272+
than the number of terms in the internal format, or to
1273+
``infinity``, the b-file is fetched.
12171274
12181275
OUTPUT: tuple of integers
12191276
@@ -1237,9 +1294,54 @@ def first_terms(self, number=None):
12371294
(1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
12381295
sage: s.first_terms(5)
12391296
(1, 1, 1, 1, 2)
1297+
1298+
sage: len(oeis(45).first_terms()) # optional -- internet
1299+
41
1300+
sage: len(oeis(45).first_terms(oo)) # optional -- internet
1301+
2001
1302+
1303+
sage: len(oeis(1).first_terms()) # optional -- internet
1304+
94
1305+
1306+
sage: oeis.options.fetch_b_file = True
1307+
sage: len(oeis(1).first_terms()) # optional -- internet
1308+
2048
1309+
sage: oeis.options._reset()
12401310
"""
1241-
fields = ['S', 'T', 'U']
1242-
return to_tuple(" ".join(flatten([self._field(a) for a in fields])))[:number]
1311+
def fetch_b_file():
1312+
url = oeis_url + f"b{self.id(format='int')}.txt"
1313+
terms = _fetch(url)
1314+
first_terms = tuple()
1315+
check = None
1316+
for term in terms.split('\n'):
1317+
if not term or term[0] == '#':
1318+
continue
1319+
k, v = [Integer(e) for e in term.strip().split()]
1320+
if check is not None and k != check + 1:
1321+
raise ValueError(f"malformed b-file {url}: key {check} followed by {k}")
1322+
check = k
1323+
first_terms += (v,)
1324+
self._first_terms = True, first_terms
1325+
1326+
if ((number is infinity or oeis.options['fetch_b_file'])
1327+
and self is not oeis._imaginary_sequence()): # all other sequences have a b-file
1328+
# self._first_terms is a pair (all?, first_terms)
1329+
if not hasattr(self, "_first_terms") or not self._first_terms[0]:
1330+
fetch_b_file()
1331+
return self._first_terms[1]
1332+
1333+
if not hasattr(self, "_first_terms"):
1334+
fields = ['S', 'T', 'U']
1335+
first_terms = to_tuple(" ".join(flatten([self._field(a) for a in fields])))
1336+
self._first_terms = (False, first_terms)
1337+
1338+
if number is None:
1339+
return self._first_terms[1]
1340+
1341+
if number > len(self._first_terms[1]) and not self._first_terms[0]:
1342+
fetch_b_file()
1343+
1344+
return self._first_terms[1][:number]
12431345

12441346
def _repr_(self):
12451347
r"""
@@ -1755,8 +1857,7 @@ def show(self):
17551857
https://oeis.org/A012345
17561858
<BLANKLINE>
17571859
AUTHOR
1758-
Patrick Demichel (patrick.demichel(AT)hp.com)
1759-
<BLANKLINE>
1860+
...
17601861
17611862
TESTS::
17621863

0 commit comments

Comments
 (0)