\Chapter{Creating quasigroups and loops}

In this chapter we describe several ways in which quasigroups and loops can be created in {\LOOPS}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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{table!Cayley}\index{Cayley table}, or a
<multiplication table>\index{table!multiplication}\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{table!quasigroup}\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 "Methods based on permutation groups".

Finally, we say that a quasigroup table is a <loop
table>\index{table!loop}\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_n$, and
returns a Cayley table in which $x_i$ has been replaced by $i$.

The operation

\>NormalizedQuasigroupTable( <T> ) O

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

And here is an example for methods concerning Cayley tables:

\beginexample
gap> ct := CanonicalCayleyTable( [[5,3],[3,5]] );
[ [ 2, 1 ], [ 1, 2 ] ]
gap> NormalizedQuasigroupTable( ct );
[ [ 1, 2 ], [ 2, 1 ] ]
gap> LoopByCayleyTable( last );
<loop of order 2>
gap> [ IsQuasigroupTable( ct ), IsLoopTable( ct ) ];
[ true, false ]
\endexample

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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{Creating quasigroups and loops by sections}

Let $P$ be a set of $n$ permutations of an $n$-element set $X$. If at most one
permutation of $P$ is the identity permutation, and if all other permutations of $P$ move
all points of $X$, the operation

\>CayleyTableByPerms( <P> ) O

returns an $n\times n$ Cayley table $C$ such that `C[i][j] = X[j]^P[i]'.

In particular, if $P$ is the left section of a quasigroup $Q$, the above
operation returns the multiplication table of $Q$.

If $P$ is a set of permutations corresponding to the left
translations of a quasigroup (or loop) $Q$, the operation

\>QuasigroupByLeftSection( <P> ) O
\>LoopByLeftSection( <P> ) O

returns the corresponding quasigroup (or loop). The order of permutations in
$P$ is important in the quasigroup case, but it is disregarded in the loop
case, since the order of rows in the corresponding multiplication table is
determined by the presence of the neutral element.

Analogously, we define

\>QuasigroupByRightSection( <P> ) O
\>LoopByRightSection( <P> ) O

Here is an example:

\beginexample
gap> S := Subloop( MoufangLoop( 12, 1 ), [ 3 ] );;
gap> ls := LeftSection( S );
[ (), (1,3,5), (1,5,3) ]
gap> CayleyTableByPerms( ls );
[ [ 1, 3, 5 ], [ 3, 5, 1 ], [ 5, 1, 3 ] ]
gap> CayleyTable( LoopByLeftSection( ls ) );
[ [ 1, 2, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ] ]
\endexample

Let $G$ be a group, $H$ a subgroup, and $T$ a right
transversal\index{transversal} to $H$ in $G$. Then the operation $\circ$
defined on the right cosets $Q = \{Ht;\;t\in T\}$ by $Ht\circ Ht' = H(tt')$
turns $Q$ into a quasigroup if and only if $T$ is a right transversal to all
conjugates $g^{-1}Hg$ of $H$ in $G$. In fact, every quasigroup $Q$ can be
obtained in this way (let $G={\rm{Mlt}}(Q)$, $H={\rm{RMlt}}(Q)$ and
$T=\{R_x;\;x\in Q\}$).

The resulting quasigroup (or loop) is returned via

\>QuasigroupByRightSection( <G>, <H>, <T> ) O
\>LoopByRightSection( <G>, <H>, <T> ) O

We do not support the dual operations for left sections since, by default,
actions in {\GAP} act on the right.

To demonstrate `LoopByRightSection', let us recall a construction due to Nagy \cite{Na}:

Let $X$ be a simple group with subgroups $Y_0$, $Y_1$ such that $Y_0\cap Y_1=1$
and $X=Y_0Y_1$. Let $G=X\times X$ and $H=Y_0\times Y_1$. Then $T =
\{(x,x^{-1});\; x\in X\}$ is a right transversal of $H$ in $G$, and the
corresponding loop is a simple (right) Bol loop.\index{Bol
loop!simple}\index{simple loop!Bol}

The next example illustrates this construction with $X=A_5$, $Y_0=A_4$, and
$Y_1 = \langle(1,2,3,4,5)\rangle$.

\beginexample
gap> Shift := function( p ) # shifts permutation "up" by 5
>       local ls;
>       ls := ListPerm( p );
>       ls := Concatenation( [1,2,3,4,5], List( ls, x -> x + 5 ) );
>       return PermList( ls );
> end;
function( p ) ... end
gap> A := AlternatingGroup( 5 );;
gap> G := DirectProduct( A, A );;
gap> H := Subgroup( G, [ (1,2,3), (2,3,4), (6,7,8,9,10) ] );;
gap> T := List( A, x -> x * Shift(x)^(-1) );;
gap> L := LoopByRightSection( G, H, T );
<loop of order 60>
gap> [ IsRightBolLoop( L ), IsSimple( L ), IsMoufangLoop( L ) ];
[ true, true, false ]
\endexample

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\Section{Creating quasigroups and loops by extensions}

If $Q$ is a loop and $K$ an abelian subgroup of $N(Q)$ then

\>NuclearExtension( <Q>, <K> ) O

returns $[K_0, F, \varphi, \theta]$, where $K_0\cong K$, $F\cong Q/K$,
$\varphi:F\to{\rm{Aut}}{K}$ is a homomorphism, $\theta:F\times F\to K$ is a
cocycle, and $Q$ is isomorphic to $K_0\times F$ with multiplication $(a,x)(b,y)
= (a\varphi_x(b)\theta(x,y),xy)$.

If $n=|F|$ and $m=|K|$, the cocycle $\theta$ is returned as an $n\times n$
array with entries in $\{1,\dots,m\}$, and $\varphi$ is returned as a list of
length $n$ of permutations of $\{1,\dots,m\}$.

Conversely, given $K$, $F$, $f = \varphi$, and $t = \theta$ as above,

\>LoopByExtension( <K>, <F>, <f>, <t> ) O

returns the extension of $K$ by $F$ using the action $f$ and cocycle $t$.

\beginexample
gap> F := IntoLoop( Group( (1,2) ) );
<loop of order 2>
gap> K := DirectProduct( F, F );;
gap> phi := [ (), (2,3) ];;
gap> theta := [ [ 1, 1 ], [ 1, 3 ] ];;
gap> LoopByExtension( K, F, phi, theta );
<loop of order 8>
gap> IsAssociative( last );
false
\endexample

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

We say that an algorithm selects a latin square of order $n$ <at
random>\index{Latin square!random}\label{random!Latin square} if every latin
square of order $n$ is returned by the algorithm with the same probability.
Selecting a latin square at random is a nontrivial problem.

In \cite{JaMa}, Jacobson and Matthews defined a random walk on the space of
latin squares and so-called <improper latin squares> that visits every latin
square with the same probability. The diameter of the space is no more than
$4(n-1)^3$, that is, no more than $4(n-1)^3$ steps are needed to travel from
one latin square of order $n$ to another.

The Jacobson-Matthews algorithm can be used to generate random quasigroups as
follows: (i) select any latin square of order $n$, for instance the canonical
multiplication table of the cyclic group of order $n$, (ii) perform
sufficiently many steps of the random walk, stopping at a proper or improper
latin square, (iii) if necessary, perform a few more steps to end up with a
proper latin square. Upon normalizing the resulting latin square, we obtain a
random loop of order $n$.

By the above result, it suffices to use about $n^3$ steps to arrive at any
latin square of order $n$ from the initial latin square. In fact, a much
smaller number of steps is probably
sufficient.\index{random!loop}\index{loop!random}

\>RandomLoop( <n> ) O

returns a random loop of order $n$, using the Jacobson-Matthews algorithm with
$n^3$ steps. Every loop of order $n$ is potentially returned, but the method is
slow for $n>50$, say. You can control the trade-off between randomness and speed with

\>RandomLoop( <n>, <iter> ) O

returns a random loop of order $n$, using the Jacobson-Matthews algorithm with
<iter> many steps. When <iter> is small, the returned loop will be close to the
canonical Cayley table of the cyclic group of order $n$.

The corresponding commands for random quasigroups
are\index{quasigroup!random}\index{random!quasigroup}

\>RandomQuasigroup( <n> ) O
\>RandomQuasigroup( <n>, <iter> ) O

Finally,

\>RandomNilpotentLoop( <lst> ) O

returns a random nilpotent loop\index{nilpotent
loop!random}\index{random!nilpotent loop} as follows (see Section "Nilpotency
and central series" for more on nilpotency):

<lst> must be a list of positive integers and/or finite abelian groups. If
<lst> $= [a_1]$ and $a_1$ is an integer, a random abelian group of order $a_1$
is returned, else $a_1$ is an abelian group and `AsLoop(' $a_1$ `)' is
returned. If <lst> $=[a_1,\dots,a_m]$, a random central extension of
`RandomNilpotentLoop(' $[a_1]$ `)' by `RandomNilpotentLoop(' $[a_2,\dots,a_m]$
`)' is returned.

To determine the nilpotency class $c$ of the resulting loop, assume that <lst>
has length at least $2$, contains only integers bigger than $1$, and let $m$ be
the last entry of <lst>. If $m>2$ then $c=$ `Length(<lst>)', else $c=$
`Length(<lst>)'$-1$.

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

As we have already mentioned, \LOOPS\ contains methods 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

\>IntoQuasigroup( <M> ) O

Note that if <M> is already declared as a quasigroup, `IntoQuasigroup( <M> )'
merely returns <M>.

Given a quasigroup $M$ and two of its elements $f$, $g$, the principal loop
isotope $x\circ y = R_g^{-1}(x)\cdot L_f^{-1}(y)$ turns $(M,\circ)$ into a loop
with neutral element $f\cdot g$ (see Section "Homomorphisms and homotopisms").
Since loops in \LOOPS\ have to have neutral element labeled as $1$, the
function

\>PrincipalLoopIsotope( <M>, <f>, <g> )

returns an isomorphic copy of the principal isotope $(M,\circ)$ via the
transposition $(1,f\cdot g)$.

If $M$ is a declared magma that happens to be a quasigroup (not necessarily a
loop!), the operation

\>IntoLoop( <M> ) O

returns a loop $L$ as follows:
\beginlist%unordered
\item{$\circ$} if $M$ is already declared as a loop, $L=M$, else
\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)$, else
\item{$\circ$}
    if $M$ does not posses a neutral element, $L$ is returned as
    `PrincipalLoopIsotope( <M>, <M.1>, <M.1> )'
\endlist

One could obtain a loop from $M$ in yet another way, by normalizing the Cayley
table of $M$. These three approaches can result in nonisomorphic loops in
general.

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

\>IntoGroup( <M> ) O

as follows:
\beginlist%unordered
\item{$\circ$} if $M$ is already declared as a group, $M$ is returned, else
\item{$\circ$} `RightMultiplciationGroup( IntoLoop( <M> ) )' is returned, i.e.,
    a permutation group isomorphic to $M$.
\endlist

All three conversion methods return `fail' if the requested conversion is not
possible.

Up to version 2.0.0, we have supported `AsQuasigroup', `AsLoop' and `AsGroup'
in place of `IntoQuasigroup', `IntoLoop' and `IntoGroup', respectively. We have
changed the terminology starting with version 2.1.0 in order to comply with
{\GAP} naming rules for `AsSomething', as explained in Chapter "How the package
works". Finally, the method `AsGroup' is built into the core of {\GAP} and it
returns an fp group if its argument is an associative loop.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\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}\index{quasigroup!opposite} 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
