  
  [1X2 [33X[0;0YImplementing circle objects[133X[101X
  
  [33X[0;0YIn  this  chapter  we  explain  how  the [5XGAP[105X system may be extended with new
  objects  using  the  circle  multiplication  as  an  example.  We follow the
  guidelines  given  in the [5XGAP[105X Reference Manual (see [14X'Reference: Creating New
  Objects'[114X and subsequent chapters), to which we refer for more details.[133X
  
  
  [1X2.1 [33X[0;0YFirst attempts[133X[101X
  
  [33X[0;0YOf course, having two ring elements, you can straightforwardly compute their
  circle  product  defined as [22Xr ⋅ s = r + s + rs[122X. 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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XCircleMultiplication := function(a,b)[127X[104X
    [4X[25X>[125X [27X     return a+b+a*b;[127X[104X
    [4X[25X>[125X [27X   end;[127X[104X
    [4X[28Xfunction( a, b ) ... end[128X[104X
    [4X[25Xgap>[125X [27XCircleMultiplication(2,3); [127X[104X
    [4X[28X11[128X[104X
    [4X[25Xgap>[125X [27XCircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );      [127X[104X
    [4X[28XZmodnZObj( 1, 8 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YHowever,  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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XCircleMultiplication( 3, ZmodnZObj(3,8) );[127X[104X
    [4X[28XZmodnZObj( 7, 8 )[128X[104X
    [4X[25Xgap>[125X [27XCircleMultiplication( [1], [2,3] );[127X[104X
    [4X[28X[ 5, 5 ][128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YYou  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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareOperation( "BetterCircleMultiplication",                             [127X[104X
    [4X[25X>[125X [27X     [IsRingElement,IsRingElement] );[127X[104X
    [4X[25Xgap>[125X [27XInstallMethod( BetterCircleMultiplication,[127X[104X
    [4X[25X>[125X [27X     IsIdenticalObj,[127X[104X
    [4X[25X>[125X [27X     [IsRingElement,IsRingElement],  [127X[104X
    [4X[25X>[125X [27X     CircleMultiplication );[127X[104X
    [4X[25Xgap>[125X [27XBetterCircleMultiplication(2,3);[127X[104X
    [4X[28X11[128X[104X
    [4X[25Xgap>[125X [27XBetterCircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );[127X[104X
    [4X[28XZmodnZObj( 1, 8 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YNevertheless,  the  functionality gained from such operation would be rather
  limited.  You  will  not  be  able  to  compute circle product via the infix
  operator  [10X*[110X,  and,  moreover,  you  will  not be able to create higher level
  objects   such   as  semigroups  and  groups  with  respect  to  the  circle
  multiplication.[133X
  
  [33X[0;0YIn  order  to  "integrate"  the  circle  multiplication into the [5XGAP[105X library
  properly, instead of defining [13Xnew[113X operations for existing objects, we should
  define  [13Xnew[113X  objects  for which the infix operator [10X*[110X will perform the circle
  multiplication. This approach is explained in the next two sections.[133X
  
  
  [1X2.2 [33X[0;0YDefining circle objects[133X[101X
  
  [33X[0;0YThus,  we  are  going to implement [13Xcircle objects[113X, for which we can envisage
  the following functionality:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XCircleObject( 2 ) * CircleObject( 3 );                       [127X[104X
    [4X[28XCircleObject( 11 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YFirst  we need to distinguish these new objects from other [5XGAP[105X objects. This
  is  done  via  the  [13Xtype[113X  of the objects, that is mainly determined by their
  [13Xcategory[113X, [13Xrepresentation[113X and [13Xfamily[113X.[133X
  
  [33X[0;0YWe  start  with  declaring  the  category [10XIsCircleObject[110X as a subcategory of
  [10XIsAssociativeElement>[110X  and  [10XIsMultiplicativeElementWithInverse[110X.  Thus,  each
  circle   object   will   "know"   that   it   is   [10XIsAssociativeElement[110X  and
  [10XIsMultiplicativeElementWithInverse[110X,  and this will make it possible to apply
  to  circle objects such operations as [10XOne[110X and [10XInverse[110X (the latter is allowed
  to  return  [9Xfail[109X  for  a  given  circle  object),  and  construct semigroups
  generated by circle objects.[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareCategory( "IsMyCircleObject", [127X[104X
    [4X[25X>[125X [27XIsAssociativeElement and IsMultiplicativeElementWithInverse );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YFurther  we  would  like to create semigroups and groups generated by circle
  objects. Such structures will be [13Xcollections[113X of circle objects, so they will
  be  in  the  category  [10XCategoryCollections(  IsCircleObject  )[110X.  This is why
  immediately  after  we declare the underlying category of circle objects, we
  need also to declare the category of their collections:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareCategoryCollections( "IsMyCircleObject" );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YOn 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[110X that is the
  list-like  representation  with  only  one  position  in  the list, and then
  declare  a synonym [10XIsDefaultCircleObject[110X that means that we are dealing with
  a circle object in one-slot representation:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareRepresentation( "IsMyPositionalObjectOneSlotRep",[127X[104X
    [4X[25X>[125X [27X    IsPositionalObjectRep, [ 1 ] );[127X[104X
    [4X[25Xgap>[125X [27XDeclareSynonym( "IsMyDefaultCircleObject",[127X[104X
    [4X[25X>[125X [27X    IsMyCircleObject and IsMyPositionalObjectOneSlotRep );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YUntil  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[110X for families:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareAttribute( "MyCircleFamily", IsFamily );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YNow  we  install  the  method that stores the corresponding circle family in
  this attribute:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( MyCircleFamily,[127X[104X
    [4X[25X>[125X [27X    "for a family",[127X[104X
    [4X[25X>[125X [27X    [ IsFamily ],[127X[104X
    [4X[25X>[125X [27X    function( Fam )[127X[104X
    [4X[25X>[125X [27X    local F;[127X[104X
    [4X[25X>[125X [27X  # create the family of circle elements[127X[104X
    [4X[25X>[125X [27X  F:= NewFamily( "MyCircleFamily(...)", IsMyCircleObject );[127X[104X
    [4X[25X>[125X [27X  if HasCharacteristic( Fam ) then[127X[104X
    [4X[25X>[125X [27X    SetCharacteristic( F, Characteristic( Fam ) );[127X[104X
    [4X[25X>[125X [27X  fi;[127X[104X
    [4X[25X>[125X [27X  # store the type of objects in the output[127X[104X
    [4X[25X>[125X [27X  F!.MyCircleType:= NewType( F, IsMyDefaultCircleObject );[127X[104X
    [4X[25X>[125X [27X  # Return the circle family[127X[104X
    [4X[25X>[125X [27X  return F;[127X[104X
    [4X[25X>[125X [27Xend );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YSimilarly,  we  want  one-to-one  correspondence between circle elements and
  underlying  ring  elements.  We declare an attribute [10XCircleObject[110X 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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareAttribute( "MyCircleObject", IsRingElement );[127X[104X
    [4X[25Xgap>[125X [27XInstallMethod( MyCircleObject,[127X[104X
    [4X[25X>[125X [27X    "for a ring element",[127X[104X
    [4X[25X>[125X [27X    [ IsRingElement ],[127X[104X
    [4X[25X>[125X [27X    obj -> Objectify( MyCircleFamily( FamilyObj( obj ) )!.MyCircleType,[127X[104X
    [4X[25X>[125X [27X                      [ Immutable( obj ) ] ) );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YOnly after entering all code above we are able to create some circle object.
  However,  it is displayed just as [10X<object>[110X, though we can get the underlying
  ring element using the "!" operator:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27Xa:=MyCircleObject(2);[127X[104X
    [4X[28X<object>[128X[104X
    [4X[25Xgap>[125X [27Xa![1];[127X[104X
    [4X[28X2[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YWe can check that the intended relation between families holds:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XFamilyObj( MyCircleObject ( 2 ) ) = MyCircleFamily( FamilyObj( 2 ) );[127X[104X
    [4X[28Xtrue[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YWe  can not multiply circle objects yet. But before implementing this, first
  let us improve the output by installing the method for [10XPrintObj[110X:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( PrintObj,[127X[104X
    [4X[25X>[125X [27X    "for object in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    function( obj )[127X[104X
    [4X[25X>[125X [27X    Print( "MyCircleObject( ", obj![1], " )" );[127X[104X
    [4X[25X>[125X [27X    end );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YThis  method  will be used by [10XPrint[110X function, and also by [10XView[110X, since we did
  not  install  special  method for [10XViewObj[110X for circle objects. As a result of
  this installation, the output became more meaningful:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27Xa;[127X[104X
    [4X[28XMyCircleObject( 2 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YWe  need  to  avoid  the  usage  of  "!" operator, which, in general, is not
  recommended  to  the  user  (for  example, if [5XGAP[105X developers will change the
  internal  representation of some object, all [5XGAP[105X 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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XDeclareAttribute("UnderlyingRingElement", IsMyCircleObject );[127X[104X
    [4X[25Xgap>[125X [27XInstallMethod( UnderlyingRingElement,[127X[104X
    [4X[25X>[125X [27X    "for a circle object", [127X[104X
    [4X[25X>[125X [27X    [ IsMyCircleObject],[127X[104X
    [4X[25X>[125X [27X    obj -> obj![1] );[127X[104X
    [4X[25Xgap>[125X [27XUnderlyingRingElement(a);[127X[104X
    [4X[28X2[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  
  [1X2.3 [33X[0;0YInstalling operations for circle objects[133X[101X
  
  [33X[0;0YNow 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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( \*,[127X[104X
    [4X[25X>[125X [27X    "for two objects in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    IsIdenticalObj,[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    function( a, b )[127X[104X
    [4X[25X>[125X [27X    return MyCircleObject( a![1] + b![1] + a![1]*b![1] );[127X[104X
    [4X[25X>[125X [27X    end );[127X[104X
    [4X[25Xgap>[125X [27XMyCircleObject(2)*MyCircleObject(3);[127X[104X
    [4X[28XMyCircleObject( 11 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YHowever,  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:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( \=,[127X[104X
    [4X[25X>[125X [27X    "for two objects in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    IsIdenticalObj,[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    function( a, b )[127X[104X
    [4X[25X>[125X [27X    return a![1] = b![1];[127X[104X
    [4X[25X>[125X [27X    end );[127X[104X
    [4X[25Xgap>[125X [27XInstallMethod( \<,[127X[104X
    [4X[25X>[125X [27X    "for two objects in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    IsIdenticalObj,[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    function( a, b )[127X[104X
    [4X[25X>[125X [27X    return a![1] < b![1];[127X[104X
    [4X[25X>[125X [27X    end );[127X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YFurther,  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[110X that returns circle object for the corresponding zero
  object:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( OneOp,[127X[104X
    [4X[25X>[125X [27X    "for an object in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    a -> MyCircleObject( Zero( a![1] ) ) );[127X[104X
    [4X[25Xgap>[125X [27XOne(a);[127X[104X
    [4X[28XMyCircleObject( 0 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YNow we are already able to create monoids generated by circle objects:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XS:=Monoid(a);[127X[104X
    [4X[28X<commutative monoid with 1 generator>[128X[104X
    [4X[25Xgap>[125X [27XOne(S);[127X[104X
    [4X[28XMyCircleObject( 0 )[128X[104X
    [4X[25Xgap>[125X [27XS:=Monoid( MyCircleObject( ZmodnZObj( 2,8) ) );[127X[104X
    [4X[28X<commutative monoid with 1 generator>[128X[104X
    [4X[25Xgap>[125X [27XSize(S);[127X[104X
    [4X[28X2[128X[104X
    [4X[25Xgap>[125X [27XAsList(S);[127X[104X
    [4X[28X[ MyCircleObject( ZmodnZObj( 0, 8 ) ), MyCircleObject( ZmodnZObj( 2, 8 ) ) ][128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YFinally,  to  generate  groups using circle objects, we need to add a method
  for  the [10XInverseOp[110X. 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 [22Xx[122X exists, than it can be computed as [22X-x(1+x)^-1[122X:[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XInstallMethod( InverseOp,[127X[104X
    [4X[25X>[125X [27X    "for an object in `IsMyCircleObject'",[127X[104X
    [4X[25X>[125X [27X    [ IsMyDefaultCircleObject ],[127X[104X
    [4X[25X>[125X [27X    function( a )[127X[104X
    [4X[25X>[125X [27X    local x;[127X[104X
    [4X[25X>[125X [27X    x := Inverse( One( a![1] ) + a![1] );[127X[104X
    [4X[25X>[125X [27X    if x = fail then[127X[104X
    [4X[25X>[125X [27X      return fail;[127X[104X
    [4X[25X>[125X [27X    else[127X[104X
    [4X[25X>[125X [27X      return MyCircleObject( -a![1] * x );[127X[104X
    [4X[25X>[125X [27X    fi;[127X[104X
    [4X[25X>[125X [27X    end );[127X[104X
    [4X[25Xgap>[125X [27XMyCircleObject(-2)^-1;                [127X[104X
    [4X[28XMyCircleObject( -2 )[128X[104X
    [4X[25Xgap>[125X [27XMyCircleObject(2)^-1; [127X[104X
    [4X[28XMyCircleObject( -2/3 )[128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YThe  last  method  already  makes  it possible to create groups generated by
  circle objects (the warning may be ignored):[133X
  
  [4X[32X  Example  [32X[104X
    [4X[28X[128X[104X
    [4X[25Xgap>[125X [27XGroup( MyCircleObject(2) );;[127X[104X
    [4X[28X#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for[128X[104X
    [4X[28X[ MyCircleObject( 2 ) ][128X[104X
    [4X[25Xgap>[125X [27XG:=Group( [MyCircleObject( ZmodnZObj( 2,8 ) )  ]);;[127X[104X
    [4X[28X#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for[128X[104X
    [4X[28X[ MyCircleObject( ZmodnZObj( 2, 8 ) ) ][128X[104X
    [4X[25Xgap>[125X [27XSize(G);[127X[104X
    [4X[28X2[128X[104X
    [4X[25Xgap>[125X [27XAsList(G);[127X[104X
    [4X[28X[ MyCircleObject( ZmodnZObj( 0, 8 ) ), MyCircleObject( ZmodnZObj( 2, 8 ) ) ][128X[104X
    [4X[28X[128X[104X
  [4X[32X[104X
  
  [33X[0;0YThe   [5XGAP[105X   code   used   in   this  Chapter,  is  contained  in  the  files
  [11Xcircle/lib/circle.gd[111X  and  [11Xcircle/lib/circle.gi[111X (without [10XMy[110X in identifiers).
  For  more  examples  of implementing new [5XGAP[105X objects and further details see
  [14X'Reference:  Creating  New  Objects'[114X  and  subsequent  chapters  in  the [5XGAP[105X
  Reference Manual.[133X
  
