\Chapter{Basic methods and attributes}

We describe the basic core methods of the {\LOOPS} package in this chapter. The
methods discussed here (and more) are declared and implemented in files
`quasigrp.gd' and `quasigrp.gi', respectively.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{About Cayley tables}

Let $X=\{x_1,\dots,x_n\}$ be a set and $\cdot$ a binary operation on $X$. Then
an $n$ by $n$ array with rows and columns bordered by $x_1$, $\dots$, $x_n$, in
this order, is a <Cayley table>
%
\index{Cayley table}
%
, or a <multiplication table>
%
\index{multiplication table}
%
 of
$\cdot$, if the entry in the row $x_i$ and column $x_j$ is $x_i\cdot x_j$.

A Cayley table is a <quasigroup table>
%
\index{quasigroup table}
%
 if it is a <Latin square>
%
\index{Latin square}
%
,
i.e., if every entry $x_i$ appears in every column and every row exactly once.

An annoying feature of quasigroup tables in practice is that they are often not
bordered, and it is up to the reader to figure out what is meant. Throughout
this manual and in {\LOOPS}, we therefore make the following assumption: <All
distinct entries in a quasigroup table must be integers, say>
$x_1\<x_2\<\cdots\<x_n$<, and if no border is specified, we assume that the
table is bordered by> $x_1$, $\dots$, $x_n$<, in this order.> Note that we do
not assume that the distinct entries $x_1$, $\dots$, $x_n$ form the interval
$1$, $\dots$, $n$. The significance of this observation will become clear in
Chapter "Some methods based on permutation groups".

Finally, we say that a quasigroup table is a <loop table>
%
\index{loop table}
%
 if the first row
and the first column are the same, and if the entries in the first row are
ordered in an ascending fashion.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Testing Cayley tables}

A square array with integral entries is called a <matrix> in {\GAP}. The
following synonymous operations test if a matrix $T$ is a quasigroup table, as
defined above:

\>IsQuasigroupTable( <T> ) O
\>IsQuasigroupCayleyTable( <T> ) O

The following synonymous operations test if a matrix $T$ is a loop table:

\>IsLoopTable( <T> ) O
\>IsLoopCayleyTable( <T> ) O

We would like to call attention to the fact that the package \package{GUAVA}
also has some operations dealing with Latin squares. In particular,
`IsLatinSquare' is declared in \package{GUAVA}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Canonical and normalized Cayley tables}

Although we do not assume that a quasigroup table with distinct entries
$x_1\<\cdots\<x_n$ satisfies $x_i=i$, it is often desirable to present
quasigroup tables in the latter way. The rather general operation

\>CanonicalCayleyTable( <T> ) O

takes any Cayley table $T$ with distinct entries $x_1\<\cdots\<x_m$, and
returns a Cayley table in which $x_i$ has been replaced by $i$.

The operation

\>NormalizedQuasigroupTable( <T> )

makes a quasigroup table $T$ into a loop table by:
\beginlist%unordered
\item{$\circ$} first calling `CanonicalCayleyTable' to rename the entries to $1$,
    $\dots$, $n$,
\item{$\circ$} then permuting the columns of $T$ so that the first row reads
    $1$, $\dots$, $n$,
\item{$\circ$} and then permuting the rows of $T$ so that the first column
    reads $1$, $\dots$, $n$.
\endlist

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Creating quasigroups and loops manually}

When $T$ is a quasigroup table, the corresponding quasigroup is obtained by

\>QuasigroupByCayleyTable( <T> ) O

Since `CanonicalCayleyTable' is called within the above operation, the
resulting quasigroup will have a Cayley table with distinct entries $1$,
$\dots$, $n$.

Here is the analogous operation for a loop table $T$:

\>LoopByCayleyTable( <T> ) O

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Creating quasigroups and loops from a file}

Typing a large multiplication table manually is tedious and error-prone. We
have therefore included a universal algorithm in {\LOOPS} that reads
multiplication tables of quasigroups from a file.

Instead of writing a separate algorithm for each common format, our algorithm
relies on the user to provide a bit of information about the input file. Here
is an outline of the algorithm, with file named $F$ and a string $D$ as
arguments on the input:
\beginlist%unordered
\item{$\circ$} read the entire content of $F$ into a string $S$,
\item{$\circ$} replace all end-of-line characters in $S$ by spaces,
\item{$\circ$} replace by spaces all characters of $S$ that appear in $D$,
\item{$\circ$} split $S$ into maximal substrings without spaces, called
    <chunks>,
\item{$\circ$} recognize distinct chunks (let $n$ be the number of distinct
    chunks),
\item{$\circ$} if the number of chunks is not $n^2$, report error,
\item{$\circ$} construct the multiplication table by assigning numerical
    values $1$, $\dots$, $n$ to chunks, depending on their position among
    distinct chunks.
\endlist

The following examples clarify the algorithm and document its versatility. All
examples are of the form $F+D\Longrightarrow T$, meaning that an input file
containing $F$ together with the string $D$ produce multiplication table $T$.

\medskip

% begin examples for LoopFromFile
*Example 1:* Data does not have to be arranged into an array of any kind.
$$
\matrix{
    \matrix{
        0&1&2&1\cr
        2&0&2& \cr
        0&1& &
    }&
    +&
    ''''&
    \Longrightarrow&
    \matrix{
        1&2&3\cr
        2&3&1\cr
        3&1&2
    }
}
$$

*Example 2:* Chunks can be any strings.
$$
\matrix{
    \matrix{
        {\rm{red}}&{\rm{green}}\cr
        {\rm{green}}&{\rm{red}}
    }&
    +&
    ''''&
    \Longrightarrow&
    \matrix{
        1& 2\cr
        2& 1
    }
}
$$

*Example 3:* A typical table produced by {\GAP} is easily parsed by deleting
brackets and commas.
$$
\matrix{
    [\ [0, 1],\ [1, 0]\ ]&
    +&
    ''[,]''&
    \Longrightarrow
     \matrix{
        1& 2\cr
        2& 1
    }
}
$$

*Example 4:* A typical {\TeX} table with rows separated by lines is also easily
converted. Note that we have to use $\backslash\backslash$ to make sure that
every occurrence of $\backslash$ is deleted, since $\backslash\backslash$
represents the character $\backslash$ in {\GAP}.
$$
\matrix{
    \matrix{
        {\rm{x\&\ y\&\ z\backslash{cr}}}\cr
        {\rm{y\&\ z\&\ x\backslash{cr}}}\cr
        {\rm{z\&\ x\&\ y}}
    }
    +&
    ''\backslash\backslash{\rm{cr\&}}''&
    \Longrightarrow&
    \matrix{
        1&2&3\cr
        2&3&1\cr
        3&1&2
    }
}
$$
% end examples for LoopFromFile

\medskip

And here are the needed ${\LOOPS}$ commands:

\>QuasigroupFromFile( <F>, <D> ) O
\>LoopFromFile( <F>, <D> ) O

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Conversions}

As we have already briefly mentioned, we provide operations that convert
between magmas, quasigroups, loops and groups, provided such conversions are
possible.

If $M$ is a declared magma that happens to be a quasigroup, the corresponding
quasigroup is returned via

\>AsQuasigroup( <M> ) O

If $M$ is a magma that happens to be a quasigroup, the operation

\>AsLoop( <M> ) O

returns a loop $L$ as follows:
\beginlist%unordered
\item{$\circ$}
    if $M$ possesses a neutral element $e$ and $f$ is the first element of $M$,
    then $L$ is an isomorphic copy of $M$ via the transposition $(e,f)$,
\item{$\circ$}
    if $M$ does not posses a neutral element, $L$ is returned as
    `PrincipalLoopIsotope( <M>, <M.1>, <M.1> )'.
\endlist
Here,

\>PrincipalLoopIsotope( <Q>, <f>, <g> ) O

is the principal isotope of a quasigroup $Q$ using elements $f$, $g$ of $Q$, as
explained in Section "Homomorphisms and homotopisms".

Of course, one can obtain a loop from $M$ in different ways, for instance by
normalizing the Cayley table of $M$. The three approaches mentioned here can
result in different loops in general.

When $M$ is a declared magma that happens to be a group, then the corresponding
group is returned by

\>AsGroup( <M> ) A

Note that the conversions work in both directions, not just toward more special
structures. Thus, if $G$ is a declared group, then `AsLoop( <G> )' returns the
corresponding loop, for instance.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Products of loops}

\noindent Let $L1$, $\dots$, $Ln$ be a list consisting of loops and groups,
where $n\ge 1$. Then

\>DirectProduct( <L1>, ..., <Ln>) O

returns the direct product of $L1$, $\dots$, $Ln$.

If there are only groups among $L1$, $\dots$, $Ln$, a group is returned.
Otherwise a loop is returned. If $n=1$, $L1$ is returned.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Opposite quasigroups and loops}

When $Q$ is a quasigroup with multiplication $\cdot$, the <opposite quasigroup>
%
\index{opposite quasigroup}
%
 of $Q$ is a quasigroup with the same underlying set as $Q$ and with
multiplication $*$ defined by $x*y=y\cdot x$.

Since the quasigroup-theoretical concepts are often chiral (cf. left Bol loops
versus right Bol loops), it is useful to have access to the opposite quasigroup
of $Q$:

\>Opposite( <Q> ) O

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Basic attributes}

We associate many attributes with quasigroups in order to speed up computation.
This section lists the basic attributes of quasigroups and loops.

The list of elements of a quasigroup $Q$ is obtained by the usual command

\>Elements( <Q> ) A

The Cayley table of a quasigroup $Q$ is returned with

\>CayleyTable( <Q> ) A

One can use `Display( CayleyTable( <Q> ) )' for pretty matrix-style output of
small Cayley tables.

The neutral element of a loop $L$ is obtained via

\>One( <L> ) A

If you want to know if a quasigroup $Q$ has a neutral element, you can find out
with the standard function for magmas

\>MultiplicativeNeutralElement( <Q> ) A

The size of a quasigroup $Q$ is calculated by

\>Size( <Q> ) A

When $L$ is a <power-associative loop>
%
\index{power-associative loop}
%
, i.e., the orders of elements are
well-defined in $L$, the <exponent>
%
\index{exponent}
%
 of $L$ is the smallest positive
integer divisible by orders of all elements of $L$. The following attribute
calculates the exponent without testing for power-associativity:

\>Exponent( <L> ) A

Here is an example for operations and attributes mentioned sofar:

\beginexample
gap> A := [ [ 1, 2 ], [ 3, 4 ] ];; B := [ [ 8, 5 ], [ 5, 8 ] ];;
gap> [ IsQuasigroupTable( A ), IsQuasigroupTable( B ), IsLoopTable( B ) ];
[ false, true, false ]
gap> Q := QuasigroupByCayleyTable( B );; CayleyTable( Q );
[ [ 2, 1 ], [ 1, 2 ] ]
gap> CanonicalCayleyTable( B );
[ [ 2, 1 ], [ 1, 2 ] ]
gap> NormalizedQuasigroupTable( B );
[ [ 1, 2 ], [ 2, 1 ] ]
gap> LoopByCayleyTable( last );
<loop of order 2>
gap> [ IsQuasigroup( Q ), IsLoop( Q ), Size( Q ), Elements( Q )]
[ true, false, 2, [ q1, q2 ] ]
gap> IsQuasigroupElement( Elements( Q )[ 2 ] );
true
gap> AsLoop( Q );
<loop of order 2>
\endexample

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Basic arithmetic operations}

Each quasigroup element in {\GAP} knows which quasigroup it belongs to. It is
therefore possible to perform arithmetic operations with quasigroup elements
without referring to the quasigroup. All elements involved in the calculation
must belong to the same quasigroup.

Two elements $x$, $y$ of the same quasigroup are multiplied by $x*y$ in {\GAP}.
Since multiplication of elements is ambiguous in the nonassociative case, we
always multiply elements from left to right, i.e., $x*y*z$ means $((x*y)*z)$.
Of course, one can specify association by parentheses.

Universal algebraists introduce two additional operations for quasigroups.
Namely the <left division>
%
\index{left division}
%
 $x\backslash y$ satisfying $x\cdot(x\backslash
y)=y$, and the <right division>
%
\index{right division}
%
 $x/y$ satisfying $(x/y)\cdot y=x$. These
two operations can be found in {\LOOPS} as:

\>LeftDivision( <x>, <y> ) O
\>RightDivision( <x>, <y> ) O

When $Q$ is a quasigroup, $x$ is an element of $Q$, and $S$ is a list of
elements of $Q$, then

\>LeftDivision( <S>, <x> ) O
\>LeftDivision( <x>, <S> ) O
\>RightDivision( <S>, <x> ) O
\>RightDivision( <x>, <S> ) O

returns the list of elements obtained by performing the respective
division of $S$ by $x$, or of $x$ by $S$, using one element of $S$ at a time.

We also support $/$ in place of `RightDivision'. But we do not support
$\backslash$ in place of `LeftDivision'.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Powers and inverses}

Powers of elements are not well-defined in quasigroups, since bracketing can
matter even for a single element. We say that the quasigroup $Q$ is
<power-associative>
%
\index{power-associative}
%
, if for any $x\in Q$, the submagma generated by $x$ is
associative.

For magmas and positive integer exponents, {\GAP} defines the powers in the
following way: $x^1=x$, $x^{2k}=(x^k)\cdot(x^k)$ and $x^{2k+1}=(x^{2k})\cdot
x$. One can easily see that this returns $x^k$ in $\log_2(k)$ steps. For
{\LOOPS}, we have decided to keep this method, hoping that everybody will use
it with care for non power-associative quasigroups.

Let $x$ be an element of a loop $L$ with neutral element $1$. Then the
<left inverse>
%
\index{left inverse}
%
 $x^\lambda$ of $x$ is the unique element of $L$ satisfying
$x^\lambda x=1$. Similarly, the <right inverse>
%
\index{right inverse}
%
 $x^\rho$ satisfies
$xx^\rho=1$. If $x^\lambda=x^\rho$, we call $x^{-1}=x^\lambda=x^\rho$ the
<inverse>
%
\index{inverse}
%
 of $x$.

\>LeftInverse( <x> ) O
\>RightInverse( <x> ) O
\>Inverse( <x> ) O

The following examples illustrates the usage of arithmetic operations.
`MoufangLoop' will be explained in Chapter "Libraries of small loops". In this
example, `M.i' coincides with `Elements( M )[ i ]'.

\beginexample
gap> M := MoufangLoop( 12, 1 );; x := M.2;
l2
gap> [ x * M.3, x^2, x^(-1), Inverse( x ) ];
[ l4, l1, l2, l2 ]
gap> One( M ) = LeftDivision( x, x );
true
\endexample

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Associators and commutators}

Let $Q$ be a quasigroup and $x$, $y$, $z\in Q$. Then the <associator>
%
\index{associator}
%
 of
$x$, $y$, $z$ is the unique element $u$ such that $(xy)z=(x(yz))u$. The
<commutator>
%
\index{commutator}
%
 of $x$, $y$ is the unique element $v$ such that $xy=(yx)v$.

\>Associator( <x>, <y>, <z> ) O
\>Commutator( <x>, <y> ) O

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Generators}

The following two attributes are synonyms of `GeneratorsOfMagma':

\>GeneratorsOfQuasigroup( <Q> ) A
\>GeneratorsOfLoop( <L> ) A

As usual in {\GAP}, one can refer to the $i$th generator of a quasigroup $Q$ by
`Q.i'. Note that it is not necessarily the case that `Q.i = Elements( Q )[ i
]', since the set of generators can be a proper subset of the elements.

It is easy to prove that a quasigroup of order $n$ can be generated by a subset
containing at most $\log_2{n}$ elements. When $Q$ is a quasigroup

\>GeneratorsSmallest( <Q> ) A

returns a generating set $\{q_0$, $\dots$, $q_m\}$ of $Q$ such that
$Q_0=\emptyset$, $Q_m=Q$, $Q_i=\langle q_1$, $\dots$, $q_i \rangle$, $q_{i+1}$
is the largest element of $Q\setminus Q_i$.
