<!-- $Id: objects.xml,v 1.1 2007/04/12 16:52:54 alexk Exp $ -->
<Chapter Label="Objects">
<Heading>Implementing circle objects</Heading>

In this chapter we explain how the &GAP; 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 &GAP; Programming Tutorial and refer to them for more details.

<Section Label="ObjectsFirst">
<Heading>First attempts</Heading>

Of course, having two ring elements, you can straightforwardly compute
their circle product defined as <M> r \cdot s = r + s + rs </M>. 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:
 
<Example>
<![CDATA[
gap> CircleMultiplication := function(a,b)
>      return a+b+a*b;
>    end;
function( a, b ) ... end
gap> CircleMultiplication(2,3); 
11
gap> CircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );      
ZmodnZObj( 1, 8 )
]]>
</Example>

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:

<Example>
<![CDATA[
gap> CircleMultiplication( 3, ZmodnZObj(3,8) );
ZmodnZObj( 7, 8 )
gap> CircleMultiplication( [1], [2,3] );
[ 5, 5 ]
]]>
</Example>

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:

<Example>
<![CDATA[
gap> DeclareOperation( "BetterCircleMultiplication",                             
>      [IsRingElement,IsRingElement] );
gap> InstallMethod( BetterCircleMultiplication,
>      IsIdenticalObj,
>      [IsRingElement,IsRingElement],  
>      CircleMultiplication );
gap> BetterCircleMultiplication(2,3);
11
gap> BetterCircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );
ZmodnZObj( 1, 8 )
]]>
</Example>

Nevertheless, the functionality gained from such operation would be rather
limited. You will not be able to compute circle product via the infix
operator <C>*</C>, and, moreover, you will not be able to create higher 
level objects such as semigroups and groups with respect to the circle 
multiplication.<P/>

In order to "integrate" the circle multiplication into the &GAP; library 
properly, instead of defining <E>new</E> operations for existing objects,
we should define <E>new</E> objects for which the infix operator <C>*</C> 
will perform the circle multiplication. This approach is explained in the 
next two sections.

</Section>

<Section Label="ObjectsDefining">
<Heading>Defining circle objects</Heading>

Thus, we are going to implement <E>circle objects</E>, 
for which we can envisage the following functionality:

<Example>
<![CDATA[
gap> CircleObject( 2 ) * CircleObject( 3 );                       
CircleObject( 11 )
]]>
</Example>

First we need to distinguish these new objects from other &GAP; objects.
This is done via the <E>type</E> of the objects, that is mainly determined 
by their <E>category</E>, <E>representation</E> and <E>family</E>.
<P/>

We start with declaring the category <C>IsCircleObject</C> as a subcategory
of <C>IsMultiplicativeElementWithInverse</C>. Thus, each circle object will
"know" that it is <C>IsMultiplicativeElementWithInverse</C>, and this will
make it possible to apply to circle objects such operations as <C>One</C> 
and <C>Inverse</C> (the latter is allowed to return <K>fail</K> for a given 
circle object).
<P/>

<Example>
<![CDATA[
gap> DeclareCategory( "IsCircleObject", IsMultiplicativeElementWithInverse );
]]>
</Example>

Further we would like to create semigroups and groups generated by circle 
objects. Such structures will be <E>collections</E> of circle objects, so
they will be in the category <C>CategoryCollections( IsCircleObject )</C>. 
This is why immediately after we declare the underlying category of circle 
objects, we need also to declare the category of their collections:

<Example>
<![CDATA[
gap> DeclareCategoryCollections( "IsCircleObject" );
]]>
</Example>

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 <C>IsPositionalObjectOneSlotRep</C>
that is the list-like representation with only one position in the list, and
then declare a synonym <C>IsDefaultCircleObject</C> that means that we are
dealing with a circle object in one-slot representation: 

<Example>
<![CDATA[
gap> DeclareRepresentation( "IsPositionalObjectOneSlotRep",
>     IsPositionalObjectRep, [ 1 ] );
gap> DeclareSynonym( "IsDefaultCircleObject",
>     IsCircleObject and IsPositionalObjectOneSlotRep );
]]>
</Example>

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 <C>CircleFamily</C> for families:

<Example>
<![CDATA[
gap> DeclareAttribute( "CircleFamily", IsFamily );
]]>
</Example>

Now we install the method that stores the corresponding 
circle family in this attribute:

<Example>
<![CDATA[
gap> InstallMethod( CircleFamily,
>     "for a family",
>     [ IsFamily ],
>     function( Fam )
>     local F;
>   # create the family of circle elements
>   F:= NewFamily( "CircleFamily(...)", IsCircleObject );
>   if HasCharacteristic( Fam ) then
>     SetCharacteristic( F, Characteristic( Fam ) );
>   fi;
>   # store the type of objects in the output
>   F!.CircleType:= NewType( F, IsDefaultCircleObject );
>   # Return the circle family
>   return F;
> end );
]]>
</Example>

Similarly, we want one-to-one correspondence between 
circle elements and underlying ring elements. We declare
an attribute <C>CircleObject</C> 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: 

<Example>
<![CDATA[
gap> DeclareAttribute( "CircleObject", IsRingElement );
gap> InstallMethod( CircleObject,
>     "for a ring element",
>     [ IsRingElement ],
>     obj -> Objectify( CircleFamily( FamilyObj( obj ) )!.CircleType,
>                       [ Immutable( obj ) ] ) );
]]>
</Example>

Only after entering all code above we are able to create some circle object.
However, it is displayed just as <C>&lt;object&gt;</C>, though we can get
the underlying ring element using the "!" operator:

<Example>
<![CDATA[
gap> a:=CircleObject(2);
<object>
gap> a![1];
2
]]>
</Example>

We can check that the intended relation between families holds:
<P/>

<Example>
<![CDATA[
gap> FamilyObj( CircleObject ( 2 ) ) = CircleFamily( FamilyObj( 2 ) );
true
]]>
</Example>

We can not multiply circle objects yet. But before implementing this,
first let us improve the output by installing the method for <C>PrintObj</C>:

<Example>
<![CDATA[
gap> InstallMethod( PrintObj,
>     "for object in `IsCircleObject'",
>     [ IsDefaultCircleObject ],
>     function( obj )
>     Print( "CircleObject( ", obj![1], " )" );
>     end );
]]>
</Example>

This method will be used by <C>Print</C> function, and also 
by <C>View</C>, since we did not install special method for 
<C>ViewObj</C> for circle objects. As a result of this installation,
the output became more meaningful:

<Example>
<![CDATA[
gap> a;
CircleObject( 2 )
]]>
</Example>

We need to avoid the usage of "!" operator, which, in general, is not 
recommended to the user (for example, if &GAP; developers will
change the internal representation of some object, all &GAP; 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:

<Example>
<![CDATA[
gap> DeclareOperation("UnderlyingRingElement", [ IsCircleObject] );
gap> InstallMethod( UnderlyingRingElement,
>     "for a circle object", 
>     [ IsCircleObject],
>     obj -> obj![1] );
gap> UnderlyingRingElement(a);
2
]]>
</Example>

</Section>

<Section Label="ObjectsOperations">
<Heading>Installing operations for circle objects</Heading>

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:

<Example>
<![CDATA[
gap> InstallMethod( \*,
>     "for two objects in `IsCircleObject'",
>     IsIdenticalObj,
>     [ IsDefaultCircleObject, IsDefaultCircleObject ],
>     function( a, b )
>     return CircleObject( a![1] + b![1] + a![1]*b![1] );
>     end );
gap> CircleObject(2)*CircleObject(3);
CircleObject( 11 )
]]>
</Example>

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:

<Example>
<![CDATA[
gap> InstallMethod( \=,
>     "for two objects in `IsCircleObject'",
>     IsIdenticalObj,
>     [ IsDefaultCircleObject, IsDefaultCircleObject ],
>     function( a, b )
>     return a![1] = b![1];
>     end );
gap> InstallMethod( \<,
>     "for two objects in `IsCircleObject'",
>     IsIdenticalObj,
>     [ IsDefaultCircleObject, IsDefaultCircleObject ],
>     function( a, b )
>     return a![1] < b![1];
>     end );
]]>
</Example>

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 <C>OneOp</C> that returns circle object
for the corresponding zero object:

<Example>
<![CDATA[
gap> InstallMethod( OneOp,
>     "for an object in `IsCircleObject'",
>     [ IsDefaultCircleObject ],
>     a -> CircleObject( Zero( a![1] ) ) );
gap> One(a);
CircleObject( 0 )
]]>
</Example>

Now we are already able to create monoids generated by circle objects:

<Example>
<![CDATA[
gap> S:=Monoid(a);
<monoid with 1 generator>
gap> One(S);
CircleObject( 0 )
gap> S:=Monoid( CircleObject( ZmodnZObj( 2,8) ) );
<monoid with 1 generator>
gap> Size(S);
2
gap> AsList(S);
[ CircleObject( ZmodnZObj( 0, 8 ) ), CircleObject( ZmodnZObj( 2, 8 ) ) ]
]]>
</Example>

Finally, to generate groups using circle objects, we need to add a method
for the <C>InverseOp</C>. 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 <M>x</M> exists, than it can be computed as 
<M>-x(1+x)^{-1}</M>:

<Example>
<![CDATA[
gap> InstallMethod( InverseOp,
>     "for an object in `IsCircleObject'",
>     [ IsDefaultCircleObject ],
>     function( a )
>     local x;
>     x := Inverse( One( a![1] ) + a![1] );
>     if x = fail then
>       return fail;
>     else
>       return CircleObject( -a![1] * x );
>     fi;
>     end );
gap> CircleObject(-2)^-1;                
CircleObject( -2 )
gap> CircleObject(2)^-1; 
CircleObject( -2/3 )
]]>
</Example>

The last method already makes it possible to create groups generated by 
circle objects (the warning may be ignored):

<Example>
<![CDATA[
gap> Group( CircleObject(2) );                       
#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for 
[ CircleObject( 2 ) ]
<group with 1 generators>
gap> G:=Group( [CircleObject( ZmodnZObj( 2,8 ) )  ]);
#I  default `IsGeneratorsOfMagmaWithInverses' method returns `true' for 
[ CircleObject( ZmodnZObj( 2, 8 ) ) ]
<group with 1 generators>
gap> Size(G);
2
gap> AsList(G);
[ CircleObject( ZmodnZObj( 0, 8 ) ), CircleObject( ZmodnZObj( 2, 8 ) ) ]
]]>
</Example>

The &GAP; code used in this Chapter, is contained in the files
<File>circle/lib/circle.gd</File> and <File>circle/lib/circle.gi</File>.
We also refer to last two chapters of the &GAP; Programming Tutorial 
for another examples of implementing new &GAP; objects and further details.

</Section>

</Chapter>