GAP (ref) - Chapter 37: Associative Words

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 Bib Ind
[Top of Book] [Contents] [Previous Chapter] [Next Chapter]

[MathJax off]

37 Associative Words
37.1 Categories of Associative Words
37.1-1 IsAssocWord
37.2 Free Groups, Monoids and Semigroups
37.2-1 FreeGroup
37.2-2 IsFreeGroup
37.2-3 AssignGeneratorVariables
37.3 Comparison of Associative Words
`37.3-1 \=`
`37.3-2 \<`
37.3-3 IsShortLexLessThanOrEqual
37.3-4 IsBasicWreathLessThanOrEqual
37.4 Operations for Associative Words
37.4-1 Length
37.4-2 ExponentSumWord
37.4-3 Subword
37.4-4 PositionWord
37.4-5 SubstitutedWord
37.4-6 EliminatedWord
37.5 Operations for Associative Words by their Syllables
37.5-1 NumberSyllables
37.5-2 ExponentSyllable
37.5-3 GeneratorSyllable
37.5-4 SubSyllables
37.6 Representations for Associative Words
37.6-1 IsLetterAssocWordRep
37.6-2 IsLetterWordsFamily
37.6-3 IsBLetterAssocWordRep
37.6-4 IsBLetterWordsFamily
37.6-5 IsSyllableAssocWordRep
37.6-6 IsSyllableWordsFamily
37.6-7 Is16BitsFamily
37.6-8 LetterRepAssocWord
37.6-9 AssocWordByLetterRep
37.7 The External Representation for Associative Words
37.8 Straight Line Programs
37.8-1 IsStraightLineProgram
37.8-2 StraightLineProgram
37.8-3 LinesOfStraightLineProgram
37.8-4 NrInputsOfStraightLineProgram
37.8-5 ResultOfStraightLineProgram
37.8-6 StringOfResultOfStraightLineProgram
37.8-7 CompositionOfStraightLinePrograms
37.8-8 IntegratedStraightLineProgram
37.8-9 RestrictOutputsOfSLP
37.8-10 IntermediateResultOfSLP
37.8-11 IntermediateResultOfSLPWithoutOverwrite
37.8-12 IntermediateResultsOfSLPWithoutOverwrite
37.8-13 ProductOfStraightLinePrograms
37.8-14 SlotUsagePattern
37.9 Straight Line Program Elements
37.9-1 IsStraightLineProgElm
37.9-2 StraightLineProgElm
37.9-3 StraightLineProgGens
37.9-4 EvalStraightLineProgElm
37.9-5 StretchImportantSLPElement
37.10 Objects with Memory
37.10-1 IsObjWithMemory
37.10-2 GeneratorsWithMemory
37.10-3 GroupWithMemory
37.10-4 SLPOfElm
37.10-5 SLPOfElms
37.10-6 StripMemory
37.10-7 MethodsForObjWithMemory

### 37 Associative Words

#### 37.1 Categories of Associative Words

Associative words are used to represent elements in free groups, semigroups and monoids in GAP (see 37.2). An associative word is just a sequence of letters, where each letter is an element of an alphabet (in the following called a generator) or its inverse. Associative words can be multiplied; in free monoids also the computation of an identity is permitted, in free groups also the computation of inverses (see 37.4).

Different alphabets correspond to different families of associative words. There is no relation whatsoever between words in different families.

    gap> f:= FreeGroup( "a", "b", "c" );
    <free group on the generators [ a, b, c ]>
    gap> gens:= GeneratorsOfGroup(f);
    [ a, b, c ]
    gap> w:= gens[1]*gens[2]/gens[3]*gens[2]*gens[1]/gens[1]*gens[3]/gens[2];
    a*b*c^-1*b*c*b^-1
    gap> w^-1;
    b*c^-1*b^-1*c*b^-1*a^-1

Words are displayed as products of letters. The letters are usually printed like `f1`, `f2`, \(\ldots\), but it is possible to give user defined names to them, which can be arbitrary strings. These names do not necessarily identify a unique letter (generator), it is possible to have several letters –even in the same family– that are displayed in the same way. Note also that there is no relation between the names of letters and variable names. In the example above, we might have typed

    gap> a:= f.1;; b:= f.2;; c:= f.3;;

(Interactively, the function `AssignGeneratorVariables` (37.2-3) provides a shorthand for this.) This allows us to define `w` more conveniently:

    gap> w := a*b/c*b*a/a*c/b;
    a*b*c^-1*b*c*b^-1

Using homomorphisms it is possible to express elements of a group as words in terms of generators, see 39.5.

##### 37.1-1 IsAssocWord

`‣ IsAssocWord`( obj ) | ( category )

`‣ IsAssocWordWithOne`( obj ) | ( category )

`‣ IsAssocWordWithInverse`( obj ) | ( category )

`IsAssocWord` is the category of associative words in free semigroups, `IsAssocWordWithOne` is the category of associative words in free monoids (which admit the operation `One` (31.10-2) to compute an identity), `IsAssocWordWithInverse` is the category of associative words in free groups (which have an inverse). See `IsWord` (36.1-1) for more general categories of words.

#### 37.2 Free Groups, Monoids and Semigroups

Usually a family of associative words will be generated by constructing the free object generated by them. See `FreeMonoid` (51.2-9), `FreeSemigroup` (51.1-10) for details.

##### 37.2-1 FreeGroup

`‣ FreeGroup`( [wfilt, ]rank[, name] ) | ( function )

`‣ FreeGroup`( [wfilt][,] [name1[, name2[, ...]]] ) | ( function )

`‣ FreeGroup`( [wfilt, ]names ) | ( function )

`‣ FreeGroup`( [wfilt, ]infinity[, name][, init] ) | ( function )

`FreeGroup` returns a free group. The number of generators, and the labels given to the generators, can be specified in several different ways. Warning: the labels of generators are only an aid for printing, and do not necessarily distinguish generators; see the examples at the end of `FreeSemigroup` (51.1-10) for more information.

1: For a given rank, and an optional generator name prefix

  Called with a nonnegative integer rank, `FreeGroup` returns a free group on rank generators. The optional argument name must be a string; its default value is `"f"`.

  If name is not given but the `generatorNames` option is, then this option is respected as described in Section 50.1-16.

  Otherwise, the generators of the returned free group are labelled name`1`, ..., name`k`, where `k` is the value of rank.

2: For given generator names

  Called with various nonempty strings, `FreeGroup` returns a free group on as many generators as arguments, which are labelled name1, name2, etc.

3: For a given list of generator names

  Called with a finite list names of nonempty strings, `FreeGroup` returns a free group on `Length(names)` generators, whose `i`-th generator is labelled names`[i]`.

4: For the rank `infinity`, an optional default generator name prefix, and an optional finite list of generator names

  Called in the fourth form, `FreeGroup` returns a free group on infinitely many generators. The optional argument name must be a string; its default value is `"f"`, and the optional argument init must be a finite list of nonempty strings; its default value is an empty list. The generators are initially labelled according to the list init, followed by name`i` for each `i` in the range from `Length(init)+1` to `infinity`.

If the optional first argument wfilt is given, then it must be either `IsSyllableWordsFamily`, `IsLetterWordsFamily`, `IsWLetterWordsFamily`, or `IsBLetterWordsFamily`. This filter specifies the representation used for the elements of the free group (see 37.6). If no such filter is given, a letter representation is used.

(For interfacing to old code that omits the representation flag, use of the syllable representation is also triggered by setting the option `FreeGroupFamilyType` to the string `"syllable"`; this is overwritten by the optional first argument if it is given.)

    gap> FreeGroup(5);
    <free group on the generators [ f1, f2, f3, f4, f5 ]>
    gap> FreeGroup(4, "gen");
    <free group on the generators [ gen1, gen2, gen3, gen4 ]>
    gap> FreeGroup(3 : generatorNames := "ack");
    <free group on the generators [ ack1, ack2, ack3 ]>
    gap> FreeGroup(2 : generatorNames := ["u", "v", "w"]);
    <free group on the generators [ u, v ]>
    gap> FreeGroup();
    <free group of rank zero>
    gap> FreeGroup("a", "b", "c");
    <free group on the generators [ a, b, c ]>
    gap> FreeGroup(["x", "y"]);
    <free group on the generators [ x, y ]>
    gap> FreeGroup(infinity);
    <free group with infinity generators>
    gap> F := FreeGroup(infinity, "g", ["a", "b"]);
    <free group with infinity generators>
    gap> GeneratorsOfGroup(F){[1..4]};
    [ a, b, g3, g4 ]
    gap> GeneratorsOfGroup(FreeGroup(infinity, "gen")){[1..3]};
    [ gen1, gen2, gen3 ]
    gap> FreeGroup(IsSyllableWordsFamily, 50);
    <free group with 50 generators>

##### 37.2-2 IsFreeGroup

`‣ IsFreeGroup`( obj ) | ( category )

Any group consisting of elements in `IsAssocWordWithInverse` (37.1-1) lies in the filter `IsFreeGroup`; this holds in particular for any group created with `FreeGroup` (37.2-1), or any subgroup of such a group.

Also see Chapter 47.

##### 37.2-3 AssignGeneratorVariables

`‣ AssignGeneratorVariables`( G ) | ( operation )

If G is a group, whose generators are represented by symbols (for example a free group, a finitely presented group or a pc group) this function assigns these generators to global variables with the same names.

The aim of this function is to make it easy in interactive use to work with (for example) a free group. It is a shorthand for a sequence of assignments of the form

    var1:=GeneratorsOfGroup(G)[1];
    var2:=GeneratorsOfGroup(G)[2];
    ...
    varn:=GeneratorsOfGroup(G)[n];

However, since overwriting global variables can be very dangerous, it is not permitted to use this function within a function. (If –despite this warning– this is done, the result is undefined.)

If the assignment overwrites existing variables a warning is given, if any of the variables is write protected, or any of the generator names would not be a proper variable name, an error is raised.

#### 37.3 Comparison of Associative Words

##### `37.3-1 \=`

`‣ \=`( w1, w2 ) | ( operation )

Two associative words are equal if they are words over the same alphabet and if they are sequences of the same letters. This is equivalent to saying that the external representations of the words are equal, see 37.7 and 36.2.

There is no universal empty word, every alphabet (that is, every family of words) has its own empty word.

    gap> f:= FreeGroup( "a", "b", "b" );;
    gap> gens:= GeneratorsOfGroup(f);
    [ a, b, b ]
    gap> gens[2] = gens[3];
    false
    gap> x:= gens[1]*gens[2];
    a*b
    gap> y:= gens[2]/gens[2]*gens[1]*gens[2];
    a*b
    gap> x = y;
    true
    gap> z:= gens[2]/gens[2]*gens[1]*gens[3];
    a*b
    gap> x = z;
    false

##### `37.3-2 \<`

`‣ \<`( w1, w2 ) | ( operation )

The ordering of associative words is defined by length and lexicography (this ordering is called short-lex ordering), that is, shorter words are smaller than longer words, and words of the same length are compared w.r.t. the lexicographical ordering induced by the ordering of generators. Generators are sorted according to the order in which they were created. If the generators are invertible then each generator g is larger than its inverse g`^-1`, and g`^-1` is larger than every generator that is smaller than g.

    gap> f:= FreeGroup( 2 );;  gens:= GeneratorsOfGroup( f );;
    gap> a:= gens[1];;  b:= gens[2];;
    gap> One(f) < a^-1;  a^-1 < a;  a < b^-1;  b^-1 < b; b < a^2;  a^2 < a*b;
    true
    true
    true
    true
    true
    true

##### 37.3-3 IsShortLexLessThanOrEqual

`‣ IsShortLexLessThanOrEqual`( u, v ) | ( function )

returns `IsLessThanOrEqualUnder(ord, u, v)` where ord is the short less ordering for the family of u and v. (This is here for compatibility with GAP 4.2.)

##### 37.3-4 IsBasicWreathLessThanOrEqual

`‣ IsBasicWreathLessThanOrEqual`( u, v ) | ( function )

returns `IsLessThanOrEqualUnder(ord, u, v)` where ord is the basic wreath product ordering for the family of u and v. (This is here for compatibility with GAP 4.2.)

#### 37.4 Operations for Associative Words

The product of two given associative words is defined as the freely reduced concatenation of the words. Besides the multiplication `\*` (31.12-1), the arithmetical operators `One` (31.10-2) (if the word lies in a family with identity) and (if the generators are invertible) `Inverse` (31.10-8), `\/` (31.12-1),`\^` (31.12-1), `Comm` (31.12-3), and `LeftQuotient` (31.12-2) are applicable to associative words, see 31.12.

See also `MappedWord` (36.3-1), an operation that is applicable to arbitrary words.

See Section 37.6 for a discussion of the internal representations of associative words that are supported by GAP. Note that operations to extract or act on parts of words (letter or syllables) can carry substantially different costs, depending on the representation the words are in.

##### 37.4-1 Length

`‣ Length`( w ) | ( attribute )

For an associative word w, `Length` returns the number of letters in w.

    gap> f := FreeGroup("a","b");; gens := GeneratorsOfGroup(f);;
    gap> a := gens[1];; b := gens[2];;w := a^5*b*a^2*b^-4*a;;
    gap>  w; Length( w );  Length( a^17 );  Length( w^0 );
    a^5*b*a^2*b^-4*a
    13
    17
    0

##### 37.4-2 ExponentSumWord

`‣ ExponentSumWord`( w, gen ) | ( operation )

For an associative word w and a generator gen, `ExponentSumWord` returns the number of times gen appears in w minus the number of times its inverse appears in w. If both gen and its inverse do not occur in w then \(0\) is returned. gen may also be the inverse of a generator.

    gap> w;  ExponentSumWord( w, a );  ExponentSumWord( w, b );
    a^5*b*a^2*b^-4*a
    8
    -3
    gap> ExponentSumWord( (a*b*a^-1)^3, a );  ExponentSumWord( w, b^-1 );
    0
    3

##### 37.4-3 Subword

`‣ Subword`( w, from, to ) | ( operation )

For an associative word w and two positive integers from and to, `Subword` returns the subword of w that begins at position from and ends at position to. Indexing is done with origin 1.

    gap> w;  Subword( w, 3, 7 );
    a^5*b*a^2*b^-4*a
    a^3*b*a

##### 37.4-4 PositionWord

`‣ PositionWord`( w, sub, from ) | ( operation )

Let w and sub be associative words, and from a positive integer. `PositionWord` returns the position of the first occurrence of sub as a subword of w, starting at position from. If there is no such occurrence, `fail` is returned. Indexing is done with origin 1.

In other words, `PositionWord( w, sub, from )` is the smallest integer \(i\) larger than or equal to from such that `Subword( w,` \(i\)`,` \(i\)`+Length( sub )-1 ) =` sub, see `Subword` (37.4-3).

    gap> w;  PositionWord( w, a/b, 1 );
    a^5*b*a^2*b^-4*a
    8
    gap> Subword( w, 8, 9 );
    a*b^-1
    gap> PositionWord( w, a^2, 1 );
    1
    gap> PositionWord( w, a^2, 2 );
    2
    gap> PositionWord( w, a^2, 6 );
    7
    gap> PositionWord( w, a^2, 8 );
    fail

##### 37.4-5 SubstitutedWord

`‣ SubstitutedWord`( w, from, to, by ) | ( operation )

`‣ SubstitutedWord`( w, sub, from, by ) | ( operation )

Let w be an associative word.

In the first form, `SubstitutedWord` returns the associative word obtained by replacing the subword of w that begins at position from and ends at position to by the associative word by. from and to must be positive integers, indexing is done with origin 1. In other words, `SubstitutedWord( w, from, to, by )` is the product of the three words `Subword( w, 1, from-1 )`, by, and `Subword( w, to+1, Length( w ) )`, see `Subword` (37.4-3).

In the second form, `SubstitutedWord` returns the associative word obtained by replacing the first occurrence of the associative word sub of w, starting at position from, by the associative word by; if there is no such occurrence, `fail` is returned.

    gap> w;  SubstitutedWord( w, 3, 7, a^19 );
    a^5*b*a^2*b^-4*a
    a^22*b^-4*a
    gap> SubstitutedWord( w, a, 6, b^7 );
    a^5*b^8*a*b^-4*a
    gap> SubstitutedWord( w, a*b, 6, b^7 );
    fail

##### 37.4-6 EliminatedWord

`‣ EliminatedWord`( w, gen, by ) | ( operation )

For an associative word w, a generator gen, and an associative word by, `EliminatedWord` returns the associative word obtained by replacing each occurrence of gen in w by by.

    gap> w;  EliminatedWord( w, a, a^2 );  EliminatedWord( w, a, b^-1 );
    a^5*b*a^2*b^-4*a
    a^10*b*a^4*b^-4*a^2
    b^-11

#### 37.5 Operations for Associative Words by their Syllables

For an associative word w \(= x_1^{{n_1}} x_2^{{n_2}} \cdots x_k^{{n_k}}\) over an alphabet containing \(x_1, x_2, \ldots, x_k\), such that \(x_i \neq x_{{i+1}}^{{\pm 1}}\) for \(1 \leq i \leq k-1\), the subwords \(x_i^{{e_i}}\) are uniquely determined; these powers of generators are called the syllables of \(w\).

##### 37.5-1 NumberSyllables

`‣ NumberSyllables`( w ) | ( attribute )

`NumberSyllables` returns the number of syllables of the associative word w.

##### 37.5-2 ExponentSyllable

`‣ ExponentSyllable`( w, i ) | ( operation )

`ExponentSyllable` returns the exponent of the i-th syllable of the associative word w.

##### 37.5-3 GeneratorSyllable

`‣ GeneratorSyllable`( w, i ) | ( operation )

`GeneratorSyllable` returns the number of the generator that is involved in the i-th syllable of the associative word w.

##### 37.5-4 SubSyllables

`‣ SubSyllables`( w, from, to ) | ( operation )

`SubSyllables` returns the subword of the associative word w that consists of the syllables from positions from to to, where from and to must be positive integers, and indexing is done with origin 1.

    gap> w;  NumberSyllables( w );
    a^5*b*a^2*b^-4*a
    5
    gap> ExponentSyllable( w, 3 );
    2
    gap> GeneratorSyllable( w, 3 );
    1
    gap> SubSyllables( w, 2, 3 );
    b*a^2

#### 37.6 Representations for Associative Words

GAP provides two different internal kinds of representations of associative words. The first one are syllable representations in which words are stored in syllable (i.e. generator,exponent) form. (Older versions of GAP only used this representation.) The second kind are letter representations in which each letter in a word is represented by its index number. Negative numbers are used for inverses. Unless the syllable representation is specified explicitly when creating the free group/monoid or semigroup, a letter representation is used by default.

Depending on the task in mind, either of these two representations will perform better in time or in memory use and algorithms that are syllable or letter based (for example `GeneratorSyllable` (37.5-3) and `Subword` (37.4-3)) perform substantially better in the corresponding representation. For example when creating pc groups (see 46), it is advantageous to use a syllable representation while calculations in free groups usually benefit from using a letter representation.

##### 37.6-1 IsLetterAssocWordRep

`‣ IsLetterAssocWordRep`( obj ) | ( representation )

A word in letter representation stores a list of generator/inverses numbers (as given by `LetterRepAssocWord` (37.6-8)). Letter access is fast, syllable access is slow for such words.

##### 37.6-2 IsLetterWordsFamily

`‣ IsLetterWordsFamily`( obj ) | ( category )

A letter word family stores words by default in letter form.

Internally, there are letter representations that use integers (4 Byte) to represent a generator and letter representations that use single bytes to represent a character. The latter are more memory efficient, but can only be used if there are less than 128 generators (in which case they are used by default).

##### 37.6-3 IsBLetterAssocWordRep

`‣ IsBLetterAssocWordRep`( obj ) | ( representation )

`‣ IsWLetterAssocWordRep`( obj ) | ( representation )

these two subrepresentations of `IsLetterAssocWordRep` (37.6-1) indicate whether the word is stored as a list of bytes (in a string) or as a list of integers).

##### 37.6-4 IsBLetterWordsFamily

`‣ IsBLetterWordsFamily`( obj ) | ( category )

`‣ IsWLetterWordsFamily`( obj ) | ( category )

These two subcategories of `IsLetterWordsFamily` (37.6-2) specify the type of letter representation to be used.

##### 37.6-5 IsSyllableAssocWordRep

`‣ IsSyllableAssocWordRep`( obj ) | ( representation )

A word in syllable representation stores generator/exponents pairs (as given by `ExtRepOfObj` (79.8-1). Syllable access is fast, letter access is slow for such words.

##### 37.6-6 IsSyllableWordsFamily

`‣ IsSyllableWordsFamily`( obj ) | ( category )

A syllable word family stores words by default in syllable form. There are also different versions of syllable representations, which compress a generator exponent pair in 8, 16 or 32 bits or use a pair of integers. Internal mechanisms try to make this as memory efficient as possible.

##### 37.6-7 Is16BitsFamily

`‣ Is16BitsFamily`( obj ) | ( category )

`‣ Is32BitsFamily`( obj ) | ( category )

`‣ IsInfBitsFamily`( obj ) | ( category )

Regardless of the internal representation used, it is possible to convert a word in a list of numbers in letter or syllable representation and vice versa.

##### 37.6-8 LetterRepAssocWord

`‣ LetterRepAssocWord`( w[, gens] ) | ( operation )

The letter representation of an associated word is as a list of integers, each entry corresponding to a group generator. Inverses of the generators are represented by negative numbers. The generator numbers are as associated to the family.

This operation returns the letter representation of the associative word w.

In the call with two arguments, the generator numbers correspond to the generator order given in the list gens.

(For words stored in syllable form the letter representation has to be computed.)

##### 37.6-9 AssocWordByLetterRep

`‣ AssocWordByLetterRep`( Fam, lrep[, gens] ) | ( operation )

takes a letter representation lrep (see `LetterRepAssocWord` (37.6-8)) and returns an associative word in family fam corresponding to this letter representation.

If gens is given, the numbers in the letter representation correspond to gens.

    gap> w:=AssocWordByLetterRep( FamilyObj(a), [-1,2,1,-2,-2,-2,1,1,1,1]);
    a^-1*b*a*b^-3*a^4
    gap> LetterRepAssocWord( w^2 );
    [ -1, 2, 1, -2, -2, -2, 1, 1, 1, 2, 1, -2, -2, -2, 1, 1, 1, 1 ]

The external representation (see section 37.7) can be used if a syllable representation is needed.

#### 37.7 The External Representation for Associative Words

The external representation of the associative word \(w\) is defined as follows. If \(w = g_{{i_1}}^{{e_1}} * g_{{i_2}}^{{e_2}} * \cdots * g_{{i_k}}^{{e_k}}\) is a word over the alphabet \(g_1, g_2, \ldots\), i.e., \(g_i\) denotes the \(i\)-th generator of the family of \(w\), then \(w\) has external representation \([ i_1, e_1, i_2, e_2, \ldots, i_k, e_k ]\). The empty list describes the identity element (if exists) of the family. Exponents may be negative if the family allows inverses. The external representation of an associative word is guaranteed to be freely reduced; for example, \(g_1 * g_2 * g_2^{{-1}} * g_1\) has the external representation `[ 1, 2 ]`.

Regardless of the family preference for letter or syllable representations (see 37.6), `ExtRepOfObj` and `ObjByExtRep` can be used and interface to this syllable-like representation.

    gap> w:= ObjByExtRep( FamilyObj(a), [1,5,2,-7,1,3,2,4,1,-2] );
    a^5*b^-7*a^3*b^4*a^-2
    gap> ExtRepOfObj( w^2 );
    [ 1, 5, 2, -7, 1, 3, 2, 4, 1, 3, 2, -7, 1, 3, 2, 4, 1, -2 ]

#### 37.8 Straight Line Programs

Straight line programs describe an efficient way for evaluating an abstract word at concrete generators, in a more efficient way than with `MappedWord` (36.3-1). For example, the associative word \(ababbab\) of length \(7\) can be computed from the generators \(a\), \(b\) with only four multiplications, by first computing \(c = ab\), then \(d = cb\), and then \(cdc\); Alternatively, one can compute \(c = ab\), \(e = bc\), and \(aee\). In each step of these computations, one forms words in terms of the words computed in the previous steps.

A straight line program in GAP is represented by an object in the category `IsStraightLineProgram` (37.8-1)) that stores a list of lines each of which has one of the following three forms.

1. a nonempty dense list \(l\) of integers,

2. a pair \([ l, i ]\) where \(l\) is a list of form 1. and \(i\) is a positive integer,

3. a list \([ l_1, l_2, \ldots, l_k ]\) where each \(l_i\) is a list of form 1.; this may occur only for the last line of the program.

The lists of integers that occur are interpreted as external representations of associative words (see Section 37.7); for example, the list \([ 1, 3, 2, -1 ]\) represents the word \(g_1^3 g_2^{{-1}}\), with \(g_1\) and \(g_2\) the first and second abstract generator, respectively.

For the meaning of the list of lines, see `ResultOfStraightLineProgram` (37.8-5).

Straight line programs can be constructed using `StraightLineProgram` (37.8-2).

Defining attributes for straight line programs are `NrInputsOfStraightLineProgram` (37.8-4) and `LinesOfStraightLineProgram` (37.8-3). Another operation for straight line programs is `ResultOfStraightLineProgram` (37.8-5).

Special methods applicable to straight line programs are installed for the operations `Display` (6.3-6), `IsInternallyConsistent` (12.8-4), `PrintObj` (6.3-5), and `ViewObj` (6.3-5).

For a straight line program prog, the default `Display` (6.3-6) method prints the interpretation of prog as a sequence of assignments of associative words; a record with components `gensnames` (with value a list of strings) and `listname` (a string) may be entered as second argument of `Display` (6.3-6), in this case these names are used, the default for `gensnames` is `[ g1, g2,` \(\ldots\)`]`, the default for `listname` is \(r\).

##### 37.8-1 IsStraightLineProgram

`‣ IsStraightLineProgram`( obj ) | ( category )

Each straight line program in GAP lies in the category `IsStraightLineProgram`.

##### 37.8-2 StraightLineProgram

`‣ StraightLineProgram`( lines[, nrgens] ) | ( function )

`‣ StraightLineProgram`( string, gens ) | ( function )

`‣ StraightLineProgramNC`( lines[, nrgens] ) | ( function )

`‣ StraightLineProgramNC`( string, gens ) | ( function )

In the first form, lines must be a nonempty list of lists that defines a unique straight line program (see `IsStraightLineProgram` (37.8-1)); in this case `StraightLineProgram` returns this program, otherwise an error is signalled. The optional argument nrgens specifies the number of input generators of the program; if a line of form 1. (that is, a list of integers) occurs in lines except in the last position, this number is not determined by lines and therefore must be specified by the argument nrgens; if not then `StraightLineProgram` returns `fail`.

In the second form, string must be a nonempty string describing an arithmetic expression in terms of the strings in the list gens, where multiplication is denoted by concatenation, powering is denoted by `^`, and round brackets `(`, `)` may be used. Each entry in gens must consist only of uppercase or lowercase letters (i.e., letters in `IsAlphaChar` (27.5-4)) such that no entry is an initial part of another one. Called with this input, `StraightLineProgram` returns a straight line program that evaluates to the word corresponding to string when called with generators corresponding to gens.

The `NC` variant does the same, except that the internal consistency of the program is not checked.

##### 37.8-3 LinesOfStraightLineProgram

`‣ LinesOfStraightLineProgram`( prog ) | ( attribute )

For a straight line program prog, `LinesOfStraightLineProgram` returns the list of program lines. There is no default method to compute these lines if they are not stored.

##### 37.8-4 NrInputsOfStraightLineProgram

`‣ NrInputsOfStraightLineProgram`( prog ) | ( attribute )

For a straight line program prog, `NrInputsOfStraightLineProgram` returns the number of generators that are needed as input.

If a line of form 1. (that is, a list of integers) occurs in the lines of prog except the last line then the number of generators is not determined by the lines, and must be set in the construction of the straight line program (see `StraightLineProgram` (37.8-2)). So if prog contains a line of form 1. other than the last line and does not store the number of generators then `NrInputsOfStraightLineProgram` signals an error.

##### 37.8-5 ResultOfStraightLineProgram

`‣ ResultOfStraightLineProgram`( prog, gens ) | ( operation )

`ResultOfStraightLineProgram` evaluates the straight line program (see `IsStraightLineProgram` (37.8-1)) prog at the group elements in the list gens.

The result of a straight line program with lines \(p_1, p_2, \ldots, p_k\) when applied to gens is defined as follows.

(a)

  First a list \(r\) of intermediate results is initialized with a shallow copy of gens.

(b)

  For \(i < k\), before the \(i\)-th step, let \(r\) be of length \(n\). If \(p_i\) is the external representation of an associative word in the first \(n\) generators then the image of this word under the homomorphism that is given by mapping \(r\) to these first \(n\) generators is added to \(r\); if \(p_i\) is a pair \([ l, j ]\), for a list \(l\), then the same element is computed, but instead of being added to \(r\), it replaces the \(j\)-th entry of \(r\).

(c)

  For \(i = k\), if \(p_k\) is the external representation of an associative word then the element described in (b) is the result of the program, if \(p_k\) is a pair \([ l, j ]\), for a list \(l\), then the result is the element described by \(l\), and if \(p_k\) is a list \([ l_1, l_2, \ldots, l_k ]\) of lists then the result is a list of group elements, where each \(l_i\) is treated as in (b).

    gap> f:= FreeGroup( "x", "y" );;  gens:= GeneratorsOfGroup( f );;
    gap> x:= gens[1];;  y:= gens[2];;
    gap> prg:= StraightLineProgram( [ [] ] );
    <straight line program>
    gap> ResultOfStraightLineProgram( prg, [] );
    [  ]

The above straight line program `prg` returns –for any list of input generators– an empty list.

    gap> StraightLineProgram( [ [1,2,2,3], [3,-1] ] );
    fail
    gap> prg:= StraightLineProgram( [ [1,2,2,3], [3,-1] ], 2 );
    <straight line program>
    gap> LinesOfStraightLineProgram( prg );
    [ [ 1, 2, 2, 3 ], [ 3, -1 ] ]
    gap> prg:= StraightLineProgram( "(a^2b^3)^-1", [ "a", "b" ] );
    <straight line program>
    gap> LinesOfStraightLineProgram( prg );
    [ [ [ 1, 2, 2, 3 ], 3 ], [ [ 3, -1 ], 4 ] ]
    gap> res:= ResultOfStraightLineProgram( prg, gens );
    y^-3*x^-2
    gap> res = (x^2 * y^3)^-1;
    true
    gap> NrInputsOfStraightLineProgram( prg );
    2
    gap> Print( prg, "\n" );
    StraightLineProgram( [ [ [ 1, 2, 2, 3 ], 3 ], [ [ 3, -1 ], 4 ] ], 2 )
    gap> Display( prg );
    # input:
    r:= [ g1, g2 ];
    # program:
    r[3]:= r[1]^2*r[2]^3;
    r[4]:= r[3]^-1;
    # return value:
    r[4]
    gap> IsInternallyConsistent( StraightLineProgramNC( [ [1,2] ] ) );
    true
    gap> IsInternallyConsistent( StraightLineProgramNC( [ [1,2,3] ] ) );
    false
    gap> prg1:= StraightLineProgram( [ [1,1,2,2], [3,3,1,1] ], 2 );;
    gap> prg2:= StraightLineProgram( [ [ [1,1,2,2], 2 ], [2,3,1,1] ] );;
    gap> res1:= ResultOfStraightLineProgram( prg1, gens );
    (x*y^2)^3*x
    gap> res1 = (x*y^2)^3*x;
    true
    gap> res2:= ResultOfStraightLineProgram( prg2, gens );
    (x*y^2)^3*x
    gap> res2 = (x*y^2)^3*x;
    true
    gap> prg:= StraightLineProgram( [ [2,3], [ [3,1,1,4], [1,2,3,1] ] ], 2 );;
    gap> res:= ResultOfStraightLineProgram( prg, gens );
    [ y^3*x^4, x^2*y^3 ]

##### 37.8-6 StringOfResultOfStraightLineProgram

`‣ StringOfResultOfStraightLineProgram`( prog, gensnames[, "LaTeX"] ) | ( function )

`StringOfResultOfStraightLineProgram` returns a string that describes the result of the straight line program (see `IsStraightLineProgram` (37.8-1)) prog as word(s) in terms of the strings in the list gensnames. If the result of prog is a single element then the return value of `StringOfResultOfStraightLineProgram` is a string consisting of the entries of gensnames, opening and closing brackets `(` and `)`, and powering by integers via `^`. If the result of prog is a list of elements then the return value of `StringOfResultOfStraightLineProgram` is a comma separated concatenation of the strings of the single elements, enclosed in square brackets `[`, `]`.

    gap> prg:= StraightLineProgram( [ [ 1, 2, 2, 3 ], [ 3, -1 ] ], 2 );;
    gap> StringOfResultOfStraightLineProgram( prg, [ "a", "b" ] );
    "(a^2b^3)^-1"
    gap> StringOfResultOfStraightLineProgram( prg, [ "a", "b" ], "LaTeX" );
    "(a^{2}b^{3})^{-1}"

##### 37.8-7 CompositionOfStraightLinePrograms

`‣ CompositionOfStraightLinePrograms`( prog2, prog1 ) | ( function )

For two straight line programs prog1 and prog2, `CompositionOfStraightLinePrograms` returns a straight line program prog with the properties that prog1 and prog have the same number of inputs, and the result of prog when applied to given generators gens equals the result of prog2 when this is applied to the output of prog1 applied to gens.

(Of course the number of outputs of prog1 must be the same as the number of inputs of prog2.)

    gap> prg1:= StraightLineProgram( "a^2b", [ "a","b" ] );;
    gap> prg2:= StraightLineProgram( "c^5", [ "c" ] );;
    gap> comp:= CompositionOfStraightLinePrograms( prg2, prg1 );
    <straight line program>
    gap> StringOfResultOfStraightLineProgram( comp, [ "a", "b" ] );
    "(a^2b)^5"
    gap> prg:= StraightLineProgram( [ [2,3], [ [3,1,1,4], [1,2,3,1] ] ], 2 );;
    gap> StringOfResultOfStraightLineProgram( prg, [ "a", "b" ] );
    "[ b^3a^4, a^2b^3 ]"
    gap> comp:= CompositionOfStraightLinePrograms( prg, prg );
    <straight line program>
    gap> StringOfResultOfStraightLineProgram( comp, [ "a", "b" ] );
    "[ (a^2b^3)^3(b^3a^4)^4, (b^3a^4)^2(a^2b^3)^3 ]"

##### 37.8-8 IntegratedStraightLineProgram

`‣ IntegratedStraightLineProgram`( listofprogs ) | ( function )

For a nonempty dense list listofprogs of straight line programs \(p_1, p_2, \ldots, p_m\) that have the same number \(n\) of inputs (see `NrInputsOfStraightLineProgram` (37.8-4)), `IntegratedStraightLineProgram` returns a straight line program \(prog\) with \(n\) inputs such that for each \(n\)-tuple \(gens\) of generators, `ResultOfStraightLineProgram(` \(prog, gens\)`)` is the concatenation of the lists \(r_1, r_2, \ldots, r_m\), where \(r_i\) is equal to `ResultOfStraightLineProgram(` \(p_i, gens\)`)` if this result is a list of elements, and otherwise \(r_i\) is equal to the list of length one that contains this result.

    gap> f:= FreeGroup( "x", "y" );;  gens:= GeneratorsOfGroup( f );;
    gap> prg1:= StraightLineProgram([ [ [ 1, 2 ], 1 ], [ 1, 2, 2, -1 ] ], 2);;
    gap> prg2:= StraightLineProgram([ [ [ 2, 2 ], 3 ], [ 1, 3, 3, 2 ] ], 2);;
    gap> prg3:= StraightLineProgram([ [ 2, 2 ], [ 1, 3, 3, 2 ] ], 2);;
    gap> prg:= IntegratedStraightLineProgram( [ prg1, prg2, prg3 ] );;
    gap> ResultOfStraightLineProgram( prg, gens );
    [ x^4*y^-1, x^3*y^4, x^3*y^4 ]
    gap> prg:= IntegratedStraightLineProgram( [ prg2, prg3, prg1 ] );;
    gap> ResultOfStraightLineProgram( prg, gens );
    [ x^3*y^4, x^3*y^4, x^4*y^-1 ]
    gap> prg:= IntegratedStraightLineProgram( [ prg3, prg1, prg2 ] );;
    gap> ResultOfStraightLineProgram( prg, gens );
    [ x^3*y^4, x^4*y^-1, x^3*y^4 ]
    gap> prg:= IntegratedStraightLineProgram( [ prg, prg ] );;
    gap> ResultOfStraightLineProgram( prg, gens );
    [ x^3*y^4, x^4*y^-1, x^3*y^4, x^3*y^4, x^4*y^-1, x^3*y^4 ]

##### 37.8-9 RestrictOutputsOfSLP

`‣ RestrictOutputsOfSLP`( slp, k ) | ( function )

slp must be a straight line program returning a tuple of values. This function returns a new slp that calculates only those outputs specified by k. The argument k may be an integer or a list of integers. If k is an integer, the resulting slp calculates only the result with that number in the original output tuple. If k is a list of integers, the resulting slp calculates those results with indices k in the original output tuple. In both cases the resulting slp does only what is necessary. Obviously, the slp must have a line with enough expressions (lists) for the supplied k as its last line. slp is either an slp or a pair where the first entry are the lines of the slp and the second is the number of inputs.

##### 37.8-10 IntermediateResultOfSLP

`‣ IntermediateResultOfSLP`( slp, k ) | ( function )

Returns a new slp that calculates only the value of slot k at the end of slp doing only what is necessary. slp is either an slp or a pair where the first entry are the lines of the slp and the second is the number of inputs. Note that this assumes a general SLP with possible overwriting. If you know that your SLP does not overwrite slots, please use `IntermediateResultOfSLPWithoutOverwrite` (37.8-11), which is much faster in this case.

##### 37.8-11 IntermediateResultOfSLPWithoutOverwrite

`‣ IntermediateResultOfSLPWithoutOverwrite`( slp, k ) | ( function )

Returns a new slp that calculates only the value of slot k, which must be an integer. Note that slp must not overwrite slots but only append!!! Use `IntermediateResultOfSLP` (37.8-10) in the other case! slp is either an slp or a pair where the first entry is the list of lines of the slp and the second is the number of its inputs.

##### 37.8-12 IntermediateResultsOfSLPWithoutOverwrite

`‣ IntermediateResultsOfSLPWithoutOverwrite`( slp, k ) | ( function )

Returns a new slp that calculates only the values of slots contained in the list k. Note that slp must not overwrite slots but only append!!! Use `IntermediateResultOfSLP` (37.8-10) in the other case! slp is either a slp or a pair where the first entry is the list of lines of the slp and the second is the number of its inputs.

##### 37.8-13 ProductOfStraightLinePrograms

`‣ ProductOfStraightLinePrograms`( s1, s2 ) | ( function )

s1 and s2 must be two slps that return a single element with the same number of inputs. This function constructs an slp that returns the product of the two results the slps s1 and s2 would produce with the same input.

##### 37.8-14 SlotUsagePattern

`‣ SlotUsagePattern`( s ) | ( attribute )

Analyses the straight line program s for more efficient evaluation. This means in particular two things, when this attribute is known: First of all, intermediate results which are not actually needed later on are not computed at all, and once an intermediate result is used for the last time in this SLP, it is discarded. The latter leads to the fact that the evaluation of the SLP needs less memory.

#### 37.9 Straight Line Program Elements

When computing with very large (in terms of memory) elements, for example permutations of degree a few hundred thousands, it can be helpful (in terms of memory usage) to represent them via straight line programs in terms of an original generator set. (So every element takes only small extra storage for the straight line program.)

A straight line program element has a seed (a list of group elements) and a straight line program on the same number of generators as the length of this seed, its value is the value of the evaluated straight line program.

At the moment, the entries of the straight line program have to be simple lists (i.e. of the first form).

Straight line program elements are in the same categories and families as the elements of the seed, so they should work together with existing algorithms.

Note however, that due to the different way of storage some normally very cheap operations (such as testing for element equality) can become more expensive when dealing with straight line program elements. This is essentially the tradeoff for using less memory.

See also Section 43.13.

##### 37.9-1 IsStraightLineProgElm

`‣ IsStraightLineProgElm`( obj ) | ( representation )

A straight line program element is a group element given (for memory reasons) as a straight line program. Straight line program elements are positional objects, the first component is a record with a component `seeds`, the second component the straight line program.

##### 37.9-2 StraightLineProgElm

`‣ StraightLineProgElm`( seed, prog ) | ( function )

Creates a straight line program element for seed seed and program prog.

##### 37.9-3 StraightLineProgGens

`‣ StraightLineProgGens`( gens[, base] ) | ( function )

returns a set of straight line program elements corresponding to the generators in gens. If gens is a set of permutations then base can be given which must be a base for the group generated by gens. (Such a base will be used to speed up equality tests.)

##### 37.9-4 EvalStraightLineProgElm

`‣ EvalStraightLineProgElm`( slpel ) | ( function )

evaluates a straight line program element slpel from its seeds.

##### 37.9-5 StretchImportantSLPElement

`‣ StretchImportantSLPElement`( elm ) | ( operation )

If elm is a straight line program element whose straight line representation is very long, this operation changes the representation of elm to a straight line program element, equal to elm, whose seed contains the evaluation of elm and whose straight line program has length 1.

For other objects nothing happens.

This operation permits to designate important elements within an algorithm (elements that will be referred to often), which will be represented by guaranteed short straight line program elements.

    gap> gens:=StraightLineProgGens([(1,2,3,4),(1,2)]);
    [ <[ [ 2, 1 ] ]|(1,2,3,4)>, <[ [ 1, 1 ] ]|(1,2)> ]
    gap> g:=Group(gens);;
    gap> (gens[1]^3)^gens[2];
    <[ [ 1, -1, 2, 3, 1, 1 ] ]|(1,2,4,3)>
    gap> Size(g);
    24

#### 37.10 Objects with Memory

The idea behind objects with memory is as follows. One starts with a list of seed objects and creates corresponding new objects for them that store how they arise from the seed objects (see `GeneratorsWithMemory` (37.10-2)). Each product, inverse, commutator etc. of the new objects then also stores how it was obtained from the seed objects. One can use `SLPOfElm` (37.10-4) to create a straight line program for a given element \(x\) such that evaluating this program in the seed objects yields \(x\).

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );
    [ <(1,2,3) with mem>, <(1,2) with mem> ]
    gap> x:= (elms[1] * elms[2])^-1;
    <(2,3) with mem>
    gap> xslp:= SLPOfElm( x );
    <straight line program>
    gap> Display( xslp );
    # input:
    r:= [ g1, g2 ];
    # program:
    r[3]:= r[1]*r[2];
    r[4]:= r[3]^-1;
    # return value:
    r[4]
    gap> ResultOfStraightLineProgram( xslp, seeds );
    (2,3)

The objects with memory are intended to behave equally to the underlying objects without memory, apart from the behaviour concerning the memory information. In particular, the tests for equality and ordering are delegated to the underlying objects.

    gap> seeds = elms;
    true
    gap> ( seeds[1] < seeds[2] ) = ( elms[1] < elms[2] );
    true

It may, however, happen that some GAP function runs into an error when it is called with elements with memory. In such cases, probably a method for elements with memory is missing, and perhaps some existing methods are installed with too weak requirements. As a solution, one can then add the missing method, or ask GAP support for help.

An overview of the currently supported functionality for objects with memory can be computed with `MethodsForObjWithMemory` (37.10-7), this may be helpful for finding code that can be adapted.

##### 37.10-1 IsObjWithMemory

`‣ IsObjWithMemory`( obj ) | ( representation )

The filter `IsObjWithMemory` describes objects that can be multiplied and store information how they arise from seed objects via arithmetic operations.

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );;
    gap> IsObjWithMemory( elms[1] );
    true
    gap> IsObjWithMemory( seeds[1] );
    false

##### 37.10-2 GeneratorsWithMemory

`‣ GeneratorsWithMemory`( list ) | ( function )

Returns: a list of objects with memory

For a list list of multiplicative elements, `GeneratorsWithMemory` returns a list of corresponding objects with memory.

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );
    [ <(1,2,3) with mem>, <(1,2) with mem> ]
    gap> seeds = elms;
    true
    gap> ForAll( elms, IsObjWithMemory );
    true

##### 37.10-3 GroupWithMemory

`‣ GroupWithMemory`( G ) | ( function )

`‣ GroupWithMemory`( gens ) | ( function )

Return a group whose `GeneratorsOfGroup` (39.2-4) value is the `GeneratorsWithMemory` (37.10-2) value of `GeneratorsOfGroup(` G`)` or of gens, respectively.

    gap> G:= GroupWithMemory( SymmetricGroup( 3 ) );
    Group([ (1,2,3), (1,2) ])
    gap> IsObjWithMemory( One( G ) );
    true
    gap> G:= GroupWithMemory( [ (1,2,3) ] );
    Group([ (1,2,3) ])
    gap> IsObjWithMemory( One( G ) );
    true

##### 37.10-4 SLPOfElm

`‣ SLPOfElm`( g ) | ( function )

For g in the filter `IsObjWithMemory` (37.10-1), return a straight line program that describes g in terms of the generators that were used to initialize the memory.

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );
    [ <(1,2,3) with mem>, <(1,2) with mem> ]
    gap> g:= (elms[2] * elms[1] * elms[2])^-1;
    <(1,2,3) with mem>
    gap> slp:= SLPOfElm( g );
    <straight line program>
    gap> g = ResultOfStraightLineProgram( slp, seeds );
    true

##### 37.10-5 SLPOfElms

`‣ SLPOfElms`( list ) | ( function )

For a list list of elements in the filter `IsObjWithMemory` (37.10-1), return a straight line program that describes list in terms of the generators that were used to initialize the memory.

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );
    [ <(1,2,3) with mem>, <(1,2) with mem> ]
    gap> list:= [ elms[2] * elms[1], elms[1] * elms[2] ];
    [ <(1,3) with mem>, <(2,3) with mem> ]
    gap> slp:= SLPOfElms( list );
    <straight line program>
    gap> list = ResultOfStraightLineProgram( slp, seeds );
    true

##### 37.10-6 StripMemory

`‣ StripMemory`( obj ) | ( operation )

For a list obj, `StripMemory` returns a new list of the `StripMemory` results for the entries of obj.

If obj is not a list and in the filter `IsObjWithMemory` (37.10-1), `StripMemory` returns the underlying object without memory.

Otherwise obj is returned.

    gap> seeds:= [ (1,2,3), (1,2) ];;
    gap> elms:= GeneratorsWithMemory( seeds );
    [ <(1,2,3) with mem>, <(1,2) with mem> ]
    gap> StripMemory( elms );
    [ (1,2,3), (1,2) ]
    gap> StripMemory( elms[1] );
    (1,2,3)
    gap> IsIdenticalObj( seeds[1], StripMemory( seeds[1] ) );
    true

##### 37.10-7 MethodsForObjWithMemory

`‣ MethodsForObjWithMemory`( ) | ( function )

This function is intended to give an overview of those methods in the current GAP session for which at least one argument is required to be in the filter `IsObjWithMemory` (37.10-1).

The return value is a list of records, with the same components as the ones returned by `MethodsOperation` (7.2-2). In particular, the `func` component of each record is the method itself, and the `info` component is a string that describes its requirements.

    gap> res:= MethodsForObjWithMemory();;
    gap> for r in res{ [ 1 .. 10 ] } do
    >      Print( r.info, "\n" );
    >    od;
    ViewObj: [ IsObjWithMemory ]
    =: [ IsObjWithMemory, IsObjWithMemory ]
    =: [ IsMultiplicativeElement, IsObjWithMemory ]
    =: [ IsObjWithMemory, IsMultiplicativeElement ]
    <: [ IsObjWithMemory, IsObjWithMemory ]
    <: [ IsMultiplicativeElement, IsObjWithMemory ]
    <: [ IsObjWithMemory, IsMultiplicativeElement ]
    PrintObj: [ IsObjWithMemory ]
    Length: [ IsMatrix and IsObjWithMemory ]
    Length: [ IsObjWithMemory and IsWord ]

Note that some functions may provide a special treatment for objects in `IsObjWithMemory` (37.10-1) but no arguments of the function are objects with memory. For example, there is a `One` (31.10-2) method for a group that checks whether the generators are objects with memory, and if yes returns an identity element with memory. Such functions do not occur in the output of `MethodsForObjWithMemory`.

[Top of Book] [Contents] [Previous Chapter] [Next Chapter]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 Bib Ind

generated by GAPDoc2HTML