  
  [1X2 Implementing circle objects[0X
  
  In  this  chapter  we  explain  how  the [5XGAP[0m system may be extended with new
  objects  using  the  circle  multiplication  as  an  example.  We follow the
  guidelines  given  in  the last two chapters of the [5XGAP[0m Programming Tutorial
  and refer to them for more details.
  
  
  [1X2.1 First attempts[0X
  
  Of course, having two ring elements, you can straightforwardly compute their
  circle  product  defined as r * s = r + s + rs. You can do this in a command
  line, and it is a trivial task to write a simplest function of two arguments
  that will do this:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> CircleMultiplication := function(a,b)[0X
    [4X>      return a+b+a*b;[0X
    [4X>    end;[0X
    [4Xfunction( a, b ) ... end[0X
    [4Xgap> CircleMultiplication(2,3); [0X
    [4X11[0X
    [4Xgap> CircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );      [0X
    [4XZmodnZObj( 1, 8 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  However,  there  is  no check whether both arguments belong to the same ring
  and  whether  they  are  ring  elements at all, so it is easy to obtain some
  meaningless results:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> CircleMultiplication( 3, ZmodnZObj(3,8) );[0X
    [4XZmodnZObj( 7, 8 )[0X
    [4Xgap> CircleMultiplication( [1], [2,3] );[0X
    [4X[ 5, 5 ][0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  You  can  include  some tests for arguments, and maybe the best way of doing
  this  would  be  declaring  a  new  operation  for  two  ring  elements, and
  installing  the  previous function as a method for this operation. This will
  check automatically if the arguments are ring elements from the common ring:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareOperation( "BetterCircleMultiplication",                             [0X
    [4X>      [IsRingElement,IsRingElement] );[0X
    [4Xgap> InstallMethod( BetterCircleMultiplication,[0X
    [4X>      IsIdenticalObj,[0X
    [4X>      [IsRingElement,IsRingElement],  [0X
    [4X>      CircleMultiplication );[0X
    [4Xgap> BetterCircleMultiplication(2,3);[0X
    [4X11[0X
    [4Xgap> BetterCircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );[0X
    [4XZmodnZObj( 1, 8 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Nevertheless,  the  functionality gained from such operation would be rather
  limited.  You  will  not  be  able  to  compute circle product via the infix
  operator  [10X*[0m,  and,  moreover,  you  will  not be able to create higher level
  objects   such   as  semigroups  and  groups  with  respect  to  the  circle
  multiplication.
  
  In  order  to  "integrate"  the  circle  multiplication into the [5XGAP[0m library
  properly, instead of defining [13Xnew[0m operations for existing objects, we should
  define  [13Xnew[0m  objects  for which the infix operator [10X*[0m will perform the circle
  multiplication. This approach is explained in the next two sections.
  
  
  [1X2.2 Defining circle objects[0X
  
  Thus,  we  are  going to implement [13Xcircle objects[0m, for which we can envisage
  the following functionality:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> CircleObject( 2 ) * CircleObject( 3 );                       [0X
    [4XCircleObject( 11 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  First  we need to distinguish these new objects from other [5XGAP[0m objects. This
  is  done  via  the  [13Xtype[0m  of the objects, that is mainly determined by their
  [13Xcategory[0m, [13Xrepresentation[0m and [13Xfamily[0m.
  
  We  start  with  declaring  the  category [10XIsCircleObject[0m as a subcategory of
  [10XIsMultiplicativeElementWithInverse[0m.  Thus,  each  circle  object will "know"
  that  it  is  [10XIsMultiplicativeElementWithInverse[0m,  and  this  will  make  it
  possible  to apply to circle objects such operations as [10XOne[0m and [10XInverse[0m (the
  latter is allowed to return [9Xfail[0m for a given circle object).
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareCategory( "IsCircleObject", IsMultiplicativeElementWithInverse );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Further  we  would  like to create semigroups and groups generated by circle
  objects. Such structures will be [13Xcollections[0m of circle objects, so they will
  be  in  the  category  [10XCategoryCollections(  IsCircleObject  )[0m.  This is why
  immediately  after  we declare the underlying category of circle objects, we
  need also to declare the category of their collections:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareCategoryCollections( "IsCircleObject" );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  On the next step we should think about the internal representation of circle
  objects.  A  natural  way would be to store the underlying ring element in a
  list-like  structure at its first position. We do not foresee any other data
  that  we need to store internally in the circle object. This is quite common
  situation,  so  we may define first [10XIsPositionalObjectOneSlotRep[0m that is the
  list-like  representation  with  only  one  position  in  the list, and then
  declare  a synonym [10XIsDefaultCircleObject[0m that means that we are dealing with
  a circle object in one-slot representation:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareRepresentation( "IsPositionalObjectOneSlotRep",[0X
    [4X>     IsPositionalObjectRep, [ 1 ] );[0X
    [4Xgap> DeclareSynonym( "IsDefaultCircleObject",[0X
    [4X>     IsCircleObject and IsPositionalObjectOneSlotRep );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Until  now  we are still unable to create circle objects, because we did not
  specify  to which family they will belong. Naturally, having a ring, we want
  to  have  all circle objects for elements of this ring in the same family to
  be  able  to  multiply  them,  and  we expect circle objects for elements of
  different  rings  to be placed in different families. Thus, it would be nice
  to  establish  one-to-one correspondence between the family of ring elements
  and   a  family  of  circle  elements  for  this  ring.  We  can  store  the
  corresponding  circle family as an attribute of the ring elements family. To
  do this first we declare an attribute [10XCircleFamily[0m for families:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareAttribute( "CircleFamily", IsFamily );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Now  we  install  the  method that stores the corresponding circle family in
  this attribute:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( CircleFamily,[0X
    [4X>     "for a family",[0X
    [4X>     [ IsFamily ],[0X
    [4X>     function( Fam )[0X
    [4X>     local F;[0X
    [4X>   # create the family of circle elements[0X
    [4X>   F:= NewFamily( "CircleFamily(...)", IsCircleObject );[0X
    [4X>   if HasCharacteristic( Fam ) then[0X
    [4X>     SetCharacteristic( F, Characteristic( Fam ) );[0X
    [4X>   fi;[0X
    [4X>   # store the type of objects in the output[0X
    [4X>   F!.CircleType:= NewType( F, IsDefaultCircleObject );[0X
    [4X>   # Return the circle family[0X
    [4X>   return F;[0X
    [4X> end );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Similarly,  we  want  one-to-one  correspondence between circle elements and
  underlying  ring  elements.  We declare an attribute [10XCircleObject[0m for a ring
  element,  and  then  install the method to create new circle object from the
  ring  element.  This  method  takes  the  family  of the ring element, finds
  corresponding circle family, extracts from it the type of circle objects and
  finally creates the new circle object of that type:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareAttribute( "CircleObject", IsRingElement );[0X
    [4Xgap> InstallMethod( CircleObject,[0X
    [4X>     "for a ring element",[0X
    [4X>     [ IsRingElement ],[0X
    [4X>     obj -> Objectify( CircleFamily( FamilyObj( obj ) )!.CircleType,[0X
    [4X>                       [ Immutable( obj ) ] ) );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Only after entering all code above we are able to create some circle object.
  However,  it is displayed just as [10X<object>[0m, though we can get the underlying
  ring element using the "!" operator:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> a:=CircleObject(2);[0X
    [4X<object>[0X
    [4Xgap> a![1];[0X
    [4X2[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  We can check that the intended relation between families holds:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> FamilyObj( CircleObject ( 2 ) ) = CircleFamily( FamilyObj( 2 ) );[0X
    [4Xtrue[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  We  can not multiply circle objects yet. But before implementing this, first
  let us improve the output by installing the method for [10XPrintObj[0m:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( PrintObj,[0X
    [4X>     "for object in `IsCircleObject'",[0X
    [4X>     [ IsDefaultCircleObject ],[0X
    [4X>     function( obj )[0X
    [4X>     Print( "CircleObject( ", obj![1], " )" );[0X
    [4X>     end );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  This  method  will be used by [10XPrint[0m function, and also by [10XView[0m, since we did
  not  install  special  method for [10XViewObj[0m for circle objects. As a result of
  this installation, the output became more meaningful:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> a;[0X
    [4XCircleObject( 2 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  We  need  to  avoid  the  usage  of  "!" operator, which, in general, is not
  recommended  to  the  user  (for  example, if [5XGAP[0m developers will change the
  internal  representation of some object, all [5XGAP[0m functions that deal with it
  must  be  adjusted appropriately, while if the user's code had direct access
  to  that  representation  via  "!", an error may occur). To do this, we wrap
  getting the first component of a circle object in the following operation:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> DeclareOperation("UnderlyingRingElement", [ IsCircleObject] );[0X
    [4Xgap> InstallMethod( UnderlyingRingElement,[0X
    [4X>     "for a circle object", [0X
    [4X>     [ IsCircleObject],[0X
    [4X>     obj -> obj![1] );[0X
    [4Xgap> UnderlyingRingElement(a);[0X
    [4X2[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  
  [1X2.3 Installing operations for circle objects[0X
  
  Now we are finally able to install circle multiplication as a default method
  for  the  multiplication of circle objects, and perform the computation that
  we envisaged in the beginning:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( \*,[0X
    [4X>     "for two objects in `IsCircleObject'",[0X
    [4X>     IsIdenticalObj,[0X
    [4X>     [ IsDefaultCircleObject, IsDefaultCircleObject ],[0X
    [4X>     function( a, b )[0X
    [4X>     return CircleObject( a![1] + b![1] + a![1]*b![1] );[0X
    [4X>     end );[0X
    [4Xgap> CircleObject(2)*CircleObject(3);[0X
    [4XCircleObject( 11 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  However,  this  functionality  is  not  enough  to form semigroups or groups
  generated by circle elements. We need to be able to check whether two circle
  objects  are equal, and we need to define ordering for them (for example, to
  be  able  to  form  sets  of  circle  elements).  Since we already have both
  operations  for  underlying  ring  elements,  this  can  be implemented in a
  straightforward way:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( \=,[0X
    [4X>     "for two objects in `IsCircleObject'",[0X
    [4X>     IsIdenticalObj,[0X
    [4X>     [ IsDefaultCircleObject, IsDefaultCircleObject ],[0X
    [4X>     function( a, b )[0X
    [4X>     return a![1] = b![1];[0X
    [4X>     end );[0X
    [4Xgap> InstallMethod( \<,[0X
    [4X>     "for two objects in `IsCircleObject'",[0X
    [4X>     IsIdenticalObj,[0X
    [4X>     [ IsDefaultCircleObject, IsDefaultCircleObject ],[0X
    [4X>     function( a, b )[0X
    [4X>     return a![1] < b![1];[0X
    [4X>     end );[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Further,  zero  element  of the ring plays a role of the neutral element for
  the  circle  multiplication, and we add this knowledge to our code in a form
  of  a method for [10XOneOp[0m that returns circle object for the corresponding zero
  object:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( OneOp,[0X
    [4X>     "for an object in `IsCircleObject'",[0X
    [4X>     [ IsDefaultCircleObject ],[0X
    [4X>     a -> CircleObject( Zero( a![1] ) ) );[0X
    [4Xgap> One(a);[0X
    [4XCircleObject( 0 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Now we are already able to create monoids generated by circle objects:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> S:=Monoid(a);[0X
    [4X<monoid with 1 generator>[0X
    [4Xgap> One(S);[0X
    [4XCircleObject( 0 )[0X
    [4Xgap> S:=Monoid( CircleObject( ZmodnZObj( 2,8) ) );[0X
    [4X<monoid with 1 generator>[0X
    [4Xgap> Size(S);[0X
    [4X2[0X
    [4Xgap> AsList(S);[0X
    [4X[ CircleObject( ZmodnZObj( 0, 8 ) ), CircleObject( ZmodnZObj( 2, 8 ) ) ][0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  Finally,  to  generate  groups using circle objects, we need to add a method
  for  the [10XInverseOp[0m. In our implementation we will assume that the underlying
  ring  is  a subring of the ring with one, thus, if the circle inverse for an
  element x exists, than it can be computed as -x(1+x)^-1:
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> InstallMethod( InverseOp,[0X
    [4X>     "for an object in `IsCircleObject'",[0X
    [4X>     [ IsDefaultCircleObject ],[0X
    [4X>     function( a )[0X
    [4X>     local x;[0X
    [4X>     x := Inverse( One( a![1] ) + a![1] );[0X
    [4X>     if x = fail then[0X
    [4X>       return fail;[0X
    [4X>     else[0X
    [4X>       return CircleObject( -a![1] * x );[0X
    [4X>     fi;[0X
    [4X>     end );[0X
    [4Xgap> CircleObject(-2)^-1;                [0X
    [4XCircleObject( -2 )[0X
    [4Xgap> CircleObject(2)^-1; [0X
    [4XCircleObject( -2/3 )[0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  The  last  method  already  makes  it possible to create groups generated by
  circle objects (the warning may be ignored):
  
  [4X---------------------------  Example  ----------------------------[0X
    [4X[0X
    [4Xgap> Group( CircleObject(2) );                       [0X
    [4X#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for [0X
    [4X[ CircleObject( 2 ) ][0X
    [4X<group with 1 generators>[0X
    [4Xgap> G:=Group( [CircleObject( ZmodnZObj( 2,8 ) )  ]);[0X
    [4X#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for [0X
    [4X[ CircleObject( ZmodnZObj( 2, 8 ) ) ][0X
    [4X<group with 1 generators>[0X
    [4Xgap> Size(G);[0X
    [4X2[0X
    [4Xgap> AsList(G);[0X
    [4X[ CircleObject( ZmodnZObj( 0, 8 ) ), CircleObject( ZmodnZObj( 2, 8 ) ) ][0X
    [4X[0X
  [4X------------------------------------------------------------------[0X
  
  The   [5XGAP[0m   code   used   in   this  Chapter,  is  contained  in  the  files
  [11Xcircle/lib/circle.gd[0m  and  [11Xcircle/lib/circle.gi[0m.  We  also refer to last two
  chapters   of   the   [5XGAP[0m  Programming  Tutorial  for  another  examples  of
  implementing new [5XGAP[0m objects and further details.
  
