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/chap80.txt

  
  80 Examples of Extending the System
  
  This chapter gives a few examples of how one can extend the functionality of
  GAP.
  
  They  are  arranged  in  ascending  difficulty.  We  show how to install new
  methods, add new operations and attributes and how to implement new features
  using categories and representations. (As we do not introduce completely new
  kinds  of  objects  in these example it will not be necessary to declare any
  families.)  Finally  we  show a simple way how to create new objects with an
  own arithmetic.
  
  The examples given are all very rudimentary – no particular error checks are
  performed  and  the user interface sometimes is quite clumsy. These examples
  may   be  constructed  for  presentation  purposes  only  and  they  do  not
  necessarily constitute parts of the GAP library.
  
  Even more complex examples that create whole classes of objects anew will be
  given in the following two chapters 81 and 82.
  
  
  80.1 Addition of a Method
  
  The  easiest  case  is  the  addition  of a new algorithm as a method for an
  existing operation for the existing structures.
  
  For example, assume we wanted to implement a better method for computing the
  exponent  of  a  nilpotent  group (it is the product of the exponents of the
  Sylow subgroups).
  
  The  first  task  is  to find which operation is used by GAP (it is Exponent
  (39.16-2))  and how it is declared. We can find this in the Reference Manual
  (in our particular case in section 39.16) and the declaration in the library
  file  lib/grp.gd.  The  easiest  way to find the place of the declaration is
  usually to grep over all .gd and .g files, see section 83.
  
  In our example the declaration in the library is:
  
    Example  
    DeclareAttribute("Exponent",IsGroup);
  
  
  Similarly  we find that the filter IsNilpotentGroup (39.15-3) represents the
  concept of being nilpotent.
  
  We  then  write a function that implements the new algorithm which takes the
  right  set  of  arguments  and  install  it as a method. In our example this
  installation would be:
  
    Example  
    InstallMethod(Exponent,"for nilpotent groups",
      [IsGroup and IsNilpotent],
    function(G)
      [function body omitted]
    end);
  
  
  We have left out the optional rank argument of InstallMethod (78.2-1), which
  normally  is a wise choice –GAP automatically uses an internal ranking based
  on  the  filters  that  is only offset by the given rank. So our method will
  certainly  be  regarded  as better than a method that has been installed for
  mere groups or for solvable groups but will be ranked lower than the library
  method for abelian groups.
  
  That's  all.  Using 7.2-1 we can check for a nilpotent group that indeed our
  new method will be used.
  
  When  testing,  remember  that  the  method  selection  will  not  check for
  properties  that  are  not  known.  (This is done internally by checking the
  property tester first.) Therefore the method would not be applicable for the
  group  g  in  the  following  definition  but  only  for the –mathematically
  identical  but  endowed  with  more knowledge by GAP– group h. (Section 80.3
  shows a way around this.)
  
    Example  
    gap> g:=Group((1,2),(1,3)(2,4));;
    gap> h:=Group((1,2),(1,3)(2,4));;
    gap> IsNilpotentGroup(h); # enforce test
    true
    gap> HasIsNilpotentGroup(g);
    false
    gap> HasIsNilpotentGroup(h);
    true
  
  
  Let's  now look at a slightly more complicated example: We want to implement
  a  better method for computing normalizers in a nilpotent permutation group.
  (Such an algorithm can be found for example in [LRW97].)
  
  We  already know IsNilpotentGroup (39.15-3), the filter IsPermGroup (43.1-1)
  represents the concept of being a group of permutations.
  
  GAP   uses   Normalizer   (39.11-1)  to  compute  normalizers,  however  the
  declaration is a bit more complicated. In the library we find
  
    Example  
    InParentFOA( "Normalizer", IsGroup, IsObject, NewAttribute );
  
  
  The  full  mechanism  of  InParentFOA  (85.2-1)  is described in chapter 85,
  however  for  our purposes it is sufficient to know that for such a function
  the  actual  work  is  done  by  an  operation  NormalizerOp,  an underlying
  operation for Normalizer (39.11-1) (and all the complications are just there
  to  be  able  to  remember certain results) and that the declaration of this
  operation is given by the first arguments, it would be:
  
    Example  
    DeclareOperation( "NormalizerOp", [IsGroup, IsObject] );
  
  
  This  time  we decide to enter a non-default family predicate in the call to
  InstallMethod  (78.2-1). We could just leave it out as in the previous call;
  this  would  yield  the  default  value,  the function ReturnTrue (5.4-1) of
  arbitrary many arguments which always returns true. However, then the method
  might  be  called  in  some  cases of inconsistent input (for example matrix
  groups  in  different characteristics) that ought to fall through the method
  selection to raise an error.
  
  In our situation, we want the second group to be a subgroup of the first, so
  necessarily  both  must  have  the same family and we can use IsIdenticalObj
  (12.5-1) as family predicate.
  
  Now  we  can install the method. Again this manual is lazy and does not show
  you the actual code:
  
    Example  
    InstallMethod(NormalizerOp,"for nilpotent permutation groups",IsIdenticalObj,
      [IsPermGroup and IsNilpotentGroup,
       IsPermGroup and IsNilpotentGroup],
    function(G,U)
      [ function body omitted ]
    end);
  
  
  
  80.2 Extending the Range of Definition of an Existing Operation
  
  It  might  be  that  the operation has been defined so far only for a set of
  objects  that  is  too restrictive for our purposes (or we want to install a
  method  that  takes  another  number of arguments). If this is the case, the
  call to InstallMethod (78.2-1) causes an error message. We can avoid this by
  using InstallOtherMethod (78.2-2) instead. It is also possible to re-declare
  an  operation  with another number of arguments and/or different filters for
  its arguments.
  
  
  80.3 Enforcing Property Tests
  
  As  mentioned in Section 78.3, GAP does not check unknown properties to test
  whether  a  method  might  be applicable. In some cases one wants to enforce
  this, however, because the gain from knowing the property outweighs the cost
  of its determination.
  
  In  this  situation  one  has  to  install  a  method without the additional
  property  (so  it can be tried even if the property is not yet known) and at
  high  rank  (so it will be used before other methods). The first thing to do
  in  the  actual  function  then is to test the property and to bail out with
  TryNextMethod (78.4-1) if it turns out to be false.
  
  The above Exponent (39.16-2) example thus would become:
  
    Example  
    InstallMethod(Exponent,"test abelianity", [IsGroup],
      50,# enforced high rank
    function(G)
      if not IsAbelian(G) then
        TryNextMethod();
      fi;
      [remaining function body omitted]
    end);
  
  
  The value 50 used in this example is quite arbitrary. A better way is to use
  values that are given by the system inherently: We want this method still to
  be  ranked  as  high, as if it had the IsAbelian (35.4-9) requirement. So we
  have GAP compute the extra rank of this:
  
    Example  
    InstallMethod(Exponent,"test abelianity", [IsGroup],
      # enforced absolute rank of `IsGroup and IsAbelian' installation: Subtract
      # the rank of `IsGroup' and add the rank of `IsGroup and IsAbelian':
      RankFilter(IsGroup and IsAbelian)
      -RankFilter(IsGroup),
    function(G)
  
  
  the  slightly  complicated  construction  of  addition  and  subtraction  is
  necessary  because  IsGroup  (39.2-7) and IsAbelian (35.4-9) might imply the
  same elementary filters which we otherwise would count twice.
  
  A  somehow  similar  situation  occurs  with matrix groups. Most methods for
  matrix groups are only applicable if the group is known to be finite.
  
  However  we  should  not enforce a finiteness test early (someone else later
  might  install  good  methods  for infinite groups while the finiteness test
  would  be  too  expensive)  but just before GAP would give a no method found
  error.  This is done by redispatching, see 78.5. For example to enforce such
  a final finiteness test for normalizer calculations could be done by:
  
    Example  
    RedispatchOnCondition(NormalizerOp,IsIdenticalObj,
      [IsMatrixGroup,IsMatrixGroup],[IsFinite,IsFinite],0);
  
  
  
  80.4 Adding a new Operation
  
  Next,  we will consider how to add own operations. As an example we take the
  Sylow  normalizer  in  a  group  of  a  given prime. This operation gets two
  arguments, the first has to be a group, the second a prime number.
  
  There  is  a  function  IsPrimeInt (14.4-2), but no property for being prime
  (which  would be pointless as integers cannot store property values anyhow).
  So the second argument gets specified only as positive integer:
  
    Example  
    SylowNormalizer:=NewOperation("SylowNormalizer",[IsGroup,IsPosInt]);
  
  
  (Note  that  we  are using NewOperation (79.5-1) instead of DeclareOperation
  (79.18-12)  as  used  in  the  library.  The only difference other than that
  DeclareOperation  (79.18-12) saves some typing, is that it also protects the
  variables against overwriting. When testing code (when one probably wants to
  change  things)  this might be restricting. If this does not bother you, you
  can use
  
    Example  
    DeclareOperation("SylowNormalizer",[IsGroup,IsPosInt]);
  
  
  as well.)
  
  The  filters  IsGroup  (39.2-7) and IsPosInt (14.2-2) given are only used to
  test  that  InstallMethod  (78.2-1) installs methods with suitable arguments
  and  will  be  completely  ignored  when  using InstallOtherMethod (78.2-2).
  Technically  one  could  therefore  simply  use  IsObject  (12.1-1)  for all
  arguments  in the declaration. The main point of using more specific filters
  here  is to help documenting with which arguments the function is to be used
  (so for example a call SylowNormalizer(5,G) would be invalid).
  
  Of  course  initially  there  are  no  useful  methods  for  newly  declared
  operations; you will have to write and install them yourself.
  
  If  the  operation  only  takes  one  argument  and has reproducible results
  without  side  effects,  it  might  be  worth  declaring  it as an attribute
  instead; see Section 80.5.
  
  
  80.5 Adding a new Attribute
  
  Now  we  look  at  an  example  of how to add a new attribute. As example we
  consider the set of all primes that divide the size of a group.
  
  First we have to declare the attribute:
  
    Example  
    PrimesDividingSize:=NewAttribute("PrimesDividingSize",IsGroup);
  
  
  (See NewAttribute  (79.3-1)).  This implicitly declares attribute tester and
  setter, it is convenient however to assign these to variables as well:
  
    Example  
    HasPrimesDividingSize:=Tester(PrimesDividingSize);
    SetPrimesDividingSize:=Setter(PrimesDividingSize);
  
  
  Alternatively,  there  is  a  declaration command DeclareAttribute (79.18-9)
  that   executes  all  three  assignments  simultaneously  and  protects  the
  variables against overwriting:
  
    Example  
    DeclareAttribute("PrimesDividingSize",IsGroup);
  
  
  Next  we have to install method(s) for the attribute that compute its value.
  (This  is  not  strictly  necessary. We could use the attribute also without
  methods  only  for  storing  and  retrieving information, but calling it for
  objects  for  which  the  value is not known would produce a no method found
  error.)  For  this  purpose  we  can  imagine  the  attribute  simply  as an
  one-argument operation:
  
    Example  
    InstallMethod(PrimesDividingSize,"for finite groups",
      [IsGroup and IsFinite],
    function(G)
      return PrimeDivisors(Size(G));
    end);
  
  
  The  function  installed  must  always return a value (or call TryNextMethod
  (78.4-1)). If the object is in the representation IsAttributeStoringRep this
  return value once computed will be automatically stored and retrieved if the
  attribute  is  called  a second time. We don't have to call setter or tester
  ourselves.  (This  storage  happens  by GAP internally calling the attribute
  setter with the return value of the function. Retrieval is by a high-ranking
  method  which  is  installed under the condition HasPrimesDividingSize. This
  method was installed automatically when the attribute was declared.)
  
  
  80.6 Adding a new Representation
  
  Next,  we  look  at  the  implementation of a new representation of existing
  objects.  In  most  cases  we want to implement this representation only for
  efficiency reasons while keeping all the existing functionality.
  
  For  example,  assume we wanted (following [Wie69]) to implement permutation
  groups defined by relations.
  
  Next,  we have to decide a few basics about the representation. All existing
  permutation groups in the library are attribute storing and we probably want
  to  keep  this  for  our  new  objects.  Thus  the  representation must be a
  subrepresentation   of   IsComponentObjectRep   and   IsAttributeStoringRep.
  Furthermore  we  want each object to be a permutation group and we can imply
  this directly in the representation.
  
  We  also  decide  that  we store the degree (the largest point that might be
  moved)  in  a  component  degree  and  the defining relations in a component
  relations  (we  do  not  specify  the format of relations here. In an actual
  implementation one would have to design this as well, but it does not affect
  the declarations this chapter is about).
  
    Example  
    IsPermutationGroupByRelations:=NewRepresentation(
      "IsPermutationGroupByRelations",
      IsComponentObjectRep and IsAttributeStoringRep and IsPermGroup,
      ["degree","relations"]);
  
  
  (If  we  wanted  to  implement  sparse  matrices we might for example rather
  settle  for  a  positional  object  in  which we store a list of the nonzero
  entries.)
  
  We  can  make the new representation a subrepresentation of an existing one.
  In  such  a  case  of course we have to provide all structure of this parent
  representation as well.
  
  Next  we need to check in which family our new objects will be. This will be
  the   same   family   as  of  every  other  permutation  group,  namely  the
  CollectionsFamily(PermutationsFamily) (where the family PermutationsFamily =
  FamilyObj((1,2,3)) has been defined already in the library).
  
  Now we can write a function to create our new objects. Usually it is helpful
  to  look  at  functions from the library that are used in similar situations
  (for  example  GroupByGenerators  (39.2-2) in our case) to make sure we have
  not  forgotten  any further requirements in the declaration we might have to
  add here. However in most cases the function is straightforward:
  
    Example  
    PermutationGroupByRelations:=function(degree,relations)
    local g
      g:=Objectify(NewType(CollectionsFamily(PermutationsFamily),
    		       IsPermutationGroupByRelations),
                   rec(degree:=degree,relations:=relations));
    end;
  
  
  It  also  is  a  good idea to install a PrintObj (6.3-5) and possibly also a
  ViewObj (6.3-5) method –otherwise testing becomes quite hard:
  
    Example  
    InstallMethod(PrintObj,"for perm grps. given by relations",
      [IsPermutationGroupByRelations],
    function(G)
      Print("PermutationGroupByRelations(", G!.degree,",",G!.relations,")");
    end);
  
  
  Next  we have to write enough methods for the new representation so that the
  existing  algorithms  can  be  used. In particular we will have to implement
  methods  for all operations for which library or kernel provides methods for
  the existing (alternative) representations. In our particular case there are
  no such methods. (If we would have implemented sparse matrices we would have
  had  to  implement methods for the list access and assignment functions, see
  21.2.)  However  the  existing  way permutation groups are represented is by
  generators.  To  be able to use the existing machinery we want to be able to
  obtain  a generating set also for groups in our new representation. This can
  be  done  (albeit  not  very effectively) by a stabilizer calculation in the
  symmetric group given by the degree component. The operation function to use
  is probably a bit complicated and will depend on the format of the relations
  (we  have  not  specified  in  this example). In the following method we use
  operationfunction as a placeholder;
  
    Example  
    InstallMethod(GeneratorsOfGroup,"for perm grps. given by relations",
      [IsPermutationGroupByRelations],
    function(G)
    local S,U;
      S:=SymmetricGroup(G!.degree);
      U:=Stabilizer(S,G!.relations,  operationfunction );
      return GeneratorsOfGroup(U);
    end);
  
  
  This  is all we must do. Of course for performance reasons one might want to
  install methods for further operations as well.
  
  
  80.7 Components versus Attributes
  
  In  the  last  section  we  introduced  two  new  components,  G!.degree and
  G!.relations.  Technically,  we could have used attributes instead. There is
  no  clear  distinction  which  variant  is  to  be  preferred:  An attribute
  expresses  part  of the functionality available to certain objects (and thus
  could  be  computed later and probably even for a wider class of objects), a
  component is just part of the internal definition of an object.
  
  So if the data is of general interest, if we want the user to have access to
  it,  attributes  are  preferable.  Moreover,  attributes  can be used by the
  method  selection  (by specifying the filter HasAttr for an attribute Attr).
  They  provide a clean interface and their immutability makes it safe to hand
  the data to a user who potentially could corrupt a components entries.
  
  On  the other hand more technical data (say the encoding of a sparse matrix)
  is  better  hidden  from  the  user  in  a  component, as declaring it as an
  attribute would not give any advantage.
  
  Resource-wise,  attributes need more memory (the attribute setter and tester
  are  implicitly  declared,  and  one  filter bit is required), the attribute
  access  is one further function call in the kernel, thus components might be
  an immeasurable bit faster.
  
  
  80.8 Adding new Concepts
  
  Now we look how to implement a new concept for existing objects and fit this
  in  the  method  selection.  Three  examples that will be made more explicit
  below  would  be groups for which a length of elements (as a word in certain
  generators)  is  defined,  groups  that  can  be  decomposed as a semidirect
  product and M-groups.
  
  In  each  case  we have two possibilities for the declaration. We can either
  declare it as a property or as a category. Both are eventually filter(s) and
  in  this  way indistinguishable for the method selection. However, the value
  of  a  property for a particular object can be unknown at first and later in
  the  session  be  computed  (to  be  true  or false). This is implemented by
  reserving two filters for each property, one indicating whether the property
  value is known, and one, provided the value is known, to indicate the actual
  boolean  value. Contrary to this, the decision whether or not an object lies
  in  a  category  is  taken  at creation time and this is implemented using a
  single filter.
  
  Property:
        Properties  also  are attributes: If a property value is not known for
        an  object,  GAP tries to find a method to compute the property value.
        If no suitable method is found, an error is raised.
  
  Category:
        An  object  is in a category if it has been created in it. Testing the
        category  for  an  object  simply returns this value. Existing objects
        cannot  enter  a  new  category later in life. This means that in most
        cases one has to write own code to create objects in a new category.
  
        If  we  want  to  implement  a  completely  new  concept  so  that new
        operations   are  defined  only  for  the  new  objects  –for  example
        bialgebras  for  which  a  second  scalar  multiplication  is defined–
        usually a category is chosen.
  
        Technically,   the  behaviour  of  the  category  IsXYZ,  declared  as
        subcategory  of  IsABC  is  therefore  exactly the same as if we would
        declare  IsXYZ  to  be  a property for IsABC and install the following
        method:
  
          Example  
          InstallMethod(IsXYZ,"return false if not known",[IsABC],ReturnFalse);
        
  
        (The  word  category also has a well-defined mathematical meaning, but
        this  does  not  need  to concern us at this point. The set of objects
        which is defined to be a (GAP) category does not need to be a category
        in  the mathematical sense, vice versa not every mathematical category
        is declared as a (GAP) category.)
  
  Eventually  the  choice between category and property often becomes a matter
  of taste or style.
  
  Sometimes  there  is  even a third possibility (if you have GAP 3 experience
  this might reflect most closely an object whose operations record is XYOps):
  We  might  want to indicate this new concept simply by the fact that certain
  attributes  are  set.  In  this  case  we  could  simply  use the respective
  attribute tester(s).
  
  The  examples  given  below  each  give  a short argument why the respective
  solution was chosen, but one could argue as well for other choices.
  
  
  80.8-1 Example: M-groups
  
  M-groups are finite groups for which all irreducible complex representations
  are induced from linear representations of subgroups, it turns out that they
  are  all  solvable  and  that  every  supersolvable group is an M-group. See
  [Isa76] for further details.
  
  Solvability  and supersolvability both are testable properties. We therefore
  declare IsMGroup as a property for solvable groups:
  
    Example  
    IsMGroup:=NewProperty("IsMGroup",IsSolvableGroup);
  
  
  The  filter  IsSolvableGroup  (39.15-6)  in this declaration only means that
  methods  for  IsMGroup  by default can only be installed for groups that are
  (and  know  to be) solvable (though they could be installed for more general
  situations  using  InstallOtherMethod  (78.2-2)). It does not yet imply that
  M-groups  are  solvable. We must do this deliberately via an implication and
  we  use  the  same  technique  to imply that every supersolvable group is an
  M-group.
  
    Example  
    InstallTrueMethod(IsSolvableGroup,IsMGroup);
    InstallTrueMethod(IsMGroup,IsSupersolvableGroup);
  
  
  Now  we  might  install a method that tests for solvable groups whether they
  are M-groups:
  
    Example  
    InstallMethod(IsMGroup,"for solvable groups",[IsSolvableGroup],
    function(G)
      [... code omitted. The function must return `true' or `false' ...]
    end);
  
  
  Note  that  this  example  of  declaring  the IsMGroup property for solvable
  groups  is not a part of the GAP library, which uses a similar but different
  filter IsMonomialGroup (39.15-9).
  
  
  80.8-2 Example: Groups with a word length
  
  Our  second  example  is  that of groups for whose elements a word length is
  defined.  (We  assume that the word length is only defined in the context of
  the  group  with  respect to a preselected generating set but not for single
  elements  alone.  However  we  will  not  delve into any details of how this
  length is defined and how it could be computed.)
  
  Having  a  word  length  is  a  feature  which enables other operations (for
  example  a  word  length  function).  This  is  exactly  what categories are
  intended for and therefore we use one.
  
  First,  we declare the category. All objects in this category are groups and
  so we inherit the supercategory IsGroup (39.2-7):
  
    Example  
    DeclareCategory("IsGroupWithWordLength",IsGroup);
  
  
  We  also  define  the  operation which is enabled by this category, the word
  length  of  a  group  element,  which  is defined for a group and an element
  (remember    that   group   elements   are   described   by   the   category
  IsMultiplicativeElementWithInverse (31.14-13)):
  
    Example  
    DeclareOperation("WordLengthOfElement",[IsGroupWithWordLength,
      IsMultiplicativeElementWithInverse]);
  
  
  We  then  would  proceed by installing methods to compute the word length in
  concrete  cases and might for example add further operations to get shortest
  words in cosets.
  
  
  80.8-3 Example: Groups with a decomposition as semidirect product
  
  The  third  example  is  groups which have a (nontrivial) decomposition as a
  semidirect  product.  If  this information has been found out, we want to be
  able  to use it in algorithms. (Thus we do not only need the fact that there
  is a decomposition, but also the decomposition itself.)
  
  We  also  want  this to be applicable to every group and not only for groups
  which have been explicitly constructed via SemidirectProduct (49.2-1).
  
  Instead  we  simply  declare an attribute SemidirectProductDecomposition for
  groups.  (Again,  in  this  manual we don't go in the details of how such an
  decomposition would look like).
  
    Example  
    DeclareAttribute("SemidirectProductDecomposition",IsGroup);
  
  
  If  a  decomposition  has  been  found,  it  can  be stored in a group using
  SetSemidirectProductDecomposition.  (At  the  moment  all  groups in GAP are
  attribute storing.)
  
  Methods  that  rely  on  the  existence  of  such  a  decomposition then get
  installed for the tester filter HasSemidirectProductDecomposition.
  
  
  80.9 Creating Own Arithmetic Objects
  
  Finally  let's  look  at  a  way  to  create new objects with a user-defined
  arithmetic such that one can form for example groups, rings or vector spaces
  of  these  elements.  This  topic  is  discussed  in  much  more  detail  in
  chapter 82,  in this section we present a simple approach that may be useful
  to get started but does not permit you to exploit all potential features.
  
  The  basic design is that the user designs some way to represent her objects
  in  terms of GAPs built-in types, for example as a list or a record. We call
  this  the defining data of the new objects. Also provided are functions that
  perform  arithmetic on this defining data, that is they take objects of this
  form  and  return  objects  that  represent the result of the operation. The
  function  ArithmeticElementCreator  (80.9-1)  then  is  called  to provide a
  wrapping  such  that  proper  new  GAP-objects  are  created  which  can  be
  multiplied etc. with the default infix operations such as *.
  
  80.9-1 ArithmeticElementCreator
  
  ArithmeticElementCreator( spec )  function
  
  offers  a  simple  interface  to create new arithmetic elements by providing
  functions  that perform addition, multiplication and so forth, conforming to
  the  specification  spec.  ArithmeticElementCreator  creates a new category,
  representation and family for the new arithmetic elements being defined, and
  returns  a  function which takes the defining data of an element and returns
  the corresponding new arithmetic element.
  
  spec is a record with one or more of the following components:
  
  ElementName
        string  used  to  identify the new type of object. A global identifier
        IsElementName  will  be  defined  to indicate a category for these now
        objects. (Therefore it is not clever to have blanks in the name). Also
        a  collections  category is defined. (You will get an error message if
        the identifier IsElementName is already defined.)
  
  Equality, LessThan, One, Zero, Multiplication, Inverse, Addition, AdditiveInverse
        functions  defining the arithmetic operations. The functions interface
        on  the  level  of  defining  data,  the actual methods installed will
        perform  the  unwrapping  and  wrapping  as  objects.  Components  are
        optional,  but  of  course  if  no  multiplication is defined elements
        cannot be multiplied and so forth.
  
        There  are  default  methods  for  Equality  and LessThan which simply
        calculate  on the defining data. If one is defined, it must be ensured
        that the other is compatible (so that a < b implies not (a = b))
  
  Print
        a function which prints the object. By default, just the defining data
        is printed.
  
  MathInfo
        filters  determining  the  mathematical  properties  of  the  elements
        created.       A      typical      value      is      for      example
        IsMultiplicativeElementWithInverse for group elements.
  
  RepInfo
        filters  determining  the  representational properties of the elements
        created.  The objects created are always component objects, so in most
        cases  the  only  reasonable option is IsAttributeStoringRep to permit
        the storing of attributes.
  
  All  components  are  optional  and  will  be  filled in with default values
  (though of course an empty record will not result in useful objects).
  
  Note  that  the resulting objects are not equal to their defining data (even
  though  by  default  they  print  as  only the defining data). The operation
  UnderlyingElement  can  be  used  to  obtain  the  defining  data of such an
  element.
  
  
  80.9-2 Example: ArithmeticElementCreator
  
  As  the  first  example  we  look  at subsets of { 1, ..., 4 } and define an
  addition  as  union and multiplication as intersection. These operations are
  both commutative and we want the resulting elements to know this.
  
  We therefore use the following specification:
  
    Example  
    gap> # the whole set
    gap> w := [1,2,3,4];
    [ 1, 2, 3, 4 ]
    gap> PosetElementSpec :=rec(
    >   # name of the new elements
    >   ElementName := "PosetOn4",
    >   # arithmetic operations
    >   One := a -> w,
    >   Zero := a -> [],
    >   Multiplication := function(a, b) return Intersection(a, b); end,
    >   Addition := function(a, b) return Union(a, b); end,
    >   # Mathematical properties of the elements
    >   MathInfo := IsCommutativeElement and
    >               IsAssociativeElement and
    >               IsAdditivelyCommutativeElement
    > );;
    gap> mkposet := ArithmeticElementCreator(PosetElementSpec);
    function( x ) ... end
  
  
  Now we can create new elements, perform arithmetic on them and form domains:
  
    Example  
    gap> a := mkposet([1,2,3]);
    [ 1, 2, 3 ]
    gap> CategoriesOfObject(a);
    [ "IsExtAElement", "IsNearAdditiveElement", 
      "IsNearAdditiveElementWithZero", "IsAdditiveElement", 
      "IsExtLElement", "IsExtRElement", "IsMultiplicativeElement", 
      "IsMultiplicativeElementWithOne", "IsAssociativeElement",
      "IsAdditivelyCommutativeElement", "IsCommutativeElement",
      "IsPosetOn4" ]
    gap> a=[1,2,3];
    false
    gap> UnderlyingElement(a)=[1,2,3];
    true
    gap> b:=mkposet([2,3,4]);
    [ 2, 3, 4 ]
    gap> a+b;
    [ 1 .. 4 ]
    gap> a*b;
    [ 2, 3 ]
    gap> s:=Semigroup(a,b);
    <commutative semigroup with 2 generators>
    gap> Size(s);
    3
  
  
  The  categories  IsPosetOn4  and IsPosetOn4Collection can be used to install
  methods specific to the new objects.
  
    Example  
    gap> IsPosetOn4Collection(s);
    true
  
  

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