Some Tools for Database Handling

Two aims of the tools described in this appendix are speeding up selection functions such as for certain data libraries of &GAP; (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 .

The &GAP; objects introduced for that are database id enumerators (see ) and database attributes (see ).

Contrary to the individual interfaces to the &GAP; manuals (see Section ), the &GAP; bibliography (see Section ), and the overviews of &GAP; packages, &GAP; methods, and Conway polynomials available in &GAP; (see Section ), 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 and give an impression what is possible.

&GAP; Objects for Database Handling
<#Include Label="subsect:dbidenum">

<#Include Label="subsect:dbattr">

<#Include Label="subsect:db_how_to_use">

<#Include Label="DatabaseIdEnumerator_man">

<#Include Label="DatabaseAttributeAdd_man">

<#Include Label="DatabaseAttributeValueDefault_man">

<#Include Label="DatabaseIdEnumeratorUpdate_man">

<#Include Label="DatabaseAttributeCompute_man">

<#Include Label="DatabaseAttributeSetData_man">

Using Database Attributes for Browse Tables
<#Include Label="subsect:attr_browse_comp">

<#Include Label="BrowseTableFromDatabaseIdEnumerator_man">

Example: Database Id Enumerators and Database Attributes

As an example for the functions introduced in this appendix, we introduce the database of small integers.

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.

gap> n:= 100;; gap> smallintenum1:= DatabaseIdEnumerator( rec( > identifiers:= [ 1 .. n ], > entry:= function( dbidenum, id ) return id; end, > ) );;

Examples of attributes for this database are the properties whether or not an integer is a prime or a prime power. There are global &GAP; functions and for computing these properties, so we can define these database attributes via a name component; we choose "values" as the type value, so the values (true or false) are stored in a list of length n for each of the two database attributes.

gap> DatabaseAttributeAdd( smallintenum1, rec( > identifier:= "primes", > type:= "values", > name:= "IsPrimeInt", > ) ); gap> DatabaseAttributeAdd( smallintenum1, rec( > identifier:= "prime powers", > type:= "values", > name:= "IsPrimePowerInt", > ) );

Similarly, we consider the prime factors as a database attribute.

gap> DatabaseAttributeAdd( smallintenum1, rec( > identifier:= "factors", > type:= "values", > name:= "Factors", > ) );

Another example of an attribute of integers is the residue modulo 11. We do not want to introduce a global &GAP; function for computing the value, so we use the create component in order to define the attribute; again, the values (integers from 0 to 10) are stored in a list of length n.

gap> DatabaseAttributeAdd( smallintenum1, rec( > identifier:= "residue mod 11", > type:= "values", > create:= function( attr, id ) return id mod 11; end, > ) );

Some integers are values of , 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 type value "pairs" and precompute the information for the data component. (The default for the dataDefault component is an empty string, which is fine; so we need not prescribe this component.)

gap> factorialdata:= function( n ) > local result, i, f; > result:= []; i:= 1; f:= 1;; > while f <= n do > Add( result, [ f, i ] ); i:= i + 1; f:= f * i; > od; > return result; > end;; gap> DatabaseAttributeAdd( smallintenum1, rec( > identifier:= "inverse factorial", > type:= "pairs", > data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ), > isSorted:= true, > ) );

We use this setup for creating a browse table. The integers are shown as the first column, using the "self" 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.

gap> t1:= BrowseTableFromDatabaseIdEnumerator( smallintenum1, > [ "self" ], > [ "primes", "prime powers", "factors", "residue mod 11", > "inverse factorial" ] );;

The following session shows some of the features of the browse table.

gap> nop:= [ 14, 14, 14, 14, 14, 14 ];; # ``do nothing'' gap> sample_session:= Concatenation( > # categorize by the first column, expand categories, wait, reset > nop, "scsc", nop, "X", nop, "!", > # sort the residue column, wait, reset > "scrrrso", nop, "!", > # categorize by the inverse factorial column > "rscsrdx", nop, "!", > # and quit the application > "qQ" );; gap> BrowseData.SetReplay( sample_session ); gap> NCurses.BrowseGeneric( t1 ); gap> BrowseData.SetReplay( false ); gap> Unbind( t1.dynamic.replay );

(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 "true" and "false" shown for the Boolean valued database attributes can be replaced by the perhaps more suggestive strings "+" and "-" (or perhaps an empty string instead of "-").
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 , 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 n and factorialdata are already available.

gap> smallintenum2:= DatabaseIdEnumerator( rec( > identifiers:= [ 1 .. n ], > entry:= function( dbidenum, id ) return id; end, > viewLabel:= "", > ) );; gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "primes", > type:= "values", > name:= "IsPrimeInt", > viewLabel:= "prime?", > viewValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "+", "-" ] ), > sortParameters:= [ "add counter on categorizing", "yes" ], > align:= "c", > categoryValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "prime", "nonprime" ] ), > ) ); gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "prime powers", > type:= "values", > name:= "IsPrimePowerInt", > viewLabel:= "prime power?", > viewValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "+", "-" ] ), > sortParameters:= [ "add counter on categorizing", "yes" ], > align:= "c", > categoryValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "prime power", "not prime power" ] ), > ) ); gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "factors", > type:= "values", > name:= "Factors", > viewLabel:= "factors", > viewValue:= value -> JoinStringsWithSeparator( List( value, String ), > " * "), > widthCol:= 10, > ) ); gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "residue mod 11", > type:= "values", > create:= function( attr, id ) return id mod 11; end, > viewSort:= BrowseData.SortAsIntegers, > categoryValue:= res -> Concatenation( String( res ), " mod 11" ), > ) ); gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "inverse factorial", > type:= "pairs", > data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ), > isSorted:= true, > categoryValue:= function( k ) > if k = "" then > return "(no factorial)"; > else > return Concatenation( String( k ), "!" ); > fi; > end, > ) ); gap> t2:= BrowseTableFromDatabaseIdEnumerator( smallintenum2, > [ "self" ], > [ "primes", "prime powers", "factors", "residue mod 11", > "inverse factorial" ], > t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );;

We run the same session as with the browse table for smallintenum1.

gap> BrowseData.SetReplay( sample_session ); gap> NCurses.BrowseGeneric( t2 ); gap> BrowseData.SetReplay( false ); gap> Unbind( t2.dynamic.replay );

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 "+" 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 ), 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 ++, +-, -- for primes, prime powers, and non-prime-powers, respectively.

(We see that besides these issues, the required &GAP; code is more involved than what is needed for the examples above.)

gap> DatabaseAttributeAdd( smallintenum2, rec( > identifier:= "primes & prime powers", > type:= "values", > create:= function( attr, id ) > if IsPrimeInt( id ) then > return 2; > elif IsPrimePowerInt( id ) then > return 1; > else > return 0; > fi; > end, > viewLabel:= [ NCurses.attrs.BOLD + NCurses.ColorAttr( "red", -1 ), > "prime", NCurses.attrs.NORMAL, " power?" ], > viewValue:= value -> BrowseData.ReplacedEntry( value, > [ 0, 1, 2 ], [ "-", "+", > [ NCurses.attrs.BOLD + NCurses.ColorAttr( "red", -1 ), > true, "+", > NCurses.ColorAttr( "red", -1 ), false ] ] ), > sortParameters:= [ "add counter on categorizing", "yes" ], > align:= "c", > categoryValue:= value -> BrowseData.ReplacedEntry( value, > [ 0, 1, 2 ], > [ "not prime power", "prime power, not prime", "prime" ] ), > ) ); gap> t3:= BrowseTableFromDatabaseIdEnumerator( smallintenum2, > [ "self" ], > [ "primes & prime powers", "residue mod 11", > "inverse factorial" ], > t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );; gap> sample_session2:= Concatenation( > # categorize by the first column, expand categories, wait, reset > nop, "scsc", nop, "X", nop, "!", "Q" );; gap> BrowseData.SetReplay( sample_session2 ); gap> NCurses.BrowseGeneric( t3 ); gap> BrowseData.SetReplay( false ); gap> Unbind( t3.dynamic.replay );

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 version value of the database id enumerator, and provide version and update components for all attributes.

Again, we start the construction from scratch.

gap> smallintenum3:= DatabaseIdEnumerator( rec( > identifiers:= [ 1 .. n ], > entry:= function( dbidenum, id ) return id; end, > viewLabel:= "", > version:= n, > update:= function( dbidenum ) > dbidenum.identifiers:= [ 1 .. n ]; > dbidenum.version:= n; > return true; > end, > ) );; gap> updateByUnbindData:= function( attr ) > Unbind( attr.data ); > return true; > end;; gap> DatabaseAttributeAdd( smallintenum3, rec( > identifier:= "primes", > type:= "values", > name:= "IsPrimeInt", > viewLabel:= "prime?", > viewValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "+", "-" ] ), > sortParameters:= [ "add counter on categorizing", "yes" ], > align:= "c", > categoryValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "prime", "nonprime" ] ), > version:= n, > update:= updateByUnbindData, > ) ); gap> DatabaseAttributeAdd( smallintenum3, rec( > identifier:= "prime powers", > type:= "values", > name:= "IsPrimePowerInt", > viewLabel:= "prime power?", > viewValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "+", "-" ] ), > sortParameters:= [ "add counter on categorizing", "yes" ], > align:= "c", > categoryValue:= value -> BrowseData.ReplacedEntry( value, > [ true, false ], [ "prime power", "not prime power" ] ), > version:= n, > update:= updateByUnbindData, > ) ); gap> DatabaseAttributeAdd( smallintenum3, rec( > identifier:= "factors", > type:= "values", > name:= "Factors", > viewLabel:= "factors", > viewValue:= value -> JoinStringsWithSeparator( List( value, String ), > " * "), > widthCol:= 10, > version:= n, > update:= updateByUnbindData, > ) ); gap> DatabaseAttributeAdd( smallintenum3, rec( > identifier:= "residue mod 11", > type:= "values", > create:= function( attr, id ) return id mod 11; end, > viewSort:= BrowseData.SortAsIntegers, > categoryValue:= res -> Concatenation( String( res ), " mod 11" ), > version:= n, > update:= updateByUnbindData, > ) ); gap> DatabaseAttributeAdd( smallintenum3, rec( > identifier:= "inverse factorial", > type:= "pairs", > data:= rec( automatic:= factorialdata( n ), nonautomatic:= [] ), > isSorted:= true, > categoryValue:= function( k ) > if k = "" then > return "(no factorial)"; > else > return Concatenation( String( k ), "!" ); > fi; > end, > version:= n, > update:= function( attr ) > attr.data.automatic:= factorialdata( n ); > return true; > end, > ) );

Now we can change the set of database entries by assigning a new value to the variable n, and then calling .

gap> n:= 200;; gap> DatabaseIdEnumeratorUpdate( smallintenum3 ); true gap> t4:= BrowseTableFromDatabaseIdEnumerator( smallintenum3, > [ "self" ], [ "primes", "prime powers", "factors", "residue mod 11", > "inverse factorial" ], > t -> BrowseData.HeaderWithRowCounter( t, "Small integers", n ) );; gap> BrowseData.SetReplay( sample_session ); gap> NCurses.BrowseGeneric( t4 ); gap> BrowseData.SetReplay( false ); gap> Unbind( t4.dynamic.replay );
Example: An Overview of the &GAP; Library of Transitive Groups

The example shown in this section deals with &GAP;'s Library of Transitive Permutation Groups, see . Section introduces a browse table application for viewing information about the contents of this library, which is based on database attributes. Section introduces functions based on database attributes that can be used to select groups from the library. Finally, Section describes how these two functionalities are implemented.

<#Include Label="BrowseTransitiveGroupsInfo_man"> <#Include Label="TransitiveGroupsData.SelectTransitiveGroups_man"> <#Include Label="TransitiveGroupsData_man"> <#Include Label="transdbattr_man">