Ticket #7749: trac_7749.patch

File trac_7749.patch, 11.5 KB (added by ssivek, 13 years ago)
  • sage/databases/sloane.py

    # HG changeset patch
    # User Steven Sivek <ssivek@math.mit.edu>
    # Date 1261540804 18000
    # Node ID 1b63ab45fd6fbcd6b36cf69c4a0c3c075dfecd81
    # Parent  138245b5dfbfe306ed84da48348b1272d5923116
    Added support for names.gz database and converted sequences from ints to Integers.
    
    diff -r 138245b5dfbf -r 1b63ab45fd6f sage/databases/sloane.py
    a b  
    5555and calling the functions again may fix the problem.
    5656
    5757Alternatively, the SloaneEncyclopedia object provides access to a
    58 local copy of the database containing only the sequences. To use
    59 this you must install the optional database_sloane_oeis-2005-12
    60 package using ``sage -i
    61 database_sloane_oeis-2005-12``.
     58local copy of the database containing only the sequences and their
     59names. To use this you must download and install the database using
     60``SloaneEncyclopedia.install()``, or
     61``SloaneEncyclopedia.install_from_gz()`` if you have already
     62downloaded the database manually.
    6263
    6364To look up a sequence, type
    6465
     
    6768    sage: SloaneEncyclopedia[60843]               # optional - sloane_database
    6869    [1, 6, 21, 107]
    6970
     71To get the name of a sequence, type
     72
     73::
     74
     75    sage: SloaneEncyclopedia.sequence_name(1)     # optional - sloane_database
     76    'Number of groups of order n.'
     77
    7078To search locally for a particular subsequence, type
    7179
    7280::
     
    108116  from string form to a list of integers until absolutely necessary.
    109117  This seems to cut the loading time roughly in half.
    110118
     119- Steven Sivek (2009-12-22): added the SloaneEncyclopedia functions
     120  install() and install_from_gz() so users can get the latest versions
     121  of the OEIS without having to get an updated spkg; added
     122  sequence_name() to return the description of a sequence; and changed
     123  the data type for elements of each sequence from int to Integer.
     124
    111125"""
    112126
    113127#*****************************************************************************
     
    120134#                  http://www.gnu.org/licenses/
    121135#*****************************************************************************
    122136
    123 import bz2, gzip, os, re, urllib
     137import bz2, os, re, urllib
    124138
    125139from sage.misc.all import verbose
    126140import sage.rings.integer_ring
     
    138152        """
    139153        self.__path__ = "%s/data/sloane/"%os.environ["SAGE_ROOT"]
    140154        self.__file__ = "%ssloane-oeis.bz2"%self.__path__
     155        self.__file_names__ = "%ssloane-names.bz2"%self.__path__
    141156        self.__loaded__ = False
     157        self.__loaded_names__ = False
    142158
    143159    def __repr__(self):
    144160        """
     
    172188            return []
    173189        if self.__data__[N][1] is None: # list N has not been created yet
    174190            list = self.__data__[N][2].strip(',').split(',')
    175             self.__data__[N][1] = [int(n) for n in list]
     191            self.__data__[N][1] = [ZZ(n) for n in list]
    176192        return self.__data__[N][1]
    177193
    178194    def __len__(self):
     
    213229
    214230        return answer
    215231
    216     def install(self, oeis_url="http://www.research.att.com/~njas/sequences/stripped.gz", overwrite=False):
     232    def install(self, oeis_url="http://www.research.att.com/~njas/sequences/stripped.gz", names_url="http://www.research.att.com/~njas/sequences/names.gz", overwrite=False):
    217233        """
    218234        Download and install the online encyclopedia, raising an IOError if
    219235        either step fails.
     
    223239        - ``oeis_url`` - string (default: "http://www.research.att.com...")
    224240          The URL of the stripped.gz encyclopedia file.
    225241
     242        - ``names_url`` - string (default: "http://www.research.att.com...")
     243          The URL of the names.gz encyclopedia file.  If you do not want to
     244          download this file, set names_url=None.
     245
    226246        - ``overwrite`` - boolean (default: False) If the encyclopedia is
    227247          already installed and overwrite=True, download and install the latest
    228248          version over the installed one.
     
    231251        if not overwrite and os.path.exists(self.__file__):
    232252            raise IOError, "Sloane encyclopedia is already installed"
    233253           
     254        tm = verbose("Downloading stripped version of Sloane encyclopedia")
    234255        try:
    235             tm = verbose("Downloading stripped version of Sloane encyclopedia")
    236256            fname, _ = urllib.urlretrieve(oeis_url);
    237             verbose("Finished downloading", tm)
    238257        except IOError, msg:
    239258            raise IOError, "%s\nError fetching the following website:\n    %s\nTry checking your internet connection."%(msg, oeis_url)
    240259
    241         self.install_from_gz(fname, overwrite)
    242         os.remove(fname) # delete the temporary downloaded file
     260        if not names_url is None:
     261            try:
     262                nname, _ = urllib.urlretrieve(names_url);
     263            except IOError, msg:
     264                raise IOError, "%s\nError fetching the following website:\n    %s\nTry checking your internet connection."%(msg, names_url)
     265        else:
     266            nname = None
     267        verbose("Finished downloading", tm)
    243268
    244     def install_from_gz(self, filename, overwrite=False):
     269        self.install_from_gz(fname, nname, overwrite)
     270        # Delete the temporary downloaded files
     271        os.remove(fname)
     272        if not nname is None:
     273            os.remove(nname)
     274
     275    def install_from_gz(self, stripped_file, names_file, overwrite=False):
    245276        """
    246277        Install the online encyclopedia from a local stripped.gz file.
    247278
    248279        INPUT:
    249280
    250         - ``filename`` - string. The name of the stripped.gz encyclopedia file.
     281        - ``stripped_file`` - string. The name of the stripped.gz OEIS file.
     282
     283        - ``names_file`` - string.  The name of the names.gz OEIS file, or
     284          None if the user does not want it installed.
    251285
    252286        - ``overwrite`` - boolean (default: False) If the encyclopedia is
    253287          already installed and overwrite=True, install 'filename' over the
     
    255289        """
    256290        if not overwrite and os.path.exists(self.__file__):
    257291            raise IOError, "Sloane encyclopedia is already installed"
     292
     293        copy_gz_file(stripped_file, self.__file__)
     294
     295        if not names_file is None:
     296            copy_gz_file(names_file, self.__file_names__)
     297        else:
     298            # Delete old copies of names.gz since their sequence numbers
     299            # probably won't match the newly installed stripped.gz
     300            if os.path.exists(self.__file_names__):
     301                os.remove(self.__file_names__)
     302
     303        # Remove the old database from memory so the new one will be
     304        # automatically loaded next time the user tries to access it
     305        self.unload()
    258306       
    259         # Decompress stripped.gz
    260         try:
    261             gz_input = gzip.open(filename, 'r')
    262             db_text = gz_input.read()
    263             gz_input.close()
    264         except IOError, msg:
    265             raise IOError, "Error reading gzipped input file:\n%s"%msg
    266 
    267         # Write to the database file
    268         try:
    269             if not os.path.exists(self.__path__):
    270                 os.mkdir(self.__path__)
    271             bz2_output = bz2.BZ2File(self.__file__, 'w')
    272             bz2_output.write(db_text)
    273             bz2_output.close()
    274         except IOError, msg:
    275             raise IOError, "Error writing database file:\n%s"%msg
    276307
    277308    def load(self):
    278309        """
     
    282313        if self.__loaded__ == True:
    283314            return
    284315        try:
    285             file = bz2.BZ2File(self.__file__, 'r')
     316            file_seq = bz2.BZ2File(self.__file__, 'r')
    286317        except IOError:
    287318            raise IOError, "The Sloane Encyclopedia database must be installed.  Use e.g. 'SloaneEncyclopedia.install()' to download and install it."
    288319
     320        self.__data__ = {}
     321
     322        tm = verbose("Loading Sloane encyclopedia from disk")
    289323        entry = re.compile(r'A(?P<num>\d{6}) ,(?P<body>.*),$');
    290         self.__data__ = {}
    291         tm = verbose("Loading Sloane encyclopedia from disk")
    292         for L in file:
     324        for L in file_seq:
    293325            if len(L) == 0:
    294326                continue
    295327            m = entry.search(L)
    296328            if m:
    297                 seqnum = int(m.group('num'));
    298                 msg = m.group('body').strip();
    299                 self.__data__[seqnum] = [seqnum, None, ','+msg+',']
     329                seqnum = int(m.group('num'))
     330                msg = m.group('body').strip()
     331                self.__data__[seqnum] = [seqnum, None, ','+msg+',', None]
     332        file_seq.close()
     333
     334        try:
     335            file_names = bz2.BZ2File(self.__file_names__, 'r')
     336            entry = re.compile(r'A(?P<num>\d{6}) (?P<body>.*)$');
     337            for L in file_names:
     338                if len(L) == 0: continue
     339                m = entry.search(L)
     340                if m:
     341                    seqnum = int(m.group('num'))
     342                    self.__data__[seqnum][3] = m.group('body').strip()
     343            file_names.close()
     344            self.__loaded_names__ = True
     345        except KeyError:
     346            ### Some sequence in the names file isn't in the database
     347            raise KeyError, "Sloane OEIS sequence and name files do not match.  Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True)."
     348        except IOError, msg:
     349            ### The names database is not installed
     350            self.__loaded_names__ = False
     351
    300352        verbose("Finished loading", tm)
    301353        self.__loaded__ = True
    302354
     355    def sequence_name(self, N):
     356        """
     357        Return the name of sequence N in the encyclopedia. If sequence N
     358        does not exist, return ''.  If the names database is not installed,
     359        raise an IOError.
     360       
     361        INPUT:
     362               
     363        -  ``N`` - int
     364       
     365        OUTPUT: string
     366
     367        EXAMPLES:
     368   
     369        sage: SloaneEncyclopedia.sequence_name(1) # optional - sloane_database
     370        'Number of groups of order n.'
     371        """
     372        self.load()
     373        if not self.__loaded_names__:
     374            raise IOError, "The Sloane OEIS names file is not installed.  Try reinstalling, e.g. SloaneEncyclopedia.install(overwrite=True)."
     375
     376        if not N in self.__data__: # sequence N does not exist
     377            return ''
     378        return self.__data__[N][3]
     379
    303380    def unload(self):
    304381        """
    305382        Remove the database from memory.
     
    308385            return
    309386        del self.__data__
    310387        self.__loaded__ = False
     388        self.__loaded_names__ = False
    311389
    312390SloaneEncyclopedia = SloaneEncyclopediaClass()
    313391
     392def copy_gz_file(gz_source, bz_destination):
     393    """
     394    Decompress a gzipped file and install the bzipped verson.  This is
     395    used by SloaneEncyclopedia.install_from_gz to install several
     396    gzipped OEIS database files.
     397   
     398    INPUT:
     399
     400    - ``gz_source`` - string. The name of the gzipped file.
     401   
     402    - ``bz_destination`` - string.  The name of the newly compressed file.
     403    """
     404    import gzip
     405       
     406    # Read the gzipped input
     407    try:
     408        gz_input = gzip.open(gz_source, 'r')
     409        db_text = gz_input.read()
     410        gz_input.close()
     411    except IOError, msg:
     412        raise IOError, "Error reading gzipped input file:\n%s"%msg
     413
     414    # Write the bzipped output
     415    try:
     416        if not os.path.exists(os.path.dirname(bz_destination)):
     417            os.mkdir(os.path.dirname(bz_destination))
     418        bz2_output = bz2.BZ2File(bz_destination, 'w')
     419        bz2_output.write(db_text)
     420        bz2_output.close()
     421    except IOError, msg:
     422        raise IOError, "Error writing bzipped output file:\n%s"%msg
     423
     424
    314425def parse_sequence(text):
    315426    entry = re.compile(r'%(?P<letter>[A-Za-z]) A(?P<num>\d{6}) (?P<body>.*)$')
    316427    unsigned, signed, list = [], [], []