In this section we will show you an example that is slightly more complex than the example in the previous section. Namely we will demonstrate how one can implement parametrized domains. As an example we will implement symmetric permutation groups. This works similar to the implementation of a single domain. Therefore we can be very brief. Of course you should have read the previous section.
Note   that   everything   defined   here   is   already   in   the  file
GRPNAME/"permgrp.grp",  so there  is no need  for  you to type it in.
You may however like to make a copy of this file and modify it.
In  the  example  of  the previous  section  we  simply  had  a  variable
(GaussianIntegers), whose value  was the domain.  This can  not work in
this  example, because  there is not  one symmetric  permutation group.
The solution is obvious.  We  simply  define a  function  that takes  the
degree and  returns the symmetric permutation group of this degree  (as a
domain).
    gap> SymmetricPermGroup := function ( n )
    >     local   G;          # symmetric group on <n> points, result
    >
    >     # make the group generated by (1,n), (2,n), .., (n-1,n)
    >     G := Group( List( [1..n-1], i -> (i,n) ), () );
    >     G.degree := n;
    >
    >     # give it the correct operations record
    >     G.operations := SymmetricPermGroupOps;
    >
    >     # return the symmetric group
    >     return G;
    > end;;
The key is of course to give the domains returned by SymmetricPermGroup
a new operations record.  This operations record will hold functions that
are written especially for  symmetric permutation groups.   Note that all
symmetric groups  created by  SymmetricPermGroup  share one  operations
record.
Just as  we  inherited  in the example  in the previous section  from the
operations  record RingOps,  here  we can inherit  from  the operations
record PermGroupOps (after  all, each symmetric  permutation  group  is
also a permutation group).
    gap> SymmetricPermGroupOps := Copy( PermGroupOps ); 
We will now overlay some of the functions in this operations record with new functions that make use of the fact that the domain is a full symmetric permutation group. The first function that does this is the membership test function.
    gap> SymmetricPermGroupOps.\in := function ( g, G )
    >     return     IsPerm( g )
    >            and (   g = ()
    >                 or LargestMovedPointPerm( g ) <= G.degree);
    > end;;
The most important knowledge for a permutation group is a base and a strong generating set with respect to that base. It is not important that you understand at this point what this is mathematically. The important point here is that such a strong generating set with respect to an appropriate base is used by many generic permutation group functions, most of which we inherit for symmetric permutation groups. Therefore it is important that we are able to compute a strong generating set as fast as possible. Luckily it is possible to simply write down such a strong generating set for a full symmetric group. This is done by the following function.
    gap> SymmetricPermGroupOps.MakeStabChain := function ( G, base )
    >     local   sgs,        # strong generating system of G wrt. base
    >             last;       # last point of the base
    >
    >     # remove all unwanted points from the base
    >     base := Filtered( base, i -> i <= G.degree );
    >
    >     # extend the base with those points not already in the base
    >     base := Concatenation( base, Difference( [1..G.degree], base ) );
    >
    >     # take the last point
    >     last := base[ Length(base) ];
    >
    >     # make the strong generating set
    >     sgs := List( [1..Length(base)-1], i -> ( base[i], last ) );
    >
    >     # make the stabilizer chain
    >     MakeStabChainStrongGenerators( G, base, sgs );
    > end;;
One of the things that are very easy for symmetric groups is the computation of centralizers of elements. The next function does this. Again it is not important that you understand this mathematically. The centralizer of an element g in the symmetric group is generated by the cycles c of g and an element x for each pair of cycles of g of the same length that maps one cycle to the other.
    gap> SymmetricPermGroupOps.Centralizer := function ( G, g )
    >     local   C,      # centralizer of g in G, result
    >             sgs,    # strong generating set of C
    >             gen,    # one generator in sgs
    >             cycles, # cycles of g
    >             cycle,  # one cycle from cycles
    >             lasts,  # lasts[l] is the last cycle of length l
    >             last,   # one cycle from lasts
    >             i;      # loop variable
    >
    >     # handle special case
    >     if IsPerm( g )  and g in G  then
    >
    >         # start with the empty strong generating system
    >         sgs := [];
    >
    >         # compute the cycles and find for each length the last one
    >         cycles := Cycles( g, [1..G.degree] );
    >         lasts := [];
    >         for cycle  in cycles  do
    >             lasts[Length(cycle)] := cycle;
    >         od;
    >
    >         # loop over the cycles
    >         for cycle  in cycles  do
    >
    >             # add that cycle itself to the strong generators
    >             if Length( cycle ) <> 1  then
    >                 gen := [1..G.degree];
    >                 for i  in [1..Length(cycle)-1]  do
    >                     gen[cycle[i]] := cycle[i+1];
    >                 od;
    >                 gen[cycle[Length(cycle)]] := cycle[1];
    >                 gen := PermList( gen );
    >                 Add( sgs, gen );
    >             fi;
    >
    >             # and it can be mapped to the last cycle of this length
    >             if cycle <> lasts[ Length(cycle) ]  then
    >                 last := lasts[ Length(cycle) ];
    >                 gen := [1..G.degree];
    >                 for i  in [1..Length(cycle)]  do
    >                     gen[cycle[i]] := last[i];
    >                     gen[last[i]] := cycle[i];
    >                 od;
    >                 gen := PermList( gen );
    >                 Add( sgs, gen );
    >             fi;
    >
    >         od;
    >
    >         # make the centralizer
    >         C := Subgroup( G, sgs );
    >
    >         # make the stabilizer chain
    >         MakeStabChainStrongGenerators( C, [1..G.degree], sgs );
    >
    >     # delegate general case
    >     else
    >         C := PermGroupOps.Centralizer( G, g );
    >     fi;
    >
    >     # return the centralizer
    >     return C;
    > end;;
Note that the definition C :=  Subgroup( G, sgs );  defines a subgroup
of  a  symmetric permutation  group.  But  this subgroup is usually not a
full symmetric  permutation  group itself.   Thus  C must not  have the
operations  record SymmetricPermGroupOps, instead  it should  have  the
operations   record  PermGroupOps.   And  indeed  C  will  have  this
operations    record.      This     is     because    Subgroup    calls
G.operations.Subgroup,   and   we   inherited  this   function   from
PermGroupOps.
Note also that  we  only handle one  special  case in the function above.
Namely  the  computation of  a  centralizer  of  a  single element.  This
function can also  be called  to  compute  the  centralizer  of  a  whole
subgroup.   In  this   case   SymmetricPermGroupOps.Centralizer  simply
delegates the problem by calling PermGroupOps.Centralizer.
The next function computes the conjugacy classes of elements in a symmetric group. This is very easy, because two elements are conjugated in a symmetric group when they have the same cycle structure. Thus we can simply compute the partitions of the degree, and for each degree we get one conjugacy class.
    gap> SymmetricPermGroupOps.ConjugacyClasses := function ( G )
    >     local   classes,    # conjugacy classes of G, result
    >             prt,        # partition of G
    >             sum,        # partial sum of the entries in prt
    >             rep,        # representative of a conjugacy class of G
    >             i;          # loop variable
    >
    >     # loop over the partitions
    >     classes := [];
    >     for prt  in Partitions( G.degree )  do
    >
    >         # compute the representative of the conjugacy class
    >         rep := [2..G.degree];
    >         sum := 1;
    >         for i  in prt  do
    >             rep[sum+i-1] := sum;
    >             sum := sum + i;
    >         od;
    >         rep := PermList( rep );
    >
    >         # add the new class to the list of classes
    >         Add( classes, ConjugacyClass( G, rep ) );
    >
    >     od;
    >
    >     # return the classes
    >     return classes;
    > end;;
This concludes this example. You have seen that the implementation of a parametrized domain is not much more difficult than the implementation of a single domain. You have also seen how functions that overlay generic functions may delegate problems back to the generic function. The library file for symmetric permutation groups contain some more functions for symmetric permutation groups.
GAP 3.4.4