  
  [1XA Some Tools for Database Handling[0X
  
  Two aims of the tools described in this appendix are
  
  --    speeding   up   selection  functions  such  as  [2XAllCharacterTableNames[0m
        ([14XCTblLib:  AllCharacterTableNames[0m)  for  certain data libraries of [5XGAP[0m
        (with  not  too  many entries), in the sense that users can extend the
        list of attributes that are treated in a special way
  
  --    and  a  programmatic  extension for rendering overviews of information
        about        the        contents       of       databases,       using
        [2XBrowseTableFromDatabaseIdEnumerator[0m ([14XA.2-2[0m).
  
  The  [5XGAP[0m objects introduced for that are [13Xdatabase id enumerators[0m (see [14XA.1-1[0m)
  and [13Xdatabase attributes[0m (see [14XA.1-2[0m).
  
  Contrary  to the individual interfaces to the [5XGAP[0m manuals (see Section [14X6.5[0m),
  the  [5XGAP[0m  bibliography (see Section [14X6.6[0m), and the overviews of [5XGAP[0m packages,
  [5XGAP[0m  methods, and Conway polynomials available in [5XGAP[0m (see Section [14X6.7[0m), the
  approach  that  will  be  described  here  assumes  a  special way to access
  database  entries.  Thus  it  depends  on  the structure of a given database
  whether  the  tools  described  here  are  useful,  or whether an individual
  interface  fits  better.  Perhaps the examples shown in Sections [14XA.3[0m and [14XA.4[0m
  give an impression what is possible.
  
  
  [1XA.1 [5XGAP[1X Objects for Database Handling[0X
  
  
  [1XA.1-1 Database Id Enumerators[0X
  
  A  [13Xdatabase  id  enumerator[0m  is  a  record  [3Xr[0m  with  at  least the following
  components.
  
  [8X[10Xidentifiers[0m[8X[0m
        a  list  of  "identifiers"  of  the database entries, which provides a
        bijection with these entries,
  
  [8X[10Xentry[0m[8X[0m
        a  function  that  takes  [3Xr[0m  and an entry in the [10Xidentifiers[0m list, and
        returns the corresponding database entry,
  
  [8X[10Xattributes[0m[8X[0m
        the  record  whose  components are the database attribute records (see
        Section  [14XA.1-2[0m)  for  [3Xr[0m;  this components is automatically initialized
        when   [3Xr[0m   is  created  with  [2XDatabaseIdEnumerator[0m  ([14XA.1-4[0m);  database
        attributes can be entered with [2XDatabaseAttributeAdd[0m ([14XA.1-5[0m).
  
  If  the  [10Xidentifiers[0m  list may change over the time (because the database is
  extended or corrected) then the following components are supported. They are
  used by [2XDatabaseIdEnumeratorUpdate[0m ([14XA.1-7[0m).
  
  [8X[10Xversion[0m[8X[0m
        a  [5XGAP[0m object that describes the version of the [10Xidentifiers[0m component,
        this  can  be  for  example  a  string describing the time of the last
        change (this time need not coincide with the time of the last update);
        the  default  value  (useful  only  for  the case that the [10Xidentifiers[0m
        component is never changed) is an empty string,
  
  [8X[10Xupdate[0m[8X[0m
        a  function that takes [3Xr[0m as its argument, replaces its [10Xidentifiers[0m and
        [10Xversion[0m  values  by  up-to-date  versions if necessary (for example by
        downloading the data), and returns [9Xtrue[0m or [9Xfalse[0m, depending on whether
        the  update  process  was  successful  or  not;  the  default value is
        [2XReturnTrue[0m ([14XReference: ReturnTrue[0m),
  
  The following component is optional.
  
  [8X[10XisSorted[0m[8X[0m
        [9Xtrue[0m  means  that the [10Xidentifiers[0m list is sorted w.r.t. [5XGAP[0m's ordering
        [10X\<[0m; the default is [9Xfalse[0m.
  
  The  idea  behind  database  id  enumerator  objects  is that such an object
  defines  the set of data covered by database attributes (see Section [14XA.1-2[0m),
  it  provides  the  mapping between identifiers and the actual entries of the
  database,  and  it  defines when precomputed data of database attributes are
  outdated.
  
  
  [1XA.1-2 Database Attributes[0X
  
  A [13Xdatabase attribute[0m is a record [3Xa[0m whose components belong to the aspects of
  [13Xdefining[0m  the  attribute,  [13Xaccessing[0m  the  attribute's  data, [13Xcomputing[0m (and
  recomputing)  data,  [13Xstoring[0m  data  on files, and [13Xchecking[0m data. (Additional
  parameters  used  for creating browse table columns from database attributes
  are described in Section [14XA.2-1[0m.)
  
  The   following   components  are  [13Xdefining[0m,  except  [10Xdescription[0m  they  are
  mandatory.
  
  [8X[10Xidenumerator[0m[8X[0m
        the database id enumerator to which the attribute [3Xa[0m is related,
  
  [8X[10Xidentifier[0m[8X[0m
        a  string  that  identifies  [3Xa[0m  among  all database attributes for the
        underlying    database    id    enumerator    (this    is    used   by
        [2XBrowseTableFromDatabaseIdEnumerator[0m ([14XA.2-2[0m) and when the data of [3Xa[0m are
        entered   with  [2XDatabaseAttributeSetData[0m  ([14XA.1-9[0m),  for  example  when
        precomputed values are read from a file),
  
  [8X[10Xdescription[0m[8X[0m
        a   string  that  describes  the  attribute  in  human  readable  form
        (currently just for convenience, the default is an empty string).
  
  The  following components are used for [13Xaccessing[0m data. Except [10Xtype[0m, they are
  optional,  but  enough  information  must  be  provided in order to make the
  database  attribute  meaningful. If an individual [10XattributeValue[0m function is
  available  then  this  function  decides  what  is  needed;  for the default
  function   [2XDatabaseAttributeValueDefault[0m   ([14XA.1-6[0m),  at  least  one  of  the
  components     [10Xname[0m,     [10Xdata[0m,     [10Xdatafile[0m     must     be    bound    (see
  [2XDatabaseAttributeValueDefault[0m ([14XA.1-6[0m) for the behaviour in this case).
  
  [8X[10Xtype[0m[8X[0m
        one  of  the  strings [10X"values"[0m or [10X"pairs"[0m; the format of the component
        [10Xdata[0m is different for these cases,
  
  [8X[10Xname[0m[8X[0m
        if  bound,  a  string that is the name of a [5XGAP[0m function such that the
        database  attribute  encodes  the  values  of  this  function  for the
        database  entries;  besides  the  computation  of  attribute values on
        demand (see [2XDatabaseAttributeValueDefault[0m ([14XA.1-6[0m)), this component can
        be used by selection functions such as [2XOneCharacterTableName[0m ([14XCTblLib:
        OneCharacterTableName[0m)     or     [2XAllCharacterTableNames[0m     ([14XCTblLib:
        AllCharacterTableNames[0m),  which  take  [5XGAP[0m  functions  and  prescribed
        return  values as their arguments –of course these functions must then
        be prepared to deal with database attributes.
  
  [8X[10Xdata[0m[8X[0m
        if  bound,  the data for this attribute; if the component [10Xtype[0m has the
        value  [10X"values"[0m  then the value is a list, where the entry at position
        [3Xi[0m,  if  bound,  belongs  to  the [3Xi[0m-th entry of the [10Xidentifiers[0m list of
        [10Xidenumerator[0m;  if  [10Xtype[0m is [10X"pairs"[0m then the value is a record with the
        components  [10Xautomatic[0m  and  [10Xnonautomatic[0m,  and  the  values  of  these
        components  are  lists  such  that  each entry is a list of length two
        whose first entry occurs in the [10Xidentifiers[0m list of [3Xa[0m[10X.idenumerator[0m and
        whose second entry encodes the corresponding attribute value,
  
  [8X[10Xdatafile[0m[8X[0m
        if  bound, the absolute name of a file that contains the data for this
        attribute,
  
  [8X[10XattributeValue[0m[8X[0m
        a  function  that takes [3Xa[0m and an [10Xidentifiers[0m entry of its [10Xidenumerator[0m
        value,  and returns the attribute value for this identifier; typically
        this  is  [13Xnot[0m  a  table cell data object that can be shown in a browse
        table,    cf.    the    [10XviewValue[0m    component;    the    default   is
        [2XDatabaseAttributeValueDefault[0m  ([14XA.1-6[0m)  (Note  that  using  individual
        [10XattributeValue[0m  functions,  one  can  deal  with  database  attributes
        independent  of  actually stored date, for example without precomputed
        values, such that the values are computed on demand and afterwards are
        cached.),
  
  [8X[10XdataDefault[0m[8X[0m
        a  [5XGAP[0m  object  that  is  regarded  as  the  attribute value for those
        database  entries  for  which  [10Xdata[0m, [10Xdatafile[0m, and [10Xname[0m do not provide
        values; the default value is an empty string [10X""[0m,
  
  [8X[10Xeval[0m[8X[0m
        if this component is bound, the value is assumed to be a function that
        takes  [3Xa[0m  and  a value from its [10Xdata[0m component, and returns the actual
        attribute value; this can be useful if one does not want to create all
        attribute  values  in  advance,  because  this  would be space or time
        consuming;  another  possible  aspect of the [10Xeval[0m component is that it
        may  be  used to strip off comments that are perhaps contained in [10Xdata[0m
        entries,
  
  [8X[10XisSorted[0m[8X[0m
        if  this  component is bound to [9Xtrue[0m and if [10Xtype[0m is [10X"pairs"[0m then it is
        assumed  that  the two lists in the [10Xdata[0m record of [3Xa[0m are sorted w.r.t.
        [5XGAP[0m's ordering [10X\<[0m; the default is [9Xfalse[0m,
  
  The  following optional components are needed for [13Xcomputing[0m (or recomputing)
  data  with  [2XDatabaseAttributeCompute[0m  ([14XA.1-8[0m).  This  is  useful  mainly for
  databases which can change over the time.
  
  [8X[10Xversion[0m[8X[0m
        the  [5XGAP[0m  object  that  is  the  [10Xversion[0m component of the [10Xidenumerator[0m
        component at the time when the stored data were entered; this value is
        used  by  [2XDatabaseIdEnumeratorUpdate[0m  ([14XA.1-7[0m) for deciding whether the
        attribute  values  are  outdated;  if  [3Xa[0m[10X.datafile[0m  is bound then it is
        assumed  that the [10Xversion[0m component is set when this file is read, for
        example in the function [2XDatabaseAttributeSetData[0m ([14XA.1-9[0m),
  
  [8X[10Xupdate[0m[8X[0m
        a  function  that takes [3Xa[0m as its argument, adjusts its data components
        to  the  current  values  of [3Xa[0m[10X.dbidenum[0m if necessary, sets the [10Xversion[0m
        component  to that of [3Xa[0m[10X.dbidenum[0m, and returns [9Xtrue[0m or [9Xfalse[0m, depending
        on whether the update process was successful or not; the default value
        is [2XReturnTrue[0m ([14XReference: ReturnTrue[0m),
  
  [8X[10XneededAttributes[0m[8X[0m
        a  list  of attribute [10Xidentifier[0m strings such that the values of these
        attributes  are  needed  in  the computations for the current one, and
        therefore these should be updated/recomputed in advance; it is assumed
        that  the  [10XneededAttributes[0m  components  of all database attributes of
        [3Xa[0m[10X.idenumerator[0m  define  a  partial  ordering;  the default is an empty
        list,
  
  [8X[10XprepareAttributeComputation[0m[8X[0m
        a function with argument [3Xa[0m that must be called before the computations
        for the current attribute are started; the default value is [2XReturnTrue[0m
        ([14XReference: ReturnTrue[0m),
  
  [8X[10XcleanupAfterAttibuteComputation[0m[8X[0m
        a  function with argument [3Xa[0m that must be called after the computations
        for   the  current  attribute  are  finished;  the  default  value  is
        [2XReturnTrue[0m ([14XReference: ReturnTrue[0m), and
  
  [8X[10Xcreate[0m[8X[0m
        a  function  that  takes  a  database  attribute  and  an entry in the
        [10Xidentifiers[0m list of its database id enumerator, and returns either the
        entry that shall be stored in the [10Xdata[0m component, as the value for the
        given  identifier (if this value shall be stored in the [10Xdata[0m component
        of  [3Xa[0m)  or  the [10XdataDefault[0m component of [3Xa[0m (if this value shall [13Xnot[0m be
        stored); in order to get the actual attribute value, the [10Xeval[0m function
        of  [3Xa[0m,  if  bound, must be called with the return value. This function
        may  assume  that  the  [10XprepareAttributeComputation[0m  function has been
        called   in  advance,  and  that  the  [10XcleanupAfterAttibuteComputation[0m
        function  will be called later. The [10Xcreate[0m function is [13Xnot[0m intended to
        compute  an individual attribute value on demand, use a [10Xname[0m component
        for that. (A stored [10Xname[0m function is used to provide a default for the
        [10Xcreate[0m  function;  without  [10Xname[0m  component,  there  is no default for
        [10Xcreate[0m.)
  
  The following optional component is needed for [13Xstoring[0m data on files.
  
  [8X[10Xstring[0m[8X[0m
        if  bound,  a function that takes the pair consisting of an identifier
        and  the  return value of the [10Xcreate[0m function for this identifier, and
        returns  a  string  that  shall represent this value when the data are
        printed to a file; the default function returns the [2XString[0m ([14XReference:
        String[0m) value of the second argument.
  
  The following optional component is needed for [13Xchecking[0m stored data.
  
  [8X[10Xcheck[0m[8X[0m
        a  function that takes a string that occurs in the [10Xidentifiers[0m list of
        the  [10Xidenumerator[0m  record,  and  returns  [9Xtrue[0m  if the attribute value
        stored  for  this  string  is reasonable, and something different from
        [9Xtrue[0m  if  an error was detected. (One could argue that these tests can
        be performed also when the values are computed, but consistency checks
        may  involve  several  entries;  besides that, checking may be cheaper
        than recomputing.)
  
  
  [1XA.1-3 How to Deal with Database Id Enumerators and Database Attributes[0X
  
  The  idea is to start with a database id enumerator (see [14XA.1-1[0m), constructed
  with  [2XDatabaseIdEnumerator[0m ([14XA.1-4[0m), and to define database attributes for it
  (see [14XA.1-2[0m), using [2XDatabaseAttributeAdd[0m ([14XA.1-5[0m). The attribute values can be
  precomputed  and  stored  on  files, or they are computed when the attribute
  gets defined, or they are computed on demand.
  
  The  function  [2XDatabaseAttributeCompute[0m ([14XA.1-8[0m) can be used to "refresh" the
  attribute  values, that is, all values or selected values can be recomputed;
  this can be necessary for example when the underlying database id enumerator
  gets extended.
  
  In  data files, the function [2XDatabaseAttributeSetData[0m ([14XA.1-9[0m) can be used to
  fill the [10Xdata[0m component of the attribute.
  
  [1XA.1-4 DatabaseIdEnumerator[0m
  
  [2X> DatabaseIdEnumerator( [0X[3Xarec[0X[2X ) _____________________________________[0Xfunction
  [6XReturns:[0X  a shallow copy of the record [3Xarec[0m, extended by default values.
  
  For  a  record  [3Xarec[0m,  [2XDatabaseIdEnumerator[0m  checks  whether  the  mandatory
  components  of  a  database  id  enumerator (see Section [14XA.1-1[0m) are present,
  initializes the [10Xattributes[0m component, sets the defaults for unbound optional
  components (see [14XA.2-1[0m), and returns the resulting record.
  
  A  special  database  attribute  (see  Section  [14XA.1-2[0m) with [10Xidentifier[0m value
  [10X"self"[0m   is   constructed   automatically   for   the   returned  record  by
  [2XDatabaseIdEnumerator[0m;  its [10XattributeValue[0m function simply returns its second
  argument  (the  identifier).  The  optional components of this attribute are
  derived  from  components of the database id enumerator, so these components
  (see [14XA.2-1[0m) are supported for [3Xarec[0m. A typical use of the [10X"self"[0m attribute is
  to   provide   the   first   column   in   browse   tables   constructed  by
  [2XBrowseTableFromDatabaseIdEnumerator[0m ([14XA.2-2[0m).
  
  [1XA.1-5 DatabaseAttributeAdd[0m
  
  [2X> DatabaseAttributeAdd( [0X[3Xdbidenum, arec[0X[2X ) ___________________________[0Xfunction
  
  For    a    database   id   enumerator   [3Xdbidenum[0m   and   a   record   [3Xarec[0m,
  [2XDatabaseAttributeAdd[0m  checks  whether the mandatory components of a database
  attribute,  except  [10Xidenumerator[0m,  are  present in [3Xarec[0m (see Section [14XA.1-2[0m),
  sets  the [10Xidenumerator[0m component, and sets the defaults for unbound optional
  components (see [14XA.2-1[0m).
  
  [1XA.1-6 DatabaseAttributeValueDefault[0m
  
  [2X> DatabaseAttributeValueDefault( [0X[3Xattr, id[0X[2X ) ________________________[0Xfunction
  [6XReturns:[0X  the value of the database attribute [3Xattr[0m at [3Xid[0m.
  
  For a database attribute [3Xattr[0m and an entry [3Xid[0m of the [10Xidentifiers[0m list of the
  underlying  database  id enumerator, [2XDatabaseAttributeValueDefault[0m takes the
  [10Xdata[0m  entry for [3Xid[0m, applies the [10Xeval[0m function of [3Xattr[0m to it if available and
  returns the result.
  
  So the question is how to get the [10Xdata[0m entry.
  
  First, if the [10Xdata[0m component of [3Xattr[0m is not bound then the file given by the
  [10Xdatafile[0m   component   of   [3Xattr[0m,  if  available,  is  read,  and  otherwise
  [2XDatabaseAttributeCompute[0m  ([14XA.1-8[0m)  is  called; afterwards it is assumed that
  the [10Xdata[0m component is bound.
  
  The further steps depend on the [10Xtype[0m value of [3Xattr[0m.
  
  If  the  [10Xtype[0m  value of [3Xattr[0m is [10X"pairs"[0m then the [10Xdata[0m entry for [3Xid[0m is either
  contained  in  the [10Xautomatic[0m or in the [10Xnonautomatic[0m list of [3Xattr[0m[10X.data[0m, or it
  is  given  by  the  [10XdataDefault[0m  value of [3Xattr[0m. (So a perhaps available [10Xname[0m
  function is [13Xnot[0m used to compute the value for a missing [10Xdata[0m entry.)
  
  If the [10Xtype[0m value of [3Xattr[0m is [10X"values"[0m then the [10Xdata[0m entry for [3Xid[0m is computed
  as  follows. Let n be the position of [3Xid[0m in the [10Xidentifiers[0m component of the
  database  id  enumerator. If the n-th entry of the [10Xdata[0m component of [3Xattr[0m is
  bound  then  take it; otherwise if the [10Xname[0m component is bound then apply it
  to [3Xid[0m and take the return value; otherwise take the [10XdataDefault[0m value.
  
  If  one  wants to introduce a database attribute where this functionality is
  not  suitable  then  another –more specific– function must be entered as the
  component [10XattributeValue[0m of such an attribute.
  
  [1XA.1-7 DatabaseIdEnumeratorUpdate[0m
  
  [2X> DatabaseIdEnumeratorUpdate( [0X[3Xdbidenum[0X[2X ) ___________________________[0Xfunction
  [6XReturns:[0X  [9Xtrue[0m or [9Xfalse[0m.
  
  For    a    database   id   enumerator   [3Xdbidenum[0m   (see   Section   [14XA.1-1[0m),
  [2XDatabaseIdEnumeratorUpdate[0m  first  calls  the  [10Xupdate[0m  function of [3Xdbidenum[0m.
  Afterwards,  the  [10Xupdate[0m  components  of those of its [10Xattributes[0m records are
  called for which the [10Xversion[0m component differs from that of [3Xdbidenum[0m.
  
  The  order in which the database attributes are updates is determined by the
  [10XneededAttributes[0m component.
  
  The  return  value  is  [9Xtrue[0m  if  all these functions return [9Xtrue[0m, and [9Xfalse[0m
  otherwise.
  
  When  [2XDatabaseIdEnumeratorUpdate[0m  has  returned  [9Xtrue[0m, the data described by
  [3Xdbidenum[0m and its database attributes are consistent and up to date.
  
  [1XA.1-8 DatabaseAttributeCompute[0m
  
  [2X> DatabaseAttributeCompute( [0X[3Xdbidenum, attridentifier[0X[2X ) _____________[0Xfunction
  [6XReturns:[0X  [9Xtrue[0m or [9Xfalse[0m.
  
  This  function returns [9Xfalse[0m if [3Xdbidenum[0m is not a database id enumerator, or
  if   it   does   not   have  a  database  attribute  with  [10Xidentifier[0m  value
  [3Xattridentifier[0m, or if this attribute does not have a [10Xcreate[0m function.
  
  Otherwise  the  [10XprepareAttributeComputation[0m  function  is  called,  the [10Xdata[0m
  entries    for    the    database    attribute    are    (re)computed,   the
  [10XcleanupAfterAttibuteComputation[0m function is called, and [9Xtrue[0m is returned.
  
  If  the [10Xtype[0m value of the database attribute is [10X"pairs"[0m then only the values
  of  the  [10Xdata.automatic[0m  list  are recomputed, the [10Xdata.nonautomatic[0m list is
  left  unchanged.  If  the  [10Xtype[0m  value  is  [10X"values"[0m  then  all  values  are
  recomputed.
  
  [1XA.1-9 DatabaseAttributeSetData[0m
  
  [2X> DatabaseAttributeSetData( [0X[3Xdbidenum, attridentifier, version, data[0X[2X ) [0Xfunction
  
  Let [3Xdbidenum[0m be a database id enumerator (see Section [14XA.1-1[0m), [3Xattridentifier[0m
  be  a  string  that  is  the  [10Xidentifier[0m  value  of  a database attribute of
  [3Xdbidenum[0m,  [3Xdata[0m  be  the  [10Xdata[0m  list  or  record  for the database attribute
  (depending  on  its  [10Xtype[0m  value),  and [3Xversion[0m be the corresponding [10Xversion[0m
  value.
  
  [2XDatabaseAttributeSetData[0m  sets  the  [10Xdata[0m  and  [10Xversion[0m  components  of  the
  attribute. This function can be used for example in data files.
  
  
  [1XA.2 Using Database Attributes for Browse Tables[0X
  
  
  [1XA.2-1 Browse Relevant Components of Database Attributes[0X
  
  The  following  optional  components of database id enumerators and database
  attributes are used by [2XBrowseTableFromDatabaseIdEnumerator[0m ([14XA.2-2[0m).
  
  [8X[10XviewLabel[0m[8X[0m
        if      bound,      a      table     cell     data     object     (see
        [2XBrowseData.IsBrowseTableCellData[0m   ([14X4.2-1[0m))   that   gives   a   [13Xshort[0m
        description  of  the  attribute,  which is used as the column label in
        browse   tables   created   with   [2XBrowseTableFromDatabaseIdEnumerator[0m
        ([14XA.2-2[0m); the default for database attributes is the [10Xname[0m component, if
        bound,  and  otherwise  the  [10Xidentifier[0m  component;  the  default  for
        database id enumerators is the string [10X"name"[0m,
  
  [8X[10XviewValue[0m[8X[0m
        if  bound,  a  function  that  takes  the output of the [10XattributeValue[0m
        function    and    returns    a    table   cell   data   object   (see
        [2XBrowseData.IsBrowseTableCellData[0m ([14X4.2-1[0m)) that is used as the entry of
        the    corresponding    column   in   browse   tables   created   with
        [2XBrowseTableFromDatabaseIdEnumerator[0m  ([14XA.2-2[0m);  the  default  is [2XString[0m
        ([14XReference: String[0m),
  
  [8X[10XviewSort[0m[8X[0m
        if  bound,  a  comparison  function  that takes two database attribute
        values and returns [9Xtrue[0m if the first value is regarded as smaller than
        the  second  when  the  column  corresponding  to the attribute in the
        browse   table   constructed   by  [2XBrowseTableFromDatabaseIdEnumerator[0m
        ([14XA.2-2[0m)  gets  sorted,  and  [9Xfalse[0m  otherwise; the default is [5XGAP[0m's [10X\<[0m
        operation,
  
  [8X[10XsortParameters[0m[8X[0m
        if  bound,  a  list  in  the  same  format  as  the  last  argument of
        [10XBrowseData.SetSortParameters[0m,   which   is   used   for   the   column
        corresponding  to  the  attribute  in  the browse table constructed by
        [2XBrowseTableFromDatabaseIdEnumerator[0m  ([14XA.2-2[0m);  the default is an empty
        list,
  
  [8X[10XwidthCol[0m[8X[0m
        if  bound,  the width of the column in the browse table constructed by
        [2XBrowseTableFromDatabaseIdEnumerator[0m  ([14XA.2-2[0m);  if  a  column  width is
        prescribed  this  way  then  the function stored in the [10XattributeValue[0m
        component  must  return either a list of attribute lines that fit into
        the  column or a plain string (which then gets formatted as required);
        there  is no default for this component, meaning that the column width
        is  computed  as  the maximum of the widths of the column label and of
        all entries in the column if no value is bound,
  
  [8X[10Xalign[0m[8X[0m
        if  bound,  the  alignment  of  the values in the column of the browse
        table   constructed  by  [2XBrowseTableFromDatabaseIdEnumerator[0m  ([14XA.2-2[0m);
        admissible     values     are     substrings     of     [10X"bclt"[0m,    see
        [2XBrowseData.IsBrowseTableCellData[0m  ([14X4.2-1[0m);  the  default  is right and
        vertically  centered, but note that if the [10XviewValues[0m function returns
        a  record  (see  [2XBrowseData.IsBrowseTableCellData[0m  ([14X4.2-1[0m))  then  the
        alignment prescribed by this record is preferred,
  
  [8X[10XcategoryValue[0m[8X[0m
        if  bound,  a  function that is similar to the [10XviewValue[0m component but
        may  return a different value; for example if the column in the browse
        table  belongs  to  a  property  and  the  [10XviewValue[0m  function returns
        something  like  [10X"+"[0m  or  [10X"-"[0m, it may be useful that the category rows
        show  a  textual description of the property values; the default value
        is  the  [10XviewValue[0m  component;  if the value is a record then its [10Xrows[0m
        component  is  taken  for  forming  category  rows, if the value is an
        attribute  line  (see  [2XNCurses.IsAttributeLine[0m  ([14X2.2-3[0m)) then there is
        exactly  this  category  row, and otherwise the value is regarded as a
        list  of attribute lines, which is either concatenated to one category
        row  or  turned  into  individual  category  rows,  depending  on  the
        [10XsortParameters[0m value.
  
  [1XA.2-2 BrowseTableFromDatabaseIdEnumerator[0m
  
  [2X> BrowseTableFromDatabaseIdEnumerator( [0X[3Xdbidenum, labelids, columnids[, header[, footer]][0X[2X ) [0Xfunction
  [6XReturns:[0X  a  record  that  can be used as the input of [2XNCurses.BrowseGeneric[0m
            ([14X4.3-1[0m).
  
  For  a  database id enumerator [3Xdbidenum[0m (see Section [14XA.1-1[0m), a list [3Xlabelids[0m
  and  a  nonempty  list [3Xcolumnids[0m of [10Xidentifier[0m values of database attributes
  stored  in  [3Xdbidenum[0m  which  are  used to provide row label columns and main
  table  columns,  [2XBrowseTableFromDatabaseIdEnumerator[0m  returns a browse table
  (see [2XBrowseData.IsBrowseTable[0m ([14X4.2-3[0m)) whose columns are given by the values
  of the specified database attributes.
  
  If  the  optional  arguments  [3Xheader[0m  and [3Xfooter[0m are given then they must be
  lists  or  functions or records such that they are admissible for the [10Xheader[0m
  and  [10Xfooter[0m  components  of  the  [10Xwork[0m  record  of  the  browse  table,  see
  [2XBrowseData.IsBrowseTable[0m ([14X4.2-3[0m).
  
  
  [1XA.3 Example: Database Id Enumerators and Database Attributes[0X
  
  As  an  example  for the functions introduced in this appendix, we introduce
  the  [13Xdatabase  of  small integers[0m. For that, we fix a positive integer n and
  consider  the  integers  from  1  to n as the entries of our database. Using
  these  integers  as  their  own  identifiers,  we  construct the database id
  enumerator.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> n:= 100;;[0X
    [4Xgap> smallintenum1:= DatabaseIdEnumerator( rec([0X
    [4X>      identifiers:= [ 1 .. n ],[0X
    [4X>      entry:= function( dbidenum, id ) return id; end,[0X
    [4X>    ) );;[0X
  [4X------------------------------------------------------------------[0X
  
  Examples  of  attributes for this database are the properties whether or not
  an  integer  is  a  prime  or  a prime power. There are global [5XGAP[0m functions
  [2XIsPrimeInt[0m   ([14XReference:   IsPrimeInt[0m)   and   [2XIsPrimePowerInt[0m   ([14XReference:
  IsPrimePowerInt[0m)  for  computing  these  properties,  so we can define these
  database  attributes  via  a  [10Xname[0m component; we choose [10X"values"[0m as the [10Xtype[0m
  value,  so  the  values ([9Xtrue[0m or [9Xfalse[0m) are stored in a list of length n for
  each of the two database attributes.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum1, rec([0X
    [4X>      identifier:= "primes",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "IsPrimeInt",[0X
    [4X>    ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum1, rec([0X
    [4X>      identifier:= "prime powers",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "IsPrimePowerInt",[0X
    [4X>    ) );[0X
  [4X------------------------------------------------------------------[0X
  
  Similarly, we consider the prime factors as a database attribute.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum1, rec([0X
    [4X>      identifier:= "factors",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "Factors",[0X
    [4X>    ) );[0X
  [4X------------------------------------------------------------------[0X
  
  Another  example of an attribute of integers is the residue modulo 11. We do
  not  want  to introduce a global [5XGAP[0m function for computing the value, so we
  use the [10Xcreate[0m component in order to define the attribute; again, the values
  (integers from 0 to 10) are stored in a list of length n.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum1, rec([0X
    [4X>      identifier:= "residue mod 11",[0X
    [4X>      type:= "values",[0X
    [4X>      create:= function( attr, id ) return id mod 11; end,[0X
    [4X>    ) );[0X
  [4X------------------------------------------------------------------[0X
  
  Some integers are values of [2XFactorial[0m ([14XReference: Factorial[0m), and we want to
  record  this  information  and show it in a browse table. For most integers,
  nothing  is stored and shown for this attribute, so we choose the [10Xtype[0m value
  [10X"pairs"[0m  and precompute the information for the [10Xdata[0m component. (The default
  for  the [10XdataDefault[0m component is an empty string, which is fine; so we need
  not prescribe this component.)
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> factorialdata:= function( n )[0X
    [4X>      local result, i, f;[0X
    [4X>      result:= [];  i:= 1;  f:= 1;;[0X
    [4X>      while f <= n do[0X
    [4X>        Add( result, [ f, i ] );  i:= i + 1;  f:= f * i;[0X
    [4X>      od;[0X
    [4X>      return result;[0X
    [4X>    end;;[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum1, rec([0X
    [4X>      identifier:= "inverse factorial",[0X
    [4X>      type:= "pairs",[0X
    [4X>      data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ),[0X
    [4X>      isSorted:= true,[0X
    [4X>    ) );[0X
  [4X------------------------------------------------------------------[0X
  
  We use this setup for creating a browse table. The integers are shown as the
  first  column,  using  the [10X"self"[0m attribute. This attribute can be used as a
  column  of row labels (useful if we want to keep the column visible when one
  scrolls  the table to the right) or as a column in the main table (useful if
  we want to search for the values); here we choose the former possibility.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> t1:= BrowseTableFromDatabaseIdEnumerator( smallintenum1,[0X
    [4X>           [ "self" ],[0X
    [4X>           [ "primes", "prime powers", "factors", "residue mod 11",[0X
    [4X>             "inverse factorial" ] );;[0X
  [4X------------------------------------------------------------------[0X
  
  The following session shows some of the features of the browse table.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> nop:= [ 14, 14, 14, 14, 14, 14 ];;  # ``do nothing''[0X
    [4Xgap> sample_session:= Concatenation([0X
    [4X>         # categorize by the first column, expand categories, wait, reset[0X
    [4X>         nop, "scsc", nop, "X", nop, "!",[0X
    [4X>         # sort the residue column, wait, reset[0X
    [4X>         "scrrrso", nop, "!",[0X
    [4X>         # categorize by the inverse factorial column[0X
    [4X>         "rscsrdx", nop, "!",[0X
    [4X>         # and quit the application[0X
    [4X>         "qQ" );;[0X
    [4Xgap> BrowseData.SetReplay( sample_session );[0X
    [4Xgap> NCurses.BrowseGeneric( t1 );[0X
    [4Xgap> BrowseData.SetReplay( false );[0X
    [4Xgap> Unbind( t1.dynamic.replay );[0X
  [4X------------------------------------------------------------------[0X
  
  (Note  that  the  last  statement above is necessary to run the session more
  than  once.)  The  result is not too bad but we can improve the table, using
  the optional components of database attributes, as follows.
  
  --    The  strings  [10X"true"[0m and [10X"false"[0m shown for the Boolean valued database
        attributes  can be replaced by the perhaps more suggestive strings [10X"+"[0m
        and [10X"-"[0m (or perhaps an empty string instead of [10X"-"[0m).
  
  --    The alignment of values inside their columns can be customized.
  
  --    When  the  browse  table is categorized by a column then the values in
        this  column  do  usually  not  provide suitable category rows; we can
        prescribe individual category values.
  
  --    The column labels can be customized.
  
  --    Where  the  lexicographic  order  is not appropriate for sorting table
        entries, we can prescribe an individual comparison function.
  
  --    Sort parameters can be customized.
  
  --    We  can  prescribe  the  width  of  a  column, and thus distribute the
        attribute  values  for this column to several rows when the values are
        too long.
  
  --    Finally,  in  the call of [2XBrowseTableFromDatabaseIdEnumerator[0m ([14XA.2-2[0m),
        we can add a header to the browse table.
  
  We  create  a new database id enumerator and the corresponding browse table,
  in order to be able to compare the behaviour of the two objects. However, we
  assume that the variables [10Xn[0m and [10Xfactorialdata[0m are already available.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> smallintenum2:= DatabaseIdEnumerator( rec([0X
    [4X>      identifiers:= [ 1 .. n ],[0X
    [4X>      entry:= function( dbidenum, id ) return id; end,[0X
    [4X>      viewLabel:= "",[0X
    [4X>    ) );;[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "primes",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "IsPrimeInt",[0X
    [4X>      viewLabel:= "prime?",[0X
    [4X>      viewValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ true, false ], [ "+", "-" ] ),[0X
    [4X>      sortParameters:= [ "add counter on categorizing", "yes" ],[0X
    [4X>      align:= "c",[0X
    [4X>      categoryValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ true, false ], [ "prime", "nonprime" ] ),[0X
    [4X>    ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "prime powers",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "IsPrimePowerInt",[0X
    [4X>      viewLabel:= "prime power?",[0X
    [4X>      viewValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ true, false ], [ "+", "-" ] ),[0X
    [4X>      sortParameters:= [ "add counter on categorizing", "yes" ],[0X
    [4X>      align:= "c",[0X
    [4X>      categoryValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ true, false ], [ "prime power", "not prime power" ] ),[0X
    [4X>    ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "factors",[0X
    [4X>      type:= "values",[0X
    [4X>      name:= "Factors",[0X
    [4X>      viewLabel:= "factors",[0X
    [4X>      viewValue:= value -> JoinStringsWithSeparator( List( value, String ),[0X
    [4X>                                                     " * "),[0X
    [4X>      widthCol:= 10,[0X
    [4X>    ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "residue mod 11",[0X
    [4X>      type:= "values",[0X
    [4X>      create:= function( attr, id ) return id mod 11; end,[0X
    [4X>      viewSort:= BrowseData.SortAsIntegers,[0X
    [4X>      categoryValue:= res -> Concatenation( String( res ), " mod 11" ),[0X
    [4X>    ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "inverse factorial",[0X
    [4X>      type:= "pairs",[0X
    [4X>      data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ),[0X
    [4X>      isSorted:= true,[0X
    [4X>      categoryValue:= function( k )[0X
    [4X>        if k = "" then[0X
    [4X>          return "(no factorial)";[0X
    [4X>        else[0X
    [4X>          return Concatenation( String( k ), "!" );[0X
    [4X>        fi;[0X
    [4X>      end,[0X
    [4X>    ) );[0X
    [4Xgap> t2:= BrowseTableFromDatabaseIdEnumerator( smallintenum2,[0X
    [4X>           [ "self" ],[0X
    [4X>           [ "primes", "prime powers", "factors", "residue mod 11",[0X
    [4X>             "inverse factorial" ],[0X
    [4X>           t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );;[0X
  [4X------------------------------------------------------------------[0X
  
  We run the same session as with the browse table for [10Xsmallintenum1[0m.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> BrowseData.SetReplay( sample_session );[0X
    [4Xgap> NCurses.BrowseGeneric( t2 );[0X
    [4Xgap> BrowseData.SetReplay( false );[0X
    [4Xgap> Unbind( t2.dynamic.replay );[0X
  [4X------------------------------------------------------------------[0X
  
  Another  possibility  to  change  the  look  of  the table is to combine the
  columns  for  the  two  Boolean valued database attributes in one column, by
  showing  the string [10X"+"[0m for prime powers, as before, and showing this string
  in boldface red if the number in question is a prime. We implement this idea
  in  the  following  database attribute. However, note that this can be a bad
  idea  because  text  attributes  may be not supported in the user's terminal
  (see  Section  [14X2.1-7[0m),  or  the  user  may  have  difficulties  to see or to
  distinguish colors; also, it must be documented which information is encoded
  in  the  table,  and the column label might be not sufficient for explaining
  what  the  text  attributes  mean.  Alternatively, we could show for example
  combined  symbols  such  as  [10X++[0m,  [10X+-[0m,  [10X--[0m  for  primes,  prime  powers,  and
  non-prime-powers,  respectively.  (We  see  that  besides  these issues, the
  required  [5XGAP[0m  code  is  more  involved than what is needed for the examples
  above.)
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum2, rec([0X
    [4X>      identifier:= "primes & prime powers",[0X
    [4X>      type:= "values",[0X
    [4X>      create:= function( attr, id )[0X
    [4X>        if   IsPrimeInt( id ) then[0X
    [4X>          return 2;[0X
    [4X>        elif IsPrimePowerInt( id ) then[0X
    [4X>          return 1;[0X
    [4X>        else[0X
    [4X>          return 0;[0X
    [4X>        fi;[0X
    [4X>      end,[0X
    [4X>      viewLabel:= [ NCurses.attrs.BOLD + NCurses.ColorAttr( "red", -1 ),[0X
    [4X>                    "prime", NCurses.attrs.NORMAL, " power?" ],[0X
    [4X>      viewValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ 0, 1, 2 ], [ "-", "+",[0X
    [4X>                         [ NCurses.attrs.BOLD + NCurses.ColorAttr( "red", -1 ),[0X
    [4X>                           true, "+",[0X
    [4X>                           NCurses.ColorAttr( "red", -1 ), false ] ] ),[0X
    [4X>      sortParameters:= [ "add counter on categorizing", "yes" ],[0X
    [4X>      align:= "c",[0X
    [4X>      categoryValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>          [ 0, 1, 2 ],[0X
    [4X>          [ "not prime power", "prime power, not prime", "prime" ] ),[0X
    [4X>    ) );[0X
    [4Xgap> t3:= BrowseTableFromDatabaseIdEnumerator( smallintenum2,[0X
    [4X>           [ "self" ],[0X
    [4X>           [ "primes & prime powers", "residue mod 11",[0X
    [4X>             "inverse factorial" ],[0X
    [4X>           t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );;[0X
    [4Xgap> sample_session2:= Concatenation([0X
    [4X>         # categorize by the first column, expand categories, wait, reset[0X
    [4X>         nop, "scsc", nop, "X", nop, "!", "Q" );;[0X
    [4Xgap> BrowseData.SetReplay( sample_session2 );[0X
    [4Xgap> NCurses.BrowseGeneric( t3 );[0X
    [4Xgap> BrowseData.SetReplay( false );[0X
    [4Xgap> Unbind( t3.dynamic.replay );[0X
  [4X------------------------------------------------------------------[0X
  
  Now  we  want to consider the database as extendible, that is, we want to be
  able  to increase n after constructing the database attributes. For that, we
  use  n  as  the  [10Xversion[0m  value  of  the database id enumerator, and provide
  [10Xversion[0m and [10Xupdate[0m components for all attributes.
  
  Again, we start the construction from scratch.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> smallintenum3:= DatabaseIdEnumerator( rec([0X
    [4X>     identifiers:= [ 1 .. n ],[0X
    [4X>     entry:= function( dbidenum, id ) return id; end,[0X
    [4X>     viewLabel:= "",[0X
    [4X>     version:= n,[0X
    [4X>     update:= function( dbidenum )[0X
    [4X>         dbidenum.identifiers:= [ 1 .. n ];[0X
    [4X>         dbidenum.version:= n;[0X
    [4X>         return true;[0X
    [4X>       end,[0X
    [4X>     ) );;[0X
    [4Xgap> updateByUnbindData:= function( attr )[0X
    [4X>      Unbind( attr.data );[0X
    [4X>      return true;[0X
    [4X>    end;;[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum3, rec([0X
    [4X>     identifier:= "primes",[0X
    [4X>     type:= "values",[0X
    [4X>     name:= "IsPrimeInt",[0X
    [4X>     viewLabel:= "prime?",[0X
    [4X>     viewValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>         [ true, false ], [ "+", "-" ] ),[0X
    [4X>     sortParameters:= [ "add counter on categorizing", "yes" ],[0X
    [4X>     align:= "c",[0X
    [4X>     categoryValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>         [ true, false ], [ "prime", "nonprime" ] ),[0X
    [4X>     version:= n,[0X
    [4X>     update:= updateByUnbindData,[0X
    [4X>     ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum3, rec([0X
    [4X>     identifier:= "prime powers",[0X
    [4X>     type:= "values",[0X
    [4X>     name:= "IsPrimePowerInt",[0X
    [4X>     viewLabel:= "prime power?",[0X
    [4X>     viewValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>         [ true, false ], [ "+", "-" ] ),[0X
    [4X>     sortParameters:= [ "add counter on categorizing", "yes" ],[0X
    [4X>     align:= "c",[0X
    [4X>     categoryValue:= value -> BrowseData.ReplacedEntry( value,[0X
    [4X>         [ true, false ], [ "prime power", "not prime power" ] ),[0X
    [4X>     version:= n,[0X
    [4X>     update:= updateByUnbindData,[0X
    [4X>     ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum3, rec([0X
    [4X>     identifier:= "factors",[0X
    [4X>     type:= "values",[0X
    [4X>     name:= "Factors",[0X
    [4X>     viewLabel:= "factors",[0X
    [4X>     viewValue:= value -> JoinStringsWithSeparator( List( value, String ),[0X
    [4X>                                                    " * "),[0X
    [4X>     widthCol:= 10,[0X
    [4X>     version:= n,[0X
    [4X>     update:= updateByUnbindData,[0X
    [4X>     ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum3, rec([0X
    [4X>     identifier:= "residue mod 11",[0X
    [4X>     type:= "values",[0X
    [4X>     create:= function( attr, id ) return id mod 11; end,[0X
    [4X>     viewSort:= BrowseData.SortAsIntegers,[0X
    [4X>     categoryValue:= res -> Concatenation( String( res ), " mod 11" ),[0X
    [4X>     version:= n,[0X
    [4X>     update:= updateByUnbindData,[0X
    [4X>     ) );[0X
    [4Xgap> DatabaseAttributeAdd( smallintenum3, rec([0X
    [4X>     identifier:= "inverse factorial",[0X
    [4X>     type:= "pairs",[0X
    [4X>     data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ),[0X
    [4X>     isSorted:= true,[0X
    [4X>     categoryValue:= function( k )[0X
    [4X>       if k = "" then[0X
    [4X>         return "(no factorial)";[0X
    [4X>       else[0X
    [4X>         return Concatenation( String( k ), "!" );[0X
    [4X>       fi;[0X
    [4X>     end,[0X
    [4X>     version:= n,[0X
    [4X>     update:= function( attr )[0X
    [4X>         attr.data.automatic:= factorialdata( n );[0X
    [4X>         return true;[0X
    [4X>       end,[0X
    [4X>     ) );[0X
  [4X------------------------------------------------------------------[0X
  
  Now  we  can  change the set of database entries by assigning a new value to
  the variable [10Xn[0m, and then calling [2XDatabaseIdEnumeratorUpdate[0m ([14XA.1-7[0m).
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> n:= 200;;[0X
    [4Xgap> DatabaseIdEnumeratorUpdate( smallintenum3 );[0X
    [4Xtrue[0X
    [4Xgap> t4:= BrowseTableFromDatabaseIdEnumerator( smallintenum3,[0X
    [4X>   [ "self" ], [ "primes", "prime powers", "factors", "residue mod 11",[0X
    [4X>     "inverse factorial" ],[0X
    [4X>   t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );;[0X
    [4Xgap> BrowseData.SetReplay( sample_session );[0X
    [4Xgap> NCurses.BrowseGeneric( t4 );[0X
    [4Xgap> BrowseData.SetReplay( false );[0X
    [4Xgap> Unbind( t4.dynamic.replay );[0X
  [4X------------------------------------------------------------------[0X
  
  
  [1XA.4 Example: An Overview of the [5XGAP[1X Library of Transitive Groups[0X
  
  The  example  shown  in  this section deals with [5XGAP[0m's Library of Transitive
  Permutation  Groups, see [14X'Reference: Transitive Permutation Groups'[0m. Section
  [14XA.4-1[0m  introduces  a  browse table application for viewing information about
  the contents of this library, which is based on database attributes. Section
  [14XA.4-2[0m  introduces functions based on database attributes that can be used to
  select  groups  from the library. Finally, Section [14XA.4-3[0m describes how these
  two functionalities are implemented.
  
  [1XA.4-1 BrowseTransitiveGroupsInfo[0m
  
  [2X> BrowseTransitiveGroupsInfo( [0X[3X[arec][0X[2X ) _____________________________[0Xfunction
  [6XReturns:[0X  the list of "clicked" groups.
  
  This   function  shows  the  contents  of  the  [5XGAP[0m  Library  of  Transitive
  Permutation Groups in a browse table.
  
  The  table  rows  correspond to the groups. If no argument is given then the
  columns of the table contain information about the degree of the permutation
  representation, group order, names for the group, primitivity, transitivity,
  and  sign.  Otherwise,  the  argument  [3Xarec[0m  must be a record; the component
  [10Xchoice[0m of [3Xarec[0m can be used to prescribe columns, the value of this component
  must  then  be  a  list  of  strings  that are [10Xidentifier[0m values of database
  attributes    (see    [14XA.1-2[0m)    for    the    [10XIdEnumerator[0m    component   of
  [2XTransitiveGroupsData[0m ([14XA.4-3[0m), see Section [14XA.4-4[0m for examples.
  
  The return value is the list of transitive groups whose table rows have been
  "clicked" in visual mode.
  
  The  full  functionality  of  the  function [2XNCurses.BrowseGeneric[0m ([14X4.3-1[0m) is
  available.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> c:= [ NCurses.keys.ENTER ];;[0X
    [4Xgap> BrowseData.SetReplay( Concatenation([0X
    [4X>        "scrrrr/5", c,     # search for transitivity 5,[0X
    [4X>        "nn", c,           # go to the third occurrence, click on it,[0X
    [4X>        "Q" ) );;          # and quit the browse table[0X
    [4Xgap> BrowseTransitiveGroupsInfo();[0X
    [4X[ M(12) ][0X
    [4Xgap> BrowseData.SetReplay( false );[0X
  [4X------------------------------------------------------------------[0X
  
  [1XA.4-2 TransitiveGroupsData.AllTransitiveGroups[0m
  
  [2X> TransitiveGroupsData.AllTransitiveGroups( [0X[3Xfun, res[, ...][0X[2X ) ______[0Xfunction
  [2X> TransitiveGroupsData.OneTransitiveGroup( [0X[3Xfun, res[, ...][0X[2X ) _______[0Xfunction
  [6XReturns:[0X  the  list of groups from the [5XGAP[0m Library of Transitive Permutation
            Groups with the given properties, or one such group, or [9Xfail[0m.
  
  These   functions   are   analogues   of   [2XAllTransitiveGroups[0m   ([14XReference:
  AllTransitiveGroups[0m) and [2XOneTransitiveGroup[0m ([14XReference: OneTransitiveGroup[0m).
  The  only  difference is that they are based on the database attributes that
  are defined in [2XTransitiveGroupsData[0m ([14XA.4-3[0m).
  
  Besides  those [5XGAP[0m attributes such as [2XSize[0m ([14XReference: Size[0m) and [2XIsPrimitive[0m
  ([14XReference:   IsPrimitive[0m)   for   which  special  support  is  provided  in
  [2XAllTransitiveGroups[0m  ([14XReference: AllTransitiveGroups[0m) and [2XOneTransitiveGroup[0m
  ([14XReference:  OneTransitiveGroup[0m),  database  attributes are defined for some
  other     [5XGAP[0m    properties,    see    [14XA.4-4[0m.    One    could    speed    up
  [2XTransitiveGroupsData.AllTransitiveGroups[0m  for  given  conditions  by  adding
  further precomputed database attributes.
  
  After  defining  a  database attribute, it is automatically used in calls to
  [2XTransitiveGroupsData.AllTransitiveGroups[0m. In order to make the values appear
  in  the  table shown by [2XBrowseTransitiveGroupsInfo[0m ([14XA.4-1[0m), one has to enter
  an  argument record that contains the name of the database attributes in its
  [10Xchoice[0m list.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> TransitiveGroupsData.AllTransitiveGroups([0X
    [4X>      NrMovedPoints, [ 5 .. 28 ],[0X
    [4X>      IsSimpleGroup, true, IsAbelian, true );[0X
    [4X[ C(5) = 5, C(7) = 7, C(11)=11, C(13)=13, C(17)=17, C(19)=19, C(23) ][0X
  [4X------------------------------------------------------------------[0X
  
  [1XA.4-3 TransitiveGroupsData[0m
  
  [2X> TransitiveGroupsData_______________________________________[0Xglobal variable
  
  This is a record that contains the data needed by [2XBrowseTransitiveGroupsInfo[0m
  ([14XA.4-1[0m),      [2XTransitiveGroupsData.AllTransitiveGroups[0m      ([14XA.4-2[0m),     and
  [2XTransitiveGroupsData.OneTransitiveGroup[0m  ([14XA.4-2[0m). The component [10XIdEnumerator[0m
  contains  a  database  id  enumerator  (see  [14XA.1-1[0m)  for  the [5XGAP[0m Library of
  Transitive Permutation Groups.
  
  Note   that   although  [2XNrTransitiveGroups[0m  ([14XReference:  NrTransitiveGroups[0m)
  returns [10X1[0m when it is called with the argument [10X1[0m, [2XTransitiveGroup[0m ([14XReference:
  TransitiveGroup[0m)  runs  into  an error when it is called with first argument
  equal  to  [10X1[0m,  and  the  degree  [10X1[0m  is  explicitly  excluded from results of
  [2XAllTransitiveGroups[0m  ([14XReference: AllTransitiveGroups[0m) and [2XOneTransitiveGroup[0m
  ([14XReference:  OneTransitiveGroup[0m).  For  the  sake  of  consistency with this
  inconsistency  in  the  [5XGAP[0m  Library  of  Transitive  Permutation Groups, we
  exclude  the  degree [10X1[0m from [2XTransitiveGroupsData[0m. (Those who want to include
  this  degree can change the value of [10XTransitiveGroupsData.MinimalDegree[0m from
  its default value [10X2[0m to [10X1[0m in the file [11Xapp/transbrowse.g[0m of the package.)
  
  
  [1XA.4-4 Additional Database Attributes for Transitive Groups[0X
  
  Database  attributes  for  the  following  [5XGAP[0m  properties  are  defined  in
  [2XTransitiveGroupsData[0m     ([14XA.4-3[0m):    [2XIsAbelian[0m    ([14XReference:    IsAbelian[0m),
  [2XIsPerfectGroup[0m   ([14XReference:   IsPerfectGroup[0m),   [2XIsSimpleGroup[0m  ([14XReference:
  IsSimpleGroup[0m), and [2XIsSolvableGroup[0m ([14XReference: IsSolvableGroup[0m). The values
  of  these  database  attributes  are  precomputed  and  stored  in  the file
  [11Xapp/transdbattr.g[0m of the package.
  
  So   the  above  [5XGAP[0m  properties  have  special  support  as  conditions  in
  [2XTransitiveGroupsData.AllTransitiveGroups[0m             ([14XA.4-2[0m)             and
  [2XTransitiveGroupsData.OneTransitiveGroup[0m       ([14XA.4-2[0m),      contrary      to
  [2XAllTransitiveGroups[0m  ([14XReference: AllTransitiveGroups[0m) and [2XOneTransitiveGroup[0m
  ([14XReference:  OneTransitiveGroup[0m).  In  practice,  the difference is that the
  former  functions  need not construct and check those transitive groups that
  do not have the properties in question.
  
  These  database  attributes  can also be used as columns in the Browse table
  shown by [2XBrowseTransitiveGroupsInfo[0m ([14XA.4-1[0m), for example as follows.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> n:= [ 14, 14, 14 ];;  # ``do nothing'' input (means timeout)[0X
    [4Xgap> BrowseData.SetReplay( Concatenation([0X
    [4X>        "scrrrsc", n, n,        # categorize by solvability info[0X
    [4X>        "!", n,                 # reset[0X
    [4X>        "scrrrrsc", n, n,       # categorize by abelianity info[0X
    [4X>        "Q" ) );;               # quit the browse table[0X
    [4Xgap> BrowseTransitiveGroupsInfo( rec( choice:= [ "degree", "size",[0X
    [4X>      "names", "IsSolvableGroup", "IsAbelian", "IsPerfectGroup",[0X
    [4X>      "IsSimpleGroup" ] ) );;[0X
    [4Xgap> BrowseData.SetReplay( false );[0X
  [4X------------------------------------------------------------------[0X
  
  The  data  in  the  file  [11Xapp/transdbattr.g[0m are lists of Booleans, which are
  encoded  as  strings via [10XHexStringBlistEncode[0m, and these strings are decoded
  with  [10XBlistStringDecode[0m. Note that for most of the groups in the library are
  not  abelian,  not  perfect, not simple, but solvable; Therefore in fact the
  inverse  values  of  the solvability info are actually stored –this yields a
  shorter string.
  
  [4X---------------------------  Example  ----------------------------[0X
    [4Xgap> TransitiveGroupsData.MinimalDegree;[0X
    [4X2[0X
    [4Xgap> attrs:= TransitiveGroupsData.IdEnumerator.attributes;;[0X
    [4Xgap> oldlen:= SizeScreen();;  SizeScreen( [ 60 ] );;[0X
    [4Xgap> HexStringBlistEncode( attrs.IsAbelian.data );[0X
    [4X"D88400040Es0503s0480s040406s252010s0720s0C3EsF30803s7A040\[0X
    [4X5s8B20s1302s0740E0sFFsFFsFFsFFsFFsFFsFFsFFsFFsFFsFFsFFs40C\[0X
    [4X0s1910s0B1AsFFs2B18sE74040sFFsFF"[0X
    [4Xgap> SizeScreen( oldlen );;[0X
  [4X------------------------------------------------------------------[0X
  
  Removing  the  [10Xdatafile[0m  component  from  the four database attributes would
  yield the situation that attribute values are computed at runtime. Computing
  the  values  for  all groups in the library –for example by categorizing the
  Browse  table  by  one  of  the  columns  that  correspond to these database
  attributes–  will  require  several seconds. Note that if we assume that the
  information  about solvability is already known, the test for [2XIsPerfectGroup[0m
  ([14XReference: IsPerfectGroup[0m) is needed only for the few nonsolvable groups in
  the  library  but  each such test is expensive; on the other hand, each test
  for  [2XIsAbelian[0m ([14XReference: IsAbelian[0m) is cheap but such tests are needed for
  the  many solvable groups in the library, in particular these groups must be
  constructed for the tests.
  
