Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /usr/share/gap/doc/ref/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //usr/share/gap/doc/ref/chap79.txt

  
  79 Creating New Objects
  
  This chapter is divided into three parts.
  
  In  the  first  part, it is explained how to create filters (seeĀ 79.1, 79.2,
  79.3,  79.4),  operations (seeĀ 79.5), families (seeĀ 79.7), types (seeĀ 79.8),
  and objects with given type (seeĀ 79.9).
  
  In  the  second part, first a few small examples are given, for dealing with
  the  usual  cases  of  component  objects (seeĀ 79.10) and positional objects
  (seeĀ 79.11),  and  for  the  implementation of new kinds of lists (seeĀ 79.12
  andĀ 79.15).  Finally,  the  external representation of objects is introduced
  (seeĀ 79.16), as a tool for representation independent access to an object.
  
  The  third part deals with some rules concerning the organization of the GAP
  library;  namely,  some commands for creating global variables are explained
  (seeĀ 79.18)  that  correspond to the ones discussed in the first part of the
  chapter,  and the idea of distinguishing declaration and implementation part
  of GAP packages is outlined (seeĀ 79.19).
  
  See  also  ChapterĀ 81 for examples how the functions from the first part are
  used,  and why it is useful to have a declaration part and an implementation
  part.
  
  
  79.1 Creating Categories
  
  79.1-1 NewCategory
  
  NewCategory( name, super[, rank] )  function
  
  NewCategory  returns  a  new  category  cat  that  has  the name name and is
  contained in the filter super, seeĀ 13.2. This means that every object in cat
  lies  automatically  also  in  super.  We  say also that super is an implied
  filter of cat.
  
  For  example, if one wants to create a category of group elements then super
  should  be IsMultiplicativeElementWithInverse (31.14-13) or a subcategory of
  it.  If  no  specific  supercategory  of cat is known, super may be IsObject
  (12.1-1).
  
  The  optional third argument rank denotes the incremental rank (seeĀ 13.2) of
  cat, the default value is 1.
  
  79.1-2 CategoryFamily
  
  CategoryFamily( cat )  function
  
  For  a category cat, CategoryFamily returns the family category of cat. This
  is  a  category in which all families lie that know from their creation that
  all their elements are in the category cat, seeĀ 79.7.
  
  For   example,   a   family   of   associative  words  is  in  the  category
  CategoryFamily(  IsAssocWord  ),  and one can distinguish such a family from
  others by this category. So it is possible to install methods for operations
  that require one argument to be a family of associative words.
  
  CategoryFamily is quite technical, and in fact of minor importance.
  
  See also CategoryCollections (30.2-4).
  
  
  79.2 Creating Representations
  
  79.2-1 NewRepresentation
  
  NewRepresentation( name, super, slots[, req] )  function
  
  NewRepresentation  returns  a  new representation rep that has the name name
  and  is  a  subrepresentation  of  the representation super. This means that
  every object in rep lies automatically also in super. We say also that super
  is an implied filter of rep.
  
  Each representation in GAP is a subrepresentation of exactly one of the four
  representations    IsInternalRep,   IsDataObjectRep,   IsComponentObjectRep,
  IsPositionalObjectRep.  The data describing objects in the former two can be
  accessed  only  via GAP kernel functions, the data describing objects in the
  latter  two is accessible also in library functions, seeĀ 79.10 andĀ 79.11 for
  the details.
  
  The  third argument slots is a list either of integers or of strings. In the
  former case, rep must be IsPositionalObjectRep or a subrepresentation of it,
  and  slots tells what positions of the objects in the representation rep may
  be  bound.  In  the  latter  case,  rep  must  be  IsComponentObjectRep or a
  subrepresentation  of,  and  slots  lists the admissible names of components
  that  objects  in  the representation rep may have. The admissible positions
  resp. component names of super need not be be listed in slots.
  
  The incremental rank (seeĀ 13.2) of rep is 1.
  
  Note  that  for  objects  in  the  representation rep, of course some of the
  component names and positions reserved via slots may be unbound.
  
  Examples  for the use of NewRepresentation can be found inĀ 79.10, 79.11, and
  also in 81.3.
  
  
  79.3 Creating Attributes and Properties
  
  Each   method  that  is  installed  for  an  attribute  or  a  property  via
  InstallMethod  (78.2-1) must require exactly one argument, and this must lie
  in  the  filter  filter  that was entered as second argument of NewAttribute
  (79.3-1) resp. NewProperty (79.3-2).
  
  As  for  any  operation  (seeĀ 79.5),  for  attributes and properties one can
  install  a  method  taking  an  argument  that  does  not  lie  in  filt via
  InstallOtherMethod  (78.2-2), or a method for more than one argument; in the
  latter case, clearly the result value is not stored in any of the arguments.
  
  79.3-1 NewAttribute
  
  NewAttribute( name, filter[, "mutable"][, rank] )  function
  
  NewAttribute  returns  a  new  attribute  getter  with  name  name  that  is
  applicable to objects with the property filter.
  
  Contrary to the situation with categories and representations, the tester of
  the  new  attribute  does  not  imply filter. This is exactly because of the
  possibility to install methods that do not require filter.
  
  For  example, the attribute Size (30.4-6) was created with second argument a
  list  or  a collection, but there is also a method for Size (30.4-6) that is
  applicable to a character table, which is neither a list nor a collection.
  
  For  the  optional  third  and  fourth  arguments,  there  are the following
  possibilities.
  
      The  integer  argument  rank  causes the attribute tester to have this
        incremental rank (seeĀ 13.2),
  
      If  the  argument mutable is the string "mutable" or the boolean true,
        then the values of the attribute are mutable.
  
      If  the  argument mutable is the boolean false, then the values of the
        attribute are immutable.
  
  When  a  value  of  such  mutable attribute is set then this value itself is
  stored,  not an immutable copy of it, and it is the user's responsibility to
  set  an  object that is mutable. This is useful for an attribute whose value
  is  some partial information that may be completed later. For example, there
  is  an  attribute  ComputedSylowSubgroups  for  the list holding those Sylow
  subgroups  of  a  group  that  have  been  computed  already by the function
  SylowSubgroup  (39.13-1),  and  this list is mutable because one may want to
  enter groups into it as they are computed.
  
  If no argument for rank is given, then the rank of the tester is 1.
  
  Each  method for the new attribute that does not require its argument to lie
  in filter must be installed using InstallOtherMethod (78.2-2).
  
  79.3-2 NewProperty
  
  NewProperty( name, filter[, rank] )  function
  
  NewProperty  returns a new property prop with name name (see alsoĀ 13.7). The
  filter  filter  describes  the  involved  filters of prop. As in the case of
  attributes, filter is not implied by prop.
  
  The  optional third argument rank denotes the incremental rank (seeĀ 13.2) of
  the property prop itself, i.e. not of its tester; the default value is 1.
  
  
  79.4 Creating Other Filters
  
  In  order to change the value of filt for an object obj, one can use logical
  implications (seeĀ 78.7) or SetFilterObj (79.4-2), ResetFilterObj (79.4-3).
  
  79.4-1 NewFilter
  
  NewFilter( name[, rank] )  function
  
  NewFilter  returns  a  simple filter with name name (seeĀ 13.8). The optional
  second  argument rank denotes the incremental rank (seeĀ 13.2) of the filter,
  the default value is 1.
  
  The default value of the new simple filter for each object is false.
  
  79.4-2 SetFilterObj
  
  SetFilterObj( obj, filter )  function
  
  SetFilterObj sets the value of filter (and of all filters implied by filter)
  for obj to true,
  
  79.4-3 ResetFilterObj
  
  ResetFilterObj( obj, filter )  function
  
  ResetFilterObj  sets  the value of filter for obj to false. (Implied filters
  of  filt  are  not  touched.  This  might  create inconsistent situations if
  applied carelessly).
  
  
  79.5 Creating Operations
  
  79.5-1 NewOperation
  
  NewOperation( name, args-filts )  function
  
  NewOperation  returns  an  operation opr with name name. The list args-filts
  describes  requirements  about  the  arguments  of opr, namely the number of
  arguments  must  be equal to the length of args-filts, and the i-th argument
  must lie in the filter args-filts[i].
  
  Each  method  that  is  installed  for  opr  via InstallMethod (78.2-1) must
  require that the i-th argument lies in the filter args-filts[i].
  
  One  can  install  methods  for other argument tuples via InstallOtherMethod
  (78.2-2),  this  way  it is also possible to install methods for a different
  number of arguments than the length of args-filts.
  
  
  79.6 Creating Constructors
  
  79.6-1 NewConstructor
  
  NewConstructor( name, args-filts )  function
  
  NewConstructor   returns  a  constructor  cons  with  name  name.  The  list
  args-filts  describes  requirements  about the arguments of cons. Namely the
  number  of arguments must be equal to the length of args-filts, and the i-th
  argument  must  lie  in  the  filter  args-filts[i] for i ≠ 1. A constructor
  expects  the  first argument to be a filter instead of an object and it must
  be a subset of the filter args-filts[1].
  
  Each  method  that  is  installed  for  cons via InstallMethod (78.2-1) must
  require  that  the i-th argument lies in the filter args-filts[i] for i ≠ 1.
  Its  first  argument  is  a  filter  and  must  be  a  subset  of the filter
  args-filts[1].
  
  One  can  install  methods  for other argument tuples via InstallOtherMethod
  (78.2-2),  this  way  it is also possible to install methods for a different
  number of arguments than the length of args-filts.
  
  Note  that  the method selection for constructors works slightly differently
  than  for  usual  operations.  As  stated  above,  applicabilty to the first
  argument  in  an  argument  tuple  is  tested  by  determining  whether  the
  argument-filter is a subset of args-filts[1].
  
  The  rank  of  a  method installed for a constructor is determined solely by
  args-filts[1]  of  the  method.  Instead  of  taking the sum of the ranks of
  filters  involved  in its args-filts[1], the sum of -1 times these values is
  taken.  The  result  is  added  to  the  number  val  used  in  the  call of
  InstallMethod (78.2-1).
  
  This  has the following effects on the method selection for constructors. If
  cons  is  called  with  an argument tuple whose first argument is the filter
  filt,  any  method  whose  first  argument  is  more  specific  than filt is
  applicable  (if  its  other args-filts also match). Then the method with the
  most  general  filter args-filts[1] is chosen, since the rank is computed by
  taking  -1  times  the ranks of the involved filters. Thus, a constructor is
  chosen  which  returns  an  object  in  filt  using  as few extra filters as
  possible,  which  presumably  is  both  more  flexible  to use and easier to
  construct.
  
  The  following  example  showcases  this  behaviour.  Note that the argument
  filter is only used for method dispatch.
  
    Example  
    DeclareFilter( "IsMyObj" );
    DeclareFilter( "IsMyFilter" );
    DeclareFilter( "IsMyOtherFilter" );
    BindGlobal( "MyFamily", NewFamily( "MyFamily" ) );
    
    DeclareConstructor( "NewMyObj", [ IsMyObj ] );
    
    InstallMethod( NewMyObj,
    [ IsMyObj ],
    function( filter )
        local type;
        Print("General constructor\n");
        type := NewType( MyFamily, IsMyObj );
        return Objectify( type, [] );
    end );
    InstallMethod( NewMyObj,
    [ IsMyObj and IsMyFilter and IsMyOtherFilter ],
    function( filter )
        local type;
        Print("Special constructor\n");
        type := NewType( MyFamily, IsMyObj and IsMyFilter and IsMyOtherFilter );
        return Objectify( type, [] );
    end );
  
  
  If  only  IsMyObj  is  given,  both  methods  are applicable and the general
  constructor  is  called.  If  also  IsMyFilter  is  given,  only the special
  constructor is applicable.
  
    Example  
    gap> a := NewMyObj( IsMyObj );;
    General constructor
    gap> IsMyOtherFilter(a);
    false
    gap> b := NewMyObj( IsMyObj and IsMyFilter );;
    Special constructor
    gap> IsMyOtherFilter(b);
    true
    gap> c := NewMyObj( IsMyObj and IsMyFilter and IsMyOtherFilter );;
    Special constructor
    gap> IsMyOtherFilter(c);
    true
  
  
  
  79.7 Creating Families
  
  Families are probably the least obvious part of the GAP type system, so some
  remarks  about  the  role of families are necessary. When one uses GAP as it
  is,  one  will (better: should) not meet families at all. The two situations
  where families come into play are the following.
  
  First,  since  families  are used to describe relations between arguments of
  operations  in  the  method  selection  mechanism  (see ChapterĀ 78, and also
  ChapterĀ 13),   one   has  to  prescribe  such  a  relation  in  each  method
  installation (seeĀ 78.2); usual relations are ReturnTrue (5.4-1) (which means
  that  any  relation  of  the actual arguments is admissible), IsIdenticalObj
  (12.5-1)  (which  means  that  there  are two arguments that lie in the same
  family),  and  IsCollsElms  (which  means  that there are two arguments, the
  first  being  a  collection  of  elements that lie in the same family as the
  second argument).
  
  Second  –and  this is the more complicated situation– whenever one creates a
  new  kind of objects, one has to decide what its family shall be. If the new
  object  shall  be  equal  to  existing  objects,  for  example if it is just
  represented  in a different way, there is no choice: The new object must lie
  in  the same family as all objects that shall be equal to it. So only if the
  new  object is different (w.r.t.Ā the equality =) from all other GAP objects,
  we are likely to create a new family for it. Note that enlarging an existing
  family  by  such new objects may be problematic because of implications that
  have been installed for all objects of the family in question. The choice of
  families  depends  on  the applications one has in mind. For example, if the
  new  objects  in  question  are not likely to be arguments of operations for
  which   family   relations  are  relevant  (for  example  binary  arithmetic
  operations), one could create one family for all such objects, and regard it
  as the family of all those GAP objects that would in fact not need a family.
  On the other extreme, if one wants to create domains of the new objects then
  one  has  to choose the family in such a way that all intended elements of a
  domain  do  in  fact  lie  in  the same family. (Remember that a domain is a
  collection,  see ChapterĀ 12.4, and that a collection consists of elements in
  the same family, see ChapterĀ 30 and SectionĀ 13.1.)
  
  Let  us  look  at  an example. Suppose that no permutations are available in
  GAP,  and that we want to implement permutations. Clearly we want to support
  permutation  groups,  but it is not a priori clear how to distribute the new
  permutations  into  families.  We  can put all permutations into one family;
  this  is  how in fact permutations are implemented in GAP. But it would also
  be possible to put all permutations of a given degree into a family of their
  own;  this  would  for  example  mean  that  for each degree, there would be
  distinguished  trivial  permutations, and that the stabilizer of the point 5
  in  the  symmetric group on the points 1, 2, ..., 5 is not regarded as equal
  to  the  symmetric  group on 1, 2, 3, 4. Note that the latter approach would
  have  the  advantage  that  it  is  no problem to construct permutations and
  permutation  groups  acting  on  arbitrary  (finite)  sets,  for  example by
  constructing  first  the  symmetric group on the set and then generating any
  desired permutation group as a subgroup of this symmetric group.
  
  So  one  aspect  concerning  a  reasonable choice of families is to make the
  families large enough for being able to form interesting domains of elements
  in  the  family.  But on the other hand, it is useful to choose the families
  small  enough  for  admitting  meaningful  relations  between  objects.  For
  example,  the  elements  of  different  free  groups in GAP lie in different
  families;  the  multiplication  of free group elements is installed only for
  the  case that the two operands lie in the same family, with the effect that
  one  cannot  erroneously  form  the  product of elements from different free
  groups.  In  this  case,  families  appear  as  a  tool for providing useful
  restrictions.
  
  As  another  example,  note that an element and a collection containing this
  element  never  lie  in  the  same  family, by the general implementation of
  collections;  namely,  the  family of a collection of elements in the family
  Fam  is the collections family of Fam (seeĀ CollectionsFamily (30.2-1)). This
  means  that  for  a collection, we need not (because we cannot) decide about
  its family.
  
  A  few  functions in GAP return families, see CollectionsFamily (30.2-1) and
  ElementsFamily (30.2-3).
  
  79.7-1 NewFamily
  
  NewFamily( name[, req[, imp[, famfilter]]] )  function
  
  NewFamily  returns  a  new  family  fam with name name. The argument req, if
  present,  is a filter of which fam shall be a subset. If one tries to create
  an  object  in  fam that does not lie in the filter req, an error message is
  printed.  Also  the argument imp, if present, is a filter of which fam shall
  be  a  subset.  Any  object  that  is  created  in  the  family fam will lie
  automatically in the filter imp.
  
  The  filter  famfilter,  if given, specifies a filter that will hold for the
  family fam (not for objects in fam).
  
  Families are always represented as component objects (seeĀ 79.10). This means
  that components can be used to store and access useful information about the
  family.
  
  
  79.8 Creating Types
  
  79.8-1 NewType
  
  NewType( family, filter[, data] )  function
  
  NewType  returns  the type given by the family family and the filter filter.
  The optional third argument data is any object that denotes defining data of
  the desired type.
  
  For  examples  where  NewType  is used, seeĀ 79.10, 79.11, and the example in
  Chapter 81.
  
  
  79.9 Creating Objects
  
  79.9-1 Objectify
  
  Objectify( type, data )  function
  
  New  objects  are created by Objectify. data is a list or a record, and type
  is the type that the desired object shall have. Objectify turns data into an
  object  with type type. That is, data is changed, and afterwards it will not
  be a list or a record unless type is of type list resp. record.
  
  If  data is a list then Objectify turns it into a positional object, if data
  is  a  record then Objectify turns it into a component object (for examples,
  seeĀ 79.10 andĀ 79.11).
  
  Objectify does also return the object that it made out of data.
  
  For  examples  where Objectify is used, seeĀ 79.10, 79.11, and the example in
  ChapterĀ 81.
  
  79.9-2 ObjectifyWithAttributes
  
  ObjectifyWithAttributes( obj, type, attr1, val1, attr2, val2, ... )  function
  
  Attribute  assignments will change the type of an object. If you create many
  objects, code of the form
  
    Example  
    o:=Objectify(type,rec());
    SetMyAttribute(o,value);
  
  
  will  take a lot of time for type changes. You can avoid this by setting the
  attributes   immediately   while   the   object   is  created,  as  follows.
  ObjectifyWithAttributes changes the type of object obj to type type and sets
  attribute attr1 to val1, sets attribute attr2 to val2 and so forth.
  
  If  the  filter list of type includes that these attributes are set (and the
  properties  also  include values of the properties) and if no special setter
  methods  are  installed for any of the involved attributes then they are set
  simultaneously without type changes. This can produce a substantial speedup.
  
  If  the  conditions  of  the  last  sentence  are not fulfilled, an ordinary
  Objectify  (79.9-1)  with  subsequent  setter  calls  for  the attributes is
  performed instead.
  
  
  79.10 Component Objects
  
  A  component  object is an object in the representation IsComponentObjectRep
  or  a  subrepresentation of it. Such an object cobj is built from subobjects
  that can be accessed via cobj!.name, similar to components of a record. Also
  analogously  to  records,  values  can be assigned to components of cobj via
  cobj!.name:=  val. For the creation of component objects, seeĀ 79.9. One must
  be  very  careful  when  using  the  !.  operator, in order to interpret the
  component  in the right way, and even more careful when using the assignment
  to  components  using  !.,  in  order to keep the information stored in cobj
  consistent.
  
  First  of  all,  in  the access or assignment to a component as shown above,
  name  must be among the admissible component names for the representation of
  cobj,  seeĀ 79.2.  Second, preferably only few low level functions should use
  !., whereas this operator should not occur in user interactions.
  
  Note  that even if cobj claims that it is immutable, i.e., if cobj is not in
  the category IsMutable (12.6-2), access and assignment via !. and !.:= work.
  This  is  necessary  for being able to store newly discovered information in
  immutable objects.
  
  The following example shows the implementation of an iterator (seeĀ 30.8) for
  the  domain of integers, which is represented as component object. SeeĀ 79.11
  for  an  implementation  using  positional  objects.  (In  practice, such an
  iterator   can  be  implemented  more  elegantly  using  IteratorByFunctions
  (30.8-8), seeĀ 79.14.)
  
  The used succession of integers is 0, 1, -1, 2, -2, 3, -3, ..., that is, a_n
  = n/2 if n is even, and a_n = (1-n)/2 otherwise.
  
    Example  
    IsIntegersIteratorCompRep := NewRepresentation( "IsIntegersIteratorRep",
        IsComponentObjectRep, [ "counter" ] );
  
  
  The  above  command  creates  a  new  representation  (seeĀ NewRepresentation
  (79.2-1))    IsIntegersIteratorCompRep,    as    a    subrepresentation   of
  IsComponentObjectRep, and with one admissible component counter. So no other
  components than counter will be needed.
  
    Example  
    InstallMethod( Iterator,
        "method for `Integers'",
        [ IsIntegers ],
        function( Integers )
        return Objectify( NewType( IteratorsFamily,
                                       IsIterator
                                   and IsIntegersIteratorCompRep ),
                          rec( counter := 0 ) );
        end );
  
  
  After  the  above  method  installation,  one  can already ask for Iterator(
  Integers  ).  Note  that  exactly the domain of integers is described by the
  filter IsIntegers (14.1-2).
  
  By  the  call  to  NewType  (79.8-1), the returned object lies in the family
  containing  all iterators, which is IteratorsFamily, it lies in the category
  IsIterator  (30.8-3)  and  in  the representation IsIntegersIteratorCompRep;
  furthermore, it has the component counter with value 0.
  
  What  is  missing now are methods for the two basic operations of iterators,
  namely  IsDoneIterator  (30.8-4)  and NextIterator (30.8-5). The former must
  always  return  false,  since there are infinitely many integers. The latter
  must  return  the  next integer in the iteration, and update the information
  stored  in  the  iterator,  that  is,  increase  the  value of the component
  counter.
  
    Example  
    InstallMethod( IsDoneIterator,
        "method for iterator of `Integers'",
        [ IsIterator and IsIntegersIteratorCompRep ],
        ReturnFalse );
    
    InstallMethod( NextIterator,
        "method for iterator of `Integers'",
        [ IsIntegersIteratorCompRep ],
        function( iter )
        iter!.counter:= iter!.counter + 1;
        if iter!.counter mod 2 = 0 then
          return iter!.counter / 2;
        else
          return ( 1 - iter!.counter ) / 2;
        fi;
        end );
  
  
  79.10-1 NamesOfComponents
  
  NamesOfComponents( comobj )  function
  
  For  a component object comobj, NamesOfComponents returns a list of strings,
  which are the names of components currently bound in comobj.
  
  For  a  record  comobj,  NamesOfComponents  returns  the  result of RecNames
  (29.1-2).
  
  
  79.11 Positional Objects
  
  A positional object is an object in the representation IsPositionalObjectRep
  or  a  subrepresentation of it. Such an object pobj is built from subobjects
  that  can  be  accessed via pobj![pos], similar to positions in a list. Also
  analogously  to  lists,  values  can  be  assigned  to positions of pobj via
  pobj![pos]:= val. For the creation of positional objects, seeĀ 79.9.
  
  One  must be very careful when using the ![] operator, in order to interpret
  the  position  in  the  right  way,  and  even  more  careful when using the
  assignment  to  positions using ![], in order to keep the information stored
  in pobj consistent.
  
  First  of all, in the access or assignment to a position as shown above, pos
  must  be  among  the  admissible  positions  for the representation of pobj,
  seeĀ 79.2.  Second,  preferably  only few low level functions should use ![],
  whereas this operator should not occur in user interactions.
  
  Note  that even if pobj claims that it is immutable, i.e., if pobj is not in
  the category IsMutable (12.6-2), access and assignment via ![] work. This is
  necessary  for being able to store newly discovered information in immutable
  objects.
  
  The following example shows the implementation of an iterator (seeĀ 30.8) for
  the domain of integers, which is represented as positional object. SeeĀ 79.10
  for an implementation using component objects, and more details.
  
    Example  
    IsIntegersIteratorPosRep := NewRepresentation( "IsIntegersIteratorRep",
        IsPositionalObjectRep, [ 1 ] );
  
  
  The  above  command  creates  a  new  representation  (seeĀ NewRepresentation
  (79.2-1))    IsIntegersIteratorPosRep,    as    a    subrepresentation    of
  IsComponentObjectRep,  and with only the first position being admissible for
  storing data.
  
    Example  
    InstallMethod( Iterator,
        "method for `Integers'",
        [ IsIntegers ],
        function( Integers )
        return Objectify( NewType( IteratorsFamily,
                                       IsIterator
                                   and IsIntegersIteratorRep ),
                          [ 0 ] );
        end );
  
  
  After  the  above  method  installation,  one  can already ask for Iterator(
  Integers  ).  Note  that  exactly the domain of integers is described by the
  filter IsIntegers (14.1-2).
  
  By  the  call  to  NewType  (79.8-1), the returned object lies in the family
  containing  all iterators, which is IteratorsFamily, it lies in the category
  IsIterator  (30.8-3)  and  in  the  representation IsIntegersIteratorPosRep;
  furthermore, the first position has value 0.
  
  What  is  missing now are methods for the two basic operations of iterators,
  namely  IsDoneIterator  (30.8-4)  and NextIterator (30.8-5). The former must
  always  return  false,  since there are infinitely many integers. The latter
  must  return  the  next integer in the iteration, and update the information
  stored  in  the  iterator,  that  is, increase the value stored in the first
  position.
  
    Example  
    InstallMethod( IsDoneIterator,
        "method for iterator of `Integers'",
        [ IsIterator and IsIntegersIteratorPosRep ],
        ReturnFalse );
    
    InstallMethod( NextIterator,
        "method for iterator of `Integers'",
        [ IsIntegersIteratorPosRep ],
        function( iter )
        iter![1]:= iter![1] + 1;
        if iter![1] mod 2 = 0 then
          return iter![1] / 2;
        else
          return ( 1 - iter![1] ) / 2;
        fi;
        end );
  
  
  It  should be noted that one can of course install both the methods shown in
  SectionĀ 79.10 and 79.11. The call Iterator( Integers ) will cause one of the
  methods  to  be selected, and for the returned iterator, which will have one
  of  the  representations  we  constructed,  the  right NextIterator (30.8-5)
  method will be chosen.
  
  
  79.12 Implementing New List Objects
  
  This  section  gives some hints for the quite usual situation that one wants
  to implement new objects that are lists. More precisely, one either wants to
  deal  with  lists  that  have  additional  features,  or one wants that some
  objects also behave as lists. An example can be found inĀ 79.13.
  
  A list in GAP is an object in the category IsList (21.1-1). Basic operations
  for  lists  are  Length  (21.17-5),  \[\] (21.2-1), and IsBound\[\] (21.2-1)
  (seeĀ 21.2).
  
  Note  that  the access to the position pos in the list list via list[pos] is
  handled  by  the  call  \[\]( list, pos ) to the operation \[\] (21.2-1). To
  explain  the  somewhat  strange  name  \[\]  of  this  operation,  note that
  non-alphanumeric  characters  like  [  and ] may occur in GAP variable names
  only if they are escaped by a \ character.
  
  Analogously,  the check IsBound( list[pos] ) whether the position pos of the
  list  list  is  bound is handled by the call IsBound\[\]( list, pos ) to the
  operation IsBound\[\] (21.2-1).
  
  For  mutable  lists, also assignment to positions and unbinding of positions
  via  the  operations  \[\]\:\=  (21.2-1)  and  Unbind\[\] (21.2-1) are basic
  operations.  The assignment list[pos]:= val is handled by the call \[\]\:\=(
  list, pos, val ), and Unbind( list[pos] ) is handled by the call Unbind\[\](
  list, pos ).
  
  All  other  operations  for  lists, e.g., Add (21.4-2), Append (21.4-5), Sum
  (21.20-26),  are based on these operations. This means that it is sufficient
  to install methods for the new list objects only for the basic operations.
  
  So  if  one  wants  to  implement  new list objects then one creates them as
  objects  in  the  category  IsList (21.1-1), and installs methods for Length
  (21.17-5),  \[\]  (21.2-1), and IsBound\[\] (21.2-1). If the new lists shall
  be  mutable,  one  needs  to  install also methods for \[\]\:\= (21.2-1) and
  Unbind\[\] (21.2-1).
  
  One  application  for this is the implementation of enumerators for domains.
  An  enumerator  for  the  domain  D  is  a  dense  list whose entries are in
  bijection  with  the  elements  of D. If D is large then it is not useful to
  write  down  all  elements.  Instead  one  can  implement  such  a bijection
  implicitly. This works also for infinite domains.
  
  In this situation, one implements a new representation of the lists that are
  already  available  in  GAP,  in particular the family of such a list is the
  same as the family of the domain D.
  
  But  it  is  also  possible  to implement new kinds of lists that lie in new
  families, and thus are not equal to lists that were available in GAP before.
  An  example  for this is the implementation of matrices whose multiplication
  via * is the Lie product of matrices.
  
  In  this  situation, it makes no sense to put the new matrices into the same
  family  as  the original matrices. Note that the product of two Lie matrices
  shall be defined but not the product of an ordinary matrix and a Lie matrix.
  So  it is possible to have two lists that have the same entries but that are
  not equal w.r.t. = because they lie in different families.
  
  
  79.13 Example – Constructing Enumerators
  
  When  dealing  with  countable sets, a usual task is to define enumerations,
  i.e.,  bijections  to the positive integers. In GAP, this can be implemented
  via  enumerators  (seeĀ 21.23).  These are lists containing the elements in a
  specified  ordering,  and  the operations Position (21.16-1) and list access
  via  \[\]  (21.2-1)  define  the desired bijection. For implementing such an
  enumerator,  one mainly needs to install the appropriate functions for these
  operations.
  
  A  general  setup  for creating such lists is given by EnumeratorByFunctions
  (30.3-4).
  
  If  the  set  in  question is a domain D for which a Size (30.4-6) method is
  available  then  all  one  has  to  do  is  to  write down the functions for
  computing  the  n-th element of the list and for computing the position of a
  given  GAP object in the list, to put them into the components ElementNumber
  and  NumberElement  of  a record, and to call EnumeratorByFunctions (30.3-4)
  with  the  domain D and this record as arguments. For example, the following
  lines  of  code install an Enumerator (30.3-2) method for the case that D is
  the  domain of rational integers. (Note that IsIntegers (14.1-2) is a filter
  that describes exactly the domain of rational integers.)
  
    Example  
    InstallMethod( Enumerator,
        "for integers",
        [ IsIntegers ],
        Integers -> EnumeratorByFunctions( Integers, rec(
                        ElementNumber := function( e, n ) ... end,
                        NumberElement := function( e, x ) ... end ) ) );
  
  
  The  bodies  of the functions have been omitted above; here is the code that
  is actually used in GAP. (The ordering coincides with that for the iterators
  for  the  domain  of  rational  integers  that  have been discussed inĀ 79.10
  andĀ 79.11.)
  
    Example  
    gap> enum:= Enumerator( Integers );
    <enumerator of Integers>
    gap> Print( enum!.NumberElement, "\n" );
    function ( e, x )
        local  pos;
        if not IsInt( x )  then
            return fail;
        elif 0 < x  then
            pos := 2 * x;
        else
            pos := -2 * x + 1;
        fi;
        return pos;
    end
    gap> Print( enum!.ElementNumber, "\n" );
    function ( e, n )
        if n mod 2 = 0  then
            return n / 2;
        else
            return (1 - n) / 2;
        fi;
        return;
    end
  
  
  The  situation becomes slightly more complicated if the set S in question is
  not  a  domain.  This is because one must provide also at least a method for
  computing  the  length  of  the  list,  and because one has to determine the
  family  in  which  it  lies  (seeĀ 79.9).  The latter should usually not be a
  problem  since  either  S  is  nonempty and all its elements lie in the same
  family  –in  this case one takes the collections family of any element in S–
  or the family of the enumerator must be ListsFamily.
  
  An  example in the GAP library is an enumerator for the set of k-tuples over
  a finite set; the function is called EnumeratorOfTuples (16.2-9).
  
    Example  
    gap> Print( EnumeratorOfTuples, "\n" );
    function ( set, k )
        local  enum;
        if k = 0  then
            return Immutable( [ [  ] ] );
        elif IsEmpty( set )  then
            return Immutable( [  ] );
        fi;
        enum 
         := EnumeratorByFunctions( CollectionsFamily( FamilyObj( set ) ), 
           rec(
              ElementNumber := function ( enum, n )
                    local  nn, t, i;
                    nn := n - 1;
                    t := [  ];
                    for i  in [ 1 .. enum!.k ]  do
                        t[i] := RemInt( nn, Length( enum!.set ) ) + 1;
                        nn := QuoInt( nn, Length( enum!.set ) );
                    od;
                    if nn <> 0  then
                        Error( "<enum>[", n, 
                         "] must have an assigned value" );
                    fi;
                    nn := enum!.set{Reversed( t )};
                    MakeImmutable( nn );
                    return nn;
                end,
              NumberElement := function ( enum, elm )
                    local  n, i;
                    if not IsList( elm )  then
                        return fail;
                    fi;
                    elm := List( elm, function ( x )
                            return Position( enum!.set, x );
                        end );
                    if fail in elm or Length( elm ) <> enum!.k  then
                        return fail;
                    fi;
                    n := 0;
                    for i  in [ 1 .. enum!.k ]  do
                        n := Length( enum!.set ) * n + elm[i] - 1;
                    od;
                    return n + 1;
                end,
              Length := function ( enum )
                    return Length( enum!.set ) ^ enum!.k;
                end,
              PrintObj := function ( enum )
                    Print( "EnumeratorOfTuples( ", enum!.set, ", ", 
                     enum!.k, " )" );
                    return;
                end,
              set := Set( set ),
              k := k ) );
        SetIsSSortedList( enum, true );
        return enum;
    end
  
  
  We  see  that  the  enumerator  is a homogeneous list that stores individual
  functions  ElementNumber, NumberElement, Length, and PrintObj; besides that,
  the data components S and k are contained.
  
  
  79.14 Example – Constructing Iterators
  
  Iterators  are a kind of objects that is implemented for several collections
  in  the  GAP  library  and  which  might be interesting also in other cases,
  seeĀ 30.8.  A  general  setup  for  implementing new iterators is provided by
  IteratorByFunctions (30.8-8).
  
  All  one has to do is to write down the functions for NextIterator (30.8-5),
  IsDoneIterator   (30.8-4),   and   ShallowCopy   (12.7-1),   and   to   call
  IteratorByFunctions  (30.8-8) with this record as argument. For example, the
  following  lines  of  code  install an Iterator (30.8-1) method for the case
  that the argument is the domain of rational integers.
  
  (Note that IsIntegers (14.1-2) is a filter that describes exactly the domain
  of rational integers.)
  
    Example  
    InstallMethod( Iterator,
        "for integers",
        [ IsIntegers ],
        Integers -> IteratorByFunctions( rec(
                        NextIterator:= function( iter ) ... end,
                        IsDoneIterator := ReturnFalse,
                        ShallowCopy := function( iter ) ... end ) ) );
  
  
  The bodies of two of the functions have been omitted above; here is the code
  that  is  actually  used  in  GAP. (The ordering coincides with that for the
  iterators  for  the  domain  of  rational  integers that have been discussed
  inĀ 79.10 andĀ 79.11.)
  
    Example  
    gap> iter:= Iterator( Integers );
    <iterator of Integers at 0>
    gap> Print( iter!.NextIterator, "\n" );
    function ( iter )
        iter!.counter := iter!.counter + 1;
        if iter!.counter mod 2 = 0  then
            return iter!.counter / 2;
        else
            return (1 - iter!.counter) / 2;
        fi;
        return;
    end
    gap> Print( iter!.ShallowCopy, "\n" );   
    function ( iter )
        return rec(
            counter := iter!.counter );
    end
  
  
  Note  that  the  ShallowCopy component of the record must be a function that
  does not return an iterator but a record that can be used as the argument of
  IteratorByFunctions (30.8-8) in order to create the desired shallow copy.
  
  
  79.15 Arithmetic Issues in the Implementation of New Kinds of Lists
  
  When  designing  a  new kind of list objects in GAP, defining the arithmetic
  behaviour of these objects is an issue.
  
  There  are  situations  where  arithmetic  operations  of  list  objects are
  unimportant  in the sense that adding two such lists need not be represented
  in  a  special  way.  In  such cases it might be useful either to support no
  arithmetics  at  all  for the new lists, or to enable the default arithmetic
  methods.   The   former   can   be  achieved  by  not  setting  the  filters
  IsGeneralizedRowVector  (21.12-1)  and  IsMultiplicativeGeneralizedRowVector
  (21.12-2)  in  the types of the lists, the latter can be achieved by setting
  the filter IsListDefault (21.12-3). (for details, seeĀ 21.12). An example for
  wrapped  lists with default behaviour are vector space bases; they are lists
  with  additional  properties concerning the computation of coefficients, but
  arithmetic  properties  are  not  important.  So it is no loss to enable the
  default methods for these lists.
  
  However,  often  the  arithmetic behaviour of new list objects is important,
  and  one  wants  to keep these lists away from default methods for addition,
  multiplication  etc.  For  example,  the sum and the product of (compatible)
  block  matrices  shall  be  represented  as  a  block matrix, so the default
  methods  for  sum  and product of matrices shall not be applicable, although
  the  results will be equal to those of the default methods in the sense that
  their entries at corresponding positions are equal.
  
  So  one  does  not set the filter IsListDefault (21.12-3) in such cases, and
  thus  one  can  implement  one's  own methods for arithmetic operations. (Of
  course  can  means on the other hand that one must implement such methods if
  one is interested in arithmetics of the new lists.)
  
  The  specific binary arithmetic methods for the new lists will usually cover
  the  case  that  both  arguments  are  of the new kind, and perhaps also the
  interaction  between a list of the new kind and certain other kinds of lists
  may be handled if this appears to be useful.
  
  For  the  last  situation, interaction between a new kind of lists and other
  kinds  of  lists,  GAP  provides  already  a  setup.  Namely,  there are the
  categories           IsGeneralizedRowVector           (21.12-1)          and
  IsMultiplicativeGeneralizedRowVector (21.12-2), which are concerned with the
  additive and the multiplicative behaviour, respectively, of lists. For lists
  in  these  filters, the structure of the results of arithmetic operations is
  prescribed (seeĀ 21.13 and 21.14).
  
  For     example,     if     one     implements     block     matrices     in
  IsMultiplicativeGeneralizedRowVector   (21.12-2)   then   automatically  the
  product  of  such  a  block matrix and a (plain) list of such block matrices
  will be defined as the obvious list of matrix products, and a default method
  for plain lists will handle this multiplication. (Note that this method will
  rely  on  a  method  for computing the product of the block matrices, and of
  course  no  default  method is available for that.) Conversely, if the block
  matrices  are not in IsMultiplicativeGeneralizedRowVector (21.12-2) then the
  product  of  a  block  matrix  and  a  (plain) list of block matrices is not
  defined.  (There  is no default method for it, and one can define the result
  and provide a method for computing it.)
  
  Thus  if one decides to set the filters IsGeneralizedRowVector (21.12-1) and
  IsMultiplicativeGeneralizedRowVector (21.12-2) for the new lists, on the one
  hand  one  loses  freedom in defining arithmetic behaviour, but on the other
  hand one gains several default methods for a more or less natural behaviour.
  
  If    a    list    in    the    filter    IsGeneralizedRowVector   (21.12-1)
  (IsMultiplicativeGeneralizedRowVector        (21.12-2))        lies       in
  IsAttributeStoringRep, the values of additive (multiplicative) nesting depth
  is  stored  in  the  list  and  need  not  be calculated for each arithmetic
  operation.  One  can  then  store  the value(s) already upon creation of the
  lists,  with  the  effect that the default arithmetic operations will access
  elements of these lists only if this is unavoidable. For example, the sum of
  two  plain lists of wrapped matrices with stored nesting depths are computed
  via  the method for adding two such wrapped lists, and without accessing any
  of  their  rows (which might be expensive). In this sense, the wrapped lists
  are treated as black boxes.
  
  
  79.16 External Representation
  
  An  operation  is  defined for elements rather than for objects in the sense
  that  if  the  arguments  are  replaced by objects that are equal to the old
  arguments w.r.t. the equivalence relation = then the result must be equal to
  the old result w.r.t.Ā =.
  
  But  the  implementation  of many methods is representation dependent in the
  sense that certain representation dependent subobjects are accessed.
  
  For example, a method that implements the addition of univariate polynomials
  may  access  coefficients  lists  of  its  arguments only if they are really
  stored,  while  in  the case of sparsely represented polynomials a different
  approach is needed.
  
  In  spite  of  this,  for  many operations one does not want to write an own
  method  for  each  possible  representations  of  each argument, for example
  because  none  of  the  methods could in fact take advantage of the actually
  given representations of the objects. Another reason could be that one wants
  to  install first a representation independent method, and then add specific
  methods as they are needed to gain more efficiency, by really exploiting the
  fact that the arguments have certain representations.
  
  For the purpose of admitting representation independent code, one can define
  an  external representation of objects in a given family, install methods to
  compute this external representation for each representation of the objects,
  and  then  use  this  external  representation  of the objects whenever they
  occur.
  
  We  cannot  provide  conversion functions that allow us to first convert any
  object  in  question  to  one  particular  standard representation, and then
  access  the  data in the way defined for this representation, simply because
  it  may be impossible to choose such a standard representation uniformly for
  all objects in the given family.
  
  So  the  aim  of  an external representation of an object obj is a different
  one,  namely to describe the data from which obj is composed. In particular,
  the   external   representation  of  obj  is  not  one  possible  (standard)
  representation  of  obj,  in  fact  the external representation of obj is in
  general  different  from  obj  w.r.t.Ā =,  first  of all because the external
  representation of obj does in general not lie in the same family as obj.
  
  For  example the external representation of a rational function is a list of
  length  two or three, the first entry being the zero coefficient, the second
  being a list describing the coefficients and monomials of the numerator, and
  the  third, if bound, being a list describing the coefficients and monomials
  of  the  denominator.  In  particular,  the  external  representation  of  a
  polynomial is a list and not a polynomial.
  
  The  other way round, the external representation of obj encodes obj in such
  a  way  that  from this data and the family of obj, one can create an object
  that  is equal to obj. Usually the external representation of an object is a
  list or a record.
  
  Although  the external representation of obj is by definition independent of
  the  actually  available  representations  for  obj,  it  is  usual  that  a
  representation  of  obj  exists  for  which  the computation of the external
  representation  is  obtained  by  just  unpacking obj, in the sense that the
  desired  data  is  stored  in  a component or a position of obj, if obj is a
  component object (seeĀ 79.10) or a positional object (seeĀ 79.11).
  
  To  implement  an  external  representation means to install methods for the
  following two operations.
  
  79.16-1 ExtRepOfObj
  
  ExtRepOfObj( obj )  operation
  ObjByExtRep( fam, data )  operation
  
  ExtRepOfObj  returns  the  external  representation  of  its  argument,  and
  ObjByExtRep   returns  an  object  in  the  family  fam  that  has  external
  representation data.
  
  Of course, ObjByExtRep( FamilyObj( obj ), ExtRepOfObj( obj ) ) must be equal
  to  obj w.r.t. the operation \= (31.11-1). But it is not required that equal
  objects have equal external representations.
  
  Note  that  if  one  defines  a  new  representation of objects for which an
  external representation does already exist then one must install a method to
  compute   this   external   representation   for  the  objects  in  the  new
  representation.
  
  
  79.17 Mutability and Copying
  
  Any  GAP  object is either mutable or immutable. This can be tested with the
  function  IsMutable  (12.6-2).  The  intended meaning of (im)mutability is a
  mathematical one: an immutable object should never change in such a way that
  it  represents  a  different  Element. Objects may change in other ways, for
  instance  to  store more information, or represent an element in a different
  way.
  
  Immutability  is  enforced  in  different  ways  for  built-in objects (like
  records, or lists) and for external objects (made using Objectify (79.9-1)).
  
  For  built-in  objects which are immutable, the kernel will prevent you from
  changing them. Thus
  
    Example  
    gap> l := [1,2,4];
    [ 1, 2, 4 ]
    gap> MakeImmutable(l);
    [ 1, 2, 4 ]
    gap> l[3] := 5;
    Error, List Assignment: <list> must be a mutable list
  
  
  For  external  objects, the situation is different. An external object which
  claims  to  be immutable (i.e. its type does not contain IsMutable (12.6-2))
  should  not  admit  any  methods which change the element it represents. The
  kernel  does  not prevent the use of !. and ![ to change the underlying data
  structure.  This  is  used  for  instance  by the code that stores attribute
  values  for  reuse.  In  general,  these ! operations should only be used in
  methods  which  depend  on the representation of the object. Furthermore, we
  would   not   recommend  users  to  install  methods  which  depend  on  the
  representations  of  objects  created  by the library or by GAP packages, as
  there  is  certainly  no  guarantee of the representations being the same in
  future versions of GAP.
  
  Here  we  see  an  immutable  object (the group S_4), in which we improperly
  install a new component.
  
    Example  
    gap> g := SymmetricGroup(IsPermGroup,4);
    Sym( [ 1 .. 4 ] )
    gap> IsMutable(g);
    false
    gap> NamesOfComponents(g);
    [ "Size", "NrMovedPoints", "MovedPoints", 
      "GeneratorsOfMagmaWithInverses" ]
    gap> g!.silly := "rubbish";
    "rubbish"
    gap> NamesOfComponents(g);
    [ "Size", "NrMovedPoints", "MovedPoints", 
      "GeneratorsOfMagmaWithInverses", "silly" ]
    gap> g!.silly;
    "rubbish"
  
  
  On  the  other hand, if we form an immutable externally represented list, we
  find that GAP will not let us change the object.
  
    Example  
    gap> e := Enumerator(g);
    <enumerator of perm group>
    gap> IsMutable(e);
    false
    gap> IsList(e);
    true
    gap> e[3];
    (1,2,4)
    gap> e[3] := false;
    Error, The list you are trying to assign to is immutable
  
  
  When we consider copying objects, another filter IsCopyable (12.6-1), enters
  the  game  and we find that ShallowCopy (12.7-1) and StructuralCopy (12.7-2)
  behave  quite  differently.  Objects  can  be  divided for this purpose into
  three:  mutable  objects,  immutable  but copyable objects, and non-copyable
  objects (called constants).
  
  A  mutable  or  copyable  object  should  have  a  method  for the operation
  ShallowCopy  (12.7-1),  which  should make a new mutable object, sharing its
  top-level  subobjects  with  the original. The exact definition of top-level
  subobject may be defined by the implementor for new kinds of object.
  
  ShallowCopy (12.7-1) applied to a constant simply returns the constant.
  
  StructuralCopy  (12.7-2)  is  expected to be much less used than ShallowCopy
  (12.7-1). Applied to a mutable object, it returns a new mutable object which
  shares no mutable sub-objects with the input. Applied to an immutable object
  (even  a  copyable  one), it just returns the object. It is not an operation
  (indeed, it's a rather special kernel function).
  
    Example  
    gap> e1 := StructuralCopy(e);
    <enumerator of perm group>
    gap> IsMutable(e1);
    false
    gap> e2 := ShallowCopy(e);
    [ (), (1,4), (1,2,4), (1,3,4), (2,4), (1,4,2), (1,2), (1,3,4,2), 
      (2,3,4), (1,4,2,3), (1,2,3), (1,3)(2,4), (3,4), (1,4,3), (1,2,4,3), 
      (1,3), (2,4,3), (1,4,3,2), (1,2)(3,4), (1,3,2), (2,3), (1,4)(2,3), 
      (1,2,3,4), (1,3,2,4) ]
    gap> 
  
  
  There are two other related functions: Immutable (12.6-3), which makes a new
  immutable  object  which  shares  no  mutable  subobjects with its input and
  MakeImmutable (12.6-4) which changes an object and its mutable subobjects in
  place  to  be immutable. It should only be used on new objects that you have
  just created, and which cannot share mutable subobjects with anything else.
  
  Both  Immutable (12.6-3) and MakeImmutable (12.6-4) work on external objects
  by  just  resetting the IsMutable (12.6-2) filter in the object's type. This
  should  make  ineligible  any  methods  that  might  change the object. As a
  consequence, you must allow for the possibility of immutable versions of any
  objects you create.
  
  So,  if  you are implementing your own external objects. The rules amount to
  the following:
  
  1   You decide if your objects should be mutable or copyable or constants,
        by fixing whether their type includes IsMutable (12.6-2) or IsCopyable
        (12.6-1).
  
  2   You install methods for your objects respecting that decision:
  
        for constants:
              no methods change the underlying elements;
  
        for copyables:
              you provide a method for ShallowCopy (12.7-1);
  
        for mutables:
              you  may  have  methods  that change the underlying elements and
              these should explicitly require IsMutable (12.6-2).
  
  
  79.18 Global Variables in the Library
  
  Global  variables in the GAP library are usually read-only in order to avoid
  their being overwritten accidentally. See also Section 4.9.
  
  79.18-1 DeclareCategory
  
  DeclareCategory( name, super[, rank] )  function
  
  does  the  same  as NewCategory (79.1-1) and additionally makes the variable
  name read-only.
  
  79.18-2 TypeOfOperation
  
  TypeOfOperation( object )  function
  
  returns  a  string  from  the  list  [ "Attribute", "Operation", "Property",
  "Category",  "Representation",  "Filter", "Setter"] reflecting which type of
  operation op is.
  
  (seeĀ 13.3, 13.4, 13.5, 13.6, 13.7, 13.8)
  
  79.18-3 IsCategory
  
  IsCategory( object )  function
  
  returns true if object is a category (seeĀ 13.3), and false otherwise.
  
  Note that GAP categories are not categories in the usual mathematical sense.
  
  79.18-4 IsRepresentation
  
  IsRepresentation( object )  function
  
  returns true if object is a representation (seeĀ 13.4), and false otherwise.
  
  79.18-5 IsProperty
  
  IsProperty( object )  function
  
  returns true if object is a property (seeĀ 13.7), and false otherwise.
  
  79.18-6 IsAttribute
  
  IsAttribute( object )  function
  
  returns true if object is an attribute (seeĀ 13.5), and false otherwise.
  
  79.18-7 CategoryByName
  
  CategoryByName( name )  function
  
  returns the category with name name if it is found, or fail otherwise.
  
  79.18-8 DeclareRepresentation
  
  DeclareRepresentation( name, super, slots[, req] )  function
  
  does  the  same  as  NewRepresentation  (79.2-1)  and additionally makes the
  variable name read-only.
  
  79.18-9 DeclareAttribute
  
  DeclareAttribute( name, filter[, "mutable"][, rank] )  function
  
  does the same as NewAttribute (79.3-1), additionally makes the variable name
  read-only  and  also binds read-only global variables with names Hasname and
  Setname for the tester and setter of the attribute (see Section 13.6).
  
  79.18-10 DeclareProperty
  
  DeclareProperty( name, filter[, rank] )  function
  
  does  the same as NewProperty (79.3-2), additionally makes the variable name
  read-only  and  also binds read-only global variables with names Hasname and
  Setname for the tester and setter of the property (see Section 13.6).
  
  79.18-11 DeclareFilter
  
  DeclareFilter( name[, rank] )  function
  
  does the same as NewFilter (79.4-1) and additionally makes the variable name
  read-only.
  
  79.18-12 DeclareOperation
  
  DeclareOperation( name, filters )  function
  
  does  the  same as NewOperation (79.5-1) and additionally makes the variable
  name read-only.
  
  79.18-13 DeclareConstructor
  
  DeclareConstructor( name, filters )  function
  
  does the same as NewConstructor (79.6-1) and additionally makes the variable
  name read-only.
  
  Note  that  for operations which are constructors special rules with respect
  to  applicability  and  rank of the corresponding methods apply (see section
  NewConstructor (79.6-1)).
  
  79.18-14 DeclareGlobalFunction
  
  DeclareGlobalFunction( name, info )  function
  InstallGlobalFunction( oper, func )  function
  
  DeclareGlobalFunction  GAP  functions  that  are not operations and that are
  intended  to be called by users should be notified to GAP in the declaration
  part     of    the    respective    package    (see    SectionĀ 79.19)    via
  DeclareGlobalFunction,  which  returns  a  function  that  serves as a place
  holder for the function that will be installed later, and that will print an
  error message if it is called. See alsoĀ DeclareSynonym (79.18-17).
  
  A global function declared with DeclareGlobalFunction can be given its value
  func  via  InstallGlobalFunction;  gvar  is the global variable (or a string
  denoting   its   name)   named  with  the  name  argument  of  the  call  to
  DeclareGlobalFunction. For example, a declaration like
  
    Example  
    DeclareGlobalFunction( "SumOfTwoCubes" );
  
  
  in  the  declaration  part  (see  SectionĀ 79.19)  might have a corresponding
  implementation part of:
  
    Example  
    InstallGlobalFunction( SumOfTwoCubes, function(x, y) return x^3 + y^3; end);
  
  
  79.18-15 DeclareGlobalVariable
  
  DeclareGlobalVariable( name[, description] )  function
  
  For  global  variables  that  are not functions, instead of using BindGlobal
  (4.9-8)  one  can also declare the variable with DeclareGlobalVariable which
  creates  a  new  global  variable  named  by  the string name. If the second
  argument  description  is  entered then this must be a string that describes
  the  meaning  of the global variable. DeclareGlobalVariable shall be used in
  the  declaration part of the respective package (seeĀ 79.19), values can then
  be   assigned   to   the   new   variable   with   InstallValue  (79.18-16),
  InstallFlushableValue    (79.18-16)   or   InstallFlushableValueFromFunction
  (79.18-16), in the implementation part (again, seeĀ 79.19).
  
  79.18-16 InstallValue
  
  InstallValue( gvar, value )  function
  InstallFlushableValue( gvar, value )  function
  InstallFlushableValueFromFunction( gvar, func )  function
  
  InstallValue   assigns   the  value  value  to  the  global  variable  gvar.
  InstallFlushableValue does the same but additionally provides that each call
  of  FlushCaches  (79.18-18)  will assign a structural copy of value to gvar.
  InstallFlushableValueFromFunction instead assigns the result of func to gvar
  (func is re-evaluated for each invocation of FlushCaches (79.18-18)
  
  InstallValue  does  not  work  if  value  is  an  immediate object, i.e., an
  internally  represented small integer or finite field element. It also fails
  for  booleans.  Furthermore,  InstallFlushableValue works only if value is a
  list  or  a  record.  (Note  that InstallFlushableValue makes sense only for
  mutable global variables.)
  
  79.18-17 DeclareSynonym
  
  DeclareSynonym( name, value )  function
  DeclareSynonymAttr( name, value )  function
  
  DeclareSynonym assigns the string name to a global variable as a synonym for
  value. Two typical intended usages are to declare an and-filter, e.g.
  
    Example  
    DeclareSynonym( "IsGroup", IsMagmaWithInverses and IsAssociative );
  
  
  and  to  provide  a  previously declared global function with an alternative
  name, e.g.
  
    Example  
    DeclareGlobalFunction( "SizeOfSomething" );
    DeclareSynonym( "OrderOfSomething", SizeOfSomething );
  
  
  Note:  Before  using  DeclareSynonym  in the way of this second example, one
  should  determine  whether  the  synonym  is really needed. Perhaps an extra
  index entry in the documentation would be sufficient.
  
  When  value is actually an attribute then DeclareSynonymAttr should be used;
  this  binds  also  globals  variables Setname and Hasname for its setter and
  tester, respectively.
  
    Example  
    DeclareSynonymAttr( "IsField", IsDivisionRing and IsCommutative );
    DeclareAttribute( "GeneratorsOfDivisionRing", IsDivisionRing );
    DeclareSynonymAttr( "GeneratorsOfField", GeneratorsOfDivisionRing );
  
  
  79.18-18 FlushCaches
  
  FlushCaches(  )  operation
  
  FlushCaches  resets the value of each global variable that has been declared
  with  DeclareGlobalVariable  (79.18-15)  and for which the initial value has
  been       set       with      InstallFlushableValue      (79.18-16)      or
  InstallFlushableValueFromFunction (79.18-16) to this initial value.
  
  FlushCaches  should  be used only for debugging purposes, since the involved
  global  variables  include  for  example  lists that store finite fields and
  cyclotomic  fields  used  in the current GAP session, in order to avoid that
  these  fields  are  constructed  anew  in  each  call  to GF (59.3-2) and CF
  (60.1-1).
  
  79.18-19 FilterByName
  
  FilterByName( name )  function
  
  finds  the  filter with name name in the global FILTERS list. This is useful
  to find filters that were created but not bound to a global variable.
  
  
  79.19 Declaration and Implementation Part
  
  Each  package  of  GAP code consists of two parts, the declaration part that
  defines  the new categories and operations for the objects the package deals
  with,  and  the  implementation  part  where  the  corresponding methods are
  installed.  The  declaration  part  should  be  representation  independent,
  representation   dependent   information   should   be  dealt  with  in  the
  implementation part.
  
  GAP  functions that are not operations and that are intended to be called by
  users   should   be   notified   to   GAP   in   the  declaration  part  via
  DeclareGlobalFunction   (79.18-14).   Values  for  these  functions  can  be
  installed in the implementation part via InstallGlobalFunction (79.18-14).
  
  Calls to the following functions belong to the declaration part.
  
  DeclareAttribute (79.18-9),
  
  DeclareCategory (79.18-1),
  
  DeclareFilter (79.18-11),
  
  DeclareOperation (79.18-12),
  
  DeclareGlobalFunction (79.18-14),
  
  DeclareSynonym (79.18-17),
  
  DeclareSynonymAttr (79.18-17),
  
  DeclareProperty (79.18-10),
  
  InstallTrueMethod (78.7-1).
  
  Calls to the following functions belong to the implementation part.
  
  DeclareRepresentation (79.18-8),
  
  InstallGlobalFunction (79.18-14),
  
  InstallMethod (78.2-1),
  
  InstallImmediateMethod (78.6-1),
  
  InstallOtherMethod (78.2-2),
  
  NewFamily (79.7-1),
  
  NewType (79.8-1),
  
  Objectify (79.9-1).
  
  Whenever  both  a  NewSomething and a DeclareSomething variant of a function
  exist  (seeĀ 79.18),  the use of DeclareSomething is recommended because this
  protects  the  variables in question from being overwritten. Note that there
  are  no functions DeclareFamily and DeclareType since families and types are
  created  dynamically,  hence  usually  no global variables are associated to
  them.  Further  note  that  DeclareRepresentation  (79.18-8)  is regarded as
  belonging  to  the  implementation  part, because usually representations of
  objects  are  accessed only in very few places, and all code that involves a
  particular   representation   is   contained   in  one  file;  additionally,
  representations  of objects are often not interesting for the user, so there
  is   no   need   to   provide   a  user  interface  or  documentation  about
  representations.
  
  It should be emphasized that declaration means only an explicit notification
  of  mathematical  or  technical  terms  or  of concepts to GAP. For example,
  declaring  a category or property with name IsInteresting does of course not
  tell   GAP   what  this  shall  mean,  and  it  is  necessary  to  implement
  possibilities  to  create  objects  that  know  already  that  they  lie  in
  IsInteresting  in the case that it is a category, or to install implications
  or  methods  in order to compute for a given object whether IsInteresting is
  true or false for it in the case that IsInteresting is a property.
  

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net