Sophie

Sophie

distrib > Mandriva > 2010.0 > i586 > media > contrib-release > by-pkgid > 91213ddcfbe7f54821d42c2d9e091326 > files > 23

gap-system-packages-4.4.12-5mdv2010.0.i586.rpm

  
  6 Examples of Applications based on NCurses.BrowseGeneric
  
  This  chapter  introduces  the  operation  Browse  (6.1-1) and lists several
  examples  how the function NCurses.BrowseGeneric (4.3-1) can be utilized for
  rendering  GAP related data or for playing games. Each section describes the
  relevant  GAP  functions  and  briefly sketches the technical aspects of the
  implementation;  more  details  can  be  found  in the GAP files, in the app
  directory of the package.
  
  Only  Section  6.3  describes  a  standard  application  in the sense of the
  introduction  to  Chapter  4,  perhaps except for a special function that is
  needed  to compare table entries. The other examples in this chapter require
  some of the programming described in Chapter 5.
  
  
  6.1 The Operation Browse
  
  6.1-1 Browse
  
  > Browse( obj[, arec] ) ___________________________________________operation
  
  This operation displays the GAP object obj in a nice, formatted way, similar
  to the operation Display (Reference: Display). The difference is that Browse
  is intended to use ncurses facilities.
  
  Currently  there  are  methods for character tables (see Browse (6.2-1)) and
  for tables of marks (see Browse (6.3-1)).
  
  
  6.2 Character Table Display
  
  The GAP library provides a Display (Reference: Display) method for character
  tables  that  breaks  the  table  into columns fitting on the screen. Browse
  provides  an  alternative,  using  the  standard  facilities of the function
  NCurses.BrowseGeneric  (4.3-1),  i. e.,  one  can  scroll  in  the matrix of
  character values, searching and sorting are provided etc.
  
  The  Browse  (6.1-1)  method  for  character tables can be called instead of
  Display  (Reference:  Display).  For  convenience, one can additionally make
  this  function the default Display (Reference: Display) method for character
  tables,  by  assigning  it  to  the  Display  component in the global record
  CharacterTableDisplayDefaults.User,   see 'Reference:   Printing   Character
  Tables';  for example, one can do this in one's .gaprc file, see 'Reference:
  The   .gaprc   file'.  (This  can  be  undone  by  unbinding  the  component
  CharacterTableDisplayDefaults.User.Display.)
  
  6.2-1 Browse
  
  > Browse( tbl[, options] ) ___________________________________________method
  
  This  method  displays  the  character  table  tbl in a window. The optional
  record  options  describes what shall be displayed, the supported components
  and  the  default  values  are  described  in 'Reference: Printing Character
  Tables'.
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  ---------------------------  Example  ----------------------------
    gap> BrowseData.SetReplay( Concatenation(
    >         # scroll in the table
    >         "DRULdddddrrrrrlluu",
    >         # select an entry and move it around
    >         "seddrruuuddlll",
    >         # search for the pattern 135 (six times)
    >         "/135", [ NCurses.keys.ENTER ], "nnnnn",
    >         # deselect the entry, select the first column
    >         "qLsc",
    >         # sort and categorize by this column
    >         "sc",
    >         # select the first row, move down the selection
    >         "srdddd",
    >         # expand the selected category, scroll the selection down
    >         "xd",
    >         # and quit the application
    >         "Q" ) );
    gap> Browse( CharacterTable( "HN" ) );
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks:  The  first  part of the code in the Browse (6.1-1)
  method for character tables is almost identical with the code for extracting
  the  data  to  be  displayed from the input data in the GAP library function
  CharacterTableDisplayDefault.  The  second part of the code transforms these
  data  into  a  browse  table.  Character names and (if applicable) indicator
  values are used as row labels, and centralizer orders, power maps, and class
  names  are used as column labels. The identifier of the table is used as the
  static  header.  When an irrational entry is selected, a description of this
  entry is shown in the dynamic footer.
  
  The  standard  modes  in  BrowseData (5.4-1) have been extended by three new
  actions.  The  first  two  of  them  open  pagers  giving an overview of all
  irrationalities in the table, or of all those irrationalities that have been
  shown  on  the  screen  in the current call, respectively. The corresponding
  user  inputs  are  the  I  and  the  i  key.  (The  names  assigned  to  the
  irrationalities  are  generated column-wise. If one just scrolls through the
  table,  without  jumping, then these names coincide with the names generated
  by  the  default  Display  (Reference: Display) method for character tables;
  this  is  in general not the case, for example when a row-wise search in the
  table  is  performed.)  The third new action, which is associated with the p
  key,  toggles the visibility status of the column label rows for centralizer
  orders and power maps.
  
  An  individual  minyx function does not only check whether the desired table
  fits  into  the  window but also whether a table with too high column labels
  (centralizer  orders and power maps) would fit if these labels get collapsed
  via the p key. In this case, the labels are automatically collapsed, and the
  p key is disabled.
  
  In  order  to keep the required space small also for large character tables,
  caching  of  formatted  matrix  entries  is  disabled, and the strings to be
  displayed  are computed on demand with a Main function in the work component
  of  the  browse  table. For the same reason, the constant height one for all
  table  rows  is set in advance, so one need not inspect a whole character if
  only a few values of it shall be shown.
  
  Special  functions  are  provided  for sorting (concerning the comparison of
  character values, which can be integers or irrationalities) and categorizing
  the table by a column (the value in the category row involves the class name
  of the column in question).
  
  The code can be found in the file app/ctbldisp.g of the package.
  
  
  6.3 Table of Marks Display
  
  The GAP library provides a Display (Reference: Display) method for tables of
  marks  that  breaks the table into columns fitting on the screen. Similar to
  the  situation  with  character  tables,  see  Section 6.2,  but with a much
  simpler implementation, Browse provides an alternative based on the function
  NCurses.BrowseGeneric (4.3-1).
  
  Browse  (6.1-1)  can  be  called instead of Display (Reference: Display) for
  tables of marks, cf. 'Reference: Printing Tables of Marks'.
  
  6.3-1 Browse
  
  > Browse( tom[, options] ) ___________________________________________method
  
  This method displays the table of marks tom in a window. The optional record
  options  describes what shall be displayed, the supported components and the
  default values are described in 'Reference: Printing Tables of Marks'.
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  ---------------------------  Example  ----------------------------
    gap> BrowseData.SetReplay( Concatenation(
    >         # scroll in the table
    >         "DDRRR",
    >         # search for the (exact) value 100 (three times)
    >         "/100",
    >         [ NCurses.keys.DOWN, NCurses.keys.DOWN, NCurses.keys.RIGHT ],
    >         [ NCurses.keys.DOWN, NCurses.keys.DOWN, NCurses.keys.DOWN ],
    >         [ NCurses.keys.RIGHT, NCurses.keys.ENTER ], "nn",
    >         # no more occurrences of 100, confirm
    >         [ NCurses.keys.ENTER ],
    >         # and quit the application
    >         "Q" ) );
    gap> Browse( TableOfMarks( "A10" ) );
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation remarks: Rows and columns are indexed by their positions. The
  identifier of the table is used as the static header, there is no footer.
  
  In  order  to  keep the required space small also for large tables of marks,
  caching  of  formatted  matrix  entries  is  disabled, and the strings to be
  displayed  are computed on demand with a Main function in the work component
  of  the  browse  table. For the same reason, the constant height one for the
  table  rows is set in advance. (For example, the table of marks of the group
  with  identifier  "O8+(2)",  with  11171 rows and columns, can be shown with
  Browse (6.1-1) in a GAP session requiring about 100 MB.)
  
  The code can be found in the file app/tomdisp.g of the package.
  
  
  6.4 Table of Contents of AtlasRep
  
  The  GAP  package  AtlasRep (see [WPNBB07]) is an interface to a database of
  representations and related data. The table of contents of this database can
  be  displayed via the function DisplayAtlasInfo (AtlasRep: DisplayAtlasInfo)
  of  this  package.  The  Browse package provides an alternative based on the
  function  NCurses.BrowseGeneric  (4.3-1);  one can scroll, search, and fetch
  representations for later use.
  
  6.4-1 BrowseAtlasInfo
  
  > BrowseAtlasInfo(  ) ______________________________________________function
  Returns:  the list of "clicked" representations.
  
  This  function  shows the table of contents of the GAP package AtlasRep in a
  browse table, cf. Section 'AtlasRep: Accessing Data of the AtlasRep Package'
  in the package manual. When one "clicks" on one of the table rows or entries
  then  a  browse  table with an overview of the available representations for
  this  group  is  shown,  and  "clicking"  on  one  of  its  rows  adds  this
  representation to the list of return values of BrowseAtlasInfo.
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  The  following  example  shows  how  BrowseAtlasInfo  can  be  used to fetch
  permutation representations of the alternating groups A_5 and A_6: We search
  for  the  group  name  "A5" in the overview table, and the first cell in the
  table  row  for  A_5  becomes  selected;  hitting the Enter key causes a new
  window  to  be opened, with an overview of the available representations for
  A_5;  moving  down  by  one  row  and hitting the Enter key again causes the
  second  representation  to be added to the result list, the second window is
  closed,  and  we  are back in the overview table; we move the selection down
  twice  (to  the  row for the group A_6), and choose the first representation
  for this group; finally we leave the table, and the return value is the list
  with the data for the two representations.
  
  ---------------------------  Example  ----------------------------
    gap> d:= [ NCurses.keys.DOWN ];;  r:= [ NCurses.keys.RIGHT ];;
    gap> c:= [ NCurses.keys.ENTER ];;
    gap> BrowseData.SetReplay( Concatenation(
    >        "/A5",         # Find the string A5 ...
    >        d, d, r,       # ... such that just the word matches,
    >        c,             # start the search,
    >        c,             # click the table entry A5,
    >        d,             # move down by one row,
    >        c,             # click the row for this representation,
    >        d, d,          # move down by two rows,
    >        c,             # click the table entry A6,
    >        c,             # click the first row,
    >        "Q" ) );       # and quit the application.
    gap> tworeps:= BrowseAtlasInfo();;
    gap> BrowseData.SetReplay( false );
    gap> if fail in tworeps then
    >      Print( "no access to the Web ATLAS\n" );
    >    else
    >      Print( List( tworeps, x -> x.identifier[1] ), "\n" );
    [ "A5", "A6" ]
    >    fi;
  ------------------------------------------------------------------
  
  Implementation remarks: The first browse table shown has a static header, no
  footer  and row labels, one row of column labels describing the type of data
  summarized in the columns.
  
  Row  and  column  separators  are  drawn as grids (cf. NCurses.Grid (2.2-8))
  composed  from  the special characters described in Section 2.1-6, using the
  component work.SpecialGrid of the browse table, see BrowseData (5.4-1).
  
  When  a row is selected, the "click" functionality opens a new window (via a
  second level call to NCurses.BrowseGeneric (4.3-1)), in which a browse table
  with  the list of available representations for the given group is shown; in
  this  table,  "click"  results  in adding the selected representation to the
  result list, and leaving the second level table, So one returns to the first
  browse  table  and  can  choose  further  representations,  perhaps of other
  groups.   When   the   first  level  table  is  left,  the  list  of  chosen
  representations is returned.
  
  This function is available only if the GAP package AtlasRep is available.
  
  The code can be found in the file app/atlasbrowse.g of the package.
  
  
  6.5 Access to GAP Manuals–a Variant
  
  A  Browse  adapted way to access several manuals is to show the hierarchy of
  books, chapters, sections, and subsections as collapsible category rows, and
  to  regard  the  contents  of each subsection as a data row of a matrix with
  only one column.
  
  This  application  is  mainly  intended  as an example with table cells that
  exceed the screen, and as an example with several category levels.
  
  6.5-1 BrowseGapManuals
  
  > BrowseGapManuals( [start] ) ______________________________________function
  
  This function displays the contents of the GAP manuals (the main GAP manuals
  as  well  as  the loaded package manuals) in a window. The optional argument
  start  describes  the  initial  status,  admissible  values  are the strings
  "inline/collapsed",      "inline/expanded",      "pager/collapsed",      and
  "pager/expanded".
  
  In the inline cases, the parts of the manuals are shown in the browse table,
  and  in  the  pager  case, the parts of the manuals are shown in a different
  window  when they are "clicked", using the user's favourite help viewer, see
  'Reference: Changing the Help Viewer'.
  
  In  the  collapsed cases, all category rows are collapsed, and the first row
  is  selected; typical next steps are moving down the selection and expanding
  single category rows. In the expanded cases, all category rows are expanded,
  and  nothing is selected; a typical next step in the inline/expanded case is
  a  search  for  a string in the manuals. (Note that searching in quite slow:
  For  viewing  a part of a manual, the file with the corresponding section is
  read  into GAP, the text is formatted, the relevant part is cut out from the
  section, perhaps markup is stripped off, and finally the search is performed
  in the resulting strings.)
  
  If  no  argument  is  given  then the user is asked for selecting an initial
  status, using NCurses.Select (3.1-2).
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  ---------------------------  Example  ----------------------------
    gap> n:= [ 14, 14, 14 ];;  # ``do nothing''
    gap> BrowseData.SetReplay( Concatenation(
    >        "xdxd",                             # expand a Tutorial section
    >        n, "Q" ) );                         # and quit
    gap> BrowseGapManuals( "inline/collapsed" );
    gap> BrowseData.SetReplay( Concatenation(
    >        "/Browse", [ NCurses.keys.ENTER ],  # search for "Browse"
    >        "xdxddxd",                          # expand a section
    >        n, "Q" ) );                         # and quit
    gap> BrowseGapManuals( "inline/collapsed" );
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks:  The  browse table has a dynamic header showing the
  name  of  the currently selected manual, no footer, no row or column labels,
  and  exactly  one  column  of  fixed  width  equal  to the screen width. The
  category rows are precomputed, i. e., they do not arise from a table column;
  this  way, the contents of each data cell can be computed on demand, as soon
  as  it  is  shown  on  the  screen,  in particular the category hierarchy is
  computed  without  reading the manuals into GAP. Also, the data rows are not
  cached.  There is no return value. The heights of many cells are bigger than
  the  screen  height, so scrolling is a mixture of scrolling to the next cell
  and  scrolling  inside a cell. The different initial states are realized via
  executing different initial steps before the table is shown to the user.
  
  For  the  variants  that  show  the manuals in a pager, the code temporarily
  replaces  the  show function of the default viewer "screen" (see 'Reference:
  Changing  the  Help  Viewer') by a function that uses NCurses.Pager (3.1-4).
  Note  that in the case that the manual bit in question fits into one screen,
  the  default show function writes this text directly to the screen, but this
  is used already by the browse table.
  
  The implementation should be regarded as a sketch.
  
  For  example, the markup available in the text file format of GAPDoc manuals
  (using  Esc  sequences)  is stripped off instead of being transferred to the
  attribute lines that arise, because of the highlighting problem mentioned in
  Section 2.2-3.
  
  Some  heuristics  used  in  the  code  are due to deficiencies of the manual
  formats.
  
  For  the  inline  variant  of  the  browse  table,  the  titles of chapters,
  sections, and subsections are not regarded as parts of the actual text since
  they appear already as category rows; however, the functions of the GAP help
  system  deliver  the text together with these titles, so these lines must be
  stripped off afterwards.
  
  The  category  hierarchy representing the tables of contents is created from
  the  manual.six  files  of  the  manuals.  These files do not contain enough
  information  for  determining  whether  several  functions  define  the same
  subsection,  in  the  sense  that there is a common description text after a
  series  of  manual lines introducing different functions. In such cases, the
  browse  table  contains a category row for each of these functions (with its
  own number), but the corresponding text appears only under the last of these
  category  rows,  the  data rows for the others are empty. (This problem does
  not  occur  in  the  GAPDoc  manual  format because this introduces explicit
  subsection   titles,   involving   only   the   first  of  several  function
  definitions.)
  
  Also, index entries and sectioning entries in manual.six files of manuals in
  GAPDoc format are not explicitly distinguished.
  
  The code can be found in the file app/manual.g of the package.
  
  
  6.6 Overview of the GAP Bibliography
  
  The  GAP  documentation contains a bibliography of GAP related publications,
  see [GAP].  Browse  provides  access  to  this information in GAP, using the
  standard  facilities  of  the function NCurses.BrowseGeneric (4.3-1), i. e.,
  one  can  scroll  in  the  list,  search for entries, sort by year, sort and
  categorize by authors etc.
  
  The   Browse   package   contains  a  (perhaps  outdated)  version  of  this
  bibliography. One can get an updated version as follows.
  
  wget -N http://www.gap-system.org/Doc/Bib/gap-publishednicer.bib
  
  6.6-1 BrowseBibliography
  
  > BrowseBibliography( [bibfiles] ) _________________________________function
  Returns:  a    record    as   returned   by   ParseBibXMLExtFiles   (GAPDoc:
            ParseBibXMLextFiles).
  
  This  function  shows the list of bibliography entries in the files given by
  bibfiles, which may be a string or a list of strings (denoting a filename or
  a  list of filenames, respectively) or a record (see below for the supported
  components).
  
  If  no  argument  is  given then the file bibl/gap-publishednicer.bib in the
  Browse  package  directory  is  taken, and "GAP Bibliography" is used as the
  header.
  
  Another  perhaps  interesting  data file that should be available in the GAP
  distribution is doc/manualbib.xml. This file can be located as follows.
  
  ---------------------------  Example  ----------------------------
    gap> file:= Filename( DirectoriesLibrary( "doc" ), "manualbib.xml" );;
  ------------------------------------------------------------------
  
  Both  BibTeX format and the XML based extended format provided by the GAPDoc
  package  are supported by BrowseBibliography, see Chapter 'GAPDoc: Utilities
  for Bibliographies'.
  
  In  the  case  of  BibTeX  format  input, first a conversion to the extended
  format  takes  place,  via StringBibAsXMLext (GAPDoc: StringBibAsXMLext) and
  ParseBibXMLextString (GAPDoc: ParseBibXMLextString). Note that syntactically
  incorrect  entries  are  rejected  in this conversion –this is signaled with
  InfoBibTools  (GAPDoc: InfoBibTools) warnings– and that only a subset of the
  possible  LaTeX  markup  is  recognized  –other markup appears in the browse
  table except that the leading backslash is removed.
  
  In both cases of input, the problem arises that in visual mode, currently we
  can  show  only  ASCII  characters (and the symbols in NCurses.lineDraw, but
  these  are  handled  differently,  see Section 2.1-6). Therefore, we use the
  function   SimplifiedUnicodeString   (GAPDoc:  SimplifiedUnicodeString)  for
  replacing other unicode characters by ASCII text.
  
  The  return  value  is  a record as returned by ParseBibXMLExtFiles (GAPDoc:
  ParseBibXMLextFiles),  its entries component corresponds to the bibliography
  entries  that have been "clicked" in visual mode. This record can be used as
  input for WriteBibFile (GAPDoc: WriteBibFile) or WriteBibXMLextFile (GAPDoc:
  WriteBibXMLextFile),  in  order to produce a bibliography file, or it can be
  used as input for StringBibXMLEntry (GAPDoc: StringBibXMLEntry), in order to
  produce strings from the entries, in various formats.
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  ---------------------------  Example  ----------------------------
    gap> # sort and categorize by year, scroll down, expand a category row
    gap> BrowseData.SetReplay( "scrrscsedddddxdddddQ" );
    gap> BrowseBibliography();;
    gap> # sort & categorize by authors, expand all category rows, scroll down
    gap> BrowseData.SetReplay( "scscXseddddddQ" );
    gap> BrowseBibliography();;
    gap> # sort and categorize by journal, search for a journal name, expand
    gap> BrowseData.SetReplay( Concatenation( "scrrrsc/J. Algebra",
    >        [ NCurses.keys.ENTER ], "nxdddQ" ) );
    gap> BrowseBibliography();;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks:  The browse table has a dynamic header (showing the
  number  of  entries, which can vary when the table is restricted), no footer
  and row labels; one row of column labels is given by the descriptions of the
  table columns (authors, title, year, journal).
  
  Row  and  column  separators  are  drawn as grids (cf. NCurses.Grid (2.2-8))
  composed  from  the special characters described in Section 2.1-6, using the
  component work.SpecialGrid of the browse table, see BrowseData (5.4-1).
  
  For categorizing by authors, the sort parameter "split rows on categorizing"
  is  set to "yes", so the authors are distributed to different category rows,
  hence  each  entry  appears  once for each of its authors in the categorized
  table.  When  a data row or an entry in a data row is selected, "click" adds
  the corresponding bibliographhy entry to the result.
  
  The width of the title column is preset; usually titles are too long for one
  line, and the contents of this column is formatted as a paragraph, using the
  function  FormatParagraph  (GAPDoc:  FormatParagraph).  For  the authors and
  journal columns, maximal widths are prescribed, and FormatParagraph (GAPDoc:
  FormatParagraph) is used for longer entries.
  
  For  three  columns, the sort parameters are defined as follows: The authors
  column  does  not  become  hidden when the table is categorized according to
  this column, sorting by the year yields a descending order, and the category
  rows  arising  from these columns and the journal column show the numbers of
  the data rows that belong to them.
  
  Those  standard  modes  in BrowseData (5.4-1) where an entry or a row of the
  table  is  selected  have  been  extended by three new actions, which open a
  pager  showing  the  BibTeX,  HTML,  and  Text format of the selected entry,
  respectively. The corresponding user inputs are the vb, vh, and vt.
  
  This  function  requires  some  of the utilities provided by the GAP package
  GAPDoc  (see [LN07]),  such  as  FormatParagraph  (GAPDoc: FormatParagraph),
  NormalizeNameAndKey   (GAPDoc:   NormalizeNameAndKey),  NormalizedNameAndKey
  (GAPDoc:   NormalizedNameAndKey),   ParseBibFiles  (GAPDoc:  ParseBibFiles),
  ParseBibXMLextFiles   (GAPDoc:   ParseBibXMLextFiles),  ParseBibXMLextString
  (GAPDoc: ParseBibXMLextString), RecBibXMLEntry (GAPDoc: RecBibXMLEntry), and
  StringBibAsXMLext (GAPDoc: StringBibAsXMLext).
  
  The code can be found in the file app/gapbibl.g of the package.
  
  The  browse  table can be customized by entering a record as the argument of
  BrowseBibliography, with the following supported components.
  
  files
        a nonempty list of filenames containing the data to be shown; there is
        no default for this component.
  
  filesshort
        a  list  of  the  same  length as the files component, the entries are
        strings  which  are  shown in the "sourcefilename" column of the table
        (if this column is present); the default is the list of filenames.
  
  filecontents
        a  list  of  the  same  length as the files component, the entries are
        strings  which  are  shown  as  category  values  when  the  table  is
        categorized by the "sourcefilename" column; the default is the list of
        filenames.
  
  header
        is  the  constant part of the header shown above the browse table, the
        default is the first filename.
  
  columns
        is  a  list  of  records  that  are  valid  as  the second argument of
        DatabaseAttributeAdd  (A.1-5),  where the first argument is a database
        id  enumerator  created  from the bibliography entries in the files in
        question.  Each  entry (and also the corresponding identifier) of this
        database   id   enumerator   is   a  list  of  records  obtained  from
        ParseBibXMLextFiles  (GAPDoc:  ParseBibXMLextFiles) and RecBibXMLEntry
        (GAPDoc:    RecBibXMLEntry),    or    from    ParseBibFiles   (GAPDoc:
        ParseBibFiles),  such that the list elements are regarded as equal, in
        the  sense  that their fingerprints (see below) are equal. The records
        in  the columns list are available for constructing the desired browse
        table,  the  actual  appearance  is controlled by the choice component
        described  below.  Columns  showing authors, title, year, journal, and
        filename are predefined and need not be listed here.
  
  choice
        a  list of strings denoting the identifier components of those columns
        that shall actually be shown in the table, the default is [ "authors",
        "title", "year", "journal" ].
  
  fingerprint
        a  list  of  strings  denoting  component  names of the entries of the
        database  id enumerator that is constructed from the data (see above);
        two  data  records  are  regarded  as  equal  if  the  values of these
        components   are   equal;   the  default  is  [  "mrnumber",  "title",
        "authorAsList", "editorAsList", "author" ].
  
  sortKeyFunction
        either  fail  or  a  function  that  takes  a  record  as  returned by
        RecBibXMLEntry  (GAPDoc:  RecBibXMLEntry)  and  returns a list that is
        used  for comparing and thus sorting the records; the default is fail,
        which  means that the rows of the table appear in the same ordering as
        in the source files.
  
  
  6.7 Overview of GAP Data
  
  The GAP system contains several data collections such as libraries of groups
  and character tables. Clearly the function NCurses.BrowseGeneric (4.3-1) can
  be used to visualize interesting information about such data collections, in
  the  form of an "overview table" whose rows correspond to the objects in the
  collection;  each column of the table shows a piece of information about the
  objects.   (One   possibility   to   create   such  overviews  is  given  by
  BrowseTableFromDatabaseIdEnumerator (A.2-2).)
  
  6.7-1 BrowseGapData
  
  > BrowseGapData(  ) ________________________________________________function
  Returns:  the return value of the chosen application if there is one.
  
  The    function    BrowseGapData    shows    the   choices   in   the   list
  BrowseData.GapDataOverviews,  in  a  browse  table  with one column. When an
  entry  is "clicked" then the associated function is called, and the table of
  choices is closed.
  
  The  idea  is  that  each  entry of BrowseData.GapDataOverviews describes an
  overview of a GAP data collection.
  
  The  Browse package provides overviews of the Conway polynomials in GAP, the
  GAP  bibliography  (see Section 6.6), the GAP manuals (see Section 6.5), GAP
  operations and methods, and the installed GAP packages.
  
  Other GAP packages may add more overviews to the choices, using the function
  BrowseGapDataAdd (6.7-2).
  
  Except that always one table cell is selected, the full functionality of the
  function NCurses.BrowseGeneric (4.3-1) is available.
  
  ---------------------------  Example  ----------------------------
    gap> n:= [ 14, 14, 14 ];;  # ``do nothing''
    gap> # open the overview of Conway polynomials
    gap> BrowseData.SetReplay( Concatenation( "/Conway Polynomials",
    >      [ NCurses.keys.ENTER, NCurses.keys.ENTER ], "srdddd", n, "Q" ) );
    gap> BrowseGapData();;
    gap> # open the overview of GAP packages
    gap> BrowseData.SetReplay( Concatenation( "/GAP Packages",
    >      [ NCurses.keys.ENTER, NCurses.keys.ENTER ], "/Browse",
    >      [ NCurses.keys.ENTER ], "n", n, "Q" ) );
    gap> BrowseGapData();;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks:  The  browse  table  has a static header, a dynamic
  footer  showing  the  description of the currently selected entry, no row or
  column  labels,  and  exactly  one column of fixed width equal to the screen
  width. If the chosen application has a return value then this is returned by
  BrowseGapData, otherwise nothing is returned. The component work.SpecialGrid
  of  the browse table is used to draw a border around the list of choices and
  another  border around the footer. Only one mode is needed in which an entry
  is selected.
  
  The code can be found in the file app/gapdata.g of the package.
  
  6.7-2 BrowseGapDataAdd
  
  > BrowseGapDataAdd( title, call, ret, documentation ) ______________function
  
  This  function  extends the list BrowseData.GapDataOverviews by a new entry.
  The list is used by BrowseGapData (6.7-1).
  
  title  must be a string of length at most 76; it will be shown in the browse
  table  that is opened by BrowseGapData (6.7-1). call must be a function that
  takes  no  arguments; it will be called when title is "clicked". ret must be
  true  if  call  has a return value and if BrowseGapData (6.7-1) shall return
  this  value,  and  false  otherwise.  documentation  must  be  a string that
  describes what happens when the function call is called; it will be shown in
  the  footer  of  the  table  opened  by  BrowseGapData (6.7-1) when title is
  selected.
  
  
  6.8 Navigating in a Directory Tree
  
  A  natural  way to visualize the contents of a directory is via a tree whose
  leaves  denote  plain  files,  and the other vertices denote subdirectories.
  Browse  provides  a  function  based  on  NCurses.BrowseGeneric  (4.3-1) for
  displaying such trees; the leaves correspond to the data rows, and the other
  vertices correspond to category rows.
  
  6.8-1 BrowseDirectory
  
  > BrowseDirectory( [startpath] ) ___________________________________function
  Returns:  a list of the "clicked" filenames.
  
  If no argument is given then the contents of the current directory is shown,
  see DirectoryCurrent (Reference: DirectoryCurrent). If a string startpath is
  given as the only argument then it is understood as a directory path, in the
  sense  of  Directory  (Reference:  Directory),  and  the  contents  of  this
  directory is shown.
  
  The  full  functionality  of  the  function NCurses.BrowseGeneric (4.3-1) is
  available.
  
  ---------------------------  Example  ----------------------------
    gap> n:= [ 14, 14, 14 ];;  # ``do nothing''
    gap> BrowseData.SetReplay( Concatenation(
    >        "q",                                  # leave the selection
    >        "X",                                  # expand all categories
    >        "/filetree", [ NCurses.keys.ENTER ],  # search for "filetree"
    >        n, "Q" ) );                           # and quit
    gap> dir:= Filename( DirectoriesPackageLibrary( "Browse", "" ), "" );;
    gap> BrowseDirectory( dir );;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks: The browse table has a static header, no footer, no
  row  or  column  labels,  and exactly one data column. The category rows are
  precomputed,  i. e.,  they  do  not  arise  from  a  table  column. The tree
  structure  is  visualized  via a special grid that is shown in the separator
  column  before  the  table column; the width of this column is computed from
  the  largest  nesting  depth  of files. For technical reasons, category rows
  representing  empty  directories  are  realized  via  "dummy"  table rows; a
  special ShowTables function guarantees that these rows are always hidden,
  
  When  a  data  row  or  an  entry  in this row is selected, "click" adds the
  corresponding  filename  to  the  result  list.  Initially, the first row is
  selected.  (So  if you want to search in the whole tree then you should quit
  this selection by hitting the q key.)
  
  The  category  hierarchy  is  computed  using  DirectoryContents (Reference:
  DirectoryContents).
  
  This  function  is  available  only  if  the GAP package IO (see [Neu07]) is
  available,  because  the  check  for  cycles  uses the function IO_stat (IO:
  IO_stat) from this package.
  
  The code can be found in the file app/filetree.g of the package.
  
  
  6.9 A Puzzle
  
  We  consider  an m by n rectangle of squares numbered from 1 to m n - 1, the
  bottom  right  square  is  left  empty. The numbered squares are permuted by
  successively  exchanging the empty square and a neighboring square such that
  in the end, the empty cell is again in the bottom right corner.
  
      ---------------------
      | 7  | 13 | 14 | 2  | 
      ---------------------
      | 1  | 4  | 15 | 11 | 
      ---------------------
      | 6  | 8  | 3  | 9  | 
      ---------------------
      | 10 | 5  | 12 |    | 
      ---------------------
  
  The aim of the game is to order the numbered squares via these moves.
  
  For  the  case  m  =  n  = 4, the puzzle is known under the name "Sam Loyd's
  Fifteen", see [Bog] and [OR] for more information and references.
  
  6.9-1 BrowsePuzzle
  
  > BrowsePuzzle( [m, n[, pi]] ) _____________________________________function
  Returns:  a record describing the initial and final status of the puzzle.
  
  This function shows the rectangle in a window.
  
  The  arguments  m and n are the dimensions of the rectangle, the default for
  both values is 4. The initial distribution of the numbers in the squares can
  be  prescribed  via a permutation pi, the default is a random element in the
  alternating  group on the points 1, 2, ..., m n - 1. (Note that the game has
  not always a solution.)
  
  In  any  case, the empty cell is selected, and the selection can be moved to
  neighboring  cells  via  the  arrow keys, or to any place in the same row or
  column via a mouse click.
  
  The  return  value  is a record with the components dim (the pair [ m, n ]),
  init  (the  initial  permutation),  final (the final permutation), and steps
  (the number of transpositions that were needed).
  
  ---------------------------  Example  ----------------------------
    gap> BrowseData.SetReplay( Concatenation(
    >        BrowsePuzzleSolution.steps, "Q" ) );
    gap> BrowsePuzzle( 4, 4, BrowsePuzzleSolution.init );;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  An implementation using only mouse clicks but no key strokes is available in
  the GAP package XGAP (see [CN04]).
  
  Implementation  remarks:  The  game board is implemented via a browse table,
  without  row  and  column  labels,  with  static header, dynamic footer, and
  individual  minyx  function.  Only  one  mode is needed in which one cell is
  selected,  and  besides  the standard actions for quitting the table, asking
  for  help,  and  saving the current window contents, only the four moves via
  the arrow keys and mouse clicks are admissible.
  
  Some   standard   NCurses.BrowseGeneric   (4.3-1)   functionality,  such  as
  scrolling, selecting, and searching, are not available in this application.
  
  The code can be found in the file app/puzzle.g of the package.
  
  
  6.10 Peg Solitaire
  
  Peg  solitaire  is  a  board game for one player. The game board consists of
  several  holes some of which contain pegs. In each step of the game, one peg
  is  moved  horizontally  or  vertically to an empty hole at distance two, by
  jumping over a neighboring peg which is then removed from the board.
  
                -------------
                | o | o | o |
                -------------
                | o | o | o |
        -----------------------------
        | o | o | o | o | o | o | o |
        -----------------------------
        | o | o | o |   | o | o | o |
        -----------------------------
        | o | o | o | o | o | o | o |
        -----------------------------
                | o | o | o |
                -------------
                | o | o | o |
                -------------
  
  We  consider  the game that in the beginning, exactly one hole is empty, and
  in the end, exactly one peg is left.
  
  6.10-1 PegSolitaire
  
  > PegSolitaire( [format, ][nrholes, ][twoModes] ) __________________function
  
  This function shows the game board in a window.
  
  If  the  argument format is one of the strings "small" or "large" then small
  or large pegs are shown, the default is "small".
  
  Three  shapes  of  the  game board are supported, with 33, 37, and 45 holes,
  respectively;  this  number  can  be specified via the argument nrholes, the
  default  is  33.  In  the cases of 33 and 45 holes, the position of both the
  initial  hole  and  the  destination  of  the  final peg is the middle cell,
  whereas  in  the  case  of  37  holes,  the  initial hole is in the top left
  position and the final peg has to be placed in the bottom right position.
  
  If a Boolean twoModes is entered as an argument then it determines whether a
  browse  table  with  one  or  two  modes is used; the default false yields a
  browse table with only one mode.
  
  In  any  case,  one  cell of the board is selected, and the selection can be
  moved  to  neighboring  cells via the arrow keys. A peg in the selected cell
  jumps  over  a neighboring peg to an adjacent hole via the j key followed by
  the appropriate arrow key.
  
  ---------------------------  Example  ----------------------------
    gap> for n in [ 33, 37, 45 ] do
    >      BrowseData.SetReplay( Concatenation(
    >          PegSolitaireSolutions.( String( n ) ), "Q" ) );
    >      PegSolitaire( n );
    > od;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  For  more  information  such  as  variations  of  the  game  and references,
  see [Köla].  Also the solutions stored in the variable PegSolitaireSolutions
  have been taken from this web page.
  
  Implementation  remarks:  The  game board is implemented via a browse table,
  without  row  and  column  labels,  with  static header, dynamic footer, and
  individual  minyx  function.  In fact, two implementations are provided. The
  first  one  needs  only  one  mode in which one cell is selected; moving the
  selection  and  jumping with the peg in the selected cell in one of the four
  directions  are  the supported user actions. The second implementation needs
  two modes, one for moving the selection and one for jumping.
  
  Some   standard   NCurses.BrowseGeneric   (4.3-1)   functionality,  such  as
  scrolling, selecting, and searching, are not available in this application.
  
  The code can be found in the file app/solitair.g of the package.
  
  
  6.11 Rubik's Cube
  
  We  visualize  the  transformations of Rubik's magic cube in a model that is
  given by "unfolding" the faces and numbering them as follows.
  
                     +--------------+
                     |  1    2    3 |
                     |  4   top   5 |
                     |  6    7    8 |
      +--------------+--------------+--------------+--------------+
      |  9   10   11 | 17   18   19 | 25   26   27 | 33   34   35 |
      | 12  left  13 | 20 front  21 | 28 right  29 | 36  back  37 |
      | 14   15   16 | 22   23   24 | 30   31   32 | 38   39   40 |
      +--------------+--------------+--------------+--------------+
                     | 41   42   43 |
                     | 44  down  45 |
                     | 46   47   48 |
                     +--------------+
  
  Clockwise  turns of the six layers (top, left, front, right, back, and down)
  are represented by the following permutations.
  
  ---------------------------  Example  ----------------------------
    gap> cubegens := [
    >   ( 1, 3, 8, 6)( 2, 5, 7, 4)( 9,33,25,17)(10,34,26,18)(11,35,27,19),
    >   ( 9,11,16,14)(10,13,15,12)( 1,17,41,40)( 4,20,44,37)( 6,22,46,35),
    >   (17,19,24,22)(18,21,23,20)( 6,25,43,16)( 7,28,42,13)( 8,30,41,11),
    >   (25,27,32,30)(26,29,31,28)( 3,38,43,19)( 5,36,45,21)( 8,33,48,24),
    >   (33,35,40,38)(34,37,39,36)( 3, 9,46,32)( 2,12,47,29)( 1,14,48,27),
    >   (41,43,48,46)(42,45,47,44)(14,22,30,38)(15,23,31,39)(16,24,32,40)
    > ];;
  ------------------------------------------------------------------
  
  GAP  computations  analyzing  this  permutation  group have been part of the
  announcements of GAP 3 releases. For a GAP 4 equivalent, see [Sch]. For more
  information and references (not GAP related) about Rubik's cube, see [Kölb].
  
  6.11-1 BrowseRubiksCube
  
  > BrowseRubiksCube( [format, ][pi] ) _______________________________function
  
  This function shows the model of the cube in a window.
  
  If  the  argument format is one of the strings "small" or "large" then small
  or large cells are shown, the default is "small".
  
  The  argument  pi  is the initial permutation of the faces, the default is a
  random permutation in the cube group, see 'Reference: Random'.
  
  Supported  user inputs are the keys t, l, f, r, b, and d for clockwise turns
  of   the   six   layers,   and   the   corresponding   capital  letters  for
  counter-clockwise  turns.  If the terminal supports colors, according to the
  global  variable  NCurses.attrs.has_colors  (2.2-1),  the  input  s switches
  between  a  screen that shows only the colors of the faces and a screen that
  shows the numbers; the color screen is the default.
  
  The return value is a record with the components inputs (a string describing
  the  user inputs), init, and final (the initial and final permutation of the
  faces,  respectively).  (The  inputs  component  can  be used for the replay
  feature, see the example below.)
  
  In  the  following  example,  a  word  in terms of the generators is used to
  initialize the browse table, and then the letters in this word are used as a
  series  of input steps, except that in between, the display is switched once
  from colors to numbers and back.
  
  ---------------------------  Example  ----------------------------
    gap> choice:= List( [ 1 .. 30 ], i -> Random( [ 1 .. 6 ] ) );;
    gap> input:= List( "tlfrbd", INT_CHAR ){ choice };;
    gap> BrowseData.SetReplay( Concatenation(
    >        input{ [ 1 .. 20 ] },
    >        "s",                    # switch to number display
    >        input{ [ 21 .. 25 ] },
    >        "s",                    # switch to color display
    >        input{ [ 26 .. 30 ] },
    >        "Q" ) );;               # quit the browse table
    gap> BrowseRubiksCube( Product( cubegens{ choice } ) );;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks: The cube is implemented via a browse table, without
  row  and  column  labels, with static header, dynamic footer, and individual
  minyx  function.  Only  one mode is needed, and besides the standard actions
  for  quitting  the  table,  asking  for  help, and saving the current window
  contents,  only  the  twelve  moves  and the switch between color and number
  display are admissible.
  
  Switching  between  the  two  display  formats is implemented via a function
  work.Main, so this relies on not caching the formatted cells in work.main.
  
  Row  and  column separators of the browse table are whitespace of height and
  width  one.  The  separating lines are drawn using an individual SpecialGrid
  function  in  the  browse  table. Note that the relevant cells do not form a
  rectangular array.
  
  Some   standard   NCurses.BrowseGeneric   (4.3-1)   functionality,  such  as
  scrolling, selecting, and searching, are not available in this application.
  
  The code can be found in the file app/rubik.g of the package.
  
  
  6.12 Changing Sides
  
  We  consider  a  5 by 5 board of squares filled with two types of stones, as
  follows. The square in the middle is left empty.
  
             ---------------------
             | x | x | x | x | x |
             ---------------------
             | o | x | x | x | x |
             ---------------------
             | o | o |   | x | x |
             ---------------------
             | o | o | o | o | x |
             ---------------------
             | o | o | o | o | o |
             ---------------------
  
  The aim of the game is to exchange the two types of stones via a sequence of
  single  steps  that  move one stone to the empty position on the board. Only
  those  moves  are  allowed that increase or decrease one coordinate by 2 and
  increase  or  decrease  the  other  by 1; these are the allowed moves of the
  knight in chess.
  
  This game has been part of the MacTutor system [OR00].
  
  6.12-1 BrowseChangeSides
  
  > BrowseChangeSides(  ) ____________________________________________function
  
  This function shows the game board in a window.
  
  Each  move  is  encoded  as  a  sequence  of  three arrow keys; there are 24
  admissible inputs.
  
  ---------------------------  Example  ----------------------------
    gap> for entry in BrowseChangeSidesSolutions do
    >      BrowseData.SetReplay( Concatenation( entry, "Q" ) );
    >      BrowseChangeSides();
    > od;
    gap> BrowseData.SetReplay( false );
  ------------------------------------------------------------------
  
  Implementation  remarks:  The  game board is implemented via a browse table,
  without  row  and  column  labels,  with  static header, dynamic footer, and
  individual minyx function. Only one mode is needed, and besides the standard
  actions  for  quitting  the  table,  asking for help, and saving the current
  window  contents,  only  moves  via  combinations of the four arrow keys are
  admissible.
  
  The  separating  lines are drawn using an individual SpecialGrid function in
  the browse table.
  
  Some   standard   NCurses.BrowseGeneric   (4.3-1)   functionality,  such  as
  scrolling, selecting, and searching, are not available in this application.
  
  The code can be found in the file app/knight.g of the package.
  
  
  6.13 Sudoku
  
  We  consider  a  9  by 9 board of squares. Some squares are initially filled
  with  numbers  from 1 to 9. The aim of the game is to fill the empty squares
  in  such  a  way  that  each row, each column, and each of the marked 3 by 3
  subsquares contains all numbers from 1 to 9. A proper Sudoku game is defined
  as one with a unique solution. Here is an example.
  
                   ------------- ------------- -------------
                   |   |   |   | |   |   |   | | 5 |   |   |
                   ------------- ------------- -------------
                   |   | 1 | 5 | | 4 |   | 6 | |   | 2 |   |
                   ------------- ------------- -------------
                   | 9 |   |   | |   | 5 |   | | 3 |   |   |
                   ------------- ------------- -------------
                   ------------- ------------- -------------
                   | 6 |   | 4 | |   |   |   | |   |   |   |
                   ------------- ------------- -------------
                   |   |   |   | | 8 |   |   | |   |   |   |
                   ------------- ------------- -------------
                   | 8 |   |   | | 9 |   |   | |   | 5 | 3 |
                   ------------- ------------- -------------
                   ------------- ------------- -------------
                   |   |   |   | |   |   | 5 | |   |   |   |
                   ------------- ------------- -------------
                   |   | 4 |   | |   |   | 7 | |   |   | 2 |
                   ------------- ------------- -------------
                   |   |   | 9 | | 1 |   |   | | 8 |   |   |
                   ------------- ------------- -------------
  
  The Browse package contains functions to create, play and solve these games.
  There  are  basic  command line functions for this, which we describe first,
  and there is a user interface PlaySudoku (6.13-7) which is implemented using
  the generic browse functionality described in Chapter 4.
  
  6.13-1 Sudoku.Init
  
  > Sudoku.Init( [arg] ) _____________________________________________function
  Returns:  A record describing a Sudoku board or fail.
  
  This  function constructs a record describing a Sudoku game. This is used by
  the  other  functions described below. There a several possibilities for the
  argument arg.
  
  arg is a string
        The  entries  of  a Sudoku board are numbered row-wise from 1 to 81. A
        board  is encoded as a string as follows. If one of the numbers 1 to 9
        is  in  entry  i  then the corresponding digit character is written in
        position  i  of the string. If an entry is empty any character, except
        '1'  to  '9'  or  '|' is written in position i of the string. Trailing
        empty  entries  can  be  left  out.  Afterwards  '|'-characters can be
        inserted  in  the string (for example to mark line ends). Such strings
        can be used for arg.
  
  arg is a matrix
        A  Sudoku board can also be encoded as a 9 by 9-matrix, that is a list
        of  9 lists of length 9, whose (i,j)-th entry is the (i,j)-th entry of
        the  board  as  integer if it is not empty. Empty entries of the board
        correspond to unbound entries in the matrix.
  
  arg is a list of integers
        Instead of the matrix just described the argument can also be given by
        the  concatenation  of  the rows of the matrix (so, a list of integers
        and holes).
  
  ---------------------------  Example  ----------------------------
    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;
  ------------------------------------------------------------------
  
  6.13-2 Sudoku.Place
  
  > Sudoku.Place( game, i, n ) _______________________________________function
  > Sudoku.Remove( game, i ) _________________________________________function
  Returns:  The changed game.
  
  Here  game is a record describing a Sudoku board, as returned by Sudoku.Init
  (6.13-1).  The argument i is the number of an entry, counted row-wise from 1
  to  81,  and  n  is  an integer from 1 to 9 to be placed on the board. These
  functions change game.
  
  Sudoku.Place  tries  to place number n on entry i. It is an error if entry i
  is  not  empty.  The  number  is not placed if n is already used in the row,
  column  or  subsquare of entry i. In this case the component game.impossible
  is bound.
  
  Sudoku.Remove  tries to remove the number placed on position i of the board.
  It  does  not  change the board if entry i is empty, or if entry i was given
  when  the  board  game  was  created.  In the latter case game.impossible is
  bound.
  
  ---------------------------  Example  ----------------------------
    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;
    gap> Sudoku.Place(game, 1, 3);; # 3 is already in first row
    gap> IsBound(game.impossible);
    true
    gap> Sudoku.Place(game, 1, 2);; # 2 is not in row, col or subsquare
    gap> IsBound(game.impossible);
    false
  ------------------------------------------------------------------
  
  6.13-3 Sudoku.RandomGame
  
  > Sudoku.RandomGame( [seed] ) ______________________________________function
  Returns:  A pair [str, seed] of string and seed.
  
  The  optional argument seed, if given, must be an integer. If not given some
  random integer from the current GAP session is used. This function returns a
  random  proper Sudoku game, where the board is described by a string str, as
  explained  in  Sudoku.Init  (6.13-1).  With  the same seed the same board is
  returned.
  
  The  games  computed  by this function have the property that after removing
  any given entry the puzzle does no longer have a unique solution.
  
  ---------------------------  Example  ----------------------------
    gap> Sudoku.RandomGame(5833750);
    [ " 1         2     43  2   68   72    8     6 2   1 9 8  8 3   \
    9     47 3   7  18  ", 5833750 ]
    gap> last = Sudoku.RandomGame(last[2]);
    true
  ------------------------------------------------------------------
  
  6.13-4 Sudoku.SimpleDisplay
  
  > Sudoku.SimpleDisplay( game ) _____________________________________function
  
  Displays  a Sudoku board on the terminal. (But see PlaySudoku (6.13-7) for a
  fancier interface.)
  
  ---------------------------  Example  ----------------------------
    gap> game := Sudoku.Init(" 3   68  | 85  1 69|  97   53|      79 |\
    >  6  47   |45  2    |89   2 1 | 4   8 7 | ");;
    gap> Sudoku.SimpleDisplay(game);
     3 |  6|8  
     85|  1| 69
      9|7  | 53
    -----------
       |   |79 
     6 | 47|   
    45 | 2 |   
    -----------
    89 |  2| 1 
     4 |  8| 7 
       |   |   
  ------------------------------------------------------------------
  
  6.13-5 Sudoku.OneSolution
  
  > Sudoku.OneSolution( game ) _______________________________________function
  Returns:  A completed Sudoku board that solves game, or fail.
  
  Here  game must be a Sudoku board as returned by Sudoku.Init (6.13-1). It is
  not  necessary  that  game  describes  a  proper  Sudoku  game (has a unique
  solution).  It  may  have  several  solutions,  then  one random solution is
  returned. Or it may have no solution, then fail is returned.
  
  ---------------------------  Example  ----------------------------
    gap> Sudoku.SimpleDisplay(Sudoku.OneSolution(Sudoku.Init("  3")));
    493|876|251
    861|542|739
    527|193|648
    -----------
    942|618|573
    156|739|482
    738|425|916
    -----------
    289|354|167
    375|961|824
    614|287|395
  ------------------------------------------------------------------
  
  6.13-6 Sudoku.UniqueSolution
  
  > Sudoku.UniqueSolution( game ) ____________________________________function
  Returns:  A completed Sudoku board that solves game, or false, or fail.
  
  Here  game must be a Sudoku board as returned by Sudoku.Init (6.13-1). It is
  not  necessary  that  game describes a proper Sudoku game. If it has several
  solutions,  then  false  is  returned.  If  it has no solution, then fail is
  returned. Otherwise a board with the unique solution is returned.
  
  ---------------------------  Example  ----------------------------
    gap> s := "      5  | 154 6 2 |9   5 3  |6 4      |   8     |8  9   53\
    > |     5   | 4   7  2|  91  8  ";;
    gap> sol := Sudoku.UniqueSolution(Sudoku.Init(s));;
    gap> Sudoku.SimpleDisplay(sol);
    438|219|576
    715|436|928
    962|758|314
    -----------
    694|573|281
    153|862|749
    827|941|653
    -----------
    281|695|437
    546|387|192
    379|124|865
  ------------------------------------------------------------------
  
  6.13-7 PlaySudoku
  
  > PlaySudoku( [arg] ) ______________________________________________function
  Returns:  A record describing the latest status of a Sudoku board.
  
  This  function  allows  one to solve Sudoku puzzles interactively. There are
  several  possibilities  for  the  optional  argument arg. It can either be a
  string,  matrix  or  list  of holes and integers as described in Sudoku.Init
  (6.13-1),  or  a  board as returned by Sudoku.Init (6.13-1). Furthermore arg
  can  be  an integer or not be given, in that case Sudoku.RandomGame (6.13-3)
  is called to produce a random game.
  
  The  usage of this function is self-explanatory, pressing the ? key displays
  a  help screen. Here, we mention two keys with a particular action: Pressing
  the  h  key  you  get a hint, either an empty entry is filled or the program
  tells you that there is no solution (so you must delete some entries and try
  others).  Pressing the s key the puzzle is solved by the program or it tells
  you that there is no or no unique solution.
  
  Implementation  remarks:  The  game board is implemented via a browse table,
  without  row  and  column  labels,  with  static header, dynamic footer, and
  individual  minyx  function.  Two  modes  are  supported,  with the standard
  actions  for quitting the table and asking for help; one cell is selected in
  each  mode. The first mode provides actions for moving the selected cell via
  arrow  keys, for changing the value in the selected cell, for getting a hint
  or  the  (unique) solution. (Initial entries of the matrix cannot be changed
  via  user  input.  They  are  shown in boldface.) The second mode serves for
  error handling: When the user enters an invalid number, i. e., a number that
  occurs  already  in  the  current  row  or  column  or  subsquare,  then the
  application  switches  to this mode, which causes that a message is shown in
  the  footer,  and the invalid entry is shown in red and blinking; similarly,
  error mode is entered if a hint or solution does not exist.
  
  The  separating  lines are drawn using an individual SpecialGrid function in
  the  browse  table, since they cannot be specified within the generic browse
  table functions.
  
  Some   standard   NCurses.BrowseGeneric   (4.3-1)   functionality,  such  as
  scrolling, selecting, and searching, are not available in this application.
  
  The code can be found in the file app/sudoku.g of the package.
  
  6.13-8 Sudoku.HTMLGame
  
  > Sudoku.HTMLGame( game ) __________________________________________function
  > Sudoku.LaTeXGame( game ) _________________________________________function
  Returns:  A string with HTML or LaTeX code, respectively.
  
  The  argument of these functions is a record describing a Sudoku game. These
  functions  return  code for including the current status of the board into a
  webpage or a LaTeX document.