GAP

Groups, Algorithms and Programming version 3 release 3

Martin Schönert together with

Hans Ulrich Besche Thomas Breuer Frank Celler Volkmar Felsch Alexander Hulpke

Jürgen Mnich Götz Pfeiffer Udo Polis

Heiko Theissen

Lehrstuhl D für Mathematik, RWTH Aachen

Alice Niemeyer

Mathematics Research Section, ANU, Canberra, Australia

9. Nov. 1993 Copyright \begin{matrix}c \\ ◯ \end{matrix} 1992 by Lehrstuhl D für Mathematik

RWTH, Templergraben 64, D 5100 Aachen, Germany

GAP can be copied and distributed freely for any non-commercial purpose.

If you copy GAP for somebody else, you may ask this person for refund of your expenses. This should cover cost of media, copying and shipping. You are not allowed to ask for more than this. In any case you must give a copy of this copyright notice along with the program.

If you obtain GAP please send us a short notice to that effect, e.g., an e-mail message to the address gap@samson.math.rwth-aachen.de, containing your full name and address. This allows us to keep track of the number of GAP users.

If you publish a mathematical result that was partly obtained using GAP, please cite GAP, just as you would cite another paper that you used. Also we would appreciate it if you could inform us about such a paper.

You are permitted to modify and redistribute GAP, but you are not allowed to restrict further redistribution. That is to say proprietary modifications will not be allowed. We want all versions of GAP to remain free.

If you modify any part of GAP and redistribute it, you must supply a README document. This should specify what modifications you made in which files. We do not want to take credit or be blamed for your modifications.

Of course we are interested in all of your modifications. In particular we would like to see bug-fixes, improvements and new functions. So again we would appreciate it if you would inform us about all modifications you make.

GAP is distributed by us without any warranty, to the extent permitted by applicable state law. We distribute GAP as is without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose.

The entire risk as to the quality and performance of the program is with you. Should GAP prove defective, you assume the cost of all necessary servicing, repair or correction.

In no case unless required by applicable law will we, and/or any other party who may modify and redistribute GAP as permitted above, be liable to you for damages, including lost profits, lost monies or other special, incidental or consequential damages arising out of the use or inability to use GAP.

2

Preface

GAP stands for Groups, Algorithms and Programming. The name was chosen to reflect the aim of the system, which is introduced in this manual.

Until well into the eighties the interest of pure mathematicians in computational group theory was stirred by, but in most cases also confined to the information that was produced by group theoretical software for their special research problems – and hampered by the uneasy feeling that one was using black boxes of uncontrollable reliability. However the last years have seen a rapid spread of interest in the understanding, design and even implementation of group theoretical algorithms. These are gradually becoming accepted both as standard tools for a working group theoretician, like certain methods of proof, and as worthwhile objects of study, like connections between notions expressed in theorems.

GAP was started as an attempt to meet this interest. Therefore a primary design goal has been to give its user full access to algorithms and the data structures used by them, thus allowing critical study as well as modification of existing methods. We also intend to relieve the user from unwanted technical chores and to assist him in the programming, thus supporting invention and implementation of new algorithms as well as experimentation with them.

We have tried to achieve these goals by a design which in addition makes GAP easily portable, even to computers such as Atari ST and Amiga, and at the same time facilitates the maintenance of GAP with the limited resources of an academic environment.

While I had felt for some time rather strongly the wish for such a truly open system for computational group theory, the concrete idea of GAP was born when, together with a larger group of students, among whom where Johannes Meier, Alice Niemeyer, Werner Nickel and Martin Schönert who eventually wrote the first version of GAP, I had my first contact with the Maple system at the EUROCAL meeting in Linz/Austria in 1985. Maple demonstrated to us the feasibility of a strong and efficient computer algebra system built from a small kernel, with an interpreted library of routines written in a problem-adapted language. The discussion of the plan of a system for computational group theory organized in a similar way started in the fall of 1985, programming only in the second half of 1986. A first version of GAP was operational by the end of 1986. The system was first presented at the Oberwolfach meeting on computational group theory in May 1988. Version 2.4 was the first officially to be given away from Aachen starting in December 1988. The strong interest in this version, in spite of its still rather small collection of group theoretical routines, as well as constructive criticism by many colleagues, confirmed our belief in the general design principles of the system. Nevertheless over three years had gone by until in April 1992 version 3.1 was released, which now in December 1992 is followed by version 3.2.

A main reason for this much time and the fact that there had not been intermediate releases was that we have found it advisable to make a number of changes to basic data structures until with version 3.1 we hope to have reached a state where we can maintain upward compatibility over further releases, which are planned to follow much more frequently. Of course the time has also been used to extend the scope of the methods implemented in GAP. A rough guess puts the size of the program library of version 3.2 at about twelve times the size of that of version 2.4, while for version 3.1 the factor was about eight. In spite of this growth there are still gaps in what I would like to see in GAP.

The system that you are getting now consists of four parts:

1. A comparatively small kernel, written in C, which provides the user with:

- automatic dynamical storage management, which the user needn’t bother about in his programming;

- a set of time-critical basic functions, e.g. ”arithmetic”operations for integers, finite fields, permutations and words, as well as natural operations for lists and records;

- an interpreter for the GAP language, which belongs to the Pascal family, but, while allowing additional types for group theoretical objects, does not require type declarations;

- a set of programming tools for testing, debugging, and timing algorithms.

2. A much larger library of GAP functions that implement group theoretical and other algorithms. Since this is written entirely in the GAP language, in contrast to the situation in older group theoretical software, the GAP language is both the main implementation language and the user language of the system. Therefore the user can as easily as the original programmers investigate and vary algorithms of the library and add new ones to it, first for his own and eventually for the benefit of all GAP users. We hope that moreover the structuring of the library using the concept of domains and the techniques used for their handling that have been introduced into GAP 3.1 by Martin Schönert will be further helpful in this respect.

3. A library of group theoretical data which already contains e.g. a list of primitive groups of Charles Sims, a list of the 2-groups of order up to 256 of Eamonn O’Brien and a large collection of character tables including all of the Cambridge Atlas of Finite Groups, but which we hope to extend further with the help of colleagues who have undertaken larger classifications of groups.

4. The documentation. This is available as a file that can either be used for online help or be printed out to form this manual. Some advice for using this manual may be helpful. The first chapter About GAP is really an introduction to the use of the system, starting from scratch and, for the beginning, assuming neither much knowledge about group theory nor much versatility in using a computer. Some of the later sections of chapter 1 assume more, however. For instance section 1.25 definitely assumes familiarity with representation theory of finite groups, while in particular sections 1.27 to 1.30 address more advanced users who want to extend the system to meet their special needs. The further chapters of the manual give then a full description of the functions presently available in GAP.

The policy for the further development of GAP is to keep the kernel as small as possible, extending the set of basic functions only by very selected ones that have proved to be timecritical and, wherever feasible, of general use. In the interest of the possibility of exchanging functions written in the GAP language the kernel has to be maintained in a single place which in the foreseeable future will be Aachen. On the other hand we hoped from the beginning that the design of GAP would allow the library to grow not only by continued work in Aachen but, as does any other part of mathematics, by exchange of contributions from many sides. Both has in fact happened. Notable extensions from version 3.1 to version 3.2, which had been announced as our plans in the preface of version 3.1 were the following:

- the removal of the restriction of the degree of permutations,

- extensions of the methods for for permutation groups, e.g., for finding the composition factors,

- extension of the methods for finite presentations, e.g., by reduced Reidemeister-Schreier, Modified Todd-Coxeter, and Tietze methods,

- further tools for getting character tables, e.g., by the Dixon-Schneider method working from class multiplication coefficients, and for working with character tables, e.g., for finding permutation characters. Other extensions include a package for univariate polynomials over a coefficient ring.

Since version 3.2 GAP provides separate packages in the form of a GAP share library (see chapter 48 for details). At present there are four such packages, the ANU p-quotient and p-group generation package by Eamonn O’Brien and Mike Newman, GRAPE, a system for working with graphs, by Leonard Soicher, the (nonperiodic) Nilpotent Quotient program of Werner Nickel, and a package for Weyl groups and Hecke algebras by Meinolf Geck.

Version 3.3 is essentially a consolidated version with some additional character tables.

Of course the development of GAP will go on. Of our own plans for this that were already mentioned in the introduction to version 3.1 there remain the removal of the present restriction of the size of finite fields and better means of working with the output of the subgroup lattice program. Further plans for the near future include still further additions to the methods for permutation groups, the start of a package of tools for matrix groups, the provision of graphical output through an X-window interface and a debugger. Again some of these plans are already in some state of implementation, but just did not make it for the deadline that we had set for this release.

There are five other points to make on further policy:

- When we began work on GAP the typical user that we had in mind was the one wanting to implement his own algorithmic ideas. While we certainly hope that we still serve such users well it has become clear from the experience of the last years that there are even more users of two different species, on the one hand the established theoretician, sometimes with little experience in the use of computers, who wants an easily understandable tool, on the other hand the student, often quite familiar with computers, who wants to get assistance in learning the theory by being able to do nontrivial examples. We think that in fact GAP can well be used by both, but we realize that for each a special introduction would be desirable. We apologize that we have not had the time yet to write such, however have learned (through the GAP forum) that in a couple of places work on the development of Laboratory Manuals for the use of GAP alongside with standard Algebra texts is undertaken.

- When we began work on GAP, we designed it as a system for doing group theory. It has already turned out that in fact the design of the system is general enough, and some of its functions are also useful, for doing work in other neighbouring areas. For instance Leonard Soicher has used GAP to develop a system GRAPE for working with graphs, which meanwhile is available as share library. We certainly enjoy seeing this happen, but we want to emphasize that our primary interest is the development of a group theory system and that we do not plan to try to extend it beyond our abilities into a general computer algebra system.

- Rather we hope to provide tools for linking GAP to other systems that represent years of work and experience in areas such as commutative algebra, or to very efficient special purpose stand-alone programs.

- We invite you to further extend GAP. We are willing to make such extensions available through the same channels as GAP in the form of the above mentioned share library. Of course, we will do this only if the extension can be distributed free of charge like GAP. The copyright for such extensions shall remain with you.

- Finally to answer an often asked question: The GAP language is in principle designed to be compilable. We may one day try writing a compiler for it, but this is not yet far enough under way to make any promise of prediction.

GAP is given away under the conditions that have always been in use between mathematicians, i.e. in particular completely in source and free of charge. We hope that the possibility offered by modern technology of depositing GAP on a number of computers to be fetched from them by ftp, will assist us in this policy. We want to emphasize, however, two points. GAP is not public domain software; we want to maintain a copyright that in particular forbids commercialization of GAP. Further we ask that use of GAP be quoted in publications like the use of any other mathematical work, and we would be grateful if we could keep track of where GAP is implemented. Therefore we ask you to notify us if you have got GAP, e.g., by sending a short e-mail message to gap@samson.math.rwth-aachen.de. The simple reason, on top of our curiosity, is that as anybody else in an academic environment we have from time to time to prove that we are doing meaningful work. We have established a GAP forum, where interested users can discuss GAP related topics by e-mail. In particular this forum is for questions about GAP, general comments, bug reports, and maybe bug fixes. We will read this forum and answer questions and comments, and distribute bug fixes. Of course others are also invited to answer questions, etc. We will also announce future releases of GAP on this forum. To subscribe send an e-mail message to listserv@samson.math.rwth-aachen.de containing the line subscribe gap-forum your-name, where your-name should be your full name, not your e-mail address. You will receive an acknowledgement, and from then on all e-mail

messages sent to gap-forum@samson.math.rwth-aachen.de.
listserv@samson.math.rwth-aachen.de also accepts the following requests. help for a
short help on how to use listserv, unsubscribe gap-forum to unsubscribe, recipients

gap-forum to get a list of subscribers, and statistics gap-forum to see how many e-mail messages each subscriber has sent so far.

The reliability of large systems of computer programs is a well known general problem and, although over the past year the record of GAP in this respect has not been too bad, of course GAP is not exempt from this problem. We therefore feel that it is mandatory that we, but also other users, are warned of bugs that have been encountered in GAP or when doubts have arisen. We ask all users of GAP to use the GAP forum for issuing such warnings.

GAP was started as a joint Diplom project of four students whose names have already been mentioned. Since then over a dozen finished Diplom projects have contributed to GAP and further students are presently extending the library in the course of their work for the Diplom as well as other members of Lehrstuhl D and colleagues from other institutes. Their individual contributions to the programs and to the manual are documented in the respective files. To all of them as well as to all who have helped proofreading and improving this manual I want to express my thanks for their engagement and enthusiasm as well as to many users of GAP who have helped us by pointing out deficiencies and suggesting improvements. Very special thanks however go to Martin Schönert. Not only does GAP owe many of its basic design features to his profound knowledge of computer languages and the techniques for their implementation, but in many long discussions he has in the name of future users always been the strongest defender of clarity of the design against my impatience and the temptation for ”quick and dirty” solutions.

As with the previous versions we send this version out hoping for further feedback of constructive criticism. Of course we ask to be notified about bugs, but moreover we shall appreciate any suggestion for the improvement of the basic system as well as of the algorithms in the library. Most of all, however, we hope that in spite of such criticism you will enjoy working with GAP.

Aachen, January 30.,1993, Joachim Neubüser.

8 PREFACE

Contents

1 About GAP 13 2 The Programming Language 133 3 Environment 151 4 Domains 163 5 Rings 177 6 Fields 193 7 Groups 203 8 Operations of Groups 265 9 Vector Spaces 285 10 Integers 291 11 Number Theory 303 12 Rationals 309 13 Cyclotomics 313 14 Gaussians 323 15 Subfields of Cyclotomic Fields 329 16 Unknowns 339 17 Finite Fields 343 10 CONTENTS 18 Polynomials 351 19 Permutations 365 20 Permutation Groups 371 21 Words in Abstract Generators 389 22 Finitely Presented Groups 397 23 Words in Finite Polycyclic Groups 427 24 Finite Polycyclic Groups 435 25 Lists 481 26 Sets 503 27 Boolean Lists 509 28 Strings and Characters 515 29 Ranges 521 30 Vectors 525 31 Row Spaces 529 32 Matrices 533 33 Matrix Rings 541 34 Matrix Groups 543 35 Group Libraries 547 36 Mappings 561 37 Homomorphisms 579 38 Booleans 585 39 Records 589 CONTENTS 11 40 Combinatorics 603 41 Tables of Marks 617 42 Character Tables 629 43 Generic Character Tables 671 44 Characters 675 45 Maps and Parametrized Maps 703 46 Character Table Libraries 723 47 Getting and Installing GAP 735 48 Share Libraries 767 49 ANU Pq 789 50 Weyl Groups and Hecke Algebras 797 51 Grape 817 52 Sisyphos 843 12 CONTENTS

Contents

1 About GAP 13 1.1 About Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.2 About Starting and Leaving GAP . . . . . . . . . . . . . . . . . . . . . . 14 1.3 About First Steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.4 About Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.5 About Syntax Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.6 About Constants and Operators . . . . . . . . . . . . . . . . . . . . . . . 16 1.7 About Variables and Assignments . . . . . . . . . . . . . . . . . . . . . . 18 1.8 About Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 1.9 About Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.10 About Identical Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.11 About Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 1.12 About Vectors and Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . 26 1.13 About Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 1.14 About Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 1.15 About Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 1.16 About Further List Operations . . . . . . . . . . . . . . . . . . . . . . . . 32 1.17 About Writing Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.18 About Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 1.19 About Operations of Groups . . . . . . . . . . . . . . . . . . . . . . . . . 45 1.20 About Finitely Presented Groups and Presentations . . . . . . . . . . . . 53 1.21 About Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 1.22 About Matrix Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 1.23 About Domains and Categories . . . . . . . . . . . . . . . . . . . . . . . . 63 1.24 About Mappings and Homomorphisms . . . . . . . . . . . . . . . . . . . . 71 1.25 About Character Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 1.26 About Group Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 1.27 About the Implementation of Domains . . . . . . . . . . . . . . . . . . . . 95 1.28 About Defining New Domains . . . . . . . . . . . . . . . . . . . . . . . . 104 1.29 About Defining New Parametrized Domains . . . . . . . . . . . . . . . . . 112 1.30 About Defining New Group Elements . . . . . . . . . . . . . . . . . . . . 116

2 The Programming Language 133 2.1 Lexical Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 2.2 Symbols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 2.3 Whitespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 2.4 Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 2.5 Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 2.6 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 2.7 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 2.8 Function Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 2.9 Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 2.10 Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 2.11 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 2.12 Assignments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 2.13 Procedure Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 2.14 If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 2.15 While . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 2.16 Repeat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 2.17 For . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 2.18 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 2.19 Return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 2.20 The Syntax in BNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

3 Environment 151 3.1 Main Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 3.2 Break Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 3.3 Error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 3.4 Line Editing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 3.5 Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 3.6 Reading Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 3.7 Format of Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 3.8 Browsing through the Sections . . . . . . . . . . . . . . . . . . . . . . . . 156 3.9 Redisplaying a Section . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 3.10 Abbreviating Section Names . . . . . . . . . . . . . . . . . . . . . . . . . 157 3.11 Help Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 3.12 Read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 3.13 ReadLib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 3.14 Print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 3.15 PrintTo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 3.16 AppendTo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 3.17 LogTo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 3.18 LogInputTo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 3.19 SizeScreen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 3.20 Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 3.21 Profile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 3.22 Exec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 3.23 Edit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

4 Domains 163 4.1 Domain Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 4.2 Dispatchers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 4.3 More about Dispatchers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165 4.4 An Example of a Computation in a Domain . . . . . . . . . . . . . . . . . 166 4.5 Domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 4.6 Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 4.7 Comparisons of Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 4.8 Membership Test for Domains . . . . . . . . . . . . . . . . . . . . . . . . 170 4.9 IsFinite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 4.10 Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 4.11 IsSubset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 4.12 Intersection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 4.13 Union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 4.14 Difference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 4.15 Representative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 4.16 Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 16 CONTENTS

5 Rings 177 5.1 IsRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 5.2 Ring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 5.3 DefaultRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 5.4 Comparisons of Ring Elements . . . . . . . . . . . . . . . . . . . . . . . . 179 5.5 Operations for Ring Elements . . . . . . . . . . . . . . . . . . . . . . . . . 179 5.6 Quotient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 5.7 IsCommutativeRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 5.8 IsIntegralRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 5.9 IsUniqueFactorizationRing . . . . . . . . . . . . . . . . . . . . . . . . . . 181 5.10 IsEuclideanRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 5.11 IsUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 5.12 Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 5.13 IsAssociated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 5.14 StandardAssociate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 5.15 Associates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 5.16 IsIrreducible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 5.17 IsPrime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 5.18 Factors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 5.19 EuclideanDegree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 5.20 EuclideanRemainder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 5.21 EuclideanQuotient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 5.22 QuotientRemainder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 5.23 Mod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 5.24 QuotientMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 5.25 PowerMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 5.26 Gcd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 5.27 GcdRepresentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 5.28 Lcm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 5.29 Ring Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 6 Fields 193

6.1 IsField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 6.2 Field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 6.3 DefaultField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 6.4 Fields over Subfields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 6.5 Comparisons of Field Elements . . . . . . . . . . . . . . . . . . . . . . . . 195 6.6 Operations for Field Elements . . . . . . . . . . . . . . . . . . . . . . . . 196 6.7 GaloisGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 6.8 Conjugates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 6.9 Norm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 6.10 Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 6.11 CharPol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 6.12 MinPol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 6.13 Field Homomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 6.14 IsFieldHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 6.15 KernelFieldHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . 201 6.16 Mapping Functions for Field Homomorphisms . . . . . . . . . . . . . . . . 201 6.17 Field Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

7 Groups 203 7.1 Group Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 7.2 Comparisons of Group Elements . . . . . . . . . . . . . . . . . . . . . . . 204 7.3 Operations for Group Elements . . . . . . . . . . . . . . . . . . . . . . . . 204 7.4 IsGroupElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 7.5 Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 7.6 More about Groups and Subgroups . . . . . . . . . . . . . . . . . . . . . . 206 7.7 IsParent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 7.8 Parent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 7.9 Group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 7.10 AsGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 7.11 IsGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 7.12 Subgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 7.13 AsSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 7.14 Subgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 7.15 Agemo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210 7.16 Centralizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 7.17 Centre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 7.18 Closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 7.19 CommutatorSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 7.20 ConjugateSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 7.21 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 7.22 DerivedSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 7.23 FittingSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 7.24 NormalClosure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 7.25 NormalIntersection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 7.26 Normalizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 7.27 PCore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 7.28 Radical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 7.29 SylowSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 7.30 TrivialSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 7.31 FactorGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 7.32 FactorGroupElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 7.33 CommutatorFactorGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 7.34 Series of Subgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 7.35 DerivedSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 7.36 CompositionSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 7.37 ElementaryAbelianSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 7.38 JenningsSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 7.39 LowerCentralSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 7.40 PCentralSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220 7.41 SubnormalSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 7.42 UpperCentralSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 7.43 Properties and Property Tests . . . . . . . . . . . . . . . . . . . . . . . . 221 7.44 AbelianInvariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 7.45 DimensionsLoewyFactors . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 7.46 Exponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 7.47 Factorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 7.48 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 7.49 IsAbelian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 7.50 IsCentral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 7.51 IsConjugate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224 7.52 IsCyclic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 7.53 IsElementaryAbelian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 7.54 IsNilpotent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 7.55 IsNormal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 7.56 IsPerfect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 7.57 IsSimple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 7.58 IsSolvable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 7.59 IsSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 7.60 IsSubnormal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 7.61 IsTrivial for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 7.62 PermutationCharacter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 7.63 Conjugacy Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 7.64 ConjugacyClasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 7.65 ConjugacyClass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 7.66 IsConjugacyClass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 7.67 Set Functions for Conjugacy Classes . . . . . . . . . . . . . . . . . . . . . 230 7.68 Conjugacy Class Records . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 7.69 ConjugacyClassesSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . 231 7.70 Lattice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 7.71 ConjugacyClassSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 7.72 IsConjugacyClassSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . 239 7.73 Set Functions for Subgroup Conjugacy Classes . . . . . . . . . . . . . . . 239 7.74 Subgroup Conjugacy Class Records . . . . . . . . . . . . . . . . . . . . . 239 7.75 NormalSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 7.76 ConjugateSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 7.77 Cosets of Subgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 7.78 RightCosets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 7.79 RightCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 7.80 IsRightCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 7.81 Set Functions for Right Cosets . . . . . . . . . . . . . . . . . . . . . . . . 242 7.82 Right Cosets Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 7.83 LeftCosets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 7.84 LeftCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 7.85 IsLeftCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 7.86 DoubleCosets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 7.87 DoubleCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 7.88 IsDoubleCoset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 7.89 Set Functions for Double Cosets . . . . . . . . . . . . . . . . . . . . . . . 246 7.90 Double Coset Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 7.91 Group Constructions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 7.92 DirectProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248 7.93 DirectProduct for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 7.94 SemidirectProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 7.95 SemidirectProduct for Groups . . . . . . . . . . . . . . . . . . . . . . . . 251 7.96 SubdirectProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 7.97 WreathProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252 7.98 WreathProduct for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . 252 7.99 Group Homomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 7.100 IsGroupHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 7.101 KernelGroupHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . 254 7.102 Mapping Functions for Group Homomorphisms . . . . . . . . . . . . . . . 255 7.103 NaturalHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 7.104 ConjugationGroupHomomorphism . . . . . . . . . . . . . . . . . . . . . . 257 7.105 InnerAutomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 7.106 GroupHomomorphismByImages . . . . . . . . . . . . . . . . . . . . . . . 258 7.107 Set Functions for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 7.108 Elements for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 7.109 Intersection for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 7.110 Operations for Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 7.111 Group Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

8 Operations of Groups 265 8.1 Other Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 8.2 Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 8.3 CycleLength . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 8.4 Cycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 8.5 CycleLengths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 8.6 Permutation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 8.7 IsFixpoint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 8.8 IsFixpointFree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 8.9 DegreeOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 8.10 IsTransitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 8.11 Transitivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 8.12 IsRegular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 8.13 IsSemiRegular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 8.14 Orbit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274 8.15 OrbitLength . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275 8.16 Orbits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 8.17 OrbitLengths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 8.18 Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 8.19 OperationHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 8.20 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 8.21 IsPrimitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 8.22 Stabilizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 8.23 RepresentativeOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . 281 8.24 RepresentativesOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . 282 8.25 IsEquivalentOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283

9 Vector Spaces 285 9.1 VectorSpace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 9.2 IsVectorSpace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 9.3 Vector Space Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286 9.4 Set Functions for Vector Spaces . . . . . . . . . . . . . . . . . . . . . . . . 287 9.5 IsSubspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 9.6 Base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 9.7 AddBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288 9.8 Dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 9.9 LinearCombination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 9.10 Coefficients . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290

10 Integers 291 10.1 Comparisons of Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 10.2 Operations for Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 10.3 QuoInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 10.4 RemInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 10.5 IsInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 10.6 Int . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 10.7 AbsInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 10.8 SignInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294 10.9 ChineseRem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 10.10 LogInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 10.11 RootInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 10.12 SmallestRootInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 10.13 Set Functions for Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 10.14 Ring Functions for Integers . . . . . . . . . . . . . . . . . . . . . . . . . . 297 10.15 Primes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 10.16 IsPrimeInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298 10.17 IsPrimePowerInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 10.18 NextPrimeInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 10.19 PrevPrimeInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 10.20 FactorsInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 10.21 DivisorsInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300 10.22 Sigma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 10.23 Tau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 10.24 MoebiusMu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301

11 Number Theory 303 11.1 PrimeResidues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303 11.2 Phi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 11.3 Lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304 11.4 OrderMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 11.5 IsPrimitiveRootMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305 11.6 PrimitiveRootMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 11.7 Jacobi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 11.8 Legendre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306 11.9 RootMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 11.10 RootsUnityMod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307

12 Rationals 309 12.1 IsRat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 12.2 Numerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 12.3 Denominator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310 12.4 Comparisons of Rationals . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 12.5 Operations for Rationals . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 12.6 Set Functions for Rationals . . . . . . . . . . . . . . . . . . . . . . . . . . 312 12.7 Field Functions for Rationals . . . . . . . . . . . . . . . . . . . . . . . . . 312 13 Cyclotomics 313

13.1 More about Cyclotomics . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313 13.2 Cyclotomic Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 13.3 IntCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 13.4 RoundCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 13.5 IsCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 13.6 IsCycInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315 13.7 NofCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 13.8 CoeffsCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 13.9 Comparisons of Cyclotomics . . . . . . . . . . . . . . . . . . . . . . . . . 316 13.10 Operations for Cyclotomics . . . . . . . . . . . . . . . . . . . . . . . . . . 317 13.11 GaloisCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317 13.12 ATLAS irrationalities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318 13.13 StarCyc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319 13.14 Quadratic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319 13.15 GaloisMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 13.16 RationalizedMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

14 Gaussians 323 14.1 Comparisons of Gaussians . . . . . . . . . . . . . . . . . . . . . . . . . . . 323 14.2 Operations for Gaussians . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 14.3 IsGaussRat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 14.4 IsGaussInt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 14.5 Set Functions for Gaussians . . . . . . . . . . . . . . . . . . . . . . . . . . 325 14.6 Field Functions for Gaussian Rationals . . . . . . . . . . . . . . . . . . . 326 14.7 Ring Functions for Gaussian Integers . . . . . . . . . . . . . . . . . . . . . 326 14.8 TwoSquares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

15 Subfields of Cyclotomic Fields 329 15.1 IsNumberField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 15.2 IsCyclotomicField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 15.3 Number Field Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 15.4 Cyclotomic Field Records . . . . . . . . . . . . . . . . . . . . . . . . . . . 331 15.5 DefaultField and Field for Cyclotomics . . . . . . . . . . . . . . . . . . . 332 15.6 DefaultRing and Ring for Cyclotomic Integers . . . . . . . . . . . . . . . 333 15.7 GeneratorsPrimeResidues . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 15.8 GaloisGroup for Number Fields . . . . . . . . . . . . . . . . . . . . . . . . 334 15.9 ZumbroichBase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334 15.10 Integral Bases for Number Fields . . . . . . . . . . . . . . . . . . . . . . . 335 15.11 NormalBaseNumberField . . . . . . . . . . . . . . . . . . . . . . . . . . . 336 15.12 Coefficients for Number Fields . . . . . . . . . . . . . . . . . . . . . . . . 336 15.13 Domain Functions for Number Fields . . . . . . . . . . . . . . . . . . . . 337

16 Unknowns 339 16.1 Unknown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 16.2 IsUnknown . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340 16.3 Comparisons of Unknowns . . . . . . . . . . . . . . . . . . . . . . . . . . 341 16.4 Operations for Unknowns . . . . . . . . . . . . . . . . . . . . . . . . . . . 341

17 Finite Fields 343 17.1 Finite Field Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343 17.2 Comparisons of Finite Field Elements . . . . . . . . . . . . . . . . . . . . 344 17.3 Operations for Finite Field Elements . . . . . . . . . . . . . . . . . . . . . 345 17.4 IsFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 17.5 CharFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 17.6 DegreeFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 17.7 OrderFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 17.8 IntFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347 17.9 LogFFE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 17.10 GaloisField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 17.11 FrobeniusAutomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349 17.12 Set Functions for Finite Fields . . . . . . . . . . . . . . . . . . . . . . . . 349 17.13 Field Functions for Finite Fields . . . . . . . . . . . . . . . . . . . . . . . 350

18 Polynomials 351 18.1 Multivariate Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 18.2 Indeterminate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353 18.3 Polynomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 18.4 IsPolynomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354 18.5 Comparisons of Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . 354 18.6 Operations for Polynomials . . . . . . . . . . . . . . . . . . . . . . . . . . 355 18.7 Degree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 18.8 LeadingCoefficient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 18.9 Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 18.10 Derivative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 18.11 InterpolatedPolynomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 18.12 PolynomialRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 18.13 IsPolynomialRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 18.14 LaurentPolynomialRing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 18.15 IsLaurentPolynomialRing . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 18.16 Ring Functions for Polynomial Rings . . . . . . . . . . . . . . . . . . . . . 360 18.17 Ring Functions for Laurent Polynomial Rings . . . . . . . . . . . . . . . . 362

19 Permutations 365 19.1 Comparisons of Permutations . . . . . . . . . . . . . . . . . . . . . . . . . 366 19.2 Operations for Permutations . . . . . . . . . . . . . . . . . . . . . . . . . 366 19.3 IsPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 19.4 LargestMovedPointPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 19.5 SmallestMovedPointPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 19.6 SignPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 19.7 SmallestGeneratorPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 19.8 ListPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 19.9 PermList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 19.10 RestrictedPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 19.11 MappingPermListList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369

20 Permutation Groups 371 20.1 IsPermGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 20.2 PermGroupOps.MovedPoints . . . . . . . . . . . . . . . . . . . . . . . . . 372 20.3 PermGroupOps.SmallestMovedPoint . . . . . . . . . . . . . . . . . . . . . 372 20.4 PermGroupOps.LargestMovedPoint . . . . . . . . . . . . . . . . . . . . . 372 20.5 PermGroupOps.NrMovedPoints . . . . . . . . . . . . . . . . . . . . . . . . 372 20.6 Stabilizer Chains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373 20.7 MakeStabChain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374 20.8 ExtendStabChain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 20.9 ReduceStabChain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 20.10 MakeStabChainStrongGenerators . . . . . . . . . . . . . . . . . . . . . . . 376 20.11 Base for Permutation Groups . . . . . . . . . . . . . . . . . . . . . . . . . 376 20.12 PermGroupOps.Indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376 20.13 PermGroupOps.StrongGenerators . . . . . . . . . . . . . . . . . . . . . . 377 20.14 ListStabChain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 20.15 PermGroupOps.ElementProperty . . . . . . . . . . . . . . . . . . . . . . . 377 20.16 PermGroupOps.SubgroupProperty . . . . . . . . . . . . . . . . . . . . . . 379 20.17 CentralCompositionSeriesPPermGroup . . . . . . . . . . . . . . . . . . . 379 20.18 PermGroupOps.PgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 20.19 Set Functions for Permutation Groups . . . . . . . . . . . . . . . . . . . . 380 20.20 Group Functions for Permutation Groups . . . . . . . . . . . . . . . . . . 380 20.21 Operations of Permutation Groups . . . . . . . . . . . . . . . . . . . . . . 384 20.22 Homomorphisms for Permutation Groups . . . . . . . . . . . . . . . . . . 385 20.23 Permutation Group Records . . . . . . . . . . . . . . . . . . . . . . . . . . 387

21 Words in Abstract Generators 389 21.1 AbstractGenerator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390 21.2 AbstractGenerators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390 21.3 Comparisons of Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 21.4 Operations for Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391 21.5 IsWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392 21.6 LengthWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 21.7 ExponentSumWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 21.8 Subword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 21.9 PositionWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 21.10 SubstitutedWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 21.11 EliminatedWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 21.12 MappedWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395

22 Finitely Presented Groups 397 22.1 FreeGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398 22.2 Set Functions for Finitely Presented Groups . . . . . . . . . . . . . . . . . 398 22.3 Group Functions for Finitely Presented Groups . . . . . . . . . . . . . . . 399 22.4 CosetTableFpGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 22.5 OperationCosetsFpGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 22.6 LowIndexSubgroupsFpGroup . . . . . . . . . . . . . . . . . . . . . . . . . 403 22.7 Presentation Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404 22.8 Changing Presentations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407 22.9 Subgroup Presentations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408 22.10 SimplifiedFpGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411 22.11 Tietze Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 22.12 DecodeTree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 23 Words in Finite Polycyclic Groups 427

23.1 More about Ag Words . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 23.2 Ag Word Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428 23.3 CentralWeight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 23.4 CompositionLength . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 23.5 Depth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429 23.6 IsAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 23.7 LeadingExponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 23.8 RelativeOrder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 23.9 CanonicalAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 23.10 DifferenceAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 23.11 ReducedAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 23.12 SiftedAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 23.13 SumAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432 23.14 ExponentAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433 23.15 ExponentsAgWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433

24 Finite Polycyclic Groups 435 24.1 More about Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435 24.2 Construction of Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . 436 24.3 Ag Group Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436 24.4 Ag Group Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437 24.5 Set Functions for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . 437 24.6 Elements for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 24.7 Intersection for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . 438 24.8 Size for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438 24.9 Group Functions for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . 439 24.10 AsGroup for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442 24.11 Group for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 24.12 CommutatorSubgroup for Ag Groups . . . . . . . . . . . . . . . . . . . . 443 24.13 Normalizer for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . 443 24.14 AbelianInvariants for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . 443 24.15 IsCyclic for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 24.16 IsNormal for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 24.17 IsSubgroup for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 24.18 Stabilizer for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 24.19 CyclicGroup for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . 444 24.20 ElementaryAbelianGroup for Ag Groups . . . . . . . . . . . . . . . . . . . 445 24.21 DirectProduct for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . 445 24.22 WreathProduct for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . 446 24.23 RightCoset for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . 446 24.24 FpGroup for Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 24.25 Ag Group Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 24.26 AgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 24.27 IsAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448 24.28 AgGroupFpGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 448 24.29 IsConsistent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449 24.30 IsElementaryAbelianAgSeries . . . . . . . . . . . . . . . . . . . . . . . . . 449 24.31 MatGroupAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 24.32 PermGroupAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 24.33 RefinedAgSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 24.34 ChangeCollector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 24.35 The Prime Quotient Algorithm . . . . . . . . . . . . . . . . . . . . . . . . 451 24.36 PQuotient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451 24.37 Save . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453 24.38 PQp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454 24.39 InitPQp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454 24.40 FirstClassPQp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 24.41 NextClassPQp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 24.42 Weight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 24.43 Factorization for PQp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455 24.44 Generating Systems of Ag Groups . . . . . . . . . . . . . . . . . . . . . . 455 24.45 AgSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456 24.46 Cgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456 24.47 Igs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 456 24.48 IsNormalized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 24.49 Normalize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 24.50 Normalized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 24.51 MergedCgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 24.52 MergedIgs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458 24.53 Factor Groups of Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . 458 24.54 FactorGroup for AgGroups . . . . . . . . . . . . . . . . . . . . . . . . . . 458 24.55 CollectorlessFactorGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 24.56 FactorArg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 24.57 Subgroups and Properties of Ag Groups . . . . . . . . . . . . . . . . . . . 460 24.58 CompositionSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460 24.59 HallSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460 24.60 PRump . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 24.61 RefinedSubnormalSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461 24.62 SylowComplements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 24.63 SylowSystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462 24.64 MinimalGeneratingSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 24.65 IsElementAgSeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 24.66 IsPNilpotent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 24.67 NumberConjugacyClasses . . . . . . . . . . . . . . . . . . . . . . . . . . . 464 24.68 Exponents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 464 24.69 FactorsAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 24.70 MaximalElement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465 24.71 Orbitalgorithms of Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . 465 24.72 AffineOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 24.73 AgOrbitStabilizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 24.74 LinearOperation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467 24.75 Intersections of Ag Groups . . . . . . . . . . . . . . . . . . . . . . . . . . 467 24.76 ExtendedIntersectionSumAgGroup . . . . . . . . . . . . . . . . . . . . . . 468 24.77 IntersectionSumAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 24.78 SumAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469 24.79 SumFactorizationFunctionAgGroup . . . . . . . . . . . . . . . . . . . . . 470 24.80 One Cohomology Group . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470 24.81 OneCoboundaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 24.82 OneCocycles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 24.83 Complements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 24.84 Complement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473 24.85 Complementclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474 24.86 CoprimeComplement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475 24.87 ComplementConjugatingAgWord . . . . . . . . . . . . . . . . . . . . . . . 475 24.88 HallConjugatingWordAgGroup . . . . . . . . . . . . . . . . . . . . . . . . 476 24.89 Example, normal closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476 25 Lists 481

25.1 IsList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 25.2 List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 25.3 List Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483 25.4 Length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 25.5 List Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 25.6 Add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 25.7 Append . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486 25.8 Identical Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 25.9 IsIdentical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488 25.10 Enlarging Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489 25.11 Comparisons of Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490 25.12 Operations for Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490 25.13 In . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 25.14 Position . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 25.15 PositionSorted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492 25.16 PositionProperty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 25.17 Concatenation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 25.18 Flat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 25.19 Reversed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 25.20 Sublist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 25.21 Cartesian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494 25.22 Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 25.23 Collected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 495 25.24 Filtered . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496 25.25 ForAll . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496 25.26 ForAny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 25.27 First . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 25.28 Sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497 25.29 SortParallel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 25.30 Sortex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 25.31 Permuted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498 25.32 Product . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 25.33 Sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 25.34 Maximum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499 25.35 Minimum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500 25.36 Iterated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500 25.37 RandomList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500 26 Sets 503

26.1 IsSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504 26.2 Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504 26.3 IsEqualSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504 26.4 AddSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 26.5 RemoveSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 26.6 UniteSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505 26.7 IntersectSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506 26.8 SubtractSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506 26.9 Set Functions for Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 506 26.10 More about Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 507

27 Boolean Lists 509 27.1 BlistList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509 27.2 ListBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 27.3 IsBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 27.4 SizeBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 510 27.5 IsSubsetBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 27.6 UnionBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 27.7 IntersectionBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 27.8 DifferenceBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 27.9 UniteBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 27.10 IntersectBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 27.11 SubtractBlist . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512 27.12 More about Boolean Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . 513

28 Strings and Characters 515 28.1 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 28.2 ConcatenationString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 28.3 SubString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 28.4 Comparisons of Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518 28.5 IsString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519 28.6 LengthString . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519

29 Ranges 521 29.1 IsRange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522 29.2 More about Ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522 30 Vectors 525

30.1 Operations for Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 30.2 IsVector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 30.3 NormedVector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 30.4 More about Vectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527

31 Row Spaces 529 31.1 RowSpace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529 31.2 IsRowSpace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529 31.3 Row Space Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 31.4 Set Functions for Row Spaces . . . . . . . . . . . . . . . . . . . . . . . . . 530 31.5 VectorSpace Functions for Row Spaces . . . . . . . . . . . . . . . . . . . . 530 31.6 Base for Row Spaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 31.7 SiftedVector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530 31.8 Row Space Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531

32 Matrices 533 32.1 Operations for Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533 32.2 IsMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535 32.3 IdentityMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 32.4 NullMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 32.5 TransposedMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536 32.6 KroneckerProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 32.7 DimensionsMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 32.8 TraceMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 32.9 DeterminantMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 537 32.10 RankMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 32.11 OrderMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 32.12 TriangulizeMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538 32.13 BaseMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 32.14 NullspaceMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 32.15 SolutionMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539 32.16 DiagonalizeMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540 32.17 ElementaryDivisorsMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540

33 Matrix Rings 541 33.1 Set Functions for Matrix Rings . . . . . . . . . . . . . . . . . . . . . . . . 541 33.2 Ring Functions for Matrix Rings . . . . . . . . . . . . . . . . . . . . . . . 542 34 Matrix Groups 543

34.1 Set Functions for Matrix Groups . . . . . . . . . . . . . . . . . . . . . . . 543 34.2 Group Functions for Matrix Groups . . . . . . . . . . . . . . . . . . . . . 544 34.3 Matrix Group Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545

35 Group Libraries 547 35.1 The Basic Groups Library . . . . . . . . . . . . . . . . . . . . . . . . . . . 547 35.2 Selection Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550 35.3 Example Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551 35.4 Extraction Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552 35.5 The Primitive Groups Library . . . . . . . . . . . . . . . . . . . . . . . . 553 35.6 The Solvable Groups Library . . . . . . . . . . . . . . . . . . . . . . . . . 554 35.7 The 2-Groups Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 555 35.8 The 3-Groups Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 35.9 The Irreducible Solvable Linear Groups Library . . . . . . . . . . . . . . . 559

36 Mappings 561 36.1 IsGeneralMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562 36.2 IsMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562 36.3 IsInjective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563 36.4 IsSurjective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563 36.5 IsBijection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564 36.6 Comparisons of Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . 565 36.7 Operations for Mappings . . . . . . . . . . . . . . . . . . . . . . . . . . . 566 36.8 Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568 36.9 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570 36.10 ImagesRepresentative . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571 36.11 PreImage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571 36.12 PreImages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 36.13 PreImagesRepresentative . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 36.14 CompositionMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 36.15 PowerMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 575 36.16 InverseMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 36.17 IdentityMapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576 36.18 MappingByFunction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 36.19 Mapping Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 37 Homomorphisms 579

37.1 IsHomomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579 37.2 IsMonomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 580 37.3 IsEpimorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581 37.4 IsIsomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581 37.5 IsEndomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582 37.6 IsAutomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582 37.7 Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583

38 Booleans 585 38.1 Comparisons of Booleans . . . . . . . . . . . . . . . . . . . . . . . . . . . 585 38.2 Operations for Booleans . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586 38.3 IsBool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587

39 Records 589 39.1 Accessing Record Elements . . . . . . . . . . . . . . . . . . . . . . . . . . 590 39.2 Record Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 590 39.3 Identical Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 591 39.4 Comparisons of Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593 39.5 Operations for Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595 39.6 In for Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596 39.7 Printing of Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597 39.8 IsRec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598 39.9 IsBound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598 39.10 Unbind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599 39.11 Copy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599 39.12 ShallowCopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600 39.13 RecFields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601

40 Combinatorics 603 40.1 Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603 40.2 Binomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604 40.3 Bell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604 40.4 Stirling1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605 40.5 Stirling2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605 40.6 Combinations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 40.7 Arrangements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606 40.8 UnorderedTuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607 40.9 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608 40.10 PermutationsList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608 40.11 Derangements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 40.12 Permanent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609 40.13 PartitionsSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610 40.14 Partitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610 40.15 OrderedPartitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611 40.16 RestrictedPartitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612 40.17 SignPartition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 40.18 AssociatedPartition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 40.19 PowerPartition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 40.20 PartitionTuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613 40.21 Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614 40.22 Lucas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614 40.23 Bernoulli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615

41 Tables of Marks 617 41.1 More about Tables of Marks . . . . . . . . . . . . . . . . . . . . . . . . . 617 41.2 Table of Marks Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618 41.3 The Library of Tables of Marks . . . . . . . . . . . . . . . . . . . . . . . . 618 41.4 TableOfMarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619 41.5 Marks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620 41.6 NrSubs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620 41.7 WeightsTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620 41.8 MatTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621 41.9 TomMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621 41.10 DecomposedFixedPointVector . . . . . . . . . . . . . . . . . . . . . . . . . 621 41.11 TestTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622 41.12 DisplayTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622 41.13 NormalizerTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623 41.14 IntersectionsTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623 41.15 IsCyclicTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624 41.16 FusionCharTableTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624 41.17 PermCharsTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624 41.18 MoebiusTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624 41.19 CyclicExtensionsTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 41.20 IdempotentsTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 41.21 ClassTypesTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 41.22 ClassNamesTom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625 41.23 TomCyclic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626 41.24 TomDihedral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626 41.25 TomFrobenius . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 627

42 Character Tables 629 42.1 Some Notes on Character Theory in GAP . . . . . . . . . . . . . . . . . . 629 42.2 Character Table Records . . . . . . . . . . . . . . . . . . . . . . . . . . . 631 42.3 Brauer Table Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635 42.4 IsCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636 42.5 TestCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637 42.6 Operations Records for Character Tables . . . . . . . . . . . . . . . . . . 637 42.7 Conventions for Character Tables . . . . . . . . . . . . . . . . . . . . . . . 637 42.8 Getting Character Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . 638 42.9 CharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639 42.10 Advanced Methods for Dixon Schneider Calculations . . . . . . . . . . . . 642 42.11 An Example of Advanced Dixon Schneider Calculations . . . . . . . . . . 643 42.12 CharTableFactorGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645 42.13 CharTableNormalSubgroup . . . . . . . . . . . . . . . . . . . . . . . . . . 646 42.14 CharTableDirectProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . 647 42.15 CharTableWreathSymmetric . . . . . . . . . . . . . . . . . . . . . . . . . 647 42.16 CharTableRegular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649 42.17 CharTableIsoclinic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650 42.18 CharTableSplitClasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650 42.19 CharTableCollapsedClasses . . . . . . . . . . . . . . . . . . . . . . . . . . 651 42.20 CharDegAgGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 42.21 CharTableSSGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652 42.22 MatRepresentationsPGroup . . . . . . . . . . . . . . . . . . . . . . . . . . 653 42.23 CharTablePGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654 42.24 InitClassesCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654 42.25 InverseClassesCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655 42.26 ClassNamesCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655 42.27 ClassMultCoeffCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . 655 42.28 MatClassMultCoeffsCharTable . . . . . . . . . . . . . . . . . . . . . . . . 656 42.29 ClassStructureCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . 656 42.30 RealClassesCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 42.31 ClassOrbitCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 42.32 ClassRootsCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 42.33 NrPolyhedralSubgroups . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 42.34 DisplayCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 658 42.35 SortCharactersCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . 660 42.36 SortClassesCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661 42.37 SortCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 662 42.38 MatAutomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663 42.39 TableAutomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 664 42.40 TransformingPermutations . . . . . . . . . . . . . . . . . . . . . . . . . . 664 42.41 TransformingPermutationsCharTables . . . . . . . . . . . . . . . . . . . . 664 42.42 GetFusionMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665 42.43 StoreFusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 42.44 FusionConjugacyClasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666 42.45 MAKElb11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 42.46 ScanMOC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 42.47 MOCChars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 42.48 GAPChars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 42.49 MOCTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667 42.50 PrintToMOC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669 42.51 PrintToCAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669

43 Generic Character Tables 671 43.1 More about Generic Character Tables . . . . . . . . . . . . . . . . . . . . 671 43.2 Examples of Generic Character Tables . . . . . . . . . . . . . . . . . . . . 672 43.3 CharTableSpecialized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674

44 Characters 675 44.1 ScalarProduct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 675 44.2 MatScalarProducts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676 44.3 Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676 44.4 Subroutines of Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . 677 44.5 KernelChar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678 44.6 PrimeBlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678 44.7 Indicator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 44.8 Eigenvalues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679 44.9 Reduced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 44.10 ReducedOrdinary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680 44.11 Tensored . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681 44.12 Symmetrisations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681 44.13 SymmetricParts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 44.14 AntiSymmetricParts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 44.15 MinusCharacter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 44.16 OrthogonalComponents . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682 44.17 SymplecticComponents . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683 44.18 IrreducibleDifferences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684 44.19 Restricted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684 44.20 Inflated . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 44.21 Induced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685 44.22 InducedCyclic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686 44.23 CollapsedMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686 44.24 Power . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687 44.25 Permutation Character Candidates . . . . . . . . . . . . . . . . . . . . . . 687 44.26 IsPermChar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687 44.27 PermCharInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688 44.28 Inequalities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 44.29 PermBounds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 44.30 PermChars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689 44.31 Faithful Permutation Characters . . . . . . . . . . . . . . . . . . . . . . . 691 44.32 LLLReducedBasis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691 44.33 LLLReducedGramMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692 44.34 LLL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693 44.35 OrthogonalEmbeddings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693 44.36 ShortestVectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695 44.37 Extract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695 44.38 Decreased . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696 44.39 DnLattice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697 44.40 ContainedDecomposables . . . . . . . . . . . . . . . . . . . . . . . . . . . 698 44.41 ContainedCharacters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699 44.42 ContainedSpecialVectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699 44.43 ContainedPossibleCharacters . . . . . . . . . . . . . . . . . . . . . . . . . 700 44.44 ContainedPossibleVirtualCharacters . . . . . . . . . . . . . . . . . . . . . 701 45 Maps and Parametrized Maps 703

45.1 More about Maps and Parametrized Maps . . . . . . . . . . . . . . . . . 703 45.2 CompositionMaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 45.3 InverseMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704 45.4 ProjectionMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 45.5 Parametrized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 45.6 ContainedMaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705 45.7 UpdateMap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706 45.8 CommutativeDiagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706 45.9 TransferDiagram . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707 45.10 Indeterminateness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708 45.11 PrintAmbiguity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708 45.12 Powermap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 45.13 SubgroupFusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709 45.14 InitPowermap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710 45.15 Congruences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711 45.16 ConsiderKernels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712 45.17 ConsiderSmallerPowermaps . . . . . . . . . . . . . . . . . . . . . . . . . . 712 45.18 InitFusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713 45.19 CheckPermChar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713 45.20 CheckFixedPoints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714 45.21 TestConsistencyMaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714 45.22 ConsiderTableAutomorphisms . . . . . . . . . . . . . . . . . . . . . . . . 715 45.23 PowermapsAllowedBySymmetrisations . . . . . . . . . . . . . . . . . . . . 715 45.24 FusionsAllowedByRestrictions . . . . . . . . . . . . . . . . . . . . . . . . . 716 45.25 OrbitFusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717 45.26 OrbitPowermaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718 45.27 RepresentativesFusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718 45.28 RepresentativesPowermaps . . . . . . . . . . . . . . . . . . . . . . . . . . 719 45.29 Indirected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719 45.30 Powmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 45.31 ElementOrdersPowermap . . . . . . . . . . . . . . . . . . . . . . . . . . . 720 46 Character Table Libraries 723

46.1 Contents of the Table Libraries . . . . . . . . . . . . . . . . . . . . . . . . 723 46.2 ATLAS Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725 46.3 Examples of the ATLAS format for GAP tables . . . . . . . . . . . . . . 727 46.4 CAS Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 730 46.5 Organization of the Table Libraries . . . . . . . . . . . . . . . . . . . . . . 731 46.6 How to Extend a Table Library . . . . . . . . . . . . . . . . . . . . . . . . 733 46.7 FirstNameCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734 46.8 FileNameCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734

47 Getting and Installing GAP 735 47.1 Getting GAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735 47.2 Upgrading GAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737 47.3 GAP for UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741 47.4 Installation of GAP for UNIX . . . . . . . . . . . . . . . . . . . . . . . . . 742 47.5 Features of GAP for UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . 746 47.6 GAP for MS-DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748 47.7 Copyright of GAP for MS-DOS . . . . . . . . . . . . . . . . . . . . . . . . 749 47.8 Installation of GAP for MS-DOS . . . . . . . . . . . . . . . . . . . . . . . 750 47.9 Features of GAP for MS-DOS . . . . . . . . . . . . . . . . . . . . . . . . . 754 47.10 GAP for TOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756 47.11 Copyright of GAP for TOS . . . . . . . . . . . . . . . . . . . . . . . . . . 757 47.12 Installation of GAP for TOS . . . . . . . . . . . . . . . . . . . . . . . . . 757 47.13 Features of GAP for TOS . . . . . . . . . . . . . . . . . . . . . . . . . . . 761 47.14 Porting GAP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 764

48 Share Libraries 767 48.1 RequirePackage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768 48.2 ANU pq Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768 48.3 Installing the ANU pq Package . . . . . . . . . . . . . . . . . . . . . . . . 769 48.4 GRAPE Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775 48.5 Installing the GRAPE Package . . . . . . . . . . . . . . . . . . . . . . . . 776 48.6 NQ Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779 48.7 Installing the NQ Package . . . . . . . . . . . . . . . . . . . . . . . . . . . 780 48.8 SISYPHOS Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782 48.9 Installing the SISYPHOS Package . . . . . . . . . . . . . . . . . . . . . . 782 48.10 Weyl Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784 48.11 Installing the Weyl Package . . . . . . . . . . . . . . . . . . . . . . . . . . 785 48.12 The XGap Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786 48.13 Installing the XGap Package . . . . . . . . . . . . . . . . . . . . . . . . . 787 49 ANU Pq 789

49.1 Pq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789 49.2 PqDescendants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 790 49.3 PqList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793 49.4 SavePqList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793 49.5 StandardPresentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 794 49.6 IsIsomorphicPGroup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796

50 Weyl Groups and Hecke Algebras 797 50.1 CartanMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 50.2 DirectSumCartanMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805 50.3 SimpleReflectionMatrices . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 50.4 Rootsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 50.5 PermRepresentationRoots . . . . . . . . . . . . . . . . . . . . . . . . . . . 806 50.6 Weyl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807 50.7 PermWeylWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 50.8 WeylWordPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 50.9 WeylLengthPerm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 50.10 ReducedWeylWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808 50.11 LongestWeylWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 50.12 WeylReflections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809 50.13 WeylRightCosetRepresentatives . . . . . . . . . . . . . . . . . . . . . . . . 809 50.14 WeylCosetPermRepresentation . . . . . . . . . . . . . . . . . . . . . . . . 809 50.15 WeylElements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810 50.16 WeylConjugacyClasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 810 50.17 ParametersCentralizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 50.18 Bruhat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811 50.19 KazhdanLusztigPolynomial . . . . . . . . . . . . . . . . . . . . . . . . . . 811 50.20 KLCoefficient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 50.21 WeylMueMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 50.22 DecomposedLeftCells . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 812 50.23 LeftCells . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 813 50.24 LeftCellRepresentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814 50.25 Hecke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814 50.26 WeylClassPolynomials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814 50.27 HeckeReflectionRepresentation . . . . . . . . . . . . . . . . . . . . . . . . 815 50.28 CheckHeckeDefiningRelations . . . . . . . . . . . . . . . . . . . . . . . . . 815 50.29 CharHeckeRepresentation . . . . . . . . . . . . . . . . . . . . . . . . . . . 815 50.30 HeckeCharTable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816 51 Grape 817

51.1 Functions to construct and modify graphs . . . . . . . . . . . . . . . . . . 818 51.2 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818 51.3 EdgeOrbitsGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819 51.4 NullGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820 51.5 CompleteGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820 51.6 JohnsonGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 820 51.7 AddEdgeOrbit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821 51.8 RemoveEdgeOrbit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 51.9 AssignVertexNames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822 51.10 Functions to inspect graphs, vertices and edges . . . . . . . . . . . . . . . 823 51.11 IsGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823 51.12 OrderGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823 51.13 IsVertex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823 51.14 VertexName . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 51.15 Vertices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 51.16 VertexDegree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 51.17 VertexDegrees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 51.18 IsLoopy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824 51.19 IsSimpleGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 51.20 Adjacency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 51.21 IsEdge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 51.22 DirectedEdges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825 51.23 UndirectedEdges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826 51.24 Distance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826 51.25 Diameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 826 51.26 Girth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 51.27 IsConnectedGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 51.28 IsBipartite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827 51.29 IsNullGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828 51.30 IsCompleteGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828 51.31 Functions to determine regularity properties of graphs . . . . . . . . . . . 828 51.32 IsRegularGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 51.33 LocalParameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 51.34 GlobalParameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 51.35 IsDistanceRegular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829 CONTENTS 43

51.36 CollapsedAdjacencyMat . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830 51.37 OrbitalGraphIntersectionMatrices . . . . . . . . . . . . . . . . . . . . . . 830 51.38 Some special vertex subsets of a graph . . . . . . . . . . . . . . . . . . . . 830 51.39 Component . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 830 51.40 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 51.41 Bicomponents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 51.42 DistanceSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 51.43 Layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831 51.44 IndependentSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832 51.45 Functions to construct new graphs from old . . . . . . . . . . . . . . . . . 832 51.46 InducedSubgraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832 51.47 DistanceSetInduced . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 832 51.48 DistanceGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833 51.49 ComplementGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833 51.50 PointGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 51.51 EdgeGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834 51.52 UnderlyingGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835 51.53 QuotientGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835 51.54 BipartiteDouble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836 51.55 GeodesicsGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836 51.56 CollapsedIndependentOrbitsGraph . . . . . . . . . . . . . . . . . . . . . . 837 51.57 CollapsedCompleteOrbitsGraph . . . . . . . . . . . . . . . . . . . . . . . 837 51.58 NewGroupGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838 51.59 Vertex-Colouring and Complete Subgraphs . . . . . . . . . . . . . . . . . 839 51.60 VertexColouring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 51.61 CompleteSubgraphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839 51.62 CompleteSubgraphsOfGivenSize . . . . . . . . . . . . . . . . . . . . . . . 839 51.63 Functions depending on nauty . . . . . . . . . . . . . . . . . . . . . . . . 840 51.64 AutGroupGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840 51.65 IsIsomorphicGraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841 51.66 An example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841 52 Sisyphos 843

52.1 PrintSISYPHOSWord . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843 52.2 PrintSisyphosInputPGroup . . . . . . . . . . . . . . . . . . . . . . . . . . 843 52.3 IsCompatiblePCentralSeries . . . . . . . . . . . . . . . . . . . . . . . . . . 844 52.4 Automorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845 52.5 AgNormalizedAutomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . 846 52.6 AgNormalizedOuterAutomorphisms . . . . . . . . . . . . . . . . . . . . . 846 52.7 IsIsomorphic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846 52.8 Isomorphisms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847 52.9 CorrespondingAutomorphism . . . . . . . . . . . . . . . . . . . . . . . . . 847 52.10 AutomorphismGroupElements . . . . . . . . . . . . . . . . . . . . . . . . 848

Chapter 1

About GAP

This chapter introduces you to the GAP system. It describes how to start GAP (you may have to ask your system administrator to install it correctly) and how to leave it. Then a step by step introduction should give you an impression of how the GAP system works. Further sections will give an overview about the features of GAP. After reading this chapter the reader should know what kind of problems can be handled with GAP and how they can be handled.

There is some repetition in this chapter and much of the material is repeated in later chapters in a more compact and precise way. Yes, there are even some little inaccuracies in this chapter simplifying things for better understanding. It should be used as a tutorial introduction while later chapters form the reference manual.

GAP is an interactive system. It continuously executes a read–evaluate–print cycle. Each expression you type at the keyboard is read by GAP, evaluated, and then the result is printed.

The interactive nature of GAP allows you to type an expression at the keyboard and see its value immediately. You can define a function and apply it to arguments to see how it works. You may even write whole programs containing lots of functions and test them without leaving the program.

When your program is large it will be more convenient to write it on a file and then read that file into GAP. Preparing your functions in a file has several advantages. You can compose your functions more carefully in a file (with your favorite text editor), you can correct errors without retyping the whole function and you can keep a copy for later use. Moreover you can write lots of comments into the program text, which are ignored by GAP, but are very useful for human readers of your program text.

GAP treats input from a file in the same way that it treats input from the keyboard.

The printed examples in this first chapter encourage you to try running GAP on your computer. This will support your feeling for GAP as a tool, which is the leading aim of this chapter. Do not believe any statement in this chapter so long as you cannot verify it for your own version of GAP. You will learn to distinguish between small deviations of the behavior of your personal GAP from the printed examples and serious nonsense.

Since the printing routines of GAP are in some sense machine dependent you will for instance encounter a different layout of the printed objects in different environments. But the contents should always be the same.

In case you encounter serious nonsense it is highly recommended that you send a bug report

to gap-forum@samson.math.rwth-aachen.de.

If you read this introduction on-line you should now enter ?> to read the next section.

1.1 About Conventions

Throughout this manual both the input given to GAP and the output that GAP returns are printed in typewriter font just as if they were typed at the keyboard.

An italic font is used for keys that have no printed representation, such as e.g. the newline key and the ctl key. This font is also used for the formal parameters of functions that are described in later chapters.

A combination like ctl-P means pressing both keys, that is holding the control key ctl and pressing the key P while ctl is still pressed.

New terms are introduced in bold face.

In most places whitespace characters (i.e. spaces, tabs and newlines) are insignificant for the meaning of GAP input. Identifiers and keywords must however not contain any whitespace. On the other hand, sometimes there must be whitespace around identifiers and keywords to separate them from each other and from numbers. We will use whitespace to format more complicated commands for better readability.

A comment in GAP starts with the symbol # and continues to the end of the line. Comments are treated like whitespace by GAP.

Besides of such comments which are part of the input of a GAP session, we use additional comments which are part of the manual description, but not of the respective GAP session. In the printed version of this manual these comments will be printed in a normal font for better readability, hence they start with the symbol #.

The example of GAP sessions given in ths manual have been run with the line length set to 72 (by the SizeScreen command). You should be able to reproduce the results except of a few lines of output which we have edited a little bit with respect to blanks or line breaks in order to improve the readability. Note, however, that for each particular section we have run the examples in one continuous session. Hence, as soon as random processes are involved, you may get differnt results if you extract single examples and run them separately.

1.2 About Starting and Leaving GAP

If the program is correctly installed then you start GAP by simply typing gap at the prompt of your operating system followed by the return or the newline key.

    $ gap

GAP answers your request with its beautiful banner (which you can surpress with the command line option -b) and then it shows its own prompt gap> asking you for further input.

    gap>

The usual way to end a GAP session is to type quit; at the gap> prompt. Do not omit the semicolon!

    gap> quit;
    $

On some systems you may as well type ctl-D to yield the same effect. In any situation GAP is ended by typing ctl-C twice within a second.

1.3 About First Steps

A simple calculation with GAP is as easy as one can imagine. You type the problem just after the prompt, terminate it with a semicolon and then pass the problem to the program with the return key. For example, to multiply the difference between 9 and 7 by the sum of 5 and 6, that is to calculate (9 − 7) ∗ (5 + 6), you type exactly this last sequence of symbols followed by ; and return.

    gap> (9 - 7) * (5 + 6);
    22
    gap>

Then GAP echoes the result 22 on the next line and shows with the prompt that it is ready for the next problem.

If you did omit the semicolon at the end of the line but have already typed return, then GAP has read everything you typed, but does not know that the command is complete. The program is waiting for further input and indicates this with a partial prompt >. This little problem is solved by simply typing the missing semicolon on the next line of input. Then the result is printed and the normal prompt returns.

    gap> (9 - 7) * (5 + 6)
    > ;
    22
    gap>

Whenever you see this partial prompt and you cannot decide what GAP is still waiting for, then you have to type semicolons until the normal prompt returns.

In every situation this is the exact meaning of the prompt gap> : the program is waiting for a new problem. In the following examples we will omit this prompt on the line after the result. Considering each example as a continuation of its predecessor this prompt occurs in the next example.

In this section you have seen how simple arithmetic problems can be solved by GAP by simply typing them in. You have seen that it doesn’t matter whether you complete your input on one line. GAP reads your input line by line and starts evaluating if it has seen the terminating semicolon and return.

It is, however, also possible (and might be advisable for large amounts of input data) to write your input first into a file, and then read this into GAP; see 3.23 and 3.12 for this.

Also in GAP, there is the possibility to edit the input data, see 3.4.

1.4 About Help

The contents of the GAP manual is also available as on-line help, see 3.5–3.11. If you need information about a section of the manual, just enter a question mark followed by the header of the section. E.g., entering ?About Help will print the section you are reading now.

??topic will print all entries in GAP’s index that contain the substring topic.

1.5 About Syntax Errors

Even if you mistyped the command you do not have to type it all again as GAP permits a lot of command line editing. Maybe you mistyped or forgot the last closing parenthesis. Then your command is syntactically incorrect and GAP will notice it, incapable of computing the desired result.

    gap> (9 - 7) * (5 + 6;
    Syntax error: ) expected
    (9 - 7) * (5 + 6;
                    ^

Instead of the result an error message occurs indicating the place where an unexpected symbol occurred with an arrow sign ^under it. As a computer program cannot know what your intentions really were, this is only a hint. But in this case GAP is right by claiming that there should be a closing parenthesis before the semicolon. Now you can type ctl-P to recover the last line of input. It will be written after the prompt with the cursor in the first position. Type ctl-E to take the cursor to the end of the line, then ctl-B to move the cursor one character back. The cursor is now on the position of the semicolon. Enter the missing parenthesis by simply typing ). Now the line is correct and may be passed to GAP by hitting the newline key. Note that for this action it is not necessary to move the cursor past the last character of the input line.

Each line of commands you type is sent to GAP for evaluation by pressing newline regardless of the position of the cursor in that line. We will no longer mention the newline key from now on.

Sometimes a syntax error will cause GAP to enter a break loop. This is indicated by the special prompt brk>. You can leave the break loop by either typing return; or by hitting ctl-D. Then GAP will return to its normal state and show its normal prompt again.

In this section you learned that mistyped input will not lead to big confusion. If GAP detects a syntax error it will print an error message and return to its normal state. The command line editing allows you in a comfortable way to manipulate earlier input lines.

For the definition of the GAP syntax see chapter 2. A complete list of command line editing facilities is found in 3.4. The break loop is described in 3.2.

1.6 About Constants and Operators

In an expression like (9 - 7) * (5 + 6) the constants 5, 6, 7, and 9 are being composed by the operators +, * and - to result in a new value.

There are three kinds of operators in GAP, arithmetical operators, comparison operators, and logical operators. You have already seen that it is possible to form the sum, the difference, and the product of two integer values. There are some more operators applicable to integers in GAP. Of course integers may be divided by each other, possibly resulting in noninteger rational values.

    gap> 12345/25;
    2469/5

Note that the numerator and denominator are divided by their greatest common divisor and that the result is uniquely represented as a division instruction.

We haven’t met negative numbers yet. So consider the following self–explanatory examples.

    gap> -3; 17 - 23;
    -3
    -6

The exponentiation operator is written as ^. This operation in particular might lead to very large numbers. This is no problem for GAP as it can handle numbers of (almost) arbitrary size.

    gap> 3^132;
    955004950796825236893190701774414011919935138974343129836853841

The mod operator allows you to compute one value modulo another.

    gap> 17 mod 3;
    2

Note that there must be whitespace around the keyword mod in this example since 17mod3 or 17mod would be interpreted as identifiers.

GAP knows a precedence between operators that may be overridden by parentheses.

    gap> (9 - 7) * 5 = 9 - 7  * 5;
    false

Besides these arithmetical operators there are comparison operators in GAP. A comparison results in a boolean value which is another kind of constant. Every two objects within GAP are comparable via =, <>, <, <=, > and >=, that is the tests for equality, inequality, less than, less than or equal, greater than and greater than or equal. There is an ordering defined on the set of all GAP objects that respects orders on subsets that one might expect. For example the integers are ordered in the usual way.

    gap> 10^5 < 10^4;
    false

The boolean values true and false can be manipulated via logical operators, i. e., the unary operator not and the binary operators and and or. Of course boolean values can be compared, too.

    gap> not true; true and false; true or false;
    false
    false
    true
    gap> 10 > 0 and 10 < 100;
    true

Another important type of constants in GAP are permutations. They are written in cycle notation and they can be multiplied.

    gap> (1,2,3);
    (1,2,3)
    gap> (1,2,3) * (1,2);
    (2,3)

The inverse of the permutation (1,2,3) is denoted by (1,2,3)^-1. Moreover the caret operator ^is used to determine the image of a point under a permutation and to conjugate one permutation by another.

    gap> (1,2,3)^-1;
    (1,3,2)
    gap> 2^(1,2,3);
    3
    gap> (1,2,3)^(1,2);
    (1,3,2)

The last type of constants we want to introduce here are the characters, which are simply objects in GAP that represent arbitrary characters from the character set of the operating system. Character literals can be entered in GAP by enclosing the character in singlequotes

'.
    gap> 'a';
    'a'
    gap> '*';
    '*'

There are no operators defined for characters except that characters can be compared.

In this section you have seen that values may be preceded by unary operators and combined by binary operators placed between the operands. There are rules for precedence which may be overridden by parentheses. It is possible to compare any two objects. A comparison results in a boolean value. Boolean values are combined via logical operators. Moreover you have seen that GAP handles numbers of arbitrary size. Numbers and boolean values are constants. There are other types of constants in GAP like permutations. You are now in a position to use GAP as a simple desktop calculator.

Operators are explained in more detail in 2.9 and 2.10. Moreover there are sections about operators and comparisons for special types of objects in almost every chapter of this manual. You will find more information about boolean values in chapters 38 and 27. Permutations are described in chapter 19 and characters are described in chapter 28.

1.7 About Variables and Assignments

Values may be assigned to variables. A variable enables you to refer to an object via a name. The name of a variable is called an identifier. The assignment operator is :=. There must be no white space between the : and the =. Do not confuse the assignment operator := with the single equality sign = which is in GAP only used for the test of equality.

    gap> a:= (9 - 7) * (5 + 6);
    22
    gap> a;
    22
    gap> a * (a + 1);

    506
    gap> a:= 10;
    10
    gap> a * (a + 1);
    110

After an assignment the assigned value is echoed on the next line. The printing of the value of a statement may be in every case prevented by typing a double semicolon.

    gap> w:= 2;;

After the assignment the variable evaluates to that value if evaluated. Thus it is possible to refer to that value by the name of the variable in any situation. This is in fact the whole secret of an assignment. An identifier is bound to a value and from this moment points to that value. Nothing more. This binding is changed by the next assignment to that identifier. An identifier does not denote a block of memory as in some other programming languages. It simply points to a value, which has been given its place in memory by the GAP storage manager. This place may change during a GAP session, but that doesn’t bother the identifier. The identifier points to the value, not to a place in the memory. For the same reason it is not the identifier that has a type but the object. This means on the other hand that the identifier a which now is bound to an integer value may in the same session point to any other value regardless of its type. Identifiers may be sequences of letters and digits containing at least one letter. For example abc and a0bc1 are valid identifiers. But also 123a is a valid identifier as it cannot be confused with any number. Just 1234 indicates the number 1234 and cannot be at the same time the name of a variable. Since GAP distinguishes upper and lower case, a1 and A1 are different identifiers. Keywords such as quit must not be used as identifiers. You will see more keywords in the following sections. In the remaining part of this manual we will ignore the difference between variables, their names (identifiers), and the values they point at. It may be useful to think from time to time about what is really meant by terms such as the integer w. There are some predefined variables coming with GAP. Many of them you will find in the remaining chapters of this manual, since functions are also referred to via identifiers. This seems to be the right place to state the following rule. The name of every function in the GAP library starts with a capital letter. Thus if you choose only names starting with a small letter for your own variables you will not overwrite any predefined function. But there are some further interesting variables one of which shall be introduced now. Whenever GAP returns a value by printing it on the next line this value is assigned to the variable last. So if you computed

    gap> (9 - 7) * (5 + 6);
    22

and forgot to assign the value to the variable a for further use, you can still do it by the following assignment.

    gap> a:= last;
    22

Moreover there are variables last2 and last3, guess their values.

In this section you have seen how to assign values to variables. These values can later be accessed through the name of the variable, its identifier. You have also encountered the useful concept of the last variables storing the latest returned values. And you have learned that a double semicolon prevents the result of a statement from being printed.

Variables and assignments are described in more detail in 2.7 and 2.12. A complete list of keywords is contained in 2.4.

1.8 About Functions

A program written in the GAP language is called a function. Functions are special GAP objects. Most of them behave like mathematical functions. They are applied to objects and will return a new object depending on the input. The function Factorial, for example, can be applied to an integer and will return the factorial of this integer.

    gap> Factorial(17);
    355687428096000

Applying a function to arguments means to write the arguments in parentheses following the function. Several arguments are separated by commas, as for the function Gcd which computes the greatest common divisor of two integers.

    gap> Gcd(1234, 5678);
    2

There are other functions that do not return a value but only produce a side effect. They change for example one of their arguments. These functions are sometimes called procedures. The function Print is only called for the side effect to print something on the screen.

    gap> Print(1234, "\n");
    1234

In order to be able to compose arbitrary text with Print, this function itself will not produce a line break after printing. Thus we had another newline character "\n" printed to start a new line.

Some functions will both change an argument and return a value such as the function Sortex that sorts a list and returns the permutation of the list elements that it has performed.

You will not understand right now what it means to change an object. We will return to this subject several times in the next sections.

A comfortable way to define a function is given by the maps–to operator -> consisting of a minus sign and a greater sign with no whitespace between them. The function cubed which maps a number to its cube is defined on the following line.

    gap> cubed:= x -> x^3;
    function ( x ) ... end

After the function has been defined, it can now be applied.

    gap> cubed(5);

Not every GAP function can be defined in this way. You will see how to write your own GAP functions in a later section.

In this section you have seen GAP objects of type function. You have learned how to apply a function to arguments. This yields as result a new object or a side effect. A side effect may change an argument of the function. Moreover you have seen an easy way to define a function in GAP with the maps-to operator.

Function calls are described in 2.8 and in 2.13. The functions of the GAP library are described in detail in the remaining chapters of this manual, the Reference Manual.

1.9 About Lists

A list is a collection of objects separated by commas and enclosed in brackets. Let us for example construct the list primes of the first 10 prime numbers.

    gap> primes:= [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ]

The next two primes are 31 and 37. They may be appended to the existing list by the function Append which takes the existing list as its first and another list as a second argument. The second argument is appended to the list primes and no value is returned. Note that by appending another list the object primes is changed.

    gap> Append(primes, [31, 37]);
    gap> primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 ]

You can as well add single new elements to existing lists by the function Add which takes the existing list as its first argument and a new element as its second argument. The new element is added to the list primes and again no value is returned but the list primes is changed.

    gap> Add(primes, 41);
    gap> primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 ]

Single elements of a list are referred to by their position in the list. To get the value of the seventh prime, that is the seventh entry in our list primes, you simply type

    gap> primes[7];
    17

and you will get the value of the seventh prime. This value can be handled like any other value, for example multiplied by 2 or assigned to a variable. On the other hand this mechanism allows to assign a value to a position in a list. So the next prime 43 may be inserted in the list directly after the last occupied position of primes. This last occupied position is returned by the function Length.

    gap> Length(primes);
    13
    gap> primes[14]:= 43;
    43
    gap> primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43 ]

Note that this operation again has changed the object primes. Not only the next position of a list is capable of taking a new value. If you know that 71 is the 20th prime, you can as well enter it right now in the 20th position of primes. This will result in a list with holes which is however still a list and has length 20 now.

    gap> primes[20]:= 71;
    71
    gap> primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,,,,,, 71 ]
    gap> Length(primes);
    20

The list itself however must exist before a value can be assigned to a position of the list. This list may be the empty list [ ].

    gap> lll[1]:= 2;
    Error, Variable: 'lll' must have a value
    gap> lll:= [];
    [  ]
    gap> lll[1]:= 2;
    2

Of course existing entries of a list can be changed by this mechanism, too. We will not do it here because primes then may no longer be a list of primes. Try for yourself to change the 17 in the list into a 9.

To get the position of 17 in the list primes use the function Position which takes the list as its first argument and the element as its second argument and returns the position of the first occurrence of the element 17 in the list primes. Position will return false if the element is not contained in the list.

    gap> Position(primes, 17);
    7
    gap> Position(primes, 20);
    false

In all of the above changes to the list primes, the list has been automatically resized. There is no need for you to tell GAP how big you want a list to be. This is all done dynamically.

It is not necessary for the objects collected in a list to be of the same type.

    gap> lll:= [true, "This is a String",,, 3];
    [ true, "This is a String",,, 3 ]

In the same way a list may be part of another list. A list may even be part of itself.

    gap> lll[3]:= [4,5,6];; lll;
    [ true, "This is a String", [ 4, 5, 6 ],, 3 ]
    gap> lll[4]:= lll;
    [ true, "This is a String", [ 4, 5, 6 ], ~, 3 ]

Now the tilde ~ in the fourth position of lll denotes the object that is currently printed. Note that the result of the last operation is the actual value of the object lll on the right hand side of the assignment. But in fact it is identical to the value of the whole list lll on the left hand side of the assignment.

A string is a very special type of list, which is printed in a different way. A string is simply a dense list of characters. Strings are used mainly in filenames and error messages. A string literal can either be entered simply as the list of characters or by writing the characters between doublequotes ". GAP will always output strings in the latter format.

    gap> s1 := ['H','a','l','l','o',' ','w','o','r','l','d','.'];
    "Hallo world."
    gap> s2 := "Hallo world.";
    "Hallo world."
    gap> s1 := ['H','a','l','l','o',' ','w','o','r','l','d','.'];
    "Hallo world."
    gap> s1 = s2;
    true
    gap> s2[7];
    'w'

Sublists of lists can easily be extracted and assigned using the operator { }.

    gap> sl := lll{ [ 1, 2, 3 ] };
    [ true, "This is a String", [ 4, 5, 6 ] ]
    gap> sl{ [ 2, 3 ] } := [ "New String", false ];
    [ "New String", false ]
    gap> sl;
    [ true, "New String", false ]

This way you get a new list that contains at position i that element whose position is the ith entry of the argument of { }.

In this long section you have encountered the fundamental concept of a list. You have seen how to construct lists, how to extend them and how to refer to single elements of a list. Moreover you have seen that lists may contain elements of different types, even holes (unbound entries). But this is still not all we have to tell you about lists.

You will find a discussion about identity and equality of lists in the next section. Moreover you will see special kinds of lists like sets (in 1.11), vectors and matrices (in 1.12) and ranges (in 1.14). Strings are described in chapter 28.

1.10 About Identical Lists

This second section about lists is dedicated to the subtle difference between equality and identity of lists. It is really important to understand this difference in order to understand how complex data structures are realized in GAP. This section applies to all GAP objects that have subobjects, i. e., to lists and to records. After reading the section about records (1.13) you should return to this section and translate it into the record context.

Two lists are equal if all their entries are equal. This means that the equality operator = returns true for the comparison of two lists if and only if these two lists are of the same length and for each position the values in the respective lists are equal.

    gap> numbers:= primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,,,,,, 71 ]
    gap> numbers = primes;
    true

We assigned the list primes to the variable numbers and, of course they are equal as they have both the same length and the same entries. Now we will change the third number to 4 and compare the result again with primes.

    gap> numbers[3]:= 4;
    4
    gap> numbers = primes;
    true

You see that numbers and primes are still equal, check this by printing the value of primes. The list primes is no longer a list of primes! What has happened? The truth is that the lists primes and numbers are not only equal but they are identical. primes and numbers are two variables pointing to the same list. If you change the value of the subobject numbers[3] of numbers this will also change primes. Variables do not point to a certain block of storage memory but they do point to an object that occupies storage memory. So the assignment numbers:= primes did not create a new list in a different place of memory but only created the new name numbers for the same old list of primes.

The same object can have several names.

If you want to change a list with the contents of primes independently from primes you will have to make a copy of primes by the function Copy which takes an object as its argument and returns a copy of the argument. (We will first restore the old value of primes.)

    gap> primes[3]:= 5;
    5
    gap> primes;
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,,,,,, 71 ]
    gap> numbers:= Copy(primes);
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,,,,,, 71 ]
    gap> numbers = primes;
    true
    gap> numbers[3]:= 4;
    4
    gap> numbers = primes;
    false

Now numbers is no longer equal to primes and primes still is a list of primes. Check this by printing the values of numbers and primes.

The only objects that can be changed this way are records and lists, because only GAP objects of these types have subobjects. To clarify this statement consider the following example.

    gap> i:= 1;; j:= i;; i:= i+1;;

By adding 1 to i the value of i has changed. What happens to j? After the second statement j points to the same object as i, namely to the integer 1. The addition does not change the object 1 but creates a new object according to the instruction i+1. It is actually the assignment that changes the value of i. Therefore j still points to the object 1. Integers (like permutations and booleans) have no subobjects. Objects of these types cannot be changed but can only be replaced by other objects. And a replacement does not change the values of other variables. In the above example an assignment of a new value to the variable numbers would also not change the value of primes.

Finally try the following examples and explain the results.

    gap> l:= [];
    [  ]
    gap> l:= [l];
    [ [  ] ]
    gap> l[1]:= l;
    [ ~ ]

Now return to the preceding section 1.9 and find out whether the functions Add and Append change their arguments. In this section you have seen the difference between equal lists and identical lists. Lists are objects that have subobjects and therefore can be changed. Changing an object will change the values of all variables that point to that object. Be careful, since one object can have several names. The function Copy creates a copy of a list which is then a new object. You will find more about lists in chapter 25, and more about identical lists in 25.8.

1.11 About Sets

GAP knows several special kinds of lists. A set in GAP is a special kind of list. A set contains no holes and its elements are sorted according to the GAP ordering of all its objects. Moreover a set contains no object twice. The function IsSet tests whether an object is a set. It returns a boolean value. For any list there exists a corresponding set. This set is constructed by the function Set which takes the list as its argument and returns a set obtained from this list by ignoring holes and duplicates and by sorting the elements. The elements of the sets used in the examples of this section are strings.

    gap> fruits:= ["apple", "strawberry", "cherry", "plum"];
    [ "apple", "strawberry", "cherry", "plum" ]
    gap> IsSet(fruits);
    false
    gap> fruits:= Set(fruits);
    [ "apple", "cherry", "plum", "strawberry" ]

Note that the original list fruits is not changed by the function Set. We have to make a new assignment to the variable fruits in order to make it a set. The in operator is used to test whether an object is an element of a set. It returns a boolean

value true or false.
    gap> "apple" in fruits;
    true
    gap> "banana" in fruits;
    false

The in operator may as well be applied to ordinary lists. It is however much faster to perform a membership test for sets since sets are always sorted and a binary search can be used instead of a linear search. New elements may be added to a set by the function AddSet which takes the set fruits as its first argument and an element as its second argument and adds the element to the set if it wasn’t already there. Note that the object fruits is changed.

    gap> AddSet(fruits, "banana");

gap> fruits; # The banana is inserted in the right place.

    [ "apple", "banana", "cherry", "plum", "strawberry" ]
    gap> AddSet(fruits, "apple");
    gap> fruits;  # fruits has not changed.
    [ "apple", "banana", "cherry", "plum", "strawberry" ]

Sets can be intersected by the function Intersection and united by the function Union which both take two sets as their arguments and return the intersection (union) of the two sets as a new object.

    gap> breakfast:= ["tea", "apple", "egg"];
    [ "tea", "apple", "egg" ]
    gap> Intersection(breakfast, fruits);
    [ "apple" ]

It is however not necessary for the objects collected in a set to be of the same type. You may as well have additional integers and boolean values for breakfast. The arguments of the functions Intersection and Union may as well be ordinary lists, while their result is always a set. Note that in the preceding example at least one argument of Intersection was not a set. The functions IntersectSet and UniteSet also form the intersection resp. union of two sets. They will however not return the result but change their first argument to be the result. Try them carefully. In this section you have seen that sets are a special kind of list. There are functions to expand sets, intersect or unite sets, and there is the membership test with the in operator. A more detailed description of strings is contained in chapter 28. Sets are described in more detail in chapter 26.

1.12 About Vectors and Matrices

A vector is a list of elements from a common field. A matrix is a list of vectors of equal length. Vectors and matrices are special kinds of lists without holes.

    gap> v:= [3, 6, 2, 5/2];
    [ 3, 6, 2, 5/2 ]
    gap> IsVector(v);
    true

Vectors may be multiplied by scalars from their field. Multiplication of vectors of equal length results in their scalar product.

    gap> 2 * v;
    [ 6, 12, 4, 5 ]
    gap> v * 1/3;
    [ 1, 2, 2/3, 5/6 ]
    gap> v * v;

221/4 # the scalar product of v with itself Note that the expression v * 1/3 is actually evaluated by first multiplying v by 1 (which yields again v) and by then dividing by 3. This is also an allowed scalar operation. The expression v/3 would result in the same value.

A matrix is a list of vectors of equal length.

gap> m:= [[1, -1, 1],
>         [2, 0, -1],
>         [1, 1, 1]];
[ [ 1, -1, 1 ], [ 2, 0, -1 ], [ 1, 1, 1 ] ]
gap> m[2][1];
2

Syntactically a matrix is a list of lists. So the number 2 in the second row and the first column of the matrix m is referred to as the first element of the second element of the list m

via m[2][1].

A matrix may be multiplied by scalars, vectors and other matrices. The vectors and matrices involved in such a multiplication must however have suitable dimensions.

gap> m:= [[1, 2, 3, 4],
>         [5, 6, 7, 8],
>         [9,10,11,12]];
[ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ] ]
gap> PrintArray(m);
[ [   1,   2,   3,   4 ],
  [   5,   6,   7,   8 ],
  [   9,  10,  11,  12 ] ]
gap> [1, 0, 0, 0] * m;
Error, Vector *: vectors must have the same length
gap> [1, 0, 0] * m;
[ 1, 2, 3, 4 ]
gap> m * [1, 0, 0];
Error, Vector *: vectors must have the same length
gap> m * [1, 0, 0, 0];
[ 1, 5, 9 ]
gap> m * [0, 1, 0, 0];
[ 2, 6, 10 ]

Note that multiplication of a vector with a matrix will result in a linear combination of the rows of the matrix, while multiplication of a matrix with a vector results in a linear combination of the columns of the matrix. In the latter case the vector is considered as a column vector.

Submatrices can easily be extracted and assigned using the { }{ } operator.

gap> sm := m{ [ 1, 2 ] }{ [ 3, 4 ] };
[ [ 3, 4 ], [ 7, 8 ] ]
gap> sm{ [ 1, 2 ] }{ [2] } := [[1],[-1]];
[ [ 1 ], [ -1 ] ]
gap> sm;
[ [ 3, 1 ], [ 7, -1 ] ]

The first curly brackets contain the selection of rows, the second that of columns.

In this section you have met vectors and matrices as special lists. You have seen how to refer to elements of a matrix and how to multiply scalars, vectors, and matrices.

Fields are described in chapter 6. The known fields in GAP are described in chapters 12, 13, 14, 15 and 17. Vectors and matrices are described in more detail in chapters 30 and 32. Vector spaces are described in chapter 9 and further matrix related structures are described in chapters 33 and 34.

1.13 About Records

A record provides another way to build new data structures. Like a list a record is a collection of other objects. In a record the elements are not indexed by numbers but by names (i.e., identifiers). An entry in a record is called a record component (or sometimes also record field).

    gap> date:= rec(year:= 1992,
    >               month:= "Jan",
    >               day:= 13);
    rec(
      year := 1992,
      month := "Jan",
      day := 13 )

Initially a record is defined as a comma separated list of assignments to its record components. Then the value of a record component is accessible by the record name and the record component name separated by one dot as the record component selector.

    gap> date.year;
    1992
    gap> date.time:= rec(hour:= 19, minute:= 23, second:= 12);
    rec(
      hour := 19,
      minute := 23,
      second := 12 )
    gap> date;
    rec(
      year := 1992,
      month := "Jan",
      day := 13,
      time := rec(
          hour := 19,
          minute := 23,
          second := 12 ) )

Assignments to new record components are possible in the same way. The record is automatically resized to hold the new component.

Most of the complex structures that are handled by GAP are represented as records, for instance groups and character tables.

Records are objects that may be changed. An assignment to a record component changes the original object. There are many functions in the library that will do such assignments to a record component of one of their arguments. The function Size for example, will compute the size of its argument which may be a group for instance, and then store the value in the record component size. The next call of Size for this object will use this stored value rather than compute it again.

Lists and records are the only types of GAP objects that can be changed.

Sometimes it is interesting to know which components of a certain record are bound. This information is available from the function RecFields (yes, this function should be called RecComponentNames), which takes a record as its argument and returns a list of all bound components of this record as a list of strings.

    gap> RecFields(date);
    [ "year", "month", "day", "time" ]

Finally try the following examples and explain the results.

    gap> r:= rec();
    rec(
       )
    gap> r:= rec(r:= r);
    rec(
      r := rec(
           ) )
    gap> r.r:= r;
    rec(
      r := ~ )

Now return to section 1.10 and find out what that section means for records.

In this section you have seen how to define and how to use records. Record objects are changed by assignments to record fields. Lists and records are the only types of objects that can be changed.

Records and functions for records are described in detail in chapter 39. More about identical records is found in 39.3.

1.14 About Ranges

A range is a finite sequence of integers. This is another special kind of list. A range is described by its minimum (the first entry), its second entry and its maximum, separated by a comma resp. two dots and enclosed in brackets. In the usual case of an ascending list of consecutive integers the second entry may be omitted.

gap> [1..999999]; # a range of almost a million numbers

    [ 1 .. 999999 ]
    gap> [1, 2..999999]; # this is equivalent
    [ 1 .. 999999 ]
    gap> [1, 3..999999]; # here the step is 2
    [ 1, 3 .. 999999 ]
    gap> Length( last );
    500000
    gap> [ 999999, 999997 .. 1 ];
    [ 999999, 999997 .. 1 ]

This compact printed representation of a fairly long list corresponds to a compact internal representation. The function IsRange tests whether an object is a range. If this is true for a list but the list is not yet represented in the compact form of a range this will be done then.

    gap> a:= [-2,-1,0,1,2,3,4,5];
    [ -2, -1, 0, 1, 2, 3, 4, 5 ]
    gap> IsRange(a);
    true
    gap> a;
    [ -2 .. 5 ]
    gap> a[5];
    2
    gap> Length(a);
    8

Note that this change of representation does not change the value of the list a. The list a still behaves in any context in the same way as it would have in the long representation.

In this section you have seen that ascending lists of consecutive integers can be represented in a compact way as ranges.

Chapter 29 contains a detailed description of ranges. A fundamental application of ranges is introduced in the next section.

1.15 About Loops

Given a list pp of permutations we can form their product by means of a for loop instead of writing down the product explicitly.

    gap> pp:= [ (1,3,2,6,8)(4,5,9), (1,6)(2,7,8)(4,9), (1,5,7)(2,3,8,6),
    >           (1,8,9)(2,3,5,6,4), (1,9,8,6,3,4,7,2) ];;
    gap> prod:= ();
    ()
    gap> for p in pp do
    >       prod:= prod * p;
    >    od;
    gap> prod;
    (1,8,4,2,3,6,5)

First a new variable prod is initialized to the identity permutation (). Then the loop variable p takes as its value one permutation after the other from the list pp and is multiplied with the present value of prod resulting in a new value which is then assigned to prod.

The for loop has the following syntax.

for var in list do statements od;

The effect of the for loop is to execute the statements for every element of the list. A for loop is a statement and therefore terminated by a semicolon. The list of statements is enclosed by the keywords do and od (reverse do). A for loop returns no value. Therefore we had to ask explicitly for the value of prod in the preceding example.

The for loop can loop over any kind of list, even a list with holes. In many programming languages (and in former versions of GAP, too) the for loop has the form

for var from first to last do statements od; But this is merely a special case of the general for loop as defined above where the list in the loop body is a range.

for var in [first..last] do statements od;

You can for instance loop over a range to compute the factorial 15! of the number 15 in the following way.

    gap> ff:= 1;
    1
    gap> for i in [1..15] do
    >       ff:= ff * i;
    >    od;
    gap> ff;
    1307674368000

The following example introduces the while loop which has the following syntax.

while condition do statements od;

The while loop loops over the statements as long as the condition evaluates to true. Like the for loop the while loop is terminated by the keyword od followed by a semicolon.

We can use our list primes to perform a very simple factorization. We begin by initializing a list factors to the empty list. In this list we want to collect the prime factors of the number 1333. Remember that a list has to exist before any values can be assigned to positions of the list. Then we will loop over the list primes and test for each prime whether it divides the number. If it does we will divide the number by that prime, add it to the list factors and continue.

    gap> n:= 1333;
    1333
    gap> factors:= [];
    [  ]
    gap> for p in primes do
    >       while n mod p = 0 do
    >          n:= n/p;
    >          Add(factors, p);
    >       od;
    >    od;
    gap> factors;
    [ 31, 43 ]
    gap> n;
    1

As n now has the value 1 all prime factors of 1333 have been found and factors contains a complete factorization of 1333. This can of course be verified by multiplying 31 and 43.

This loop may be applied to arbitrary numbers in order to find prime factors. But as primes is not a complete list of all primes this loop may fail to find all prime factors of a number greater than 2000, say. You can try to improve it in such a way that new primes are added to the list primes if needed.

You have already seen that list objects may be changed. This holds of course also for the list in a loop body. In most cases you have to be careful not to change this list, but there are situations where this is quite useful. The following example shows a quick way to determine the primes smaller than 1000 by a sieve method. Here we will make use of the function Unbind to delete entries from a list.

    gap> primes:= [];
    [  ]
    gap> numbers:= [2..1000];
    [ 2 .. 1000 ]
    gap> for p in numbers do
    >       Add(primes, p);
    >       for n in numbers do
    >          if n mod p = 0 then
    >             Unbind(numbers[n-1]);
    >          fi;
    >       od;
    >    od;

The inner loop removes all entries from numbers that are divisible by the last detected prime p. This is done by the function Unbind which deletes the binding of the list position numbers[n-1] to the value n so that afterwards numbers[n-1] no longer has an assigned value. The next element encountered in numbers by the outer loop necessarily is the next prime.

In a similar way it is possible to enlarge the list which is looped over. This yields a nice and short orbit algorithm for the action of a group, for example.

In this section you have learned how to loop over a list by the for loop and how to loop with respect to a logical condition with the while loop. You have seen that even the list in the loop body can be changed.

The for loop is described in 2.17. The while loop is described in 2.15.

1.16 About Further List Operations

There is however a more comfortable way to compute the product of a list of numbers or permutations.

    gap> Product([1..15]);
    1307674368000
    gap> Product(pp);
    (1,8,4,2,3,6,5)

The function Product takes a list as its argument and computes the product of the elements of the list. This is possible whenever a multiplication of the elements of the list is defined. So Product is just an implementation of the loop in the example above as a function.

There are other often used loops available as functions. Guess what the function Sum does. The function List may take a list and a function as its arguments. It will then apply the function to each element of the list and return the corresponding list of results. A list of cubes is produced as follows with the function cubed from 1.8.

    gap> List([2..10], cubed);
    [ 8, 27, 64, 125, 216, 343, 512, 729, 1000 ]

To add all these cubes we might apply the function Sum to the last list. But we may as well give the function cubed to Sum as an additional argument.

    gap> Sum(last) = Sum([2..10], cubed);
    true

The primes less than 30 can be retrieved out of the list primes from section 1.9 by the function Filtered. This function takes the list primes and a property as its arguments and will return the list of those elements of primes which have this property. Such a property will be represented by a function that returns a boolean value. In this example the property of being less than 30 can be reresented by the function x-> x <30 since x <30 will evaluate to true for values x less than 30 and to false otherwise.

    gap> Filtered(primes, x-> x < 30);
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ]

Another useful thing is the operator { } that forms sublists. It takes a list of positions as its argument and will return the list of elements from the original list corresponding to these positions.

    gap> primes{ [1 .. 10] };
    [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ]

In this section you have seen some functions which implement often used for loops. There are functions like Product to form the product of the elements of a list. The function List can apply a function to all elements of a list and the functions Filtered and Sublist create sublists of a given list.

You will find more predefined for loops in chapter 25.

1.17 About Writing Functions

You have already seen how to use the functions of the GAP library, i.e., how to apply them to arguments. This section will show you how to write your own functions.

Writing a function that prints hello, world. on the screen is a simple exercise in GAP.

    gap> sayhello:= function()
    > Print("hello, world.\n");
    > end;
    function (  ) ... end

This function when called will only execute the Print statement in the second line. This will print the string hello, world. on the screen followed by a newline character \n that causes the GAP prompt to appear on the next line rather than immediately following the printed characters.

The function definition has the following syntax.

function(arguments) statements end

A function definition starts with the keyword function followed by the formal parameter list arguments enclosed in parenthesis. The formal parameter list may be empty as in the example. Several parameters are separated by commas. Note that there must be no semicolon behind the closing parenthesis. The function definition is terminated by the keyword end.

A GAP function is an expression like integers, sums and lists. It therefore may be assigned to a variable. The terminating semicolon in the example does not belong to the function definition but terminates the assignment of the function to the name sayhello. Unlike in the case of integers, sums, and lists the value of the function sayhello is echoed in the abbreviated fashion function ( ) ... end. This shows the most interesting part of a function: its formal parameter list (which is empty in this example). The complete value of sayhello is returned if you use the function Print.

    gap> Print(sayhello, "\n");
    function (  )
        Print( "hello, world.\n" );
    end

Note the additional newline character "\n" in the Print statement. It is printed after the object sayhello to start a new line. The newly defined function sayhello is executed by calling sayhello() with an empty argument list.

    gap> sayhello();
    hello, world.

This is however not a typical example as no value is returned but only a string is printed. A more useful function is given in the following example. We define a function sign which shall determine the sign of a number.

    gap> sign:= function(n)
    >        if n < 0 then
    >           return -1;
    >        elif n = 0 then
    >           return 0;
    >        else
    >           return 1;
    >        fi;
    >    end;
    function ( n ) ... end
    gap> sign(0); sign(-99); sign(11);
    0
    -1
    1
    gap> sign("abc");

1 # strings are defined to be greater than 0 This example also introduces the if statement which is used to execute statements depending on a condition. The if statement has the following syntax. if condition then statements elif condition then statements else statements fi; There may be several elif parts. The elif part as well as the else part of the if statement may be omitted. An if statement is no expression and can therefore not be assigned to a variable. Furthermore an if statement does not return a value. Fibonacci numbers are defined recursively by f(1) = f(2) = 1 and f(n) = f(n − 1) + f(n − 2). Since functions in GAP may call themselves, a function fib that computes Fibonacci numbers can be implemented basically by typing the above equations.

    gap> fib:= function(n)
    >       if n in [1, 2] then
    >          return 1;
    >       else
    >          return fib(n-1) + fib(n-2);
    >       fi;
    >    end;
    function ( n ) ... end
    gap> fib(15);
    610

There should be additional tests for the argument n being a positive integer. This function fib might lead to strange results if called with other arguments. Try to insert the tests in this example.

A function gcd that computes the greatest common divisor of two integers by Euclid’s algorithm will need a variable in addition to the formal arguments.

    gap> gcd:= function(a, b)
    >       local c;
    >       while b <> 0 do
    >          c:= b;
    >          b:= a mod b;
    >          a:= c;
    >       od;
    >       return c;
    >    end;
    function ( a, b ) ... end
    gap> gcd(30, 63);
    3

The additional variable c is declared as a local variable in the local statement of the function definition. The local statement, if present, must be the first statement of a function definition. When several local variables are declared in only one local statement they are separated by commas.

The variable c is indeed a local variable, that is local to the function gcd. If you try to use the value of c in the main loop you will see that c has no assigned value unless you have already assigned a value to the variable c in the main loop. In this case the local nature of c in the function gcd prevents the value of the c in the main loop from being overwritten.

We say that in a given scope an identifier identifies a unique variable. A scope is a lexical part of a program text. There is the global scope that encloses the entire program text, and there are local scopes that range from the function keyword, denoting the beginning of a function definition, to the corresponding end keyword. A local scope introduces new variables, whose identifiers are given in the formal argument list and the local declaration of the function. The usage of an identifier in a program text refers to the variable in the innermost scope that has this identifier as its name.

We will now write a function to determine the number of partitions of a positive integer. A partition of a positive integer is a descending list of numbers whose sum is the given integer. For example [4, 2, 1, 1] is a partition of 8. The complete set of all partitions of an integer n may be divided into subsets with respect to the largest element. The number of partitions of n therefore equals the sum of the numbers of partitions of n − i with elements less than i for all possible i. More generally the number of partitions of n with elements less than m is the sum of the numbers of partitions of n − i with elements less than i for i less than m and n. This description yields the following function.

    gap> nrparts:= function(n)
    >    local np;
    >    np:= function(n, m)
    >       local i, res;
    >       if n = 0 then
    >          return 1;
    >       fi;
    >       res:= 0;
    >       for i in [1..Minimum(n,m)] do
    >          res:= res + np(n-i, i);
    >       od;
    >       return res;
    >    end;
    >    return np(n,n);
    > end;
    function ( n ) ... end

We wanted to write a function that takes one argument. We solved the problem of determining the number of partitions in terms of a recursive procedure with two arguments. So we had to write in fact two functions. The function nrparts that can be used to compute the number of partitions takes indeed only one argument. The function np takes two arguments and solves the problem in the indicated way. The only task of the function nrparts is to call np with two equal arguments.

We made np local to nrparts. This illustrates the possibility of having local functions in GAP. It is however not necessary to put it there. np could as well be defined on the main level. But then the identifier np would be bound and could not be used for other purposes. And if it were used the essential function np would no longer be available for nrparts.

Now have a look at the function np. It has two local variables res and i. The variable res is used to collect the sum and i is a loop variable. In the loop the function np calls itself again with other arguments. It would be very disturbing if this call of np would use the same i and res as the calling np. Since the new call of np creates a new scope with new variables this is fortunately not the case.

The formal parameters n and m are treated like local variables.

It is however cheaper (in terms of computing time) to avoid such a recursive solution if this is possible (and it is possible in this case), because a function call is not very cheap.

In this section you have seen how to write functions in the GAP language. You have also seen how to use the if statement. Functions may have local variables which are declared in an initial local statement in the function definition. Functions may call themselves.

The function syntax is described in 2.18. The if statement is described in more detail in 2.14. More about Fibonacci numbers is found in 40.21 and more about partitions in 40.14.

1.18 About Groups

In this section we will show some easy computations with groups. The example uses permutation groups, but this is visible for the user only because the output contains permutations. The functions, like Group, Size or SylowSubgroup (for detailed information, see chapters 4, 7), are the same for all kinds of groups, although the algorithms which compute the information of course will be different in most cases.

It is not even necessary to know more about permutations than the two facts that they are elements of permutation groups and that they are written in disjoint cycle notation (see chapter 19). So let’s construct a permutation group:

    gap> s8:= Group( (1,2), (1,2,3,4,5,6,7,8) );
    Group( (1,2), (1,2,3,4,5,6,7,8) )

We formed the group generated by the permutations (1,2) and (1,2,3,4,5,6,7,8), which is well known as the symmetric group on eight points, and assigned it to the identifier s8. s8 contains the alternating group on eight points which can be described in several ways, e.g., as group of all even permutations in s8, or as its commutator subgroup.

    gap> a8:= CommutatorSubgroup( s8, s8 );
    Subgroup( Group( (1,2), (1,2,3,4,5,6,7,8) ),
    [ (1,3,2), (2,4,3), (3,5,4), (4,6,5), (5,7,6), (6,8,7) ] )

The alternating group a8 is printed as instruction to compute that subgroup of the group s8 that is generated by the given six permutations. This representation is much shorter than the internal structure, and it is completely self–explanatory; one could, for example, print such a group to a file and read it into GAP later. But if one object occurs several times it is useful to refer to this object; this can be settled by assigning a name to the group.

    gap> s8.name:= "s8";
    "s8"
    gap> a8;
    Subgroup( s8, [ (1,3,2), (2,4,3), (3,5,4), (4,6,5), (5,7,6), (6,8,7)
     ] )
    gap> a8.name:= "a8";
    "a8"
    gap> a8;
    a8

Whenever a group has a component name, GAP prints this name instead of the group itself. Note that there is no link between the name and the identifier, but it is of course useful to choose name and identifier compatible.

    gap> copya8:= Copy( a8 );
    a8

We examine the group a8. Like all complex GAP structures, it is represented as a record (see 7.111).

    gap> RecFields( a8 );
    [ "isDomain", "isGroup", "parent", "identity", "generators",
      "operations", "isPermGroup", "1", "2", "3", "4", "5", "orbit",
      "transversal", "stabilizer", "name" ]

Many functions store information about the group in this group record, this avoids duplicate computations. But we are not interested in the organisation of data but in the group, e.g., some of its properties (see chapter 7, especially 7.43):

gap> Size( a8 ); IsAbelian( a8 ); IsPerfect( a8 );
20160
false
true

Some interesting subgroups are the Sylow p subgroups for prime divisors p of the group order; a call of SylowSubgroup stores the required subgroup in the group record:

gap> Set( Factors( Size( a8 ) ) );
[ 2, 3, 5, 7 ]
gap> for p in last do
>      SylowSubgroup( a8, p );
>    od;
gap> a8.sylowSubgroups;
[ , Subgroup( s8, [ (1,7)(5,6), (2,3)(5,6), (1,7)(4,8), (2,5)(3,6),
      (1,4)(2,5)(3,6)(7,8), (1,2)(3,7)(4,5)(6,8) ] ),
  Subgroup( s8, [ (3,6,4), (2,5,8)(3,4,6) ] ),,
  Subgroup( s8, [ (3,4,8,6,5) ] ),,
  Subgroup( s8, [ (2,6,3,8,4,7,5) ] ) ]

The record component sylowSubgroups is a list which stores at the p–th position, if bound, the Sylow p subgroup; in this example this means that there are holes at positions 1, 4 and 6. Note that a call of SylowSubgroup for the cyclic group of order 65521 and for the prime 65521 would cause GAP to store the group at the end of a list of length 65521, so there are special situations where it is possible to bring GAP and yourselves into troubles.

We now can investigate the Sylow 2 subgroup.

gap> syl2:= last[2];;
gap> Size( syl2 );
64
gap> Normalizer( a8, syl2 );
Subgroup( s8, [ (1,7)(5,6), (2,3)(5,6), (1,7)(4,8), (2,5)(3,6),
  (1,4)(2,5)(3,6)(7,8), (1,2)(3,7)(4,5)(6,8), (4,8)(5,6) ] )
gap> last = syl2;
true
gap> Centre( syl2 );
Subgroup( s8, [ ( 1, 7)( 2, 3)( 4, 8)( 5, 6) ] )
gap> cent:= Centralizer( a8, last );
Subgroup( s8, [ ( 1, 7)( 2, 3)( 4, 8)( 5, 6), (4,5)(6,8), (4,6)(5,8),
  (2,3)(5,6), (2,4)(3,8), (1,2)(3,7) ] )
gap> Size( cent );
192
gap> DerivedSeries( cent );
[ Subgroup( s8, [ ( 1, 7)( 2, 3)( 4, 8)( 5, 6), (4,5)(6,8),
      (4,6)(5,8), (2,3)(5,6), (2,4)(3,8), (1,2)(3,7) ] ),
  Subgroup( s8, [ (4,8)(5,6), (2,4,5)(3,8,6), (1,7)(2,3),
      (1,2,4)(3,8,7) ] ),

  Subgroup( s8, [ (2,3)(4,8), (1,7)(4,8), (1,5)(2,4)(3,8)(6,7),
      (1,2)(3,7)(4,5)(6,8) ] ),
  Subgroup( s8, [ (1,7)(2,3)(4,8)(5,6) ] ), Subgroup( s8, [  ] ) ]
gap> List( last, Size );
[ 192, 96, 32, 2, 1 ]
gap> low:= LowerCentralSeries( cent );
[ Subgroup( s8, [ ( 1, 7)( 2, 3)( 4, 8)( 5, 6), (4,5)(6,8),
      (4,6)(5,8), (2,3)(5,6), (2,4)(3,8), (1,2)(3,7) ] ),
  Subgroup( s8, [ (4,8)(5,6), (2,4,5)(3,8,6), (1,7)(2,3),
      (1,2,4)(3,8,7) ] ) ]

Another kind of subgroups is given by the point stabilizers.

gap> stab:= Stabilizer( a8, 1 );
Subgroup( s8, [ (2,7,3), (2,4,3), (3,8,4), (4,6,8), (5,6,8) ] )
gap> Size( stab );
2520
gap> Index( a8, stab );
8

We can fetch an arbitrary group element and look at its centralizer in a8, and then get other subgroups by conjugation and intersection of already known subgroups. Note that we form the subgroups inside a8, but GAP regards these groups as subgroups of s8 because this is the common “parent” group of all these groups and of a8 (for the idea of parent groups, see 7.6).

gap> Random( a8 );
(1,4,3,8,2,5,7)
gap> Random( a8 );
(1,5,7,6,2,4)(3,8)
gap> cent:= Centralizer( a8, (1,2)(3,4)(5,8)(6,7) );
Subgroup( s8, [ (1,2)(3,4)(5,8)(6,7), (5,6)(7,8), (5,7)(6,8),
  (3,4)(6,7), (3,5)(4,8), (1,3)(2,4) ] )
gap> Size( cent );
192
gap> conj:= ConjugateSubgroup( cent, (2,3,4) );
Subgroup( s8, [ (1,3)(2,4)(5,8)(6,7), (5,6)(7,8), (5,7)(6,8),
  (2,4)(6,7), (2,8)(4,5), (1,4)(2,3) ] )
gap> inter:= Intersection( cent, conj );
Subgroup( s8, [ (5,6)(7,8), (5,7)(6,8), (1,2)(3,4), (1,3)(2,4) ] )
gap> Size( inter );
16
gap> IsElementaryAbelian( inter );
true
gap> norm:= Normalizer( a8, inter );
Subgroup( s8, [ (5,6)(7,8), (5,7)(6,8), (1,2)(3,4), (1,3)(2,4),
  (6,7,8), (3,4)(7,8), (2,3)(7,8), (1,5)(2,6)(3,7)(4,8) ] )
gap> Size( norm );
576

Suppose we do not only look which funny things may appear in our group but want to construct a subgroup, e.g., a group of structure 2^{3} : L_{3}(2) in a8. One idea is to look for an appropriate 2^{3} which is specified by the fact that all its involutions are fixed point free, and then compute its normalizer in a8:

gap> elab:= Group( (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8),
>                  (1,5)(2,6)(3,7)(4,8) );;
gap> Size( elab );
8
gap> IsElementaryAbelian( elab );
true
gap> norm:= Normalizer( a8, AsSubgroup( s8, elab ) );
Subgroup( s8, [ (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8),
  (1,5)(2,6)(3,7)(4,8), (5,6)(7,8), (5,7)(6,8), (3,4)(7,8),
  (3,5)(4,6), (2,3)(6,7) ] )
gap> Size( norm );
1344

Note that elab was defined as separate group, thus we had to call AsSubgroup to achieve that it has the same parent group as a8. Let’s look at some usual misuses:

gap> Normalizer( a8, elab );
Error, <G> and <H> must have the same parent group in
arg[1].operations.Parent( arg ) called from
Parent( G, U ) called from
Normalizer( a8, elab ) called from
main loop
brk> quit;

Intuitively, it is clear that we wanted to compute the normalizer of elab in a8. But GAP expects the subgroup to be explicitly given as subgroup; for that, we must apply AsSubgroup.

gap> IsSubgroup( a8, AsSubgroup( a8, elab ) );
Error, <G> must be a parent group in
AsSubgroup( a8, elab ) called from
main loop
brk> quit;
gap> IsSubgroup( a8, AsSubgroup( s8, elab ) );
true

What we tried next was better but not good enough. Since all our computations up to now are done inside s8 which is the parent of a8, it is easy to understand that IsSubgroup works for two subgroups with this parent.

By the way, you should not try the operator < instead of the function IsSubgroup. Something like

gap> elab < a8;
false

or

gap> AsSubgroup( s8, elab ) < a8;
false

will not cause an error, but the result does not tell anything about the inclusion of one group in another; < looks at the element lists for the two domains which means that it computes them if they are not already stored –which is not desirable to do for large groups– and then simply compares the lists with respect to lexicographical order (see 4.7).

On the other hand, the equality operator = in fact does test the equality of groups. Thus

gap> elab = AsSubgroup( s8, elab );
true

means that the two groups are equal in the sense that they have the same elements. Note that they may behave differently since they have different parent groups. In our example, it is necessary to work with subgroups of s8:

gap> elab:= AsSubgroup( s8, elab );;
gap> elab.name:= "elab";;

If we are given the subgroup norm of order 1344 and its subgroup elab, the factor group can be considered.

gap> f:= norm / elab;
(Subgroup( s8, [ (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8),
  (1,5)(2,6)(3,7)(4,8), (5,6)(7,8), (5,7)(6,8), (3,4)(7,8),
  (3,5)(4,6), (2,3)(6,7) ] ) / elab)
gap> Size( f );
168

As the output shows, this is not a permutation group. The factor group and its elements can, however, be handled in the usual way.

gap> Random( f );
FactorGroupElement( elab, (2,8,7)(4,6,5) )
gap> Order( f, last );
3

The natural link between the group norm and its factor group f is the natural homomorphism onto f, mapping each element of norm to its coset modulo the kernel elab. In GAP you can construct the homomorphism, but note that the images lie in f since they are elements of the factor group, but the preimage of each such element is only a coset, not a group element (for cosets, see the relevant sections in chapter 7, for homomorphisms see chapters 8 and 36).

gap> f.name:= "f";;
gap> hom:= NaturalHomomorphism( norm, f );
NaturalHomomorphism( Subgroup( s8,
[ (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8), (1,5)(2,6)(3,7)(4,8),
  (5,6)(7,8), (5,7)(6,8), (3,4)(7,8), (3,5)(4,6), (2,3)(6,7)
 ] ), (Subgroup( s8, [ (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8),
  (1,5)(2,6)(3,7)(4,8), (5,6)(7,8), (5,7)(6,8), (3,4)(7,8),
  (3,5)(4,6), (2,3)(6,7) ] ) / elab) )
gap> Kernel( hom ) = elab;
true
gap> x:= Random( norm );
(1,5,6,2)(3,4,8,7)
gap> Image( hom, x );
FactorGroupElement( elab, (2,5)(3,8) )

gap> coset:= PreImages( hom, last );
(elab*(2,5)(3,8))
gap> IsCoset( coset );
true
gap> x in coset;
true
gap> coset in f;
false

The group f acts on its elements (not on the cosets) via right multiplication, yielding the regular permutation representation of f and thus a new permutation group, namely the linear group L_{3}(2). A more elaborate discussion of operations of groups can be found in section 1.19 and chapter 8.

gap> op:= Operation( f, Elements( f ), OnRight );;
gap> IsPermGroup( op );
true
gap> Maximum( List( op.generators, LargestMovedPointPerm ) );
168
gap> IsSimple( op );
true

norm acts on the seven nontrivial elements of its normal subgroup elab by conjugation, yielding a representation of L_{3}(2) on seven points. We embed this permutation group in norm and deduce that norm is a split extension of an elementary abelian group 2^{3} with L_{3}(2).

gap> op:= Operation( norm, Elements( elab ), OnPoints );
Group( (5,6)(7,8), (5,7)(6,8), (3,4)(7,8), (3,5)(4,6), (2,3)(6,7) )
gap> IsSubgroup(   a8, AsSubgroup( s8, op ) );
true
gap> IsSubgroup( norm, AsSubgroup( s8, op ) );
true
gap> Intersection( elab, op );
Group( () )

Yet another kind of information about our a8 concerns its conjugacy classes.

gap> ccl:= ConjugacyClasses( a8 );
[ ConjugacyClass( a8, () ), ConjugacyClass( a8, (6,7,8) ),
  ConjugacyClass( a8, (5,6)(7,8) ), ConjugacyClass( a8, (4,5,6,7,8) ),
  ConjugacyClass( a8, (3,4)(5,6,7,8) ),
  ConjugacyClass( a8, (3,4,5)(6,7,8) ),
  ConjugacyClass( a8, (2,3)(4,5)(6,7,8) ),
  ConjugacyClass( a8, (2,3,4,5,6,7,8) ),
  ConjugacyClass( a8, (2,3,4,5,6,8,7) ),
  ConjugacyClass( a8, (1,2)(3,4)(5,6)(7,8) ),
  ConjugacyClass( a8, (1,2)(3,4,5,6,7,8) ),
  ConjugacyClass( a8, (1,2,3)(4,5,6,7,8) ),
  ConjugacyClass( a8, (1,2,3)(4,5,6,8,7) ),
  ConjugacyClass( a8, (1,2,3,4)(5,6,7,8) ) ]
gap> Length( ccl );

gap> reps:= List( ccl, Representative );
[ (), (6,7,8), (5,6)(7,8), (4,5,6,7,8), (3,4)(5,6,7,8),
  (3,4,5)(6,7,8), (2,3)(4,5)(6,7,8), (2,3,4,5,6,7,8), (2,3,4,5,6,8,7),
  (1,2)(3,4)(5,6)(7,8), (1,2)(3,4,5,6,7,8), (1,2,3)(4,5,6,7,8),
  (1,2,3)(4,5,6,8,7), (1,2,3,4)(5,6,7,8) ]
gap> List( reps, r -> Order( a8, r ) );
[ 1, 3, 2, 5, 4, 3, 6, 7, 7, 2, 6, 15, 15, 4 ]
gap> List( ccl, Size );
[ 1, 112, 210, 1344, 2520, 1120, 1680, 2880, 2880, 105, 3360, 1344,
  1344, 1260 ]

Note the difference between Order (which means the element order), Size (which means the size of the conjugacy class) and Length (which means the length of a list).

Having the conjugacy classes, we can consider class functions, i.e., maps that are defined on the group elements, and that are constant on each conjugacy class. One nice example is the number of fixed points; here we use that permutations act on points via ^.

gap> nrfixedpoints:= function( perm, support )
> return Number( [ 1 .. support ], x -> x^perm = x );
> end;
function ( perm, support ) ... end

Note that we must specify the support since a permutation does not know about the group it is an element of; e.g. the trivial permutation () has as many fixed points as the support denotes.

gap> permchar1:= List( reps, x -> nrfixedpoints( x, 8 ) );
[ 8, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0 ]

This is the character of the natural permutation representation of a8 (More about characters can be found in chapters 42 ff.). In order to get another representation of a8, we consider another action, namely that on the elements of a conjugacy class by conjugation; note that this is denoted by OnPoints, too.

gap> class:= ccl[2];;
gap> op:= Operation( a8, Elements( class ), OnPoints );;

We get a permutation representation op on 112 points. It is more useful to look for properties than at the permutations.

gap> IsPrimitive( op, [ 1 .. 112 ] );
false
gap> blocks:= Blocks( op, [ 1 .. 112 ] );
[ [ 1, 2 ], [ 6, 8 ], [ 4, 7 ], [ 17, 20 ], [ 3, 5 ], [ 14, 19 ],
  [ 11, 18 ], [ 36, 40 ], [ 13, 16 ], [ 10, 15 ], [ 32, 39 ],
  [ 28, 38 ], [ 24, 37 ], [ 65, 70 ], [ 31, 35 ], [ 9, 12 ],
  [ 27, 34 ], [ 23, 33 ], [ 60, 69 ], [ 55, 68 ], [ 50, 67 ],
  [ 45, 66 ], [ 106, 112 ], [ 59, 64 ], [ 26, 30 ], [ 22, 29 ],
  [ 54, 63 ], [ 49, 62 ], [ 44, 61 ], [ 100, 111 ], [ 94, 110 ],
  [ 88, 109 ], [ 76, 107 ], [ 99, 105 ], [ 53, 58 ], [ 21, 25 ],
  [ 48, 57 ], [ 43, 56 ], [ 93, 104 ], [ 87, 103 ], [ 75, 101 ],
  [ 82, 108 ], [ 92, 98 ], [ 47, 52 ], [ 42, 51 ], [ 86, 97 ],
  [ 74, 95 ], [ 81, 102 ], [ 85, 91 ], [ 41, 46 ], [ 73, 89 ],

  [ 80, 96 ], [ 79, 90 ], [ 78, 84 ], [ 72, 83 ], [ 71, 77 ] ]
gap> op2:= Operation( op, blocks, OnSets );;
gap> IsPrimitive( op2, [ 1 .. 56 ] );
true

The action of op on the given block system gave us a new representation on 56 points which is primitive, i.e., the point stabilizer is a maximal subgroup. We compute its preimage in the representation on eight points using homomorphisms (which of course are monomorphisms).

gap> ophom := OperationHomomorphism( a8, op  );;
gap> Kernel(ophom);
Subgroup( s8, [  ] )
gap> ophom2:= OperationHomomorphism( op, op2 );;
gap> stab:= Stabilizer( op2, 1 );;
gap> Size( stab );
360
gap> composition:= ophom * ophom2;;
gap> preim:= PreImage( composition, stab );
Subgroup( s8, [ (1,3,2), (2,4,3), (1,2)(7,8), (3,5,4), (6,8,7) ] )

And this is the permutation character (with respect to the succession of conjugacy classes in ccl):

gap> permchar2:= List( reps, x->nrfixedpoints(x^composition,56) );
[ 56, 11, 12, 1, 2, 2, 3, 0, 0, 0, 0, 1, 1, 0 ]

The normalizer of an element in the conjugacy class class is a group of order 360, too. In fact, it is essentially the same as the maximal subgroup we had found before

gap> sgp:= Normalizer( a8,
>                      Subgroup( s8, [ Representative(class) ] ) );
Subgroup( s8, [ (6,7,8), (3,4,5), (2,3)(4,5), (1,2)(4,5), (4,5)(7,8)
 ] )
gap> Size( sgp );
360
gap> IsConjugate( a8, sgp, preim );
true

The scalar product of permutation characters of two subgroups U, V, say, equals the number of (U, V)–double cosets (again, see chapters 42 ff. for the details). For example, the norm of the permutation character permchar1 of degree eight is two since the action of a8 on the cosets of a point stabilizer is at least doubly transitive:

gap> stab:= Stabilizer( a8, 1 );;
gap> double:= DoubleCosets( a8, stab, stab );
[ DoubleCoset( Subgroup( s8, [ (3,8,7), (2,3,8), (2,3,4), (2,6,4),
      (4,5,6) ] ), (2,5,8,3,4,6,7), Subgroup( s8,
    [ (3,8,7), (2,3,8), (2,3,4), (2,6,4), (4,5,6) ] ) ),
  DoubleCoset( Subgroup( s8, [ (3,8,7), (2,3,8), (2,3,4), (2,6,4),
      (4,5,6) ] ), (1,2,5,7)(3,4,6,8), Subgroup( s8,
    [ (3,8,7), (2,3,8), (2,3,4), (2,6,4), (4,5,6) ] ) ) ]
gap> Length( double );

We compute the numbers of (sgp, sgp) and (sgp, stab) double cosets.

    gap> Length( DoubleCosets( a8, sgp, sgp ) );
    4
    gap> Length( DoubleCosets( a8, sgp, stab ) );
    2

Thus both irreducible constituents of permchar1 are also constituents of permchar2, i.e., the difference of the two permutation characters is a proper character of a8 of norm two.

    gap> permchar2 - permchar1;
    [ 48, 6, 8, -2, 0, 0, 2, -1, -1, 0, 0, 1, 1, 0 ]

1.19 About Operations of Groups

One of the most important tools in group theory is the operation or action of a group on a certain set.

We say that a group G operates on a set D if we have a function that takes each pair (d, g) with d ∈ D and g ∈ G to another element d^{g} ∈ D, which we call the image of d under g, such that d^{identity} = d and (d^{g})^{h} = d^{gh} for each d ∈ D and g, h ∈ G.

This is equivalent to saying that an operation is a homomorphism of the group G into the full symmetric group on D. We usually call D the domain of the operation and its elements points.

In this section we will demonstrate how you can compute with operations of groups. For an example we will use the alternating group on 8 points.

    gap> a8 := Group( (1,2,3), (2,3,4,5,6,7,8) );;
    gap> a8.name := "a8";;

It is important to note however, that the applicability of the functions from the operation package is not restricted to permutation groups. All the functions mentioned in this section can also be used to compute with the operation of a matrix group on the vectors, etc. We only use a permutation group here because this makes the examples more compact.

The standard operation in GAP is always denoted by the caret (^) operator. That means that when no other operation is specified (we will see below how this can be done) all the functions from the operations package will compute the image of a point p under an element g as p^g. Note that this can already denote different operations, depending on the type of points and the type of elements. For example if the group elements are permutations it can either denote the normal operation when the points are integers or the conjugation when the points are permutations themselves (see 19.2). For another example if the group elements are matrices it can either denote the multiplication from the right when the points are vectors or again the conjugation when the points are matrices (of the same dimension) themselves (see 32.1). Which operations are available through the caret operator for a particular type of group elements is described in the chapter for this type of group elements.

    gap> 2 ^(1,2,3);
    3
    gap> 1 ^a8.2;
    1
    gap> (2,4) ^(1,2,3);

    (3,4)

The most basic function of the operations package is the function Orbit, which computes the orbit of a point under the operation of the group.

    gap> Orbit( a8, 2 );
    [ 2, 3, 1, 4, 5, 6, 7, 8 ]

Note that the orbit is not a set, because it is not sorted. See 8.14 for the definition in which order the points appear in an orbit.

We will try to find several subgroups in a8 using the operations package. One subgroup is immediately available, namely the stabilizer of one point. The index of the stabilizer must of course be equal to the length of the orbit, i.e., 8.

    gap> u8 := Stabilizer( a8, 1 );
    Subgroup( a8, [ (2,3,4,5,6,7,8), (3,8,7) ] )
    gap> Index( a8, u8 );
    8

This gives us a hint how to find further subgroups. Each subgroup is the stabilizer of a point of an appropriate transitive operation (namely the operation on the cosets of that subgroup or another operation that is equivalent to this operation).

So the question is how to find other operations. The obvious thing is to operate on pairs of points. So using the function Tuples (see 40.9) we first generate a list of all pairs.

    gap> pairs := Tuples( [1..8], 2 );
    [ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 5 ], [ 1, 6 ],
      [ 1, 7 ], [ 1, 8 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ], [ 2, 4 ],
      [ 2, 5 ], [ 2, 6 ], [ 2, 7 ], [ 2, 8 ], [ 3, 1 ], [ 3, 2 ],
      [ 3, 3 ], [ 3, 4 ], [ 3, 5 ], [ 3, 6 ], [ 3, 7 ], [ 3, 8 ],
      [ 4, 1 ], [ 4, 2 ], [ 4, 3 ], [ 4, 4 ], [ 4, 5 ], [ 4, 6 ],
      [ 4, 7 ], [ 4, 8 ], [ 5, 1 ], [ 5, 2 ], [ 5, 3 ], [ 5, 4 ],
      [ 5, 5 ], [ 5, 6 ], [ 5, 7 ], [ 5, 8 ], [ 6, 1 ], [ 6, 2 ],
      [ 6, 3 ], [ 6, 4 ], [ 6, 5 ], [ 6, 6 ], [ 6, 7 ], [ 6, 8 ],
      [ 7, 1 ], [ 7, 2 ], [ 7, 3 ], [ 7, 4 ], [ 7, 5 ], [ 7, 6 ],
      [ 7, 7 ], [ 7, 8 ], [ 8, 1 ], [ 8, 2 ], [ 8, 3 ], [ 8, 4 ],
      [ 8, 5 ], [ 8, 6 ], [ 8, 7 ], [ 8, 8 ] ]

Now we would like to have a8 operate on this domain. But we cannot use the default operation (denoted by the caret) because list ^perm is not defined. So we must tell the functions from the operations package how the group elements operate on the elements of the domain. In our example we can do this by simply passing OnPairs as optional last argument. All functions from the operations package accept such an optional argument that describes the operation. See 8.1 for a list of the available nonstandard operations.

Note that those operations are in fact simply functions that take an element of the domain and an element of the group and return the image of the element of the domain under the group element. So to compute the image of the pair [1,2] under the permutation (1,4,5) we can simply write

    gap> OnPairs( [1,2], (1,4,5) );
    [ 4, 2 ]

As was mentioned above we have to make sure that the operation is transitive. So we check this.

    gap> IsTransitive( a8, pairs, OnPairs );
    false

The operation is not transitive, so we want to find out what the orbits are. The function Orbits does that for you. It returns a list of all the orbits.

    gap> orbs := Orbits( a8, pairs, OnPairs );
    [ [ [ 1, 1 ], [ 2, 2 ], [ 3, 3 ], [ 4, 4 ], [ 5, 5 ], [ 6, 6 ],
          [ 7, 7 ], [ 8, 8 ] ],
      [ [ 1, 2 ], [ 2, 3 ], [ 1, 3 ], [ 3, 1 ], [ 3, 4 ], [ 2, 1 ],
          [ 1, 4 ], [ 4, 1 ], [ 4, 5 ], [ 3, 2 ], [ 2, 4 ], [ 1, 5 ],
          [ 4, 2 ], [ 5, 1 ], [ 5, 6 ], [ 4, 3 ], [ 3, 5 ], [ 2, 5 ],
          [ 1, 6 ], [ 5, 3 ], [ 5, 2 ], [ 6, 1 ], [ 6, 7 ], [ 5, 4 ],
          [ 4, 6 ], [ 3, 6 ], [ 2, 6 ], [ 1, 7 ], [ 6, 4 ], [ 6, 3 ],
          [ 6, 2 ], [ 7, 1 ], [ 7, 8 ], [ 6, 5 ], [ 5, 7 ], [ 4, 7 ],
          [ 3, 7 ], [ 2, 7 ], [ 1, 8 ], [ 7, 5 ], [ 7, 4 ], [ 7, 3 ],
          [ 7, 2 ], [ 8, 1 ], [ 8, 2 ], [ 7, 6 ], [ 6, 8 ], [ 5, 8 ],
          [ 4, 8 ], [ 3, 8 ], [ 2, 8 ], [ 8, 6 ], [ 8, 5 ], [ 8, 4 ],
          [ 8, 3 ], [ 8, 7 ] ] ]

The operation of a8 on the first orbit is of course equivalent to the original operation, so we ignore it and work with the second orbit.

    gap> u56 := Stabilizer( a8, [1,2], OnPairs );
    Subgroup( a8, [ (3,8,7), (3,6)(4,7,5,8), (6,7,8) ] )
    gap> Index( a8, u56 );
    56

So now we have found a second subgroup. To make the following computations a little bit easier and more efficient we would now like to work on the points [1..56] instead of the list of pairs. The function Operation does what we need. It creates a new group that operates on [1..56] in the same way that a8 operates on the second orbit.

    gap> a8_56 := Operation( a8, orbs[2], OnPairs );
    Group( ( 1, 2, 4)( 3, 6,10)( 5, 7,11)( 8,13,16)(12,18,17)(14,21,20)
    (19,27,26)(22,31,30)(28,38,37)(32,43,42)(39,51,50)(44,45,55),
    ( 1, 3, 7,12,19,28,39)( 2, 5, 9,15,23,33,45)( 4, 8,14,22,32,44, 6)
    (10,16,24,34,46,56,51)(11,17,25,35,47,43,55)(13,20,29,40,52,38,50)
    (18,26,36,48,31,42,54)(21,30,41,53,27,37,49) )
    gap> a8_56.name := "a8_56";;

We would now like to know if the subgroup u56 of index 56 that we found is maximal or not. Again we can make use of a function from the operations package. Namely a subgroup is maximal if the operation on the cosets of this subgroup is primitive, i.e., if there is no partition of the set of cosets into subsets such that the group operates setwise on those subsets.

    gap> IsPrimitive( a8_56, [1..56] );
    false

Note that we must specify the domain of the operation. You might think that in the last example IsPrimitive could use [1..56] as default domain if no domain was given. But this is not so simple, for example would the default domain of Group( (2,3,4) ) be [1..4] or [2..4]? To avoid confusion, all operations package functions require that you specify the domain of operation.

We see that a8 56 is not primitive. This means of course that the operation of a8 on orb[2] is not primitive, because those two operations are equivalent. So the stabilizer u56 is not maximal. Let us try to find its supergroups. We use the function Blocks to find a block system. The (optional) third argument in the following example tells Blocks that we want a block system where 1 and 10 lie in one block. There are several other block systems, which we could compute by specifying a different pair, it just turns out that [1,10] makes the following computation more interesting.

    gap> blocks := Blocks( a8_56, [1..56], [1,10] );
    [ [ 1, 10, 13, 21, 31, 43, 45 ], [ 2, 3, 16, 20, 30, 42, 55 ],
      [ 4, 6, 8, 14, 22, 32, 44 ], [ 5, 7, 11, 24, 29, 41, 54 ],
      [ 9, 12, 17, 18, 34, 40, 53 ], [ 15, 19, 25, 26, 27, 46, 52 ],
      [ 23, 28, 35, 36, 37, 38, 56 ], [ 33, 39, 47, 48, 49, 50, 51 ] ]

The result is a list of sets, i.e., sorted lists, such that a8 56 operates on those sets. Now we would like the stabilizer of this operation on the sets. Because we wanted to operate on the sets we have to pass OnSets as third argument.

    gap> u8_56 := Stabilizer( a8_56, blocks[1], OnSets );
    Subgroup( a8_56,
    [ (15,35,48)(19,28,39)(22,32,44)(23,33,52)(25,36,49)(26,37,50)
        (27,38,51)(29,41,54)(30,42,55)(31,43,45)(34,40,53)(46,56,47),
      ( 9,25)(12,19)(14,22)(15,34)(17,26)(18,27)(20,30)(21,31)(23,48)
        (24,29)(28,39)(32,44)(33,56)(35,47)(36,49)(37,50)(38,51)(40,52)
        (41,54)(42,55)(43,45)(46,53), ( 5,17)( 7,12)( 8,14)( 9,24)(11,18)
        (13,21)(15,25)(16,20)(23,47)(28,39)(29,34)(32,44)(33,56)(35,49)
        (36,48)(37,50)(38,51)(40,54)(41,53)(42,55)(43,45)(46,52),
      ( 2,11)( 3, 7)( 4, 8)( 5,16)( 9,17)(10,13)(20,24)(23,47)(25,26)
        (28,39)(29,30)(32,44)(33,56)(35,48)(36,50)(37,49)(38,51)(40,53)
        (41,55)(42,54)(43,45)(46,52), ( 1,10)( 2, 6)( 3, 4)( 5, 7)( 8,16)
        (12,17)(14,20)(19,26)(22,30)(23,47)(28,50)(32,55)(33,56)(35,48)
        (36,49)(37,39)(38,51)(40,53)(41,54)(42,44)(43,45)(46,52) ] )
    gap> Index( a8_56, u8_56 );
    8

Now we have a problem. We have found a new subgroup, but not as a subgroup of a8, instead it is a subgroup of a8 56. We know that a8 56 is isomorphic to a8 (in general the result of Operation is only isomorphic to a factor group of the original group, but in this case it must be isomorphic to a8, because a8 is simple and has only the full group as nontrivial factor group). But we only know that an isomorphism exists, we do not know it.

Another function comes to our rescue. OperationHomomorphism returns the homomorphism of a group onto the group that was constructed by Operation. A later section in this chapter will introduce mappings and homomorphisms in general, but for the moment we can just regard the result of OperationHomomorphism as a black box that we can use to transfer information from a8 to a8 56 and back.

    gap> h56 := OperationHomomorphism( a8, a8_56 );

OperationHomomorphism( a8, a8_56 )
gap> u8b := PreImages( h56, u8_56 );
Subgroup( a8, [ (6,7,8), (5,6)(7,8), (4,5)(7,8), (3,4)(7,8),
  (1,3)(7,8) ] )
gap> Index( a8, u8b );
8
gap> u8 = u8b;
false

So we have in fact found a new subgroup. However if we look closer we note that u8b is not totally new. It fixes the point 2, thus it lies in the stabilizer of 2, and because it has the same index as this stabilizer it must in fact be the stabilizer. Thus u8b is conjugated to u8. A nice way to check this is to check that the operation on the 8 blocks is equivalent to the original operation.

gap> IsEquivalentOperation( a8, [1..8], a8_56, blocks, OnSets );
true

Now the choice of the third argument [1,10] of Blocks becomes clear. Had we not given that argument we would have obtained the block system that has [1,3,7,12,19,28,39] as first block. The preimage of the stabilizer of this set would have been u8 itself, and we would not have been able to introduce IsEquivalentOperation. Of course we could also use the general function IsConjugate, but we want to demonstrate IsEquivalentOperation. Actually there is a third block system of a8 56 that gives rise to a third subgroup.

gap> blocks := Blocks( a8_56, [1..56], [1,6] );
[ [ 1, 6 ], [ 2, 10 ], [ 3, 4 ], [ 5, 16 ], [ 7, 8 ], [ 9, 24 ],
  [ 11, 13 ], [ 12, 14 ], [ 15, 34 ], [ 17, 20 ], [ 18, 21 ],
  [ 19, 22 ], [ 23, 46 ], [ 25, 29 ], [ 26, 30 ], [ 27, 31 ],
  [ 28, 32 ], [ 33, 56 ], [ 35, 40 ], [ 36, 41 ], [ 37, 42 ],
  [ 38, 43 ], [ 39, 44 ], [ 45, 51 ], [ 47, 52 ], [ 48, 53 ],
  [ 49, 54 ], [ 50, 55 ] ]
gap> u28_56 := Stabilizer( a8_56, [1,6], OnSets );
Subgroup( a8_56,
[ ( 2,38,51)( 3,28,39)( 4,32,44)( 5,41,54)(10,43,45)(16,36,49)
    (17,40,53)(20,35,48)(23,47,30)(26,46,52)(33,55,37)(42,56,50),
  ( 5,17,26,37,50)( 7,12,19,28,39)( 8,14,22,32,44)( 9,15,23,33,54)
    (11,18,27,38,51)(13,21,31,43,45)(16,20,30,42,55)(24,34,46,56,49)
    (25,35,47,41,53)(29,40,52,36,48),
  ( 1, 6)( 2,39,38,19,18, 7)( 3,51,28,27,12,11)( 4,45,32,31,14,13)
    ( 5,55,33,23,15, 9)( 8,10,44,43,22,21)(16,50,56,46,34,24)
    (17,54,42,47,35,25)(20,49,37,52,40,29)(26,53,41,30,48,36) ] )
gap> u28 := PreImages( h56, u28_56 );
Subgroup( a8, [ (3,7,8), (4,5,6,7,8), (1,2)(3,8,7,6,5,4) ] )
gap> Index( a8, u28 );
28

We know that the subgroup u28 of index 28 is maximal, because we know that a8 has no subgroups of index 2, 4, or 7. However we can also quickly verify this by checking that a8 56 operates primitively on the 28 blocks.

gap> IsPrimitive( a8_56, blocks, OnSets );

    true

There is a different way to obtain u28. Instead of operating on the 56 pairs [ [1,2], [1,3], ..., [8,7] ] we could operate on the 28 sets of two elements from [1..8]. But suppose we make a small mistake.

    gap> OrbitLength( a8, [2,1], OnSets );
    Error, OnSets: <tuple> must be a set

It is your responsibility to make sure that the points that you pass to functions from the operations package are in normal form. That means that they must be sets if you operate on sets with OnSets, they must be lists of length 2 if you operate on pairs with OnPairs, etc. This also applies to functions that accept a domain of operation, e.g., Operation and IsPrimitive. All points in such a domain must be in normal form. It is not guaranteed that a violation of this rule will signal an error, you may obtain incorrect results.

Note that Stabilizer is not only applicable to groups like a8 but also to their subgroups like u56. So another method to find a new subgroup is to compute the stabilizer of another point in u56. Note that u56 already leaves 1 and 2 fixed.

    gap> u336 := Stabilizer( u56, 3 );
    Subgroup( a8, [ (4,6,5), (5,6)(7,8), (6,7,8) ] )
    gap> Index( a8, u336 );
    336

Other functions are also applicable to subgroups. In the following we show that u336 operates regularly on the 60 triples of [4..8] which contain no element twice, which means that this operation is equivalent to the operations of u336 on its 60 elements from the right. Note that OnTuples is a generalization of OnPairs.

    gap> IsRegular( u336, Orbit( u336, [4,5,6], OnTuples ), OnTuples );
    true

Just as we did in the case of the operation on the pairs above, we now construct a new permutation group that operates on [1..336] in the same way that a8 operates on the cosets of u336. Note that the operation of a group on the cosets is by multiplication from the right, thus we have to specify OnRight.

    gap> a8_336 := Operation( a8, Cosets( a8, u336 ), OnRight );;
    gap> a8_336.name := "a8_336";;

To find subgroups above u336 we again check if the operation is primitive.

    gap> blocks := Blocks( a8_336, [1..336], [1,43] );
    [ [ 1, 43, 85 ], [ 2, 102, 205 ], [ 3, 95, 165 ], [ 4, 106, 251 ],
      [ 5, 117, 334 ], [ 6, 110, 294 ], [ 7, 122, 127 ], [ 8, 144, 247 ],
      [ 9, 137, 207 ], [ 10, 148, 293 ], [ 11, 45, 159 ],
      [ 12, 152, 336 ], [ 13, 164, 169 ], [ 14, 186, 289 ],
      [ 15, 179, 249 ], [ 16, 190, 335 ], [ 17, 124, 201 ],
      [ 18, 44, 194 ], [ 19, 206, 211 ], [ 20, 228, 331 ],
      [ 21, 221, 291 ], [ 22, 46, 232 ], [ 23, 166, 243 ],
      [ 24, 126, 236 ], [ 25, 248, 253 ], [ 26, 48, 270 ],
      [ 27, 263, 333 ], [ 28, 125, 274 ], [ 29, 208, 285 ],
      [ 30, 168, 278 ], [ 31, 290, 295 ], [ 32, 121, 312 ],

[ 33, 47, 305 ], [ 34, 167, 316 ], [ 35, 250, 327 ],
[ 36, 210, 320 ], [ 37, 74, 332 ], [ 38, 49, 163 ], [ 39, 81, 123 ],
[ 40, 59, 209 ], [ 41, 70, 292 ], [ 42, 66, 252 ], [ 50, 142, 230 ],
[ 51, 138, 196 ], [ 52, 146, 266 ], [ 53, 87, 131 ],
[ 54, 153, 302 ], [ 55, 160, 174 ], [ 56, 182, 268 ],
[ 57, 178, 234 ], [ 58, 189, 304 ], [ 60, 86, 199 ],
[ 61, 198, 214 ], [ 62, 225, 306 ], [ 63, 218, 269 ],
[ 64, 88, 235 ], [ 65, 162, 245 ], [ 67, 233, 254 ],
[ 68, 90, 271 ], [ 69, 261, 301 ], [ 71, 197, 288 ],
[ 72, 161, 281 ], [ 73, 265, 297 ], [ 75, 89, 307 ],
[ 76, 157, 317 ], [ 77, 229, 328 ], [ 78, 193, 324 ],
[ 79, 116, 303 ], [ 80, 91, 158 ], [ 82, 101, 195 ],
[ 83, 112, 267 ], [ 84, 108, 231 ], [ 92, 143, 237 ],
[ 93, 133, 200 ], [ 94, 150, 273 ], [ 96, 154, 309 ],
[ 97, 129, 173 ], [ 98, 184, 272 ], [ 99, 180, 238 ],
[ 100, 188, 308 ], [ 103, 202, 216 ], [ 104, 224, 310 ],
[ 105, 220, 276 ], [ 107, 128, 241 ], [ 109, 240, 256 ],
[ 111, 260, 311 ], [ 113, 204, 287 ], [ 114, 130, 277 ],
[ 115, 275, 296 ], [ 118, 132, 313 ], [ 119, 239, 330 ],
[ 120, 203, 323 ], [ 134, 185, 279 ], [ 135, 175, 242 ],
[ 136, 192, 315 ], [ 139, 171, 215 ], [ 140, 226, 314 ],
[ 141, 222, 280 ], [ 145, 244, 258 ], [ 147, 262, 318 ],
[ 149, 170, 283 ], [ 151, 282, 298 ], [ 155, 246, 329 ],
[ 156, 172, 319 ], [ 176, 227, 321 ], [ 177, 217, 284 ],
[ 181, 213, 257 ], [ 183, 264, 322 ], [ 187, 286, 300 ],
[ 191, 212, 325 ], [ 219, 259, 326 ], [ 223, 255, 299 ] ]

To find the subgroup of index 112 that belongs to this operation we could use the same methods as before, but we actually use a different trick. From the above we see that the subgroup is the union of u336 with its 43rd and its 85th coset. Thus we simply add a representative of the 43rd coset to the generators of u336.

gap> u112 := Closure( u336, Representative( Cosets(a8,u336)[43] ) );
Subgroup( a8, [ (4,6,5), (5,6)(7,8), (6,7,8), (1,3,2) ] )
gap> Index( a8, u112 );
112

Above this subgroup of index 112 lies a subgroup of index 56, which is not conjugate to u56. In fact, unlike u56 it is maximal. We obtain this subgroup in the same way that we obtained u112, this time forcing two points, namely 39 and 43 into the first block.

gap> blocks := Blocks( a8_336, [1..336], [1,39,43] );;
gap> Length( blocks );
56
gap> u56b := Closure( u112, Representative( Cosets(a8,u336)[39] ) );
Subgroup( a8, [ (4,6,5), (5,6)(7,8), (6,7,8), (1,3,2), (2,3)(7,8) ] )
gap> Index( a8, u56b );
56
gap> IsPrimitive( a8_336, blocks, OnSets );
true

We already mentioned in the beginning that there is another standard operation of permutations, namely the conjugation. E.g., because no other operation is specified in the following example OrbitLength simply operates using the caret operator and because perm1 ^perm2 is defined as the conjugation of perm2 on perm1 we effectively compute the length of the conjugacy class of (1,2)(3,4)(5,6)(7,8). (In fact element1 ^element2 is always defined as the conjugation if element1 and element2 are group elements of the same type. So the length of a conjugacy class of any element elm in an arbitrary group G can be computed as OrbitLength( G, elm ). In general however this may not be a good idea, Size( ConjugacyClass( G, elm ) ) is probably more efficient.)

    gap> OrbitLength( a8, (1,2)(3,4)(5,6)(7,8) );
    105
    gap> orb := Orbit( a8, (1,2)(3,4)(5,6)(7,8) );;
    gap> u105 := Stabilizer( a8, (1,2)(3,4)(5,6)(7,8) );
    Subgroup( a8, [ (5,6)(7,8), (1,2)(3,4)(5,6)(7,8), (5,7)(6,8),
      (3,4)(7,8), (3,5)(4,6), (1,3)(2,4) ] )
    gap> Index( a8, u105 );
    105

Of course the last stabilizer is in fact the centralizer of the element (1,2)(3,4)(5,6)(7,8). Stabilizer notices that and computes the stabilizer using the centralizer algorithm for permutation groups.

In the usual way we now look for the subgroups that lie above u105.

    gap> blocks := Blocks( a8, orb );;
    gap> Length( blocks );
    15
    gap> blocks[1];
    [ (1,2)(3,4)(5,6)(7,8), (1,3)(2,4)(5,7)(6,8), (1,4)(2,3)(5,8)(6,7),
      (1,5)(2,6)(3,7)(4,8), (1,6)(2,5)(3,8)(4,7), (1,7)(2,8)(3,5)(4,6),
      (1,8)(2,7)(3,6)(4,5) ]

To find the subgroup of index 15 we again use closure. Now we must be a little bit careful to avoid confusion. u105 is the stabilizer of (1,2)(3,4)(5,6)(7,8). We know that there is a correspondence between the points of the orbit and the cosets of u105. The point (1,2)(3,4)(5,6)(7,8) corresponds to u105. To get the subgroup of index 15 we must add to u105 an element of the coset that corresponds to the point (1,3)(2,4)(5,7)(6,8) (or any other point in the first block). That means that we must use an element of a8 that

maps (1,2)(3,4)(5,6)(7,8) to (1,3)(2,4)(5,7)(6,8). The important thing is that
(1,3)(2,4)(5,7)(6,8) will not do, in fact (1,3)(2,4)(5,7)(6,8) lies in u105.

The function RepresentativeOperation does what we need. It takes a group and two points and returns an element of the group that maps the first point to the second. In fact it also allows you to specify the operation as optional fourth argument as usual, but we do not need this here. If no such element exists in the group, i.e., if the two points do not lie in one orbit under the group, RepresentativeOperation returns false.

    gap> rep := RepresentativeOperation( a8, (1,2)(3,4)(5,6)(7,8),
    >                                         (1,3)(2,4)(5,7)(6,8) );
    (2,3)(6,7)
    gap> u15 := Closure( u105, rep );

    Subgroup( a8, [ (5,6)(7,8), (1,2)(3,4)(5,6)(7,8), (5,7)(6,8),
      (3,4)(7,8), (3,5)(4,6), (1,3)(2,4), (2,3)(6,7) ] )
    gap> Index( a8, u15 );
    15

u15 is of course a maximal subgroup, because a8 has no subgroups of index 3 or 5.

There is in fact another class of subgroups of index 15 above u105 that we get by adding

(2,3)(6,8) to u105.
    gap> u15b := Closure( u105, (2,3)(6,8) );
    Subgroup( a8, [ (5,6)(7,8), (1,2)(3,4)(5,6)(7,8), (5,7)(6,8),
      (3,4)(7,8), (3,5)(4,6), (1,3)(2,4), (2,3)(6,8) ] )
    gap> Index( a8, u15b );
    15

We now show that u15 and u15b are not conjugate. We showed that u8 and u8b are conjugate by showing that the operations on the cosets where equivalent. We could show that u15 and u15b are not conjugate by showing that the operations on their cosets are not equivalent. Instead we simply call RepresentativeOperation again.

    gap> RepresentativeOperation( a8, u15, u15b );
    false

RepresentativeOperation tells us that there is no element g in a8 such that u15^g = u15b. Because ^also denotes the conjugation of subgroups this tells us that u15 and u15b are not conjugate. Note that this operation should only be used rarely, because it is usually not very efficient. The test in this case is however reasonable efficient, and is in fact the one employed by IsConjugate (see 7.51).

This concludes our example. In this section we demonstrated some functions from the operations package. There is a whole class of functions that we did not mention, namely those that take a single element instead of a whole group as first argument, e.g., Cycle and Permutation. All functions are described in the chapter 8.

1.20 About Finitely Presented Groups and Presentations

In this section we will show you the investigation of a Coxeter group that is given by its presentation. You will see that finitely presented groups and presentations are different kinds of objects in GAP. While finitely presented groups can never be changed after they have been created as factor groups of free groups, presentations allow manipulations of the generators and relators by Tietze transformations. The investigation of the example will involve methods and algorithms like Todd-Coxeter, Reidemeister-Schreier, Nilpotent Quotient, and Tietze transformations.

We start by defining a Coxeter group c on five generators as a factor group of the free group of rank 5, whose generators we already call c.1, ..., c.5.

    gap> c := FreeGroup( 5, "c" );;
    gap> r := List( c.generators, x -> x^2 );;
    gap> Append( r, [ (c.1*c.2)^3, (c.1*c.3)^2, (c.1*c.4)^3,
    >  (c.1*c.5)^3, (c.2*c.3)^3, (c.2*c.4)^2, (c.2*c.5)^3,

    >  (c.3*c.4)^3, (c.3*c.5)^3, (c.4*c.5)^3,
    >  (c.1*c.2*c.5*c.2)^2, (c.3*c.4*c.5*c.4)^2 ] );
    gap> c := c / r;
    Group( c.1, c.2, c.3, c.4, c.5 )

If we call the function Size for this group GAP will invoke the Todd-Coxeter method, which however will fail to get a result going up to the default limit of defining 64000 cosets:

    gap> Size(c);
    Error, the coset enumeration has defined more than 64000 cosets.
    Type 'return;' if you want to continue with a new limit of
    128000 cosets, type 'quit;' if you want to quit the coset enumeration,
    type 'maxlimit := 0; return;' in order to continue without a limit
     in
    CosetTableFpGroup( G, H ) called from
    G.operations.Index( G, U ) called from
    Index( G, TrivialSubgroup( G ) ) called from
    D.operations.Size( D ) called from
    Size( c ) called from
    main loop
    brk> quit;

In fact, as we shall see later, our finitely presented group is infinite and hence we would get the same answer also with larger limits. So we next look for subgroups of small index, in our case limiting the index to four.

    gap> lis := LowIndexSubgroupsFpGroup( c, TrivialSubgroup(c), 4 );;
    gap> Length(lis);
    10

The LowIndexSubgroupsFpGroup function in fact determines generators for the subgroups, written in terms of the generators of the given group. We can find the index of these subgroups by the function Index, and the permutation representation on the cosets of these subgroups by the function OperationCosetsFpGroup, which use a Todd-Coxeter method. The size of the image of this permutation representation is found using Size which in this case uses a Schreier-Sims method for permutation groups.

    gap> List(lis, x -> [Index(c,x),Size(OperationCosetsFpGroup(c,x))]);
    [ [ 1, 1 ], [ 4, 24 ], [ 4, 24 ], [ 4, 24 ], [ 4, 24 ], [ 4, 24 ],
      [ 4, 24 ], [ 4, 24 ], [ 3, 6 ], [ 2, 2 ] ]

We next determine the commutator factor groups of the kernels of these permutation representations. Note that here the difference of finitely presented groups and presentations has to be observed: We first determine the kernel of the permutation representation by the function Core as a subgroup of c, then a presentation of this subgroup using PresentationSubgroup, which has to be converted into a finitely presented group of its own right using FpGroupPresentation, before its commutator factor group and the abelian invariants can be found using integer matrix diagonalisation of the relators matrix by an elementary divisor algorithm. The conversion is necessary because Core computes a subgroup given by words in the generators of c but CommutatorFactorGroup needs a parent group given by generators and relators.

    gap> List( lis, x -> AbelianInvariants( CommutatorFactorGroup(

>   FpGroupPresentation( PresentationSubgroup( c, Core(c,x) ) ) ) ) );
[ [ 2 ],
  [ 2, 2, 2, 2, 2, 2, 2, 2 ], [ 2, 2, 2, 2, 2, 2, 2, 2 ],
  [ 2, 2, 2, 2, 2, 2, 2, 2 ], [ 2, 2, 2, 2, 2, 2, 2, 2 ],
  [ 2, 2, 2, 2, 2, 2, 2, 2 ], [ 2, 2, 2, 2, 2, 2, 2, 2 ],
  [ 0, 0, 0, 0, 0, 0 ],
  [ 2, 2, 2, 2, 2, 2 ],
  [ 3 ] ]

Note that there is another function AbelianInvariantsSubgroupFpGroup which we could have used to obtain this list which will do an abelianized Reduced Reidemeister-Schreier. This function is much faster because it does not compute a complete presentation for the core.

The output obtained shows that the third last of the kernels has a free abelian commutator factor group of rank 6. We turn our attention to this kernel which we call n, while we call the associated presentation pr.

gap> lis[8];
Subgroup( Group( c.1, c.2, c.3, c.4, c.5 ),
[ c.1, c.2, c.3*c.2*c.5^-1, c.3*c.4*c.3^-1, c.4*c.1*c.5^-1 ] )
gap> pr := PresentationSubgroup( c, Core( c, lis[8] ) );
<< presentation with 22 gens and 41 rels of total length 156 >>
gap> n := FpGroupPresentation(pr);;

We first determine p-factor groups for primes 2, 3, 5, and 7.

gap> List( [2,3,5,7], p -> PrimeQuotient(n,p,5).dimensions );
[ [ 6, 10, 18, 30, 54 ],
  [ 6, 10, 18, 30, 54 ],
  [ 6, 10, 18, 30, 54 ],
  [ 6, 10, 18, 30, 54 ] ]

Observing that the ranks of the lower exponent-p central series are the same for these primes we suspect that the lower central series may have free abelian factors. To investigate this we have to call the package ”nq”.

gap> RequirePackage("nq");
gap> NilpotentQuotient( n, 5 );
[ [ 0, 0, 0, 0, 0, 0 ],
  [ 0, 0, 0, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0 ] ]
gap> List( last, Length );
[ 6, 4, 8, 12, 24 ]

The ranks of the factors except the first are divisible by four, and we compare them with the corresponding ranks of a free group on two generators.

gap> f2 := FreeGroup(2);
Group( f.1, f.2 )
gap> PrimeQuotient( f2, 2, 5 ).dimensions;

[ 2, 3, 5, 8, 14 ]
gap> NilpotentQuotient( f2, 5 );
[ [ 0, 0 ], [ 0 ], [ 0, 0 ], [ 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ]
gap> List( last, Length );
[ 2, 1, 2, 3, 6 ]

The result suggests a close relation of our group to the direct product of four free groups of rank two. In order to study this we want a simple presentation for our kernel n and obtain this by repeated use of Tietze transformations, using first the default simplification function TzGoGo and later specific introduction of new generators that are obtained as product of two of the existing ones using the function TzSubstitute. (Of course, this latter sequence of Tietze transformations that we display here has only been found after some trial and error.)

gap> pr := PresentationSubgroup( c, Core( c, lis[8] ) );
<< presentation with 22 gens and 41 rels of total length 156 >>
gap> TzGoGo(pr);
#I  there are 6 generators and 14 relators of total length 74
gap> TzGoGo(pr);
#I  there are 6 generators and 13 relators of total length 66
gap> TzGoGo(pr);
gap> TzPrintPairs(pr);
#I  1.  3  occurrences of  _x6 * _x11^-1
#I  2.  3  occurrences of  _x3 * _x15
#I  3.  2  occurrences of  _x11^-1 * _x15^-1
#I  4.  2  occurrences of  _x6 * _x15
#I  5.  2  occurrences of  _x6^-1 * _x15^-1
#I  6.  2  occurrences of  _x4 * _x15
#I  7.  2  occurrences of  _x4^-1 * _x15^-1
#I  8.  2  occurrences of  _x4^-1 * _x11
#I  9.  2  occurrences of  _x4 * _x6
#I  10.  2  occurrences of  _x3^-1 * _x11
gap> TzSubstitute(pr,10,2);
#I  substituting new generator _x26 defined by _x3^-1*_x11
#I  eliminating _x11 = _x3*_x26
#I  there are 6 generators and 13 relators of total length 70
gap> TzGoGo(pr);
#I  there are 6 generators and 12 relators of total length 62
#I  there are 6 generators and 12 relators of total length 60
gap> TzGoGo(pr);
gap> TzSubstitute(pr,9,2);
#I  substituting new generator _x27 defined by _x1^-1*_x15
#I  eliminating _x15 = _x27*_x1
#I  there are 6 generators and 12 relators of total length 64
gap> TzGoGo(pr);
#I  there are 6 generators and 11 relators of total length 56
gap> TzGoGo(pr);
gap> p2 := Copy(pr);
<< presentation with 6 gens and 11 rels of total length 56 >>
gap> TzPrint(p2);

#I  generators: [ _x1, _x3, _x4, _x6, _x26, _x27 ]
#I  relators:
#I  1.  4  [ -6, -1, 6, 1 ]
#I  2.  4  [ 4, 6, -4, -6 ]
#I  3.  4  [ 5, 4, -5, -4 ]
#I  4.  4  [ 4, -2, -4, 2 ]
#I  5.  4  [ -3, 2, 3, -2 ]
#I  6.  4  [ -3, -1, 3, 1 ]
#I  7.  6  [ -4, 3, 4, 6, -3, -6 ]
#I  8.  6  [ -1, -6, -2, 6, 1, 2 ]
#I  9.  6  [ -6, -2, -5, 6, 2, 5 ]
#I  10.  6  [ 2, 5, 1, -5, -2, -1 ]
#I  11.  8  [ -1, -6, -5, 3, 6, 1, 5, -3 ]
gap> TzPrintPairs(p2);
#I  1.  5  occurrences of  _x1^-1 * _x27^-1
#I  2.  3  occurrences of  _x6 * _x27
#I  3.  3  occurrences of  _x3 * _x26
#I  4.  2  occurrences of  _x3 * _x27
#I  5.  2  occurrences of  _x1 * _x4
#I  6.  2  occurrences of  _x1 * _x3
#I  7.  1  occurrence  of  _x26 * _x27
#I  8.  1  occurrence  of  _x26 * _x27^-1
#I  9.  1  occurrence  of  _x26^-1 * _x27
#I  10.  1  occurrence  of  _x6 * _x27^-1
gap> TzSubstitute(p2,1,2);
#I  substituting new generator _x28 defined by _x1^-1*_x27^-1
#I  eliminating _x27 = _x1^-1*_x28^-1
#I  there are 6 generators and 11 relators of total length 58
gap> TzGoGo(p2);
#I  there are 6 generators and 11 relators of total length 54
gap> TzGoGo(p2);
gap> p3 := Copy(p2);
<< presentation with 6 gens and 11 rels of total length 54 >>
gap> TzSubstitute(p3,3,2);
#I  substituting new generator _x29 defined by _x3*_x26
#I  eliminating _x26 = _x3^-1*_x29
gap> TzGoGo(p3);
#I  there are 6 generators and 11 relators of total length 52
gap> TzGoGo(p3);
gap> TzPrint(p3);
#I  generators: [ _x1, _x3, _x4, _x6, _x28, _x29 ]
#I  relators:
#I  1.  4  [ 6, 4, -6, -4 ]
#I  2.  4  [ 1, -6, -1, 6 ]
#I  3.  4  [ -5, -1, 5, 1 ]
#I  4.  4  [ -2, -5, 2, 5 ]
#I  5.  4  [ 4, -2, -4, 2 ]
#I  6.  4  [ -3, 2, 3, -2 ]

    #I  7.  4  [ -3, -1, 3, 1 ]
    #I  8.  6  [ -2, 5, -6, 2, -5, 6 ]
    #I  9.  6  [ 4, -1, -5, -4, 5, 1 ]
    #I  10.  6  [ -6, 3, -5, 6, -3, 5 ]
    #I  11.  6  [ 3, -5, 4, -3, -4, 5 ]

The resulting presentation could further be simplified by Tietze transformations using TzSubstitute and TzGoGo until one reaches finally a presentation on 6 generators with 11 relators, 9 of which are commutators of the generators. Working by hand from these, the kernel can be identified as a particular subgroup of the direct product of four copies of the free group on two generators.

1.21 About Fields

In this section we will show you some basic computations with fields. GAP supports at present the following fields. The rationals, cyclotomic extensions of rationals and their subfields (which we will refer to as number fields in the following), and finite fields.

Let us first take a look at the infinite fields mentioned above. While the set of rational numbers is a predefined domain in GAP to which you may refer by its identifier Rationals, cyclotomic fields are constructed by using the function CyclotomicField, which may be abbreviated as CF.

    gap> IsField( Rationals );
    true
    gap> Size( Rationals );
    "infinity"
    gap> f := CyclotomicField( 8 );
    CF(8)
    gap> IsSubset( f, Rationals );
    true

The integer argument n of the function call to CF specifies that the cyclotomic field containing all n-th roots of unity should be returned.

Cyclotomic fields are constructed as extensions of the Rationals by primitive roots of unity. Thus a primitive n-th root of unity is always an element of CF(n), where n is a natural number. In GAP, one may construct a primitive n-th root of unity by calling E(n).

    gap> (E(8) + E(8)^3)^2;
    -2
    gap> E(8) in f;
    true

For every field extension you can compute the Galois group, i.e., the group of automorphisms that leave the subfield fixed. For an example, cyclotomic fields are an extension of the rationals, so you can compute their Galois group over the rationals.

    gap> Galf := GaloisGroup( f );
    Group( NFAutomorphism( CF(8) , 7 ), NFAutomorphism( CF(8) , 5 ) )
    gap> Size( Galf );
    4

The above cyclotomic field is a small example where the Galois group is not cyclic.

    gap> IsCyclic( Galf );
    false
    gap> IsAbelian( Galf );
    true
    gap> AbelianInvariants( Galf );
    [ 2, 2 ]

This shows us that the 8th cyclotomic field has a Galois group which is isomorphic to group V_{4}.

The elements of the Galois group are GAP automorphisms, so they may be applied to the elements of the field in the same way as all mappings are usually applied to objects in GAP.

    gap> g := Galf.generators[1];
    NFAutomorphism( CF(8) , 7 )
    gap> E(8) ^g;
    -E(8)^3

There are two functions, Norm and Trace, which compute the norm and the trace of elements of the field, respectively. The norm and the trace of an element a are defined to be the product and the sum of the images of a under the Galois group. You should usually specify the field as a first argument. This argument is however optional. If you omit a default field will be used. For a cyclotomic a this is the smallest cyclotomic field that contains a (note that this is not the smallest field that contains a, which may be a number field that is not a cyclotomic field).

    gap> orb := List( Elements( Galf ), x -> E(8) ^x );
    [ E(8), E(8)^3, -E(8), -E(8)^3 ]
    gap> Sum( orb ) = Trace( f, E(8) );
    true
    gap> Product( orb ) = Norm( f, E(8) );
    true
    gap> Trace( f, 1 );
    4

The basic way to construct a finite field is to use the function GaloisField which may be abbreviated, as usual in algebra, as GF. Thus

    gap> k := GF( 3, 4 );
    GF(3^4)

or

    gap> k := GaloisField( 81 );
    GF(3^4)

will assign the finite field of order 3^{4} to the variable k.

In fact, what GF does is to set up a record which contains all necessary information, telling that it represents a finite field of degree 4 over its prime field with 3 elements. Of course, all arguments to GF others than those which represent a prime power are rejected – for obvious reasons.

Some of the more important entries of the field record are zero, one and root, which hold the corresponding elements of the field. All elements of a finite field are represented as a certain power of an appropriate primitive root, which is written as Z(q). As can be seen below the smallest possible primitive root is used.

    gap> k.one + k.root + k.root^10 - k.zero;
    Z(3^4)^52
    gap> k.root;
    Z(3^4)
    gap> k.root ^20;
    Z(3^2)^2
    gap> k.one;
    Z(3)^0

Note that of course elements from fields of different characteristic cannot be combined in operations.

    gap> Z(3^2) * k.root + k.zero + Z(3^8);
    Z(3^8)^6534
    gap> Z(2) * k.one;
    Error, Finite field *: operands must have the same characteristic

In this example we tried to multiply a primitive root of the field with two elements with the identity element of the field k. As the characteristic of k equals 3, there is no way to perform the multiplication. The first statement of the example shows, that if all the elements of the expression belong to fields of the same characteristic, the result will be computed.

As soon as a primitive root is demanded, GAP internally sets up all relevant data structures that are necessary to compute in the corresponding finite field. Each finite field is constructed as a splitting field of a Conway polynomial. These polynomials, as a set, have special properties that make it easy to embed smaller fields in larger ones and to convert the representation of the elements when doing so. All Conway polynomials for fields up to an order of 65536 have been computed and installed in the GAP kernel.

But now look at the following example.

    gap> Z(3^3) * Z(3^4);
    Error, Finite field *: smallest common superfield to large

Although both factors are elements of fields of characteristic 3, the product can not be evaluated by GAP. The reason for this is very easy to explain:In order to compute the product, GAP has to find a field in which both of the factors lie. Here in our example the smallest field containing Z(3^{3}) and Z(3^{4}) is GF (3^{12}), the field with 531441 elements. As we have mentioned above that the size of finite fields in GAP is limited at present by 65536 we now see that there is no chance to set up the internal data structures for the common field to perform the computation.

As before with cyclotomic fields, the Galois group of a finite field and the norm and trace of its elements may be computed. The calling conventions are the same as for cyclotomic fields.

    gap> Galk := GaloisGroup( k );
    Group( FrobeniusAutomorphism( GF(3^4) ) )
    gap> Size( Galk );
    4
    gap> IsCyclic( Galk );

true
gap> Norm( k, k.root ^20 );
Z(3)^0
gap> Trace( k, k.root ^20 );
0*Z(3)

So far, in our examples, we were always interested in the Galois group of a field extension k over its prime field. In fact it often will occur that, given a subfield l of k the Galois group of k over l is desired. In GAP it is possible to change the structure of a field by using the / operator. So typing

gap> l := GF(3^2);
GF(3^2)
gap> IsSubset( k, l );
true
gap> k / l;
GF(3^4)/GF(3^2)

changes the representation of k from a field extension of degree 4 over GF (3) to a field given as an extension of degree 2 over GF (3^{2}). The actual elements of the fields are still the same, only the structure of the field has changed.

gap> k = k / l;
true
gap> Galkl := GaloisGroup( k / l );
Group( FrobeniusAutomorphism( GF(3^4)/GF(3^2) )^2 )
gap> Size( Galkl );
2

Of course, all the relevant functions behave in a different way when they refer to k / l instead of k

gap> Norm( k / l, k.root ^20 );
Z(3)
gap> Trace( k / l, k.root ^20 );
Z(3^2)^6

This feature, to change the structure of the field without changing the underlying set of elements, is also available for cyclotomic fields, which we have seen at the beginning of this chapter.

gap> g := CyclotomicField( 4 );
GaussianRationals
gap> IsSubset( f, g );
true
gap> f / g;
CF(8)/GaussianRationals
gap> Galfg := GaloisGroup( f / g );
Group( NFAutomorphism( CF(8)/GaussianRationals , 5 ) )
gap> Size( Galfg );
2

The examples should have shown that, although the structure of finite fields and cyclotomic fields is rather different, there is a similar interface to them in GAP, which makes it easy to write programs that deal with both types of fields in the same way.

1.22 About Matrix Groups

This section intends to show you the things you could do with matrix groups in GAP. In principle all the set theoretic functions mentioned in chapter 4 and all group functions mentioned in chapter 7 can be applied to matrix groups. However, you should note that at present only very few functions can work efficiently with matrix groups. Especially infinite matrix groups (over the rationals or cyclotomic fields) can not be dealt with at all.

Matrix groups are created in the same way as the other types of groups, by using the function Group. Of course, in this case the arguments have to be invertable matrices over a field.

gap> m1 := [ [ Z(3)^0, Z(3)^0, Z(3) ],
>             [ Z(3), 0*Z(3), Z(3) ],
>             [ 0*Z(3), Z(3), 0*Z(3) ] ];;
gap> m2 := [ [ Z(3), Z(3), Z(3)^0 ],
>            [ Z(3), 0*Z(3), Z(3) ],
>            [ Z(3)^0, 0*Z(3), Z(3) ] ];;
gap> m := Group( m1, m2 );
Group( [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), 0*Z(3), Z(3) ],
  [ 0*Z(3), Z(3), 0*Z(3) ] ],
[ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
  [ Z(3)^0, 0*Z(3), Z(3) ] ] )

As usual for groups, the matrix group that we have constructed is represented by a record with several entries. For matrix groups, there is one additional entry which holds the field over which the matrix group is written.

gap> m.field;
GF(3)

Note that you do not specify the field when you construct the group. Group automatically takes the smallest field over which all its arguments can be written.

At this point there is the question what special functions are available for matrix groups. The size of our group, for example, may be computed using the function Size.

gap> Size( m );
864

If we now compute the size of the corresponding general linear group

gap> (3^3 - 3^0) * (3^3 - 3^1) * (3^3 - 3^2);
11232

we see that we have constructed a proper subgroup of index 13 of GL(3, 3).

Let us now set up a subgroup of m, which is generated by the matrix m2.

gap> n := Subgroup( m, [ m2 ] );
Subgroup( Group( [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), 0*Z(3), Z(3) ],
  [ 0*Z(3), Z(3), 0*Z(3) ] ],
[ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
  [ Z(3)^0, 0*Z(3), Z(3) ] ] ),
[ [ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
      [ Z(3)^0, 0*Z(3), Z(3) ] ] ] )
gap> Size( n );

    6

And to round up this example we now compute the centralizer of this subgroup in m.

    gap> c := Centralizer( m, n );
    Subgroup( Group( [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), 0*Z(3), Z(3) ],
      [ 0*Z(3), Z(3), 0*Z(3) ] ],
    [ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
      [ Z(3)^0, 0*Z(3), Z(3) ] ] ),
    [ [ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
          [ Z(3)^0, 0*Z(3), Z(3) ] ],
      [ [ Z(3), 0*Z(3), 0*Z(3) ], [ 0*Z(3), Z(3), 0*Z(3) ],
          [ 0*Z(3), 0*Z(3), Z(3) ] ] ] )
    gap> Size( c );
    12

In this section you have seen that matrix groups are constructed in the same way that all groups are constructed. You have also been warned that only very few functions can work efficiently with matrix groups. See chapter 34 to read more about matrix groups.

1.23 About Domains and Categories

Domain is GAP’s name for structured sets. We already saw examples of domains in the previous sections. For example, the groups s8 and a8 in sections 1.18 and 1.19 are domains. Likewise the fields in section 1.21 are domains. Categories are sets of domains. For example, the set of all groups forms a category, as does the set of all fields.

In those sections we treated the domains as black boxes. They were constructed by special functions such as Group and GaloisField, and they could be passed as arguments to other functions such as Size and Orbits.

In this section we will also treat domains as black boxes. We will describe how domains are created in general and what functions are applicable to all domains. Next we will show how domains with the same structure are grouped into categories and will give an overview of the categories that are available. Then we will discuss how the organization of the GAP library around the concept of domains and categories is reflected in this manual. In a later section we will open the black boxes and give an overview of the mechanism that makes all this work (see 1.27).

The first thing you must know is how you can obtain domains. You have basically three possibilities. You can use the domains that are predefined in the library, you can create new domains with domain constructors, and you can use the domains returned by many library functions. We will now discuss those three possibilities in turn.

The GAP library predefines some domains. That means that there is a global variable whose value is this domain. The following example shows some of the more important predefined domains.

    gap> Integers;

Integers # the ring of all integers

    gap> Size( Integers );
    "infinity"
    gap> GaussianRationals;

GaussianRationals # the field of all Gaussian

    gap> (1/2+E(4)) in GaussianRationals;

true # E(4) is GAP’s name for the complex root of -1

    gap> Permutations;

Permutations # the domain of all permutations Note that GAP prints those domains using the name of the global variable. You can create new domains using domain constructors such as Group, Field, etc. A domain constructor is a function that takes a certain number of arguments and returns the domain described by those arguments. For example, Group takes an arbitrary number of group elements (of the same type) and returns the group generated by those elements.

    gap> gf16 := GaloisField( 16 );

GF(2^4) # the finite field with 16 elements

    gap> Intersection( gf16, GaloisField( 64 ) );
    GF(2^2)
    gap> a5 := Group( (1,2,3), (3,4,5) );

Group( (1,2,3), (3,4,5) ) # the alternating group on 5 points

    gap> Size( a5 );
    60

Again GAP prints those domains using more or less the expression that you entered to obtain the domain. As with groups (see 1.18) a name can be assigned to an arbitrary domain D with the assignment D.name := string;, and GAP will use this name from then on in the output. Many functions in the GAP library return domains. In the last example you already saw that Intersection returned a finite field domain. Below are more examples.

    gap> GaloisGroup( gf16 );
    Group( FrobeniusAutomorphism( GF(2^4) ) )
    gap> SylowSubgroup( a5, 2 );
    Subgroup( Group( (1,2,3), (3,4,5) ), [ (2,5)(3,4), (2,3)(4,5) ] )

The distinction between domain constructors and functions that return domains is a little bit arbitrary. It is also not important for the understanding of what follows. If you are nevertheless interested, here are the principal differences. A constructor performs no computation, while a function performs a more or less complicated computation. A constructor creates the representation of the domain, while a function relies on a constructor to create the domain. A constructor knows the dirty details of the domain’s representation, while a function may be independent of the domain’s representation. A constructor may appear as printed representation of a domain, while a function usually does not. After showing how domains are created, we will now discuss what you can do with domains. You can assign a domain to a variable, put a domain into a list or into a record, pass a domain as argument to a function, and return a domain as result of a function. In this regard there is no difference between an integer value such as 17 and a domain such as Group( (1,2,3), (3,4,5) ). Of course many functions will signal an error when you call them with domains as arguments. For example, Gcd does not accept two groups as arguments, because they lie in no Euclidean ring. There are some functions that accept domains of any type as their arguments. Those functions are called the set theoretic functions. The full list of set theoretic functions is given in chapter 4.

Above we already used one of those functions, namely Size. If you look back you will see that we applied Size to the domain Integers, which is a ring, and the domain a5, which is a group. Remember that a domain was a structured set. The size of the domain is the number of elements in the set. Size returns this number or the string "infinity" if the domain is infinite. Below are more examples.

    gap> Size( GaussianRationals );

"infinity" # this string is returned for infinite domains

    gap> Size( SylowSubgroup( a5, 2 ) );
    4

IsFinite( D ) returns true if the domain D is finite and false otherwise. You could also test if a domain is finite using Size( D ) < "infinity" (GAP evaluates n < "infinity" to true for any number n). IsFinite is more efficient. For example, if D is a permutation group, IsFinite( D ) can immediately return true, while Size( D ) may take quite a while to compute the size of D.

The other function that you already saw is Intersection. Above we computed the intersection of the field with 16 elements and the field with 64 elements. The following example is similar.

    gap> Intersection( a5, Group( (1,2), (1,2,3,4) ) );

Group( (2,3,4), (1,2)(3,4) ) # alternating group on 4 points

In general Intersection tries to return a domain. In general this is not possible however. Remember that a domain is a structured set. If the two domain arguments have different structure the intersection may not have any structure at all. In this case Intersection returns the result as a proper set, i.e., as a sorted list without holes and duplicates. The following example shows such a case. ConjugacyClass returns the conjugacy class of (1,2,3,4,5) in the alternating group on 6 points as a domain. If we intersect this class with the symmetric group on 5 points we obtain a proper set of 12 permutations, which is only one half of the conjugacy class of 5 cycles in s5.

    gap> a6 := Group( (1,2,3), (2,3,4,5,6) );
    Group( (1,2,3), (2,3,4,5,6) )
    gap> class := ConjugacyClass( a6, (1,2,3,4,5) );
    ConjugacyClass( Group( (1,2,3), (2,3,4,5,6) ), (1,2,3,4,5) )
    gap> Size( class );
    72
    gap> s5 := Group( (1,2), (2,3,4,5) );
    Group( (1,2), (2,3,4,5) )
    gap> Intersection( class, s5 );
    [ (1,2,3,4,5), (1,2,4,5,3), (1,2,5,3,4), (1,3,5,4,2), (1,3,2,5,4),
      (1,3,4,2,5), (1,4,3,5,2), (1,4,5,2,3), (1,4,2,3,5), (1,5,4,3,2),
      (1,5,2,4,3), (1,5,3,2,4) ]

You can intersect arbitrary domains as the following example shows.

    gap> Intersection( Integers, a5 );

[ ] # the empty set

Note that we optimized Intersection for typical cases, e.g., computing the intersection of two permutation groups, etc. The above computation is done with a very simple–minded method, all elements of a5 are listed (with Elements, described below), and for each element Intersection tests whether it lies in Integers (with in, described below). So the same computation with the alternating group on 10 points instead of a5 will probably exhaust your patience. Just as Intersection returns a proper set occasionally, it also accepts proper sets as arguments. Intersection also takes an arbitrary number of arguments. And finally it also accepts a list of domains or sets to intersect as single argument.

    gap> Intersection( a5, [ (1,2), (1,2,3), (1,2,3,4), (1,2,3,4,5) ] );
    [ (1,2,3), (1,2,3,4,5) ]
    gap> Intersection( [2,4,6,8,10], [3,6,9,12,15], [5,10,15,20,25] );
    [  ]
    gap> Intersection( [ [1,2,4], [2,3,4], [1,3,4] ] );
    [ 4 ]

The function Union is the obvious counterpart of Intersection. Note that Union usually does not return a domain. This is because the union of two domains, even of the same type, is usually not again a domain of that type. For example, the union of two subgroups is a subgroup if and only if one of the subgroups is a subset of the other. Of course this is exactly the reason why Union is less important than Intersection in algebra. Because domains are structured sets there ought to be a membership test that tests whether an object lies in this domain or not. This is not implemented by a function, instead the operator in is used. elm in D returns true if the element elm lies in the domain D and false otherwise. We already used the in operator above when we tested whether 1/2 + E(4) lies in the domain of Gaussian integers.

    gap> (1,2,3) in a5;
    true
    gap> (1,2) in a5;
    false
    gap> (1,2,3,4,5,6,7) in a5;
    false
    gap> 17 in a5;

false # of course an integer does not lie in a permutation group

    gap> a5 in a5;
    false

As you can see in the last example, in only implements the membership test. It does not allow you to test whether a domain is a subset of another domain. For such tests the function IsSubset is available.

    gap> IsSubset( a5, a5 );
    true
    gap> IsSubset( a5, Group( (1,2,3) ) );
    true
    gap> IsSubset( Group( (1,2,3) ), a5 );
    false

In the above example you can see that IsSubset tests whether the second argument is a subset of the first argument. As a general rule GAP library functions take as first arguments those arguments that are in some sense larger or more structured. Suppose that you want to loop over all elements of a domain. For example, suppose that you want to compute the set of element orders of elements in the group a5. To use the for loop you need a list of elements in the domain D, because for var in D do statements od will not work. The function Elements does exactly that. It takes a domain D and returns the proper set of elements of D.

gap> Elements( Group( (1,2,3), (2,3,4) ) );
[ (), (2,3,4), (2,4,3), (1,2)(3,4), (1,2,3), (1,2,4), (1,3,2),
  (1,3,4), (1,3)(2,4), (1,4,2), (1,4,3), (1,4)(2,3) ]
gap> ords := [];;
gap> for elm  in Elements( a5 )  do
>        Add( ords, Order( a5, elm ) );
>    od;
gap> Set( ords );
[ 1, 2, 3, 5 ]
gap> Set( List( Elements( a5 ), elm -> Order( a5, elm ) ) );

[ 1, 2, 3, 5 ] # an easier way to compute the set of orders

Of course, if you apply Elements to an infinite domain, Elements will signal an error. It is also not a good idea to apply Elements to very large domains because the list of elements will take much space and computing this large list will probably exhaust your patience.

gap> Elements( GaussianIntegers );
Error, the ring <R> must be finite to compute its elements in
D.operations.Elements( D ) called from
Elements( GaussianIntegers ) called from
main loop
brk> quit;

There are a few more set theoretic functions. See chapter 4 for a complete list.

All the set theoretic functions treat the domains as if they had no structure. Now a domain is a structured set (excuse us for repeating this again and again, but it is really important to get this across). If the functions ignore the structure than they are effectively viewing a domain only as the set of elements.

In fact all set theoretic functions also accept proper sets, i.e., sorted lists without holes and duplicates as arguments (we already mentioned this for Intersection). Also set theoretic functions may occasionally return proper sets instead of domains as result.

This equivalence of a domain and its set of elements is particularly important for the definition of equality of domains. Two domains D and E are equal (in the sense that D = E evaluates to true) if and only if the set of elements of D is equal to the set of elements of E (as returned by Elements( D ) and Elements( E )). As a special case either of the operands of = may also be a proper set, and the value is true if this set is equal to the set of elements of the domain.

gap> a4 := Group( (1,2,3), (2,3,4) );
Group( (1,2,3), (2,3,4) )
gap> elms := Elements( a4 );
[ (), (2,3,4), (2,4,3), (1,2)(3,4), (1,2,3), (1,2,4), (1,3,2),
  (1,3,4), (1,3)(2,4), (1,4,2), (1,4,3), (1,4)(2,3) ]
gap> elms = a4;
true

However the following example shows that this does not imply that all functions return the same answer for two domains (or a domain and a proper set) that are equal. This is because those function may take the structure into account.

gap> IsGroup( a4 );
true
gap> IsGroup( elms );
false
gap> Intersection( a4, Group( (1,2), (1,2,3) ) );
Group( (1,2,3) )
gap> Intersection( elms, Group( (1,2), (1,2,3) ) );
[ (), (1,2,3), (1,3,2) ]  # this is not a group
gap> last = last2;

true # but it is equal to the above result

gap> Centre( a4 );
Subgroup( Group( (1,2,3), (2,3,4) ), [  ] )
gap> Centre( elms );
Error, <G> must be a group in
Centre( elms ) called from
main loop
brk> quit;

Generally three things may happen if you have two domains D and E that are equal but have different structure (or a domain D and a set E that are equal). First a function that tests whether a domain has a certain structure may return true for D and false for E. Second a function may return a domain for D and a proper set for E. Third a function may work for D and fail for E, because it requires the structure.

A slightly more complex example for the second case is the following.

gap> v4 := Subgroup( a4, [ (1,2)(3,4), (1,3)(2,4) ] );
Subgroup( Group( (1,2,3), (2,3,4) ), [ (1,2)(3,4), (1,3)(2,4) ] )
gap> v4.name := "v4";;
gap> rc := v4 * (1,2,3);
(v4*(2,4,3))
gap> lc := (1,2,3) * v4;
((1,2,3)*v4)
gap> rc = lc;
true
gap> rc * (1,3,2);
(v4*())
gap> lc * (1,3,2);
[ (1,3)(2,4), (), (1,2)(3,4), (1,4)(2,3) ]
gap> last = last2;
false

The two domains rc and lc (yes, cosets are domains too) are equal, because they have the same set of elements. However if we multiply both with (1,3,2) we obtain the trivial right coset for rc and a list for lc. The result for lc is not a proper set, because it is not sorted, therefore = evaluates to false. (For the curious. The multiplication of a left coset with an element from the right will generally not yield another coset, i.e., nothing that can easily be represented as a domain. Thus to multiply lc with (1,3,2) GAP first converts lc to the set of its elements with Elements. But the definition of multiplication requires that a list l multiplied by an element e yields a new list n such that each element n[i] in the new list is the product of the element l[i] at the same position of the operand list l with e.) Note that the above definition only defines what the result of the equality comparison of two domains D and E should be. It does not prescribe that this comparison is actually performed by listing all elements of D and E. For example, if D and E are groups, it is sufficient to check that all generators of D lie in E and that all generators of E lie in D. If GAP would really compute the whole set of elements, the following test could not be performed on any computer.

    gap> Group( (1,2), (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) )
    > = Group( (17,18), (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18) );
    true

If we could only apply the set theoretic functions to domains, domains would be of little use. Luckily this is not so. We already saw that we could apply GaloisGroup to the finite field with 16 elements, and SylowSubgroup to the group a5. But those functions are not applicable to all domains. The argument of GaloisGroup must be a field, and the argument of SylowSubgroup must be a group. A category is a set of domains. So we say that the argument of GaloisGroup must be an element of the category of fields, and the argument of SylowSubgroup must be an element of the category of groups. The most important categories are rings, fields, groups, and vector spaces. Which category a domain belongs to determines which functions are applicable to this domain and its elements. We want to emphasize the each domain belongs to one and only one category. This is necessary because domains in different categories have, sometimes incompatible, representations. Note that the categories only exist conceptually. That means that there is no GAP object for the categories, e.g., there is no object Groups. For each category there exists a function that tests whether a domain is an element of this category.

    gap> IsRing( gf16 );
    false
    gap> IsField( gf16 );
    true
    gap> IsGroup( gf16 );
    false
    gap> IsVectorSpace( gf16 );
    false

Note that of course mathematically the field gf16 is also a ring and a vector space. However in GAP a domain can only belong to one category. So a domain is conceptually a set of elements with one structure, e.g., a field structure. That the same set of elements may also support a different structure, e.g., a ring or vector space structure, can not be represented by this domain. So you need a different domain to represent this different structure. (We are planning to add functions that changes the structure of a domain, e.g. AsRing( field ) should return a new domain with the same elements as field but with a ring structure.) Domains may have certain properties. For example a ring may be commutative and a group may be nilpotent. Whether a domain has a certain property Property can be tested with the function IsProperty.

    gap> IsCommutativeRing( GaussianIntegers );
    true
    gap> IsNilpotent( a5 );
    false

There are also similar functions that test whether a domain (especially a group) is represented in a certain way. For example IsPermGroup tests whether a group is represented as a permutation group.

    gap> IsPermGroup( a5 );
    true
    gap> IsPermGroup( a4 / v4 );

false # a4 / v4 is represented as a generic factor group

There is a slight difference between a function such as IsNilpotent and a function such as IsPermGroup. The former tests properties of an abstract group and its outcome is independent of the representation of that group. The latter tests whether a group is given in a certain representation.

This (rather philosophical) issue is further complicated by the fact that sometimes representations and properties are not independent. This is especially subtle with IsSolvable (see 7.58) and IsAgGroup (see 24.27). IsSolvable tests whether a group G is solvable. IsAgGroup tests whether a group G is represented as a finite polycyclic group, i.e., by a finite presentation that allows to efficiently compute canonical normal forms of elements (see 24). Of course every finite polycyclic group is solvable, so IsAgGroup( G ) implies

IsSolvable( G ). On the other hand IsSolvable( G ) does not imply IsAgGroup( G

), because, even though each solvable group can be represented as a finite polycyclic group, it need not, e.g., it could also be represented as a permutation group.

The organization of the manual follows the structure of domains and categories.

After the description of the programming language and the environment chapter 4 describes the domains and the functions applicable to all domains.

Next come the chapters that describe the categories rings, fields, groups, and vector spaces.

The remaining chapters describe GAP’s data–types and the domains one can make with those elements of those data-types. The order of those chapters roughly follows the order of the categories. The data–types whose elements form rings and fields come first (e.g., integers and finite fields), followed by those whose elements form groups (e.g., permutations), and so on. The data–types whose elements support little or no algebraic structure come last (e.g., booleans). In some cases there may be two chapters for one data–type, one describing the elements and the other describing the domains made with those elements (e.g., permutations and permutation groups).

The GAP manual not only describes what you can do, it also gives some hints how GAP performs its computations. However, it can be tricky to find those hints. The index of this manual can help you.

Suppose that you want to intersect two permutation groups. If you read the section that describes the function Intersection (see 4.12) you will see that the last paragraph describes the default method used by Intersection. Such a last paragraph that describes the default method is rather typical. In this case it says that Intersection computes the proper set of elements of both domains and intersect them. It also says that this method is often overlaid with a more efficient one. You wonder whether this is the case for permutation groups. How can you find out? Well you look in the index under Intersection. There you will find a reference Intersection, for permutation groups to section Set Functions for Permutation Groups (see 20.19). This section tells you that Intersection uses a backtrack for permutation groups (and cites a book where you can find a description of the backtrack).

Let us now suppose that you intersect two factor groups. There is no reference in the index for Intersection, for factor groups. But there is a reference for Intersection, for groups to the section Set Functions for Groups (see 7.107). Since this is the next best thing, look there. This section further directs you to the section Intersection for Groups (see 7.109). This section finally tells you that Intersection computes the intersection of two groups G and H as the stabilizer in G of the trivial coset of H under the operation of G on the right cosets of H .

In this section we introduced domains and categories. You have learned that a domain is a structured set, and that domains are either predefined, created by domain constructors, or returned by library functions. You have seen most functions that are applicable to all domains. Those functions generally ignore the structure and treat a domain as the set of its elements. You have learned that categories are sets of domains, and that the category a domain belongs to determines which functions are applicable to this domain.

More information about domains can be found in chapter 4. Chapters 5, 6, 7, and 9 define the categories known to GAP. The section 1.27 opens that black boxes and shows how all this works.

1.24 About Mappings and Homomorphisms

A mapping is an object which maps each element of its source to a value in its range. Source and range can be arbitrary sets of elements. But in most applications the source and range are structured sets and the mapping, in such applications called homomorphism, is compatible with this structure.

In the last sections you have already encountered examples of homomorphisms, namely natural homomorphisms of groups onto their factor groups and operation homomorphisms of groups into symmetric groups.

Finite fields also bear a structure and homomorphisms between fields are always bijections. The Galois group of a finite field is generated by the Frobenius automorphism. It is very easy to construct.

    gap> f := FrobeniusAutomorphism( GF(81) );
    FrobeniusAutomorphism( GF(3^4) )
    gap> Image( f, Z(3^4) );
    Z(3^4)^3
    gap> A := Group( f );
    Group( FrobeniusAutomorphism( GF(3^4) ) )
    gap> Size( A );
    4
    gap> IsCyclic( A );
    true
    gap> Order( Mappings, f );

4
gap> Kernel( f );
[ 0*Z(3) ]

For finite fields and cyclotomic fields the function GaloisGroup is an easy way to construct the Galois group.

gap> GaloisGroup( GF(81) );
Group( FrobeniusAutomorphism( GF(3^4) ) )
gap> Size( last );
4
gap> GaloisGroup( CyclotomicField( 18 ) );
Group( NFAutomorphism( CF(9) , 2 ) )
gap> Size( last );
6

Not all group homomorphisms are bijections of course, natural homomorphisms do have a kernel in most cases and operation homomorphisms need neither be surjective nor injective.

gap> s4 := Group( (1,2,3,4), (1,2) );
Group( (1,2,3,4), (1,2) )
gap> s4.name := "s4";;
gap> v4 := Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] );
Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] )
gap> v4.name := "v4";;
gap> s3 := s4 / v4;
(s4 / v4)
gap> f := NaturalHomomorphism( s4, s3 );
NaturalHomomorphism( s4, (s4 / v4) )
gap> IsHomomorphism( f );
true
gap> IsEpimorphism( f );
true
gap> Image( f );
(s4 / v4)
gap> IsMonomorphism( f );
false
gap> Kernel( f );
v4

The image of a group homomorphism is always one element of the range but the preimage can be a coset. In order to get one representative of this coset you can use the function

PreImagesRepresentative.
gap> Image( f, (1,2,3,4) );
FactorGroupElement( v4, (2,4) )
gap> PreImages( f, s3.generators[1] );
(v4*(2,4))
gap> PreImagesRepresentative( f, s3.generators[1] );
(2,4)

But even if the homomorphism is a monomorphism but not surjective you can use the function PreImagesRepresentative in order to get the preimage of an element of the range.

gap> A := Z(3) * [ [ 0, 1 ], [ 1, 0 ] ];;
gap> B := Z(3) * [ [ 0, 1 ], [ -1, 0 ] ];;
gap> G := Group( A, B );
Group( [ [ 0*Z(3), Z(3) ], [ Z(3), 0*Z(3) ] ],
[ [ 0*Z(3), Z(3) ], [ Z(3)^0, 0*Z(3) ] ] )
gap> Size( G );
8
gap> G.name := "G";;
gap> d8 := Operation( G, Orbit( G, Z(3)*[1,0] ) );
Group( (1,2)(3,4), (1,2,3,4) )
gap> e := OperationHomomorphism( Subgroup( G, [B] ), d8 );
OperationHomomorphism( Subgroup( G,
[ [ [ 0*Z(3), Z(3) ], [ Z(3)^0, 0*Z(3) ] ] ] ), Group( (1,2)(3,4),
(1,2,3,4) ) )
gap> Kernel( e );
Subgroup( G, [  ] )
gap> IsSurjective( e );
false
gap> PreImages( e, (1,3)(2,4) );
(Subgroup( G, [  ] )*[ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ])
gap> PreImage( e, (1,3)(2,4) );
Error, <bij> must be a bijection, not an arbitrary mapping in
bij.operations.PreImageElm( bij, img ) called from
PreImage( e, (1,3)(2,4) ) called from
main loop
brk> quit;
gap> PreImagesRepresentative( e, (1,3)(2,4) );
[ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ]

Only bijections allow PreImage in order to get the preimage of an element of the range.

gap> Operation( G, Orbit( G, Z(3)*[1,0] ) );
Group( (1,2)(3,4), (1,2,3,4) )
gap> d := OperationHomomorphism( G, last );
OperationHomomorphism( G, Group( (1,2)(3,4), (1,2,3,4) ) )
gap> PreImage( d, (1,3)(2,4) );
[ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ]

Both PreImage and PreImages can also be applied to sets. They return the complete preimage.

gap> PreImages( d, Group( (1,2)(3,4), (1,3)(2,4) ) );
Subgroup( G, [ [ [ 0*Z(3), Z(3) ], [ Z(3), 0*Z(3) ] ],
  [ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ] ] )
gap> Size( last );
4
gap> f := NaturalHomomorphism( s4, s3 );
NaturalHomomorphism( s4, (s4 / v4) )
gap> PreImages( f, s3 );
Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4), (2,4), (3,4) ] )
gap> Size( last );

24

Another way to construct a group automorphism is to use elements in the normalizer of a subgroup and construct the induced automorphism. A special case is the inner automorphism induced by an element of a group, a more general case is a surjective homomorphism induced by arbitrary elements of the parent group.

gap> d12 := Group((1,2,3,4,5,6),(2,6)(3,5));;  d12.name := "d12";;
gap> i1 := InnerAutomorphism( d12, (1,2,3,4,5,6) );
InnerAutomorphism( d12, (1,2,3,4,5,6) )
gap> Image( i1, (2,6)(3,5) );
(1,3)(4,6)
gap> IsAutomorphism( i1 );
true

Mappings can also be multiplied, provided that the range of the first mapping is a subgroup of the source of the second mapping. The multiplication is of course defined as the composition. Note that, in line with the fact that mappings operate from the right, Image( map1 * map2 , elm ) is defined as Image( map2 , Image( map1 , elm ) ).

gap> i2 := InnerAutomorphism( d12, (2,6)(3,5) );
InnerAutomorphism( d12, (2,6)(3,5) )
gap> i1 * i2;
InnerAutomorphism( d12, (1,6)(2,5)(3,4) )
gap> Image( last, (2,6)(3,5) );
(1,5)(2,4)

Mappings can also be inverted, provided that they are bijections.

gap> i1 ^-1;
InnerAutomorphism( d12, (1,6,5,4,3,2) )
gap> Image( last, (2,6)(3,5) );
(1,5)(2,4)

Whenever you have a set of bijective mappings on a finite set (or domain) you can construct the group generated by those mappings. So in the following example we create the group of inner automorphisms of d12.

gap> autd12 := Group( i1, i2 );
Group( InnerAutomorphism( d12,
(1,2,3,4,5,6) ), InnerAutomorphism( d12, (2,6)(3,5) ) )
gap> Size( autd12 );
6
gap> Index( d12, Centre( d12 ) );
6

Note that the computation with such automorphism groups in their present implementation is not very efficient. For example to compute the size of such an automorphism group all elements are computed. Thus work with such automorphism groups should be restricted to very small examples.

The function ConjugationGroupHomomorphism is a generalization of InnerAutomorphism.

It accepts a source and a range and an element that conjugates the source into the range. Source and range must lie in a common parent group, and the conjugating element must also lie in this parent group.

gap> c2 := Subgroup( d12, [ (2,6)(3,5) ] );
Subgroup( d12, [ (2,6)(3,5) ] )
gap> v4 := Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6) ] );
Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6) ] )
gap> x := ConjugationGroupHomomorphism( c2, v4, (1,3,5)(2,4,6) );
ConjugationGroupHomomorphism( Subgroup( d12,
[ (2,6)(3,5) ] ), Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6)
 ] ), (1,3,5)(2,4,6) )
gap> IsSurjective( x );
false
gap> Image( x );
Subgroup( d12, [ (1,5)(2,4) ] )

But how can we construct homomorphisms which are not induced by elements of the parent group? The most general way to construct a group homomorphism is to define the source, range and the images of the generators under the homomorphism in mind.

gap> c := GroupHomomorphismByImages( G, s4, [A,B], [(1,2),(3,4)] );
GroupHomomorphismByImages( G, s4,
[ [ [ 0*Z(3), Z(3) ], [ Z(3), 0*Z(3) ] ],
  [ [ 0*Z(3), Z(3) ], [ Z(3)^0, 0*Z(3) ] ] ], [ (1,2), (3,4) ] )
gap> Kernel( c );
Subgroup( G, [ [ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ] ] )
gap> Image( c );
Subgroup( s4, [ (1,2), (3,4) ] )
gap> IsHomomorphism( c );
true
gap> Image( c, A );
(1,2)
gap> PreImages( c, (1,2) );
(Subgroup( G, [ [ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3) ] ] ] )*
[ [ 0*Z(3), Z(3) ], [ Z(3), 0*Z(3) ] ])

Note that it is possible to construct a general mapping this way that is not a homomorphism, because GroupHomomorphismByImages does not check if the given images fulfill the relations of the generators.

gap> b := GroupHomomorphismByImages( G, s4, [A,B], [(1,2,3),(3,4)] );
GroupHomomorphismByImages( G, s4,
[ [ [ 0*Z(3), Z(3) ], [ Z(3), 0*Z(3) ] ],
  [ [ 0*Z(3), Z(3) ], [ Z(3)^0, 0*Z(3) ] ] ], [ (1,2,3), (3,4) ] )
gap> IsHomomorphism( b );
false
gap> Images( b, A );
(Subgroup( s4, [ (1,3,2), (2,3,4), (1,3,4), (1,4)(2,3), (1,4,2)
 ] )*())

The result is a multi valued mapping, i.e., one that maps each element of its source to a set of elements in its range. The set of images of A under b is defined as follows. Take all the words of two letters w(x, y) such that w(A, B) = A, e.g., x and xyxyx. Then the set of images is the set of elements that you get by inserting the images of A and B in those words, i.e., w((1, 2, 3), (3, 4)), e.g., (1, 2, 3) and (1, 4, 2). One can show that the set of images of the identity under a multi valued mapping such as b is a subgroup and that the set of images of other elements are cosets of this subgroup.

1.25 About Character Tables

This section contains some examples of the use of GAP in character theory. First a few very simple commands for handling character tables are introduced, and afterwards we will construct the character tables of (A_{5} × 3):2 and of A_{6}.2^{2}.

GAP has a large library of character tables, so let us look at one of these tables, e.g., the table of the Mathieu group M_{11}:

gap> m11:= CharTable( "M11" );;

The double semicolon is very useful for character tables since they are printed in full length, and they contain a lot of information.

gap> DisplayCharTable( m11 );
M11
    2  4  4  1  3  .  1   3   3   .   .
    3  2  1  2  .  .  1   .   .   .   .
    5  1  .  .  .  1  .   .   .   .   .
   11  1  .  .  .  .  .   .   .   1   1
      1a 2a 3a 4a 5a 6a  8a  8b 11a 11b
   2P 1a 1a 3a 2a 5a 3a  4a  4a 11b 11a
   3P 1a 2a 1a 4a 5a 2a  8a  8b 11a 11b
   5P 1a 2a 3a 4a 1a 6a  8b  8a 11a 11b
  11P 1a 2a 3a 4a 5a 6a  8a  8b  1a  1a
X.1    1  1  1  1  1  1   1   1   1   1
X.2   10  2  1  2  . -1   .   .  -1  -1
X.3   10 -2  1  .  .  1   A  -A  -1  -1
X.4   10 -2  1  .  .  1  -A   A  -1  -1
X.5   11  3  2 -1  1  .  -1  -1   .   .
X.6   16  . -2  .  1  .   .   .   B  /B
X.7   16  . -2  .  1  .   .   .  /B   B
X.8   44  4 -1  . -1  1   .   .   .   .
X.9   45 -3  .  1  .  .  -1  -1   1   1
X.10  55 -1  1 -1  . -1   1   1   .   .
A = E(8)+E(8)^3
  = ER(-2) = i2
B = E(11)+E(11)^3+E(11)^4+E(11)^5+E(11)^9
  = (-1+ER(-11))/2 = b11

We are not too much interested in the internal structure of this character table (see 42.2); but of course we can access all information about the centralizer orders (first four lines), element orders (next line), power maps for the prime divisors of the group order (next four lines), irreducible characters (lines parametrized by X.1 … X.10) and irrational character values (last four lines), see 42.34 for a detailed description of the format of the displayed table. E.g., the irreducible characters are a list with name m11.irreducibles, and each character is a list of cyclotomic integers (see chapter 13). There are various ways to describe the irrationalities; e.g., the square root of −2 can be entered as E(8) + E(8)^3 or ER(-2), the famous ATLAS of Finite Groups [CCN^{+}85] denotes it as i2.

gap> m11.irreducibles[3];
[ 10, -2, 1, 0, 0, 1, E(8)+E(8)^3, -E(8)-E(8)^3, -1, -1 ]

We can for instance form tensor products of this character with all irreducibles, and compute the decomposition into irreducibles.

gap> tens:= Tensored( [ last ], m11.irreducibles );;
gap> MatScalarProducts( m11, m11.irreducibles, tens );
[ [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1 ],
  [ 0, 0, 0, 0, 1, 0, 0, 1, 1, 0 ], [ 1, 0, 0, 0, 0, 0, 0, 1, 0, 1 ],
  [ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 ],
  [ 0, 0, 0, 0, 0, 1, 0, 1, 1, 1 ], [ 0, 0, 1, 1, 0, 1, 1, 2, 3, 3 ],
  [ 0, 1, 0, 1, 1, 1, 1, 3, 2, 3 ], [ 0, 1, 1, 0, 1, 1, 1, 3, 3, 4 ] ]

The decomposition means for example that the third character in the list tens is the sum of the irreducible characters at positions 5, 8 and 9.

gap> tens[3];
[ 100, 4, 1, 0, 0, 1, -2, -2, 1, 1 ]
gap> tens[3] = Sum( Sublist( m11.irreducibles, [ 5, 8, 9 ] ) );
true

Or we can compute symmetrizations, e.g., the characters χ^{2+}, defined by χ^{2+}(g) = \frac{1}{2}(χ^{2}(g)+ χ(g^{2})), for all irreducibles.

gap> sym:= SymmetricParts( m11, m11.irreducibles, 2 );;
gap> MatScalarProducts( m11, m11.irreducibles, sym );
[ [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 1, 1, 0, 0, 0, 0, 0, 1, 0, 0 ],
  [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 ],
  [ 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 ], [ 0, 1, 0, 0, 1, 0, 1, 1, 0, 1 ],
  [ 0, 1, 0, 0, 1, 1, 0, 1, 0, 1 ], [ 1, 3, 0, 0, 3, 2, 2, 8, 4, 6 ],
  [ 1, 2, 0, 0, 3, 2, 2, 8, 4, 7 ],
  [ 1, 3, 1, 1, 4, 3, 3, 11, 7, 10 ] ]
gap> sym[2];
[ 55, 7, 1, 3, 0, 1, 1, 1, 0, 0 ]
gap> sym[2] = Sum( Sublist( m11.irreducibles, [ 1, 2, 8 ] ) );
true

If the subgroup fusion into a supergroup is known, characters can be induced to this group, e.g., to obtain the permutation character of the action of M_{12} on the cosets of M_{11}.

gap> m12:= CharTable( "M12" );;
gap> permchar:= Induced( m11, m12, [ m11.irreducibles[1] ] );
[ [ 12, 0, 4, 3, 0, 4, 0, 2, 0, 1, 2, 0, 0, 1, 1 ] ]
gap> MatScalarProducts( m12, m12.irreducibles, last );
[ [ 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ]
gap> DisplayCharTable( m12, rec( chars:= permchar ) );

    M12
       2  6  4  6  1  2  5  5  1  2  1  3  3   1   .   .
       3  3  1  1  3  2  .  .  .  1  1  .  .   .   .   .
       5  1  1  .  .  .  .  .  1  .  .  .  .   1   .   .
      11  1  .  .  .  .  .  .  .  .  .  .  .   .   1   1
         1a 2a 2b 3a 3b 4a 4b 5a 6a 6b 8a 8b 10a 11a 11b
      2P 1a 1a 1a 3a 3b 2b 2b 5a 3b 3a 4a 4b  5a 11b 11a
      3P 1a 2a 2b 1a 1a 4a 4b 5a 2a 2b 8a 8b 10a 11a 11b
      5P 1a 2a 2b 3a 3b 4a 4b 1a 6a 6b 8a 8b  2a 11a 11b
     11P 1a 2a 2b 3a 3b 4a 4b 5a 6a 6b 8a 8b 10a  1a  1a
    Y.1  12  .  4  3  .  4  .  2  .  1  2  .   .   1   1

It should be emphasized that the heart of character theory is dealing with lists. Characters are lists, and also the maps which occur are represented as lists. Note that the multiplication of group elements is not available, so we neither have homomorphisms. All we can talk of are class functions, and the lists are regarded as such functions, being the lists of images with respect to a fixed order of conjugacy classes. Therefore we do not write chi( cl ) or cl^chi for the value of the character chi on the class cl, but chi[i] where i is the position of the class cl.

Since the data structures are so basic, most calculations involve compositions of maps; for example, the embedding of a subgroup in a group is described by the so–called subgroup fusion which is a class function that maps each class c of the subgroup to that class of the group that contains c. Consider the symmetric group S_{5} ≅ A_{5}.2 as subgroup of M_{11}. (Do not worry about the names that are used to get library tables, see 42.9 for an overview.)

    gap> s5:= CharTable("A5.2");;
    gap> map:= GetFusionMap( s5, m11 );
    [ 1, 2, 3, 5, 2, 4, 6 ]

The subgroup fusion is already stored on the table. We see that class 1 of s5 is mapped to class 1 of m11 (which means that the identity of S_{5} maps to the identity of M_{11}), classes 2 and 5 of s5 both map to class 2 of m11 (which means that all involutions of S_{5} are conjugate in M_{11}), and so on.

The restriction of a character of m11 to s5 is just the composition of this character with the subgroup fusion map. Viewing this map as list one would call this composition an indirection.

    gap> chi:= m11.irreducibles[3];
    [ 10, -2, 1, 0, 0, 1, E(8)+E(8)^3, -E(8)-E(8)^3, -1, -1 ]
    gap> rest:= List( map, x -> chi[x] );
    [ 10, -2, 1, 0, -2, 0, 1 ]

This looks very easy, and many GAP functions in character theory do such simple calculations. But note that it is not always obvious that a list is regarded as a map, where preimages and/or images refer to positions of certain conjugacy classes.

    gap> alt:= s5.irreducibles[2];
    [ 1, 1, 1, 1, -1, -1, -1 ]

    gap> kernel:= KernelChar( last );
    [ 1, 2, 3, 4 ]

The kernel of a character is represented as the list of (positions of) classes lying in the kernel. We know that the kernel of the alternating character alt of s5 is the alternating group A_{5}. The order of the kernel can be computed as sum of the lengths of the contained classes from the character table, using that the classlengths are stored in the classes component of the table.

    gap> s5.classes;
    [ 1, 15, 20, 24, 10, 30, 20 ]
    gap> Sublist( last, kernel );
    [ 1, 15, 20, 24 ]
    gap> Sum( last );
    60

We chose those classlengths of s5 that belong to the S_{5}–classes contained in the alternating group. The same thing is done in the following command, reflecting the view of the kernel as map.

    gap> List( kernel, x -> s5.classes[x] );
    [ 1, 15, 20, 24 ]
    gap> Sum( kernel, x -> s5.classes[x] );
    60

This small example shows how the functions Sublist, List, and Sum can be used. These functions as well as Filtered were introduced in 1.16, and we will make heavy use of them; in many cases such a command might look very strange, but it is just the translation of a (hardly less complicated) mathematical formula to character theory.

And now let us construct some small character tables!

The group G = (A_{5} × 3):2 is a maximal subgroup of the alternating group A_{8}; G extends to S_{5} × S_{3} in S_{8}. We want to construct the character table of G. First the tables of the subgroup A_{5} × 3 and the supergroup S_{5} × S_{3} are constructed; the tables of the factors of each direct product are again got from the table library using admissible names, see 42.9 for this.

[figure: S_{5}; A_{5}; S_{3}; 3; G; S_{5} × S_{3}; S_{5} × 3; A_{5} × S_{3}]

    gap> a5:= CharTable( "A5" );;
    gap> c3:= CharTable( "Cyclic", 3 );;
    gap> a5xc3:= CharTableDirectProduct( a5, c3 );;
    gap> s5:= CharTable( "A5.2" );;
    gap> s3:= CharTable( "Symmetric", 3 );;
    gap> s3.irreducibles;
    [ [ 1, -1, 1 ], [ 2, 0, -1 ], [ 1, 1, 1 ] ]

# The trivial character shall be the first one.

    gap> SortCharactersCharTable( s3 ); # returns the applied permutation
    (1,2,3)
    gap> s5xs3:= CharTableDirectProduct( s5, s3 );;

G is the normal subgroup of index 2 in S_{5} × S_{3} which contains neither S_{5} nor the normal S_{3}. We want to find the classes of s5xs3 whose union is G. For that, we compute the set of kernels of irreducibles –remember that they are given simply by lists of numbers of contained classes– and then choose those kernels belonging to normal subgroups of index 2.

gap> kernels:= Set( List( s5xs3.irreducibles, KernelChar ) );
[ [ 1 ], [ 1, 2, 3 ], [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ],
  [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
      19, 20, 21 ], [ 1, 3 ],
  [ 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21 ],
  [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ], [ 1, 4, 7, 10 ],
  [ 1, 4, 7, 10, 13, 16, 19 ] ]
gap> sizes:= List( kernels, x -> Sum( Sublist( s5xs3.classes, x ) ) );
[ 1, 6, 360, 720, 3, 360, 360, 60, 120 ]
gap> s5xs3.order;
720
gap> index2:= Sublist( kernels, [ 3, 6, 7 ] );
[ [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ],
  [ 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21 ],
  [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ] ]

In order to decide which kernel describes G, we consider the embeddings of s5 and s3 in s5xs3, given by the subgroup fusions.

gap> s5ins5xs3:= GetFusionMap( s5, s5xs3 );
[ 1, 4, 7, 10, 13, 16, 19 ]
gap> s3ins5xs3:= GetFusionMap( s3, s5xs3 );
[ 1, 2, 3 ]
gap> Filtered( index2, x->Intersection(x,s5ins5xs3)<>s5ins5xs3 and
>                         Intersection(x,s3ins5xs3)<>s3ins5xs3     );
[ [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ] ]
gap> nsg:= last[1];
[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ]

We now construct a first approximation of the character table of this normal subgroup, namely the restriction of s5xs3 to the classes given by nsg.

gap> sub:= CharTableNormalSubgroup( s5xs3, nsg );
#I CharTableNormalSubgroup: classes in [ 8 ] necessarily split
rec( name := "Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ])",\
 order := 360, centralizers := [ 360, 180, 24, 12, 18, 9, 15, 15/2,
  12, 4, 6 ], orders := [ 1, 3, 2, 6, 3, 3, 5, 15, 2, 4, 6
 ], powermap := [ , [ 1, 2, 1, 2, 5, 6, 7, 8, 1, 3, 5 ],
  [ 1, 1, 3, 3, 1, 1, 7, 7, 9, 10, 9 ],,
  [ 1, 2, 3, 4, 5, 6, 1, 2, 9, 10, 11 ] ], classes :=
[ 1, 2, 15, 30, 20, 40, 24, 48, 30, 90, 60
 ], operations := CharTableOps, irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1 ],
  [ 2, -1, 2, -1, 2, -1, 2, -1, 0, 0, 0 ],
  [ 6, 6, -2, -2, 0, 0, 1, 1, 0, 0, 0 ],

[ 4, 4, 0, 0, 1, 1, -1, -1, 2, 0, -1 ],
[ 4, 4, 0, 0, 1, 1, -1, -1, -2, 0, 1 ],
[ 8, -4, 0, 0, 2, -1, -2, 1, 0, 0, 0 ],
[ 5, 5, 1, 1, -1, -1, 0, 0, 1, -1, 1 ],
[ 5, 5, 1, 1, -1, -1, 0, 0, -1, 1, -1 ],
[ 10, -5, 2, -1, -2, 1, 0, 0, 0, 0, 0 ] ], fusions := [ rec(
    name := "A5.2xS3",
    map := [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ] ) ] )

Not all restrictions of irreducible characters of s5xs3 to sub remain irreducible. We compute those restrictions with norm larger than 1.

gap> red:= Filtered( Restricted( s5xs3, sub, s5xs3.irreducibles ),
>                    x -> ScalarProduct( sub, x, x ) > 1 );
[ [ 12, -6, -4, 2, 0, 0, 2, -1, 0, 0, 0 ] ]
gap> Filtered( [ 1 .. Length( nsg ) ],
>              x -> not IsInt( sub.centralizers[x] ) );
[ 8 ]

Note that sub is not actually a character table in the sense of mathematics but only a record with components like a character table. GAP does not know about this subtleties and treats it as a character table.

As the list centralizers of centralizer orders shows, at least class 8 splits into two conjugacy classes in G, since this is the only possibility to achieve integral centralizer orders.

Since 10 restrictions of irreducible characters remain irreducible for G (sub contains 10 irreducibles), only one of the 11 irreducibles of S_{5} × S_{3} splits into two irreducibles of G, in other words, class 8 is the only splitting class.

Thus we create a new approximation of the desired character table (which we call split) where this class is split; 8th and 9th column of the known irreducibles are of course equal, and due to the splitting the second powermap for these columns is ambiguous.

gap> splitting:= [ 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11 ];;
gap> split:= CharTableSplitClasses( sub, splitting );
rec( name := "Split(Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 2\
0 ]),[ 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11 ])", order :=
360, centralizers := [ 360, 180, 24, 12, 18, 9, 15, 15, 15, 12, 4, 6
], classes := [ 1, 2, 15, 30, 20, 40, 24, 24, 24, 30, 90, 60
], orders := [ 1, 3, 2, 6, 3, 3, 5, 15, 15, 2, 4, 6 ], powermap :=
[ , [ 1, 2, 1, 2, 5, 6, 7, [ 8, 9 ], [ 8, 9 ], 1, 3, 5 ],
[ 1, 1, 3, 3, 1, 1, 7, 7, 7, 10, 11, 10 ],,
[ 1, 2, 3, 4, 5, 6, 1, 2, 2, 10, 11, 12 ] ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1 ],
[ 2, -1, 2, -1, 2, -1, 2, -1, -1, 0, 0, 0 ],
[ 6, 6, -2, -2, 0, 0, 1, 1, 1, 0, 0, 0 ],
[ 4, 4, 0, 0, 1, 1, -1, -1, -1, 2, 0, -1 ],
[ 4, 4, 0, 0, 1, 1, -1, -1, -1, -2, 0, 1 ],
[ 8, -4, 0, 0, 2, -1, -2, 1, 1, 0, 0, 0 ],
[ 5, 5, 1, 1, -1, -1, 0, 0, 0, 1, -1, 1 ],

  [ 5, 5, 1, 1, -1, -1, 0, 0, 0, -1, 1, -1 ],
  [ 10, -5, 2, -1, -2, 1, 0, 0, 0, 0, 0, 0 ] ], fusions := [ rec(
      name := "Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ])"
       ,
      map := [ 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11 ] )
 ], operations := CharTableOps )
gap> Restricted( sub, split, red );
[ [ 12, -6, -4, 2, 0, 0, 2, -1, -1, 0, 0, 0 ] ]

To complete the table means to find the missing two irreducibles and to complete the powermaps. For this, there are different possibilities. First, one can try to embed G in A_{8}.

gap> a8:= CharTable( "A8" );;
gap> fus:= SubgroupFusions( split, a8 );
[ [ 1, 4, 3, 9, 4, 5, 8, 13, 14, 3, 7, 9 ],
  [ 1, 4, 3, 9, 4, 5, 8, 14, 13, 3, 7, 9 ] ]
gap> fus:= RepresentativesFusions( split, fus, a8 );
#I RepresentativesFusions: no subtable automorphisms stored
[ [ 1, 4, 3, 9, 4, 5, 8, 13, 14, 3, 7, 9 ] ]
gap> StoreFusion( split, a8, fus[1] );

The subgroup fusion is unique up to table automorphisms. Now we restrict the irreducibles of A_{8} to G and reduce.

gap> rest:= Restricted( a8, split, a8.irreducibles );;
gap> red:= Reduced( split, split.irreducibles, rest );
rec( remainders := [  ], irreducibles :=
   [ [ 6, -3, -2, 1, 0, 0, 1, -E(15)-E(15)^2-E(15)^4-E(15)^8,
          -E(15)^7-E(15)^11-E(15)^13-E(15)^14, 0, 0, 0 ],
      [ 6, -3, -2, 1, 0, 0, 1, -E(15)^7-E(15)^11-E(15)^13-E(15)^14,
          -E(15)-E(15)^2-E(15)^4-E(15)^8, 0, 0, 0 ] ] )
gap> Append( split.irreducibles, red.irreducibles );

The list of irreducibles is now complete, but the powermaps are not yet adjusted. To complete the 2nd powermap, we transfer that of A_{8} to G using the subgroup fusion.

gap> split.powermap;
[ , [ 1, 2, 1, 2, 5, 6, 7, [ 8, 9 ], [ 8, 9 ], 1, 3, 5 ],
  [ 1, 1, 3, 3, 1, 1, 7, 7, 7, 10, 11, 10 ],,
  [ 1, 2, 3, 4, 5, 6, 1, 2, 2, 10, 11, 12 ] ]
gap> TransferDiagram( split.powermap[2], fus[1], a8.powermap[2] );;

And this is the complete table.

gap> split.name:= "(A5x3):2";;
gap> DisplayCharTable( split );
(A5x3):2
    2  3  2  3  2  1  .  .   .   .  2  2  1
    3  2  2  1  1  2  2  1   1   1  1  .  1
    5  1  1  .  .  .  .  1   1   1  .  .  .
      1a 3a 2a 6a 3b 3c 5a 15a 15b 2b 4a 6b

   2P 1a 3a 1a 3a 3b 3c 5a 15a 15b 1a 2a 3b
   3P 1a 1a 2a 2a 1a 1a 5a  5a  5a 2b 4a 2b
   5P 1a 3a 2a 6a 3b 3c 1a  3a  3a 2b 4a 6b
X.1    1  1  1  1  1  1  1   1   1  1  1  1
X.2    1  1  1  1  1  1  1   1   1 -1 -1 -1
X.3    2 -1  2 -1  2 -1  2  -1  -1  .  .  .
X.4    6  6 -2 -2  .  .  1   1   1  .  .  .
X.5    4  4  .  .  1  1 -1  -1  -1  2  . -1
X.6    4  4  .  .  1  1 -1  -1  -1 -2  .  1
X.7    8 -4  .  .  2 -1 -2   1   1  .  .  .
X.8    5  5  1  1 -1 -1  .   .   .  1 -1  1
X.9    5  5  1  1 -1 -1  .   .   . -1  1 -1
X.10  10 -5  2 -1 -2  1  .   .   .  .  .  .
X.11   6 -3 -2  1  .  .  1   A  /A  .  .  .
X.12   6 -3 -2  1  .  .  1  /A   A  .  .  .
A = -E(15)-E(15)^2-E(15)^4-E(15)^8
  = (-1-ER(-15))/2 = -1-b15

There are many ways around the block, so two further methods to complete the table split shall be demonstrated; but we will not go into details. Without use of GAP one could work as follows: The irrationalities –and there must be irrational entries in the character table of G, since the outer 2 can conjugate at most two of the four Galois conjugate classes of elements of order 15– could also have been found from the structure of G and the restriction of the irreducible S_{5} × S_{3} character of degree 12. On the classes that did not split the values of this character must just be divided by 2. Let x be one of the irrationalities. The second orthogonality relation tells us that x · \overline{x} = 4 (at class 15a) and x + x∗ = −1 (at classes 1a and 15a); here x∗ denotes the nontrivial Galois conjugate of x. This has no solution for x = \overline{x}, otherwise it leads to the quadratic equation x^{2} + x + 4 = 0 with solutions b15 = \frac{1}{2}(−1 + \sqrt{−15}) and −1 − b15. The third possibility to complete the table is to embed A_{5} × 3:

gap> split.irreducibles := split.irreducibles{ [ 1 .. 10 ] };;
gap> SubgroupFusions( a5xc3, split );
[ [ 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, [ 8, 9 ], [ 8, 9 ], 7, [ 8, 9 ],
      [ 8, 9 ] ] ]

The images of the four classes of element order 15 are not determined, the returned list parametrizes the 2^{4} possibilities.

gap> fus:= ContainedMaps( last[1] );;
gap> Length( fus );
16
gap> fus[1];
[ 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 7, 8, 8 ]

Most of these 16 possibilities are excluded using scalar products of induced characters. We take a suitable character chi of a5xc3 and compute the norm of the induced character with respect to each possible map.

    gap> chi:= a5xc3.irreducibles[5];
    [ 3, 3*E(3), 3*E(3)^2, -1, -E(3), -E(3)^2, 0, 0, 0, -E(5)-E(5)^4,
      -E(15)^2-E(15)^8, -E(15)^7-E(15)^13, -E(5)^2-E(5)^3,
      -E(15)^11-E(15)^14, -E(15)-E(15)^4 ]
    gap> List( fus, x -> List( Induced( a5xc3, split, [ chi ], x ),
    >                          y -> ScalarProduct( split, y, y ) )[1] );
    [ 8/15, -2/3*E(5)-11/15*E(5)^2-11/15*E(5)^3-2/3*E(5)^4,
      -2/3*E(5)-11/15*E(5)^2-11/15*E(5)^3-2/3*E(5)^4, 2/3,
      -11/15*E(5)-2/3*E(5)^2-2/3*E(5)^3-11/15*E(5)^4, 3/5, 1,
      -11/15*E(5)-2/3*E(5)^2-2/3*E(5)^3-11/15*E(5)^4,
      -11/15*E(5)-2/3*E(5)^2-2/3*E(5)^3-11/15*E(5)^4, 1, 3/5,
      -11/15*E(5)-2/3*E(5)^2-2/3*E(5)^3-11/15*E(5)^4, 2/3,
      -2/3*E(5)-11/15*E(5)^2-11/15*E(5)^3-2/3*E(5)^4,
      -2/3*E(5)-11/15*E(5)^2-11/15*E(5)^3-2/3*E(5)^4, 8/15 ]
    gap> Filtered( [ 1 .. Length( fus ) ], x -> IsInt( last[x] ) );
    [ 7, 10 ]

So only fusions 7 and 10 may be possible. They are equivalent (with respect to table automorphisms), and the list of induced characters contains the missing irreducibles of G:

    gap> Sublist( fus, last );
    [ [ 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 7, 9, 8 ],
      [ 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 9, 8, 7, 8, 9 ] ]
    gap> ind:= Induced( a5xc3, split, a5xc3.irreducibles, last[1] );;
    gap> Reduced( split, split.irreducibles, ind );
    rec( remainders := [  ], irreducibles :=
       [ [ 6, -3, -2, 1, 0, 0, 1, -E(15)-E(15)^2-E(15)^4-E(15)^8,
              -E(15)^7-E(15)^11-E(15)^13-E(15)^14, 0, 0, 0 ],
          [ 6, -3, -2, 1, 0, 0, 1, -E(15)^7-E(15)^11-E(15)^13-E(15)^14,
              -E(15)-E(15)^2-E(15)^4-E(15)^8, 0, 0, 0 ] ] )

The following example is thought mainly for experts. It shall demonstrate how one can work together with GAP and the ATLAS [CCN^{+}85], so better leave out the rest of this section if you are not familiar with the ATLAS.

We shall construct the character table of the group G = A_{6}.2^{2} ≅ Aut(A_{6}) from the tables of the normal subgroups A_{6}.2_{1} ≅ S_{6}, A_{6}.2_{2} ≅ P GL(2, 9) and A_{6}.2_{3} ≅ M_{10}. We regard G as a downward extension of the Klein fourgroup 2^{2} with A_{6}. The set of classes of all preimages of cyclic subgroups of 2^{2} covers the classes of G, but it may happen that some representatives are conjugate in G, i.e., the classes fuse. The ATLAS denotes the character tables of G, G.2_{1}, G.2_{2} and G.2_{3} as follows:

[figure: A_{6}; A_{6}.2_{3}; A_{6}.2_{1}; A_{6}.2_{2}; G]

1.25. ABOUT CHARACTER TABLES 117

     ;   @   @   @   @   @   @   @   ;   ;   @   @   @   @   @
       360   8   9   9   4   5   5          24  24   4   3   3
   p power   A   A   A   A   A   A           A   A   A  AB  BC
   p' part   A   A   A   A   A   A           A   A   A  AB  BC
   ind  1A  2A  3A  3B  4A  5A  B* fus ind  2B  2C  4B  6A  6B
χ_{1} +   1   1   1   1   1   1   1   :  ++   1   1   1   1   1
χ_{2} +   5   1   2  -1  -1   0   0   :  ++   3  -1   1   0  -1
χ_{3} +   5   1  -1   2  -1   0   0   :  ++  -1   3   1  -1   0
χ_{4} +   8   0  -1  -1   0 -b5   *   .   +   0   0   0   0   0
χ_{5} +   8   0  -1  -1   0   * -b5   .
χ_{6} +   9   1   0   0   1  -1  -1   :  ++   3   3  -1   0   0
χ_{7} +  10  -2   1   1   0   0   0   :  ++   2  -2   0  -1   1
   ;   ;  @   @   @   @   @   ;   ;  @   @   @
         10   4   4   5   5          2   4   4
          A   A   A  BD  AD          A   A   A
          A   A   A  AD  BD          A   A   A
 fus ind 2D  8A  B* 10A  B* fus ind 4C  8C D**
   :  ++  1   1   1   1   1   :  ++  1   1   1 χ_{1}
   .   +  0   0   0   0   0   .   +  0   0   0 χ_{2}
   .                          .  χ_{3}
   :  ++  2   0   0  b5   *   .   +  0   0   0 χ_{4}
   :  ++  2   0   0   *  b5   .  χ_{5}
   :  ++ -1   1   1  -1  -1   :  ++  1  -1  -1 χ_{6}
   :  ++  0  r2 -r2   0   0   :  oo  0  i2 -i2 χ_{7}

First we construct a table whose classes are those of the three subgroups. Note that the exponent of A_{6} is 60, so the representative orders could become at most 60 times the value in 2^{2}.

    gap> s1:= CharTable( "A6.2_1" );;

gap> s2:= CharTable( "A6.2_2" );;
gap> s3:= CharTable( "A6.2_3" );;
gap> c2:= CharTable( "Cyclic", 2 );;
gap> v4:= CharTableDirectProduct( c2, c2 );;
#I CharTableDirectProduct: existing subgroup fusion on <tbl2> replaced
#I    by actual one
gap> for tbl in [ s1, s2, s3 ] do
>      Print( tbl.irreducibles[2], "\n" );
>    od;
[ 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 ]
[ 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 ]
[ 1, 1, 1, 1, 1, -1, -1, -1 ]
gap> split:= CharTableSplitClasses( v4,
>              [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4], 60 );
rec( name := "Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, \
3, 4, 4, 4 ])", order := 4, centralizers :=
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 ], classes :=
[ 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5, 1/5,
  1/5, 1/5, 1/3, 1/3, 1/3 ], orders :=
[ 1, [ 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 ],
  [ 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 ],
  [ 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 ],
  [ 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ],
  [ 2, 4, 6, 8, 10, 12, 20, 24, 30, 40, 60, 120 ] ], powermap :=
[ , [ 1, [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ],
      [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ],
      [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ],
      [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ],
      [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ],
      [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ] ] ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1 ]
 ], fusions := [ rec(
      name := "C2xC2",

      map := [ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4 ]
     ) ], operations := CharTableOps )

Now we embed the subgroups and adjust the classlengths, order, centralizers, powermaps and thus the representative orders.

gap> StoreFusion( s1, split, [1,2,3,3,4,5,6,7,8,9,10]);
gap> StoreFusion( s2, split, [1,2,3,4,5,5,11,12,13,14,15]);
gap> StoreFusion( s3, split, [1,2,3,4,5,16,17,18]);
gap> for tbl in [ s1, s2, s3 ] do
>      fus:= GetFusionMap( tbl, split );
>      for class in Difference( [ 1 .. Length( tbl.classes ) ],
>                               KernelChar(tbl.irreducibles[2]) ) do
>        split.classes[ fus[ class ] ]:= tbl.classes[ class ];
>      od;
>    od;
gap> for class in [ 1 .. 5 ] do
>      split.classes[ class ]:= s3.classes[ class ];
>    od;
gap> split.classes;
[ 1, 45, 80, 90, 144, 15, 15, 90, 120, 120, 36, 90, 90, 72, 72, 180,
  90, 90 ]
gap> split.order:= Sum( last );
1440
gap> split.centralizers:= List( last2, x -> split.order / x );
[ 1440, 32, 18, 16, 10, 96, 96, 16, 12, 12, 40, 16, 16, 20, 20, 8,
  16, 16 ]
gap> split.powermap[3]:= InitPowermap( split, 3 );;
gap> split.powermap[5]:= InitPowermap( split, 5 );;
gap> for tbl in [ s1, s2, s3 ] do
>      fus:= GetFusionMap( tbl, split );
>      for p in [ 2, 3, 5 ] do
>        TransferDiagram( tbl.powermap[p], fus, split.powermap[p] );
>      od;
>    od;
gap> split.powermap;
[ , [ 1, 1, 3, 2, 5, 1, 1, 2, 3, 3, 1, 4, 4, 5, 5, 2, 4, 4 ],
  [ 1, 2, 1, 4, 5, 6, 7, 8, 6, 7, 11, 13, 12, 15, 14, 16, 17, 18 ],,
  [ 1, 2, 3, 4, 1, 6, 7, 8, 9, 10, 11, 13, 12, 11, 11, 16, 18, 17 ] ]
gap> split.orders:= ElementOrdersPowermap( split.powermap );
[ 1, 2, 3, 4, 5, 2, 2, 4, 6, 6, 2, 8, 8, 10, 10, 4, 8, 8 ]

In order to decide which classes fuse in G, we look at the norms of suitable induced characters, first the + extension of χ_{2} to A_{6}.2_{1}.

gap> ind:= Induced( s1, split, [ s1.irreducibles[3] ] )[1];
[ 10, 2, 1, -2, 0, 6, -2, 2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0 ]
gap> ScalarProduct( split, ind, ind );
3/2

The inertia group of this character is A_{6}.2_{1}, thus the norm of the induced character must be 1. If the classes 2B and 2C fuse, the contribution of these classes is changed from 15 · 6^{2} + 15 · (−2)^{2} to 30 · 2^{2}, the difference is 480. But we have to subtract 720 which is half the group order, so also 6A and 6B fuse. This is not surprising, since it reflects the action of the famous outer automorphism of S_{6}. Next we examine the + extension of χ_{4} to A_{6}.2_{2}.

gap> ind:= Induced( s2, split, [ s2.irreducibles[4] ] )[1];
[ 16, 0, -2, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 2*E(5)+2*E(5)^4,
  2*E(5)^2+2*E(5)^3, 0, 0, 0 ]
gap> ScalarProduct( split, ind, ind );
3/2

Again, the norm must be 1, 10A and 10B fuse.

gap> collaps:= CharTableCollapsedClasses( split,
>                    [1,2,3,4,5,6,6,7,8,8,9,10,11,12,12,13,14,15] );
rec( name := "Collapsed(Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3,\
 3, 3, 3, 3, 4, 4, 4 ]),[ 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12,\
 12, 13, 14, 15 ])", order := 1440, centralizers :=
[ 1440, 32, 18, 16, 10, 48, 16, 6, 40, 16, 16, 10, 8, 16, 16
 ], orders := [ 1, 2, 3, 4, 5, 2, 4, 6, 2, 8, 8, 10, 4, 8, 8
 ], powermap := [ , [ 1, 1, 3, 2, 5, 1, 2, 3, 1, 4, 4, 5, 2, 4, 4 ],
  [ 1, 2, 1, 4, 5, 6, 7, 6, 9, 11, 10, 12, 13, 14, 15 ],,
  [ 1, 2, 3, 4, 1, 6, 7, 8, 9, 11, 10, 9, 13, 15, 14 ]
 ], fusionsource :=
[ "Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4 \
])" ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1 ]
 ], classes := [ 1, 45, 80, 90, 144, 30, 90, 240, 36, 90, 90, 144,
  180, 90, 90 ], operations := CharTableOps )
gap> split.fusions;
[ rec(
      name := "C2xC2",
      map := [ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4 ]
     ), rec(
      name :=
       "Collapsed(Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3,\
 3, 3, 4, 4, 4 ]),[ 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 12, 1\
3, 14, 15 ])",
      map := [ 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 12, 13,
          14, 15 ] ) ]
gap> for tbl in [ s1, s2, s3 ] do
>      StoreFusion( tbl, collaps,
>                   CompositionMaps( GetFusionMap( split, collaps ),
>                                    GetFusionMap( tbl, split ) ) );
>    od;
gap> ind:= Induced( s1, collaps, [ s1.irreducibles[10] ] )[1];
[ 20, -4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
gap> ScalarProduct( collaps, ind, ind );

1

This character must be equal to any induced character of an irreducible character of degree 10 of A_{6}.2_{2} and A_{6}.2_{3}. That means, 8A fuses with 8B, and 8C with 8D.

gap> a6v4:= CharTableCollapsedClasses( collaps,
>     [1,2,3,4,5,6,7,8,9,10,10,11,12,13,13] );
rec( name := "Collapsed(Collapsed(Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2\
, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4 ]),[ 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 1\
0, 11, 12, 12, 13, 14, 15 ]),[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, \
12, 13, 13 ])", order := 1440, centralizers :=
[ 1440, 32, 18, 16, 10, 48, 16, 6, 40, 8, 10, 8, 8 ], orders :=
[ 1, 2, 3, 4, 5, 2, 4, 6, 2, 8, 10, 4, 8 ], powermap :=
[ , [ 1, 1, 3, 2, 5, 1, 2, 3, 1, 4, 5, 2, 4 ],
  [ 1, 2, 1, 4, 5, 6, 7, 6, 9, 10, 11, 12, 13 ],,
  [ 1, 2, 3, 4, 1, 6, 7, 8, 9, 10, 9, 12, 13 ] ], fusionsource :=
[ "Collapsed(Split(C2xC2,[ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3\
, 4, 4, 4 ]),[ 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 12, 13, 14\
, 15 ])" ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1 ] ], classes :=
[ 1, 45, 80, 90, 144, 30, 90, 240, 36, 180, 144, 180, 180
 ], operations := CharTableOps )
gap> for tbl in [ s1, s2, s3 ] do
>      StoreFusion( tbl, a6v4,
>                   CompositionMaps( GetFusionMap( collaps, a6v4 ),
>                                    GetFusionMap( tbl, collaps ) ) );
>    od;

Now the classes of G are known, the only remaining work is to compute the irreducibles.

gap> a6v4.irreducibles;
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1 ] ]
gap> for tbl in [ s1, s2, s3 ] do
>      ind:= Set( Induced( tbl, a6v4, tbl.irreducibles ) );
>      Append( a6v4.irreducibles,
>              Filtered( ind, x -> ScalarProduct( a6v4,x,x ) = 1 ) );
>    od;
gap> a6v4.irreducibles:= Set( a6v4.irreducibles );
[ [ 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1 ],
  [ 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 10, 2, 1, -2, 0, -2, -2, 1, 0, 0, 0, 0, 0 ],
  [ 10, 2, 1, -2, 0, 2, 2, -1, 0, 0, 0, 0, 0 ],
  [ 16, 0, -2, 0, 1, 0, 0, 0, -4, 0, 1, 0, 0 ],

  [ 16, 0, -2, 0, 1, 0, 0, 0, 4, 0, -1, 0, 0 ],
  [ 20, -4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ]
gap> sym:= Symmetrizations( a6v4, [ a6v4.irreducibles[5] ], 2 );
[ [ 45, -3, 0, 1, 0, -3, 1, 0, -5, 1, 0, -1, 1 ],
  [ 55, 7, 1, 3, 0, 7, 3, 1, 5, -1, 0, 1, -1 ] ]
gap> Reduced( a6v4, a6v4.irreducibles, sym );
rec(
  remainders := [ [ 27, 3, 0, 3, -3, 3, -1, 0, 1, -1, 1, 1, -1 ] ],
  irreducibles := [ [ 9, 1, 0, 1, -1, -3, 1, 0, -1, 1, -1, -1, 1 ] ] )
gap> Append( a6v4.irreducibles,
>            Tensored( last.irreducibles,
>                      Sublist( a6v4.irreducibles, [ 1 .. 4 ] ) ) );
gap> SortCharactersCharTable( a6v4,
>                             (1,4)(2,3)(5,6)(7,8)(9,13,10,11,12) );;
gap> a6v4.name:= "A6.2^2";;
gap> DisplayCharTable( a6v4 );
A6.2^2
    2  5  5  1  4  1  4  4  1  3  3   1  3  3
    3  2  .  2  .  .  1  .  1  .  .   .  .  .
    5  1  .  .  .  1  .  .  .  1  .   1  .  .
      1a 2a 3a 4a 5a 2b 4b 6a 2c 8a 10a 4c 8b
   2P 1a 1a 3a 2a 5a 1a 2a 3a 1a 4a  5a 2a 4a
   3P 1a 2a 1a 4a 5a 2b 4b 2b 2c 8a 10a 4c 8b
   5P 1a 2a 3a 4a 1a 2b 4b 6a 2c 8a  2c 4c 8b
X.1    1  1  1  1  1  1  1  1  1  1   1  1  1
X.2    1  1  1  1  1  1  1  1 -1 -1  -1 -1 -1
X.3    1  1  1  1  1 -1 -1 -1  1  1   1 -1 -1
X.4    1  1  1  1  1 -1 -1 -1 -1 -1  -1  1  1
X.5   10  2  1 -2  .  2  2 -1  .  .   .  .  .
X.6   10  2  1 -2  . -2 -2  1  .  .   .  .  .
X.7   16  . -2  .  1  .  .  .  4  .  -1  .  .
X.8   16  . -2  .  1  .  .  . -4  .   1  .  .
X.9    9  1  .  1 -1 -3  1  .  1 -1   1  1 -1
X.10   9  1  .  1 -1 -3  1  . -1  1  -1 -1  1
X.11   9  1  .  1 -1  3 -1  .  1 -1   1 -1  1
X.12   9  1  .  1 -1  3 -1  . -1  1  -1  1 -1
X.13  20 -4  2  .  .  .  .  .  .  .   .  .  .

1.26 About Group Libraries

When you start GAP it already knows several groups. For example, some basic groups such as cyclic groups or symmetric groups, all primitive permutation groups of degree at most 50, and all 2-groups of size at most 256. Each of the sets above is called a group library. The set of all groups that GAP knows initially is called the collection of group libraries.

In this section we show you how you can access the groups in those libraries and how you can extract groups with certain properties from those libraries.

Let us start with the basic groups, because they are not accessed in the same way as the groups in the other libraries.

To access such a basic group you just call a function with an appropriate name, such as

CyclicGroup or SymmetricGroup.
    gap> c13 := CyclicGroup( 13 );
    Group( ( 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13) )
    gap> Size( c13 );
    13
    gap> s8 := SymmetricGroup( 8 );
    Group( (1,8), (2,8), (3,8), (4,8), (5,8), (6,8), (7,8) )
    gap> Size( s8 );
    40320

The functions above also accept an optional first argument that describes the type of group. For example you can pass AgWords to CyclicGroup to get a cyclic group as a finite polycyclic group (see 24).

    gap> c13 := CyclicGroup( AgWords, 13 );
    Group( c13 )

Of course you cannot pass AgWords to SymmetricGroup, because symmetric groups are in general not polycyclic.

The default is to construct the groups as permutation groups, but you can also explicitly pass Permutations. Other possible arguments are AgWords for finite polycyclic groups, Words for finitely presented groups, and Matrices for matrix groups (however only Permutations and AgWords currently work).

Let us now turn to the other libraries. They are all accessed in a uniform way. For a first example we will use the group library of primitive permutation groups.

To extract a group from a group library you generally use the extraction function. In our example this function is called PrimitiveGroup. It takes two arguments. The first is the degree of the primitive permutation group that you want and the second is an integer that specifies which of the primitive permutation groups of that degree you want.

    gap> g := PrimitiveGroup( 12, 3 );
    M(11)
    gap> g.generators;
    [ ( 2, 6)( 3, 5)( 4, 7)( 9,10), ( 1, 5, 7)( 2, 9, 4)( 3, 8,10),
      ( 1,11)( 2, 7)( 3, 5)( 4, 6), ( 2, 5)( 3, 6)( 4, 7)(11,12) ]
    gap> Size( g );
    7920
    gap> IsSimple( g );
    true
    gap> h := PrimitiveGroup( 16, 19 );
    2^4.A(7)
    gap> Size( h );
    40320

The reason for the extraction function is as follows. A group library is usually not stored as a list of groups. Instead a more compact representation for the groups is used. For example the groups in the library of 2-groups are represented by 4 integers. The extraction function hides this representation from you, and allows you to access the group library as if it was a table of groups (two dimensional in the above example).

What arguments the extraction function accepts, and how they are interpreted is described in the sections that describe the individual group libraries in chapter 35. Those functions will of course signal an error when you pass illegal arguments.

Suppose that you want to get a list of all primitive permutation groups that have a degree 10 and are simple but not cyclic. It would be very difficult to use the extraction function to extract all groups in the group library, and test each of those. It is much simpler to use the selection function. The name of the selection function always begins with All and ends with Groups, in our example it is thus called AllPrimitiveGroups.

    gap> AllPrimitiveGroups( DegreeOperation,   10,
    >                        IsSimple,          true,
    >                        IsCyclic,          false );
    [ A(5), PSL(2,9), A(10) ]

AllPrimitiveGroups takes a variable number of argument pairs consisting of a function (e.g. DegreeOperation) and a value (e.g. 10). To understand what AllPrimitiveGroups does, imagine that the group library was stored as a long list of permutation groups. AllPrimitiveGroups takes all those groups in turn. To each group it applies each function argument and compares the result with the corresponding value argument. It selects a group if and only if all the function results are equal to the corresponding value. So in our example AllPrimitiveGroups selects those groups g for which DegreeOperation(g) =

10 and IsSimple(g) = true and IsCyclic(g) = false. Finally AllPrimitiveGroups

returns the list of the selected groups.

Next suppose that you want all the primitive permutation groups that have degree at most 10, are simple but are not cyclic. You could obtain such a list with 10 calls to AllPrimitiveGroups (i.e., one call for the degree 1 groups, another for the degree 2 groups and so on), but there is a simple way. Instead of specifying a single value that a function must return you can simply specify a list of such values.

    gap> AllPrimitiveGroups( DegreeOperation,   [1..10],
    >                        IsSimple,          true,
    >                        IsCyclic,          false );
    [ A(5), PSL(2,5), A(6), PSL(3,2), A(7), PSL(2,7), A(8), PSL(2,8),
      A(9), A(5), PSL(2,9), A(10) ]

Note that the list that you get contains A(5) twice, first in its primitive presentation on 5 points and second in its primitive presentation on 10 points.

Thus giving several argument pairs to the selection function allows you to express the logical and of properties that a group must have to be selected, and giving a list of values allows you to express a (restricted) logical or of properties that a group must have to be selected.

There is no restriction on the functions that you can use. It is even possible to use functions that you have written yourself. Of course, the functions must be unary, i.e., accept only one argument, and must be able to deal with the groups.

    gap> NumberConjugacyClasses := function ( g )

    >        return Length( ConjugacyClasses( g ) );
    > end;
    function ( g ) ... end
    gap> AllPrimitiveGroups( DegreeOperation,           [1..10],
    >                        IsSimple,                  true,
    >                        IsCyclic,                  false,
    >                        NumberConjugacyClasses,    9 );
    [ A(7), PSL(2,8) ]

Note that in some cases a selection function will issue a warning. For example if you call AllPrimitiveGroups without specifying the degree, it will issue such a warning.

    gap> AllPrimitiveGroups( Size,      [100..400],
    >                        IsSimple,  true,
    >                        IsCyclic,  false );
    #W  AllPrimitiveGroups: degree automatically restricted to [1..50]
    [ A(6), PSL(3,2), PSL(2,7), PSL(2,9), A(6) ]

If selection functions would really run over the list of all groups in a group library and apply the function arguments to each of those, they would be very inefficient. For example the 2-groups library contains 58760 groups. Simply creating all those groups would take a very long time.

Instead selection functions recognize certain functions and handle them more efficiently. For

example AllPrimitiveGroups recognizes DegreeOperation. If you pass DegreeOperation

to AllPrimitiveGroups it does not create a group to apply DegreeOperation to it. Instead it simply consults an index and quickly eliminates all groups that have a different degree. Other functions recognized by AllPrimitiveGroups are IsSimple, Size, and

Transitivity.
So in our examples AllPrimitiveGroups, recognizing DegreeOperation and IsSimple,

eliminates all but 16 groups. Then it creates those 16 groups and applies IsCyclic to them. This eliminates 4 more groups (C(2), C(3), C(5), and C(7)). Then in our last example it applies NumberConjugacyClasses to the remaining 12 groups and eliminates all

but A(7) and PSL(2,8).

The catch is that the selection functions will take a large amount of time if they cannot recognize any special functions. For example the following selection will take a large amount of time, because only IsSimple is recognized, and there are 116 simple groups in the primitive groups library.

    AllPrimitiveGroups( IsSimple, true, NumberConjugacyClasses, 9 );

So you should specify as sufficiently large set of recognizable functions when you call a selection function. It is also advisable to put those functions first (though in some group libraries the selection function will automatically rearrange the argument pairs so that the recognized functions come first). The sections describing the individual group libraries in chapter 35 tell you which functions are recognized by the selection function of that group library.

There is another function, called the example function that behaves similar to the selection function. Instead of returning a list of all groups with a certain set of properties it only returns one such group. The name of the example function is obtained by replacing All by One and stripping the s at the end of the name of the selection function.

    gap> OnePrimitiveGroup( DegreeOperation,           [1..10],
    >                       IsSimple,                  true,
    >                       IsCyclic,                  false,
    >                       NumberConjugacyClasses,    9 );
    A(7)

The example function works just like the selection function. That means that all the above comments about the special functions that are recognized also apply to the example function.

Let us now look at the 2-groups library. It is accessed in the same way as the primitive groups library. There is an extraction function TwoGroup, a selection function AllTwoGroups, and an example function OneTwoGroup.

    gap> g := TwoGroup( 128, 5 );
    Group( a1, a2, a3, a4, a5, a6, a7 )
    gap> Size( g );
    128
    gap> NumberConjugacyClasses( g );
    80

The groups are all displayed as Group( a1, a2, ..., an ), where 2^{n} is the size of the group.

    gap> AllTwoGroups( Size,   256,
    >                  Rank,   3,
    >                  pClass, 2 );
    [ Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ) ]
    gap> l := AllTwoGroups( Size,                              256,
    >                       Rank,                              3,
    >                       pClass,                            5,
    >                       g -> Length( DerivedSeries( g ) ), 4 );;
    gap> Length( l );
    28

The selection and example function of the 2-groups library recognize Size, Rank, and pClass. Note that Rank and pClass are functions that can in fact only be used in this context, i.e., they can not be applied to arbitrary groups.

The following discussion is a bit technical and you can ignore it safely.

For very big group libraries, such as the 2-groups library, the groups (or their compact representations) are not stored on a single file. This is because this file would be very large and loading it would take a long time and a lot of main memory.

Instead the groups are stored on a small number of files (27 in the case of the 2-groups). The selection and example functions are careful to load only those files that may actually contain groups with the specified properties. For example in the above example the files containing the groups of size less than 256 are never loaded. In fact in the above example only one very small file is loaded.

When a file is loaded the selection and example functions also unload the previously loaded file. That means that they forget all the groups in this file again (except those selected of course). Thus even if the selection or example functions have to search through the whole group library, only a small part of the library is held in main memory at any time. In principle it should be possible to search the whole 2-groups library with as little as 2 MByte of main memory. If you have sufficient main memory available you can explicitly load files from the 2-groups library with ReadTwo( filename ), e.g., Read( "twogp64") to load the file with the groups of size 64. Those files will then not be unloaded again. This will take up more main memory, but the selection and example function will work faster, because they do not have to load those files again each time they are needed. In this section you have seen the basic groups library and the group libraries of primitive groups and 2-groups. You have seen how you can extract a single group from such a library with the extraction function. You have seen how you can select groups with certain properties with the selection and example function. Chapter 35 tells you which other group libraries are available.

1.27 About the Implementation of Domains

In this section we will open the black boxes and describe how all this works. This is complex and you do not need to understand it if you are content to use domains only as black boxes. So you may want to skip this section (and the remainder of this chapter). Domains are represented by records, which we will call domain records in the following. Which components have to be present, which may, and what those components hold, differs from category to category, and, to a smaller extent, from domain to domain. It is possible, though, to generally distinguish four types of components. The first type of components are called the category components. They determine to which category a domain belongs. A domain D in a category Cat has a component isCat with the value true. For example, each group has the component isGroup. Also each domain has the component isDomain (again with the value true). Finally a domain may also have components that describe the representation of this domain. For example, each permutation group has a component isPermGroup (again with the value true). Functions such as IsPermGroup test whether such a component is present, and whether it has the value true. The second type of components are called the identification components. They distinguish the domain from other domains in the same category. The identification components uniquely identify the domain. For example, for groups the identification components are generators, which holds a list of generators of the group, and identity, which holds the identity of the group (needed for the trivial group, for which the list of generators is empty). The third type of components are called knowledge components. They hold all the knowledge GAP has about the domain. For example the size of the domain D is stored in the knowledge component D.size, the commutator subgroup of a group is stored in the knowledge component D.commutatorSubgroup, etc. Of course, the knowledge about a certain domain will usually increase as you work with a domain. For example, a group record may initially hold only the knowledge that the group is finite, but may later hold all kinds of knowledge, for example the derived series, the Sylow subgroups, etc. Finally each domain record contains an operations record. The operations record is discussed below.

We want to emphasize that really all information that GAP has about a domain is stored in the knowledge components. That means that you can access all this information, at least if you know where to look and how to interpret what you see. The chapters describing categories and domains will tell you what knowledge components a domain may have, and how the knowledge is represented in those components.

For an example let us return to the permutation group a5 from section 1.23. If we print the record using the function PrintRec we see all the information. GAP stores the stabilizer chain of a5 in the components orbit, transversal, and stabilizer. It is not important that you understand what a stabilizer chain is (this is discussed in chapter 20), the important point here is that it is the vital information that GAP needs to work efficiently with a5 and that you can access it.

    gap> a5 := Group( (1,2,3), (3,4,5) );
    Group( (1,2,3), (3,4,5) )
    gap> Size( a5 );
    60
    gap> PrintRec( a5 );  Print( "\n" );
    rec(
      isDomain    := true,
      isGroup     := true,
      identity    := (),
      generators  := [ (1,2,3), (3,4,5) ],
      operations  := ...,
      isPermGroup := true,
      isFinite    := true,
      1           := (1,2,3),
      2           := (3,4,5),
      orbit       := [ 1, 3, 2, 5, 4 ],
      transversal := [ (), (1,2,3), (1,2,3), (3,4,5), (3,4,5) ],
      stabilizer  := rec(
        identity    := (),
        generators  := [ (3,4,5), (2,5,3) ],
        orbit       := [ 2, 3, 5, 4 ],
        transversal := [ , (), (2,5,3), (3,4,5), (3,4,5) ],
        stabilizer  := rec(
          identity    := (),
          generators  := [ (3,4,5) ],
          orbit       := [ 3, 5, 4 ],
          transversal := [ ,, (), (3,4,5), (3,4,5) ],
          stabilizer  := rec(
            identity   := (),
            generators := [  ],
            operations := ... ),
          operations  := ... ),
        operations  := ... ),
      size        := 60 )

Note that you can not only read this information, you can also modify it. However, unless you truly understand what you are doing, we discourage you from playing around. All GAP functions assume that the information in the domain record is in a consistent state, and everything will go wrong if it is not.

    gap> a5.size := 120;
    120
    gap> Size( ConjugacyClass( a5, (1,2,3,4,5) ) );

24 # this is of course wrong

As was mentioned above, each domain record has an operations record. We have already seen that functions such as Size can be applied to various types of domains. It is clear that there is no general method that will compute the size of all domains efficiently. So Size must somehow decide which method to apply to a given domain. The operations record makes this possible.

The operations record of a domain D is the component with the name D.operations, its value is a record. For each function that you can apply to D this record contains a function that will compute the required information (hopefully in an efficient way).

To understand this let us take a look at what happens when we compute Size( a5 ). Not

much happens. Size simply calls a5.operations.Size( a5 ). a5.operations.Size is a

function written especially for permutation groups. It computes the size of a5 and returns it. Then Size returns this value.

Actually Size does a little bit more than that. It first tests whether a5 has the knowledge component a5.size. If this is the case, Size simply returns that value. Otherwise it calls a5.operations.Size( a5 ) to compute the size. Size remembers the result in the knowledge component a5.size so that it is readily available the next time Size( a5 ) is called. The complete definition of Size is as follows.

    Size := function ( D )
        local   size;
        if IsSet( D )  then
            size := Length( D );
        elif IsDomain( D )  and IsBound( D.size )  then
            size := D.size;
        elif IsDomain( D )  then
            D.size := D.operations.Size( D );
            size := D.size;
        else
            Error("<D> must be a domain or a set");
        fi;
        return size;
    end;

Because functions such as Size only dispatch to the functions in the operations record, they are called dispatcher functions. Almost all functions that you call directly are dispatcher functions, and almost all functions that do the hard work are components in an operations record.

Which function is called by a dispatcher obviously depends on the domain and its operations record (that is the whole point of having an operations record). In principle each domain could have its own Size function. In practice however, this would require too many functions. So different domains share the functions in their operations records, usually all domains with the same representation share all their operations record functions. For example all permutation groups share the same Size function. Because this shared Size function must be able to access the information in the domain record to compute the correct result, the Size dispatcher function (and all other dispatchers as well) pass the domain as first argument

In fact the domains not only have the same functions in their operations record, they share the operations record. So for example all permutation groups share a common operations record, which is called PermGroupOps. This means that changing a function in the operations record for a domain D in the following way D.operations.function := new-function; will also change this function for all domains of the same type, even those that do not yet exist at the moment of the assignment and will only be constructed later. This is usually not desirable, since supposedly new-function uses some special properties of the domain D to work more efficiently. We suggest therefore that you first make a copy of the operations record with D.operations := Copy( D.operations ); and only afterwards do D.operations.function := new-function;.

If a programmer that implements a new domain D, a new type of groups say, would have to write all functions applicable to D, this would require a lot of effort. For example, there are about 120 functions applicable to groups. Luckily many of those functions are independent of the particular type of groups. For example the following function will compute the commutator subgroup of any group, assuming that TrivialSubgroup, Closure, and NormalClosure work. We say that this function is generic.

    GroupOps.CommutatorSubgroup := function ( U, V )
        local   C, u, v, c;
        C := TrivialSubgroup( U );
        for u  in U.generators  do
            for v  in V.generators  do
                c := Comm( u, v );
                if not c in C  then
                    C := Closure( C, c );
                fi;
            od;
        od;
        return NormalClosure( Closure( U, V ), C );
    end;

So it should be possible to use this function for the new type of groups. The mechanism to do this is called inheritance. How it works is described in 1.28, but basically the programmer just copies the generic functions from the generic group operations record into the operations record for his new type of groups.

The generic functions are also called default functions, because they are used by default, unless the programmer overlaid them for the new type of groups.

There is another mechanism through which work can be simplified. It is called delegation. Suppose that a generic function works for the new type of groups, but that some special cases can be handled more efficiently for the new type of groups. Then it is possible to handle only those cases and delegate the general cases back to the generic function. An example of this is the function that computes the orbit of a point under a permutation group. If the point is an integer then the generic algorithm can be improved by keeping a second list that remembers which points have already been seen. The other cases (remember that Orbit can also be used for other operations, e.g., the operation of a permutation group on pairs of points or the operations on subgroups by conjugation) are delegated back to the generic function. How this is done can be seen in the following definition.

PermGroupOps.Orbit := function ( G, d, opr )

local orb, # orbit of d under G, result max, # largest point moved by the group G new, # boolean list indicating if a point is new gen, # one generator of the group G pnt, # one point in the orbit orb img; # image of pnt under gen

# standard operation

if   opr = OnPoints  and IsInt(d)  then

# get the largest point max moved by the group G

max := 0;
for gen  in G.generators  do
    if max < LargestMovedPointPerm(gen)  then
        max := LargestMovedPointPerm(gen);
    fi;
od;

# handle fixpoints

if not d in [1..max]  then
    return [ d ];
fi;

# start with the singleton orbit

orb := [ d ];
new := BlistList( [1..max], [1..max] );
new[d] := false;

# loop over all points found

for pnt  in orb  do
    for gen  in G.generators  do
        img := pnt ^gen;
        if new[img]  then
            Add( orb, img );
            new[img] := false;
        fi;
    od;
od;

# other operation, delegate back on default function

else
orb := GroupOps.Orbit( G, d, opr );

        fi;

# return the orbit orb

        return orb;
    end;

Inheritance and delegation allow the programmer to implement a new type of groups by merely specifying how those groups differ from generic groups. This is far less work than having to implement all possible functions (apart from the problem that in this case it is very likely that the programmer would forget some of the more exotic functions).

To make all this clearer let us look at an extended example to show you how a computation in a domain may use default and special functions to achieve its goal. Suppose you defined g, x, and y as follows.

    gap> g := SymmetricGroup( 8 );;
    gap> x := [ (2,7,4)(3,5), (1,2,6)(4,8) ];;
    gap> y := [ (2,5,7)(4,6), (1,5)(3,8,7) ];;

Now you ask for an element of g that conjugates x to y, i.e., a permutation on 8 points that

takes (2,7,4)(3,5) to (2,5,7)(4,6) and (1,2,6)(4,8) to (1,5)(3,8,7). This is done

as follows (see 8.23 and 8.1).

    gap> RepresentativeOperation( g, x, y, OnTuples );
    (1,8)(2,7)(3,4,5,6)

Now lets look at what happens step for step. First RepresentativeOperation is called. After checking the arguments it calls the function g.operations.RepresentativeOperation,

which is the function SymmetricGroupOps.RepresentativeOperation, passing the argu-

ments g, x, y, and OnTuples.

SymmetricGroupOps.RepresentativeOperation handles a lot of cases special, but the op-

eration on tuples of permutations is not among them. Therefore it delegates this problem

to the function that it overlays, which is PermGroupOps.RepresentativeOperation.

PermGroupOps.RepresentativeOperation also does not handle this special case, and delegates the problem to the function that it overlays, which is the default function called

GroupOps.RepresentativeOperation.

GroupOps.RepresentativeOperation views this problem as a general tuples problem, i.e., it does not care whether the points in the tuples are integers or permutations, and decides to solve it one step at a time. So first it looks for an element taking (2,7,4)(3,5) to

(2,5,7)(4,6) by calling RepresentativeOperation( g, (2,7,4)(3,5), (2,5,7)(4,6)
).
RepresentativeOperation calls g.operations.RepresentativeOperation next, which is
the function SymmetricGroupOps.RepresentativeOperation, passing the arguments g,
(2,7,4)(3,5), and (2,5,7)(4,6).
SymmetricGroupOps.RepresentativeOperation can handle this case. It knows that g

contains every permutation on 8 points, so it contains (3,4,7,5,6), which obviously does what we want, namely it takes x[1] to y[1]. We will call this element t.

Now GroupOps.RepresentativeOperation (see above) looks for an s in the stabilizer of

x[1] taking x[2] to y[2]^(t^-1), since then for r=s*t we have x[1]^r = (x[1]^s)^t
= x[1]^t = y[1] and also x[2]^r = (x[2]^s)^t = (y[2]^(t^-1))^t = y[2]. So the

next step is to compute the stabilizer of x[1] in g. To do this it calls Stabilizer( g,

(2,7,4)(3,5) ).
Stabilizer calls g.operations.Stabilizer, which is SymmetricGroupOps.Stabilizer,
passing the arguments g and (2,7,4)(3,5). SymmetricGroupOps.Stabilizer detects that

the second argument is a permutation, i.e., an element of the group, and calls Centralizer(

g, (2,7,4)(3,5) ). Centralizer calls the function g.operations.Centralizer, which
is SymmetricGroupOps.Centralizer, again passing the arguments g, (2,7,4)(3,5).

SymmetricGroupOps.Centralizer again knows how centralizer in symmetric groups look, and after looking at the permutation (2,7,4)(3,5) sharply for a short while returns the

centralizer as Subgroup( g, [ (1,6), (6,8), (2,7,4), (3,5) ] ), which we will call c.

Note that c is of course not a symmetric group, therefore SymmetricGroupOps.Subgroup gives it PermGroupOps as operations record and not SymmetricGroupOps.

As explained above GroupOps.RepresentativeOperation needs an element of c taking

x[2] ((1,2,6)(4,8)) to y[2]^(t^-1) ((1,7)(4,6,8)). So RepresentativeOperation(
c, (1,2,6)(4,8), (1,7)(4,6,8) ) is called. RepresentativeOperation in turn calls

the function c.operations.RepresentativeOperation, which is, since c is a permutation group, the function PermGroupOps.RepresentativeOperation, passing the arguments c,

(1,2,6)(4,8), and (1,7)(4,6,8).

PermGroupOps.RepresentativeOperation detects that the points are permutations and and performs a backtrack search through c. It finds and returns (1,8)(2,4,7)(3,5), which we call s.

Then GroupOps.RepresentativeOperation returns r = s*t = (1,8)(2,7)(3,6)(4,5),

and we are done.

In this example you have seen how functions use the structure of their domain to solve

a problem most efficiently, for example SymmetricGroupOps.RepresentativeOperation

but also the backtrack search in PermGroupOps.RepresentativeOperation, how they use

other functions, for example SymmetricGroupOps.Stabilizer called Centralizer, and

how they delegate cases which they can not handle more efficiently back to the func-

tion they overlaid, for example SymmetricGroupOps.RepresentativeOperation delegated

to PermGroupOps.RepresentativeOperation, which in turn delegated to to the function

GroupOps.RepresentativeOperation.

If you think this whole mechanism using dispatcher functions and the operations record is overly complex let us look at some of the alternatives. This is even more technical than the previous part of this section so you may want to skip the remainder of this section.

One alternative would be to let the dispatcher know about the various types of domains, test which category a domain lies in, and dispatch to an appropriate function. Then we would not need an operations record. The dispatcher function CommutatorSubgroup would then look as follows. Note this is not how CommutatorSubgroup is implemented in GAP.

    CommutatorSubgroup := function ( G )
        local   C;
        if IsAgGroup( G )  then
            C := CommutatorSubgroupAgGroup( G );
        elif IsMatGroup( G )  then
            C := CommutatorSubgroupMatGroup( G );
        elif IsPermGroup( G )  then

            C := CommutatorSubgroupPermGroup( G );
        elif IsFpGroup( G )  then
            C := CommutatorSubgroupFpGroup( G );
        elif IsFactorGroup( G )  then
            C := CommutatorSubgroupFactorGroup( G );
        elif IsDirectProduct( G )  then
            C := CommutatorSubgroupDirectProduct( G );
        elif IsDirectProductAgGroup( G )  then
            C := CommutatorSubgroupDirectProductAgGroup( G );
        elif IsSubdirectProduct( G )  then
            C := CommutatorSubgroupSubdirectProduct( G );
        elif IsSemidirectProduct( G )  then
            C := CommutatorSubgroupSemidirectProduct( G );
        elif IsWreathProduct( G )  then
            C := CommutatorSubgroupWreathProduct( G );
        elif IsGroup( G )  then
            C := CommutatorSubgroupGroup( G );
        else
            Error("<G> must be a group");
        fi;
        return C;
    end;

You already see one problem with this approach. The number of cases that the dispatcher functions would have to test is simply to large. It is even worse for set theoretic functions, because they would have to handle all different types of domains (currently about 30).

The other problem arises when a programmer implements a new domain. Then he would have to rewrite all dispatchers and add a new case to each. Also the probability that the programmer forgets one dispatcher is very high.

Another problem is that inheritance becomes more difficult. Instead of just copying one operations record the programmer would have to copy each function that should be inherited. Again the probability that he forgets one is very high.

Another alternative would be to do completely without dispatchers. In this case there would

be the functions CommutatorSugroupAgGroup, CommutatorSubgroupPermGroup, etc., and it

would be your responsibility to call the right function. For example to compute the size of a permutation group you would call SizePermGroup and to compute the size of a coset you

would call SizeCoset (or maybe even SizeCosetPermGroup).

The most obvious problem with this approach is that it is much more cumbersome. You would always have to know what kind of domain you are working with and which function you would have to call.

Another problem is that writing generic functions would be impossible. For example the above generic implementation of CommutatorSubgroup could not work, because for a concrete group it would have to call ClosurePermGroup or ClosureAgGroup etc.

If generic functions are impossible, inheritance and delegation can not be used. Thus for each type of domain all functions must be implemented. This is clearly a lot of work, more work than we are willing to do.

So we argue that our mechanism is the easiest possible that serves the following two goals. It is reasonably convenient for you to use. It allows us to implement a large (and ever increasing) number of different types of domains.

This may all sound a lot like object oriented programming to you. This is not surprising because we want to solve the same problems that object oriented programming tries to solve. Let us briefly discuss the similarities and differences to object oriented programming, taking C++ as an example (because it is probably the widest known object oriented programming language nowadays). This discussion is very technical and again you may want to skip the remainder of this section.

Let us first recall the problems that the GAP mechanism wants to handle.

1 How can we represent domains in such a way that we can handle domains of different type in a common way?

2 How can we make it possible to allow functions that take domains of different type and perform the same operation for those domains (but using different methods)?

3 How can we make it possible that the implementation of a new type of domains only requires that one implements what distinguishes this new type of domains from domains of an old type (without the need to change any old code)?

For object oriented programming the problems are the same, though the names used are different. We talk about domains, object oriented programming talks about objects, and we talk about categories, object oriented programming talks about classes.

1 How can we represent objects in such a way that we can handle objects of different classes in a common way (e.g., declare variables that can hold objects of different classes)?

2 How can we make it possible to allow functions that take objects of different classes (with a common base class) and perform the same operation for those objects (but using different methods)?

3 How can we make it possible that the implementation of a new class of objects only requires that one implements what distinguishes the objects of this new class from the objects of an old (base) class (without the need to change any old code)?

In GAP the first problem is solved by representing all domains using records. Actually because GAP does not perform strong static type checking each variable can hold objects of arbitrary type, so it would even be possible to represent some domains using lists or something else. But then, where would we put the operations record?

C++ does something similar. Objects are represented by struct-s or pointers to structures. C++ then allows that a pointer to an object of a base class actually holds a pointer to an object of a derived class.

In GAP the second problem is solved by the dispatchers and the operations record. The operations record of a given domain holds the methods that should be applied to that domain, and the dispatcher does nothing but call this method.

In C++ it is again very similar. The difference is that the dispatcher only exists conceptually. If the compiler can already decide which method will be executed by a given call to the dispatcher it directly calls this function. Otherwise (for virtual functions that may be overlaid in derived classes) it basically inlines the dispatcher. This inlined code then dispatches through the so–called virtual method table (vmt). Note that this virtual method table is the same as the operations record, except that it is a table and not a record.

In GAP the third problem is solved by inheritance and delegation. To inherit functions you simply copy them from the operations record of domains of the old category to the operations record of domains of the new category. Delegation to a method of a larger category is done by calling super-category-operations-record.function

C++ also supports inheritance and delegation. If you derive a class from a base class, you copy the methods from the base class to the derived class. Again this copying is only done conceptually in C++. Delegation is done by calling a qualified function baseclass::function.

Now that we have seen the similarities, let us discuss the differences.

The first differences is that GAP is not an object oriented programming language. We only programmed the library in an object oriented way using very few features of the language (basically all we need is that GAP has no strong static type checking, that records can hold functions, and that records can grow dynamically). Following Stroustrup’s convention we say that the GAP language only enables object oriented programming, but does not support it.

The second difference is that C++ adds a mechanism to support data hiding. That means that fields of a struct can be private. Those fields can only be accessed by the functions belonging to this class (and friend functions). This is not possible in GAP. Every field of every domain is accessible. This means that you can also modify those fields, with probably catastrophic results.

The final difference has to do with the relation between categories and their domains and classes and their objects. In GAP a category is a set of domains, thus we say that a domain is an element of a category. In C++ (and most other object oriented programming languages) a class is a prototype for its objects, thus we say that an object is an instance of the class. We believe that GAP’s relation better resembles the mathematical model.

In this section you have seen that domains are represented by domain records, and that you can therefore access all information that GAP has about a certain domain. The following sections in this chapter discuss how new domains can be created (see 1.28, and 1.29) and how you can even define a new type of elements (see 1.30).

1.28 About Defining New Domains

In this section we will show how one can add a new domain to GAP. All domains are implemented in the library in this way. We will use the ring of Gaussian integers as our example.

Note that everything defined here is already in the library file LIBNAME/"gaussian.g", so there is no need for you to type it in. You may however like to make a copy of this file and modify it.

The elements of this domain are already available, because Gaussian integers are just a special case of cyclotomic numbers. As is described in chapter 13 E(4) is GAP’s name for the complex root of -1. So all Gaussian integers can be represented as a + b*E(4), where a and b are ordinary integers.

As was already mentioned each domain is represented by a record. So we create a record to represent the Gaussian integers, which we call GaussianIntegers.

    GaussianIntegers := rec();

The first components that this record must have are those that identify this record as a record denoting a ring domain. Those components are called the category components.

    GaussianIntegers.isDomain := true;
    GaussianIntegers.isRing := true;

The next components are those that uniquely identify this ring. For rings this must be generators, zero, and one. Those components are called the identification components of the domain record. We also assign a name component. This name will be printed when the domain is printed.

    GaussianIntegers.generators := [ 1, E(4) ];
    GaussianIntegers.zero := 0;
    GaussianIntegers.one := 1;
    GaussianIntegers.name := "GaussianIntegers";

Next we enter some components that represent knowledge that we have about this domain. Those components are called the knowledge components. In our example we know that the Gaussian integers form a infinite, commutative, integral, Euclidean ring, which has an unique factorization property, with the four units 1, -1, E(4), and -E(4).

    GaussianIntegers.size                       := "infinity";
    GaussianIntegers.isFinite                   := false;
    GaussianIntegers.isCommutativeRing          := true;
    GaussianIntegers.isIntegralRing             := true;
    GaussianIntegers.isUniqueFactorizationRing  := true;
    GaussianIntegers.isEuclideanRing            := true;
    GaussianIntegers.units                      := [1,-1,E(4),-E(4)];

This was the easy part of this example. Now we have to add an operations record to the domain record. This operations record (GaussianIntegers.operations) shall contain functions that implement all the functions mentioned in chapter 5, e.g., DefaultRing,

IsCommutativeRing, Gcd, or QuotientRemainder.

Luckily we do not have to implement all this functions. The first class of functions that we need not implement are those that can simply derive the result from the knowledge components. E.g., IsCommutativeRing looks for the knowledge component isCommutativeRing,

finds it and returns this value. So GaussianIntegers.operations.IsCommutativeRing is

never called.

    gap> IsCommutativeRing( GaussianIntegers );
    true
    gap> Units( GaussianIntegers );
    [ 1, -1, E(4), -E(4) ]

The second class of functions that we need not implement are those for which there is a general algorithm that can be applied for all rings. For example once we can do a division with remainder (which we will have to implement) we can use the general Euclidean algorithm to compute the greatest common divisor of elements.

So the question is, how do we get those general functions into our operations record. This is very simple, we just initialize the operations record as a copy of the record RingOps, which contains all those general functions. We say that GaussianIntegers.operations inherits the general functions from RingOps.

    GaussianIntegersOps := Copy( RingOps );
    GaussianIntegers.operations := GaussianIntegersOps;

Now we can already test whether a Gaussian integer is a unit or not. This is because the default function inherited from RingOps tests whether the knowledge component units is present, and if it returns true if the element is in that list and false otherwise.

    gap> IsUnit( GaussianIntegers, E(4) );
    true
    gap> IsUnit( GaussianIntegers, 1 + E(4) );
    false

So now we have to add those functions whose result can not (easily) be derived from the knowledge components and that we can not inherit from RingOps.

The first such function is the membership test. This function must test whether an object is an element of the domain GaussianIntegers. IsCycInt(x) tests whether x is a cyclotomic integer and NofCyc(x) returns the smallest n such that the cyclotomic x can be written as a linear combination of powers of the primitive n-th root of unity E(n). If E(x) returns 1, x is an ordinary rational number.

    GaussianIntegersOps.'in' := function ( x, GaussInt )
        return IsCycInt( x ) and ((NofCyc( x ) = 1 or NofCyc( x ) = 4);
    end;

Note that the second argument GaussInt is not used in the function. Whenever this function is called, the second argument must be GaussianIntegers, because GaussianIntegers is the only domain that has this particular function in its operations record. This also happens for most other functions that we will write. This argument can not be dropped though, because there are other domains that share a common in function, for example all permutation groups have the same in function. If the operator in would not pass the second argument, this function could not know for which permutation group it should perform the membership test.

So now we can test whether a certain object is a Gaussian integer or not.

    gap> E(4) in GaussianIntegers;
    true
    gap> 1/2 in GaussianIntegers;
    false
    gap> GaussianIntegers in GaussianIntegers;
    false

Another function that is just as easy is the function Random that should return a random Gaussian integer.

    GaussianIntegersOps.Random := function ( GaussInt )
        return Random( Integers ) + Random( Integers ) * E( 4 );
    end;

Note that actually a Random function was inherited from RingOps. But this function can not be used. It tries to construct the sorted list of all elements of the domain and then picks a random element from that list. Therefor this function is only applicable for finite domains, and can not be used for GaussianIntegers. So we overlay this default function by simply putting another function in the operations record.

Now we finally come to more interesting stuff. The function Quotient should return the quotient of its two arguments x and y. If the quotient does not exist in the ring (i.e., if it is a proper Gaussian rational), it must return false. (Without this last requirement we could do without the Quotient function and always simply use the / operator.)

    GaussianIntegersOps.Quotient := function ( GaussInt, x, y )
        local   q;
        q := x / y;
        if not IsCycInt( q )  then
            q := false;
        fi;
        return q;
    end;

The next function is used to test if two elements are associate in the ring of Gaussian integers. In fact we need not implement this because the function that we inherit from RingOps will do fine. The following function is a little bit faster though that the inherited one.

    GaussianIntegersOps.IsAssociated := function ( GaussInt, x, y )
        return x = y  or x = -y  or x = E(4)*y  or x = -E(4)*y;
    end;

We must however implement the function StandardAssociate. It should return an associate that is in some way standard. That means, whenever we apply StandardAssociate to two associated elements we must obtain the same value. For Gaussian integers we return that associate that lies in the first quadrant of the complex plane. That is, the result is that associated element that has positive real part and nonnegative imaginary part. 0 is its own standard associate of course. Note that this is a generalization of the absolute value function, which is StandardAssociate for the integers. The reason that we must implement StandardAssociate is of course that there is no general way to compute a standard associate for an arbitrary ring, there is not even a standard way to define this!

   GaussianIntegersOps.StandardAssociate := function ( GaussInt, x )
        if   IsRat(x)  and 0 <= x  then
            return x;
        elif IsRat(x)  then
            return -x;
        elif 0 <  COEFFSCYC(x)[1]      and 0 <= COEFFSCYC(x)[2]      then
            return x;
        elif      COEFFSCYC(x)[1] <= 0 and 0 <  COEFFSCYC(x)[2]      then
            return - E(4) * x;
        elif      COEFFSCYC(x)[1] <  0 and      COEFFSCYC(x)[2] <= 0 then
            return - x;
        else
            return E(4) * x;

        fi;
    end;

Note that COEFFSCYC is an internal function that returns the coefficients of a Gaussian integer (actually of an arbitrary cyclotomic) as a list.

Now we have implemented all functions that are necessary to view the Gaussian integers plainly as a ring. Of course there is not much we can do with such a plain ring, we can compute with its elements and can do a few things that are related to the group of units.

    gap> Quotient( GaussianIntegers, 2, 1+E(4) );
    1-E(4)
    gap> Quotient( GaussianIntegers, 3, 1+E(4) );
    false
    gap> IsAssociated( GaussianIntegers, 1+E(4), 1-E(4) );
    true
    gap> StandardAssociate( GaussianIntegers, 3 - E(4) );
    1+3*E(4)

The remaining functions are related to the fact that the Gaussian integers are an Euclidean ring (and thus also a unique factorization ring).

The first such function is EuclideanDegree. In our example the Euclidean degree of a Gaussian integer is of course simply its norm. Just as with StandardAssociate we must implement this function because there is no general way to compute the Euclidean degree for an arbitrary Euclidean ring. The function itself is again very simple. The Euclidean degree of a Gaussian integer x is the product of x with its complex conjugate, which is denoted in GAP by GaloisCyc( x, -1 ).

    GaussianIntegersOps.EuclideanDegree := function ( GaussInt, x )
        return x * GaloisCyc( x, -1 );
    end;

Once we have defined the Euclidean degree we want to implement the QuotientRemainder function that gives us the Euclidean quotient and remainder of a division.

    GaussianIntegersOps.QuotientRemainder := function ( GaussInt, x, y )
        return [ RoundCyc( x/y ),  x - RoundCyc( x/y ) * y ];
    end;

Note that in the definition of QuotientRemainder we must use the function RoundCyc, which views the Gaussian rational x/y as a point in the complex plane and returns the point of the lattice spanned by 1 and E(4) closest to the point x/y. If we would truncate towards the origin instead (this is done by the function IntCyc) we could not guarantee that the result of EuclideanRemainder always has Euclidean degree less than the Euclidean degree of y as the following example shows.

    gap> x := 2 - E(4);;  EuclideanDegree( GaussianIntegers, x );
    5
    gap> y := 2 + E(4);;  EuclideanDegree( GaussianIntegers, y );
    5
    gap> q := x / y;  q := IntCyc( q );
    3/5-4/5*E(4)

    gap> EuclideanDegree( GaussianIntegers, x - q * y );
    5

Now that we have implemented the QuotientRemainder function we can compute greatest common divisors in the ring of Gaussian integers. This is because we have inherited from RingOps the general function Gcd that computes the greatest common divisor using Euclid’s algorithm, which only uses QuotientRemainder (and StandardAssociate to return the result in a normal form). Of course we can now also compute least common multiples, because that only uses Gcd.

    gap> Gcd( GaussianIntegers, 2, 5 - E(4) );
    1+E(4)
    gap> Lcm( GaussianIntegers, 2, 5 - E(4) );
    6+4*E(4)

Since the Gaussian integers are a Euclidean ring they are also a unique factorization ring. The next two functions implement the necessary operations. The first is the test for primality. A rational integer is a prime in the ring of Gaussian integers if and only if it is congruent to 3 modulo 4 (the other rational integer primes split into two irreducibles), and a Gaussian integer that is not a rational integer is a prime if its norm is a rational integer prime.

    GaussianIntegersOps.IsPrime := function ( GaussInt, x )
        if IsInt( x )  then
            return x mod 4 = 3  and IsPrimeInt( x );
        else
            return IsPrimeInt( x * GaloisCyc( x, -1 ) );
        fi;
    end;

The factorization is based on the same observation. We compute the Euclidean degree of the number that we want to factor, and factor this rational integer. Then for every rational integer prime that is congruent to 3 modulo 4 we get one factor, and we split the other rational integer primes using the function TwoSquares and test which irreducible divides.

    GaussianIntegersOps.Factors := function ( GaussInt, x )

local facs, # factors (result)

prm, # prime factors of the norm tsq; # representation of prm as x^{2} + y^{2}

# handle trivial cases

        if x in [ 0, 1, -1, E(4), -E(4) ]  then
            return [ x ];
        fi;

# loop over all factors of the norm of x

        facs := [];
        for prm  in Set( FactorsInt( EuclideanDegree( x ) ) )  do

# p = 2 and primes p = 1 mod 4 split according to p = x^{2} + y^{2}

            if prm = 2  or prm mod 4 = 1  then
                tsq := TwoSquares( prm );

                while IsCycInt( x / (tsq[1]+tsq[2]*E(4)) )  do
                    Add( facs, (tsq[1]+tsq[2]*E(4)) );
                    x := x / (tsq[1]+tsq[2]*E(4));
                od;
                while IsCycInt( x / (tsq[2]+tsq[1]*E(4)) )  do
                    Add( facs, (tsq[2]+tsq[1]*E(4)) );
                    x := x / (tsq[2]+tsq[1]*E(4));
                od;

# primes p = 3 mod 4 stay prime

            else
                while IsCycInt( x / prm )  do
                    Add( facs, prm );
                    x := x / prm;
                od;
            fi;
        od;

# the first factor takes the unit

        facs[1] := x * facs[1];

# return the result

        return facs;
    end;

So now we can factorize numbers in the ring of Gaussian integers.

    gap> Factors( GaussianIntegers, 10 );
    [ -1-E(4), 1+E(4), 1+2*E(4), 2+E(4) ]
    gap> Factors( GaussianIntegers, 103 );
    [ 103 ]

Now we have written all the functions for the operations record that implement the operations. We would like one more thing however. Namely that we can simply write Gcd( 2, 5 - E(4) ) without having to specify GaussianIntegers as first argument. Gcd and the other functions should be clever enough to find out that the arguments are Gaussian

integers and call GaussianIntegers.operations.Gcd automatically.

To do this we must first understand what happens when Gcd is called without a ring as first argument. For an example suppose that we have called Gcd( 66, 123 ) (and want to compute the gcd over the integers).

First Gcd calls DefaultRing( [ 66, 123 ] ), to obtain a ring that contains 66 and 123. DefaultRing then calls Domain( [ 66, 123 ] ) to obtain a domain, which need not be a ring, that contains 66 and 123. Domain is the only function in the whole GAP library that knows about the various types of elements. So it looks at its argument and decides to return the domain Integers (which is in fact already a ring, but it could in princi-

ple also return Rationals). DefaultRing now calls Integers.operations.DefaultRing(

[ 66, 123 ] ) and expects a ring in which the requested gcd computation can be per-

formed. Integers.operations.DefaultRing( [ 66, 123 ] ) also returns Integers. So

DefaultRing returns Integers to Gcd and Gcd finally calls Integers.operations.Gcd(
Integers, 66, 123 ).

So the first thing we must do is to tell Domain about Gaussian integers. We do this by extending Domain with the two lines

elif ForAll( elms, IsGaussInt )  then
    return GaussianIntegers;

so that it now looks as follows.

Domain := function ( elms )
local   elm;
if   ForAll( elms, IsInt )  then
    return Integers;
elif ForAll( elms, IsRat )  then
    return Rationals;
elif ForAll( elms, IsFFE )  then
    return FiniteFieldElements;
elif ForAll( elms, IsPerm )  then
    return Permutations;
elif ForAll( elms, IsMat )  then
    return Matrices;
elif ForAll( elms, IsWord )  then
    return Words;
elif ForAll( elms, IsAgWord )  then
    return AgWords;
elif ForAll( elms, IsGaussInt )  then
    return GaussianIntegers;
elif ForAll( elms, IsCyc )  then
    return Cyclotomics;
else
    for elm  in elms do
        if    IsRec(elm)  and IsBound(elm.domain)
          and ForAll( elms, l -> l in elm.domain )
        then
            return elm.domain;
        fi;
    od;
    Error("sorry, the elements lie in no common domain");
fi;
end;

Of course we must define a function IsGaussInt, otherwise this could not possibly work. This function is similar to the membership test we already defined above.

IsGaussInt := function ( x )
return IsCycInt( x ) and ((NofCyc( x ) = 1 or NofCyc( x ) = 4);
end;

Then we must define a function DefaultRing for the Gaussian integers that does nothing

but return GaussianIntegers.

    GaussianIntegersOps.DefaultRing := function ( elms )
        return GaussianIntegers;
    end;

Now we can call Gcd with two Gaussian integers without having to pass GaussianIntegers as first argument.

    gap> Gcd( 2, 5 - E(4) );
    1+E(4)

Of course GAP can not read your mind. In the following example it assumes that you want to factor 10 over the ring of integers, not over the ring of Gaussian integers (because Integers is the default ring containing 10). So if you want to factor a rational integer over the ring of Gaussian integers you must pass GaussianIntegers as first argument.

    gap> Factors( 10 );
    [ 2, 5 ]
    gap> Factors( GaussianIntegers, 10 );
    [ -1-E(4), 1+E(4), 1+2*E(4), 2+E(4) ]

This concludes our example. In the file LIBNAME/"gaussian.g" you will also find the definition of the field of Gaussian rationals. It is so similar to the above definition that there is no point in discussing it here. The next section shows you what further considerations are necessary when implementing a type of parametrized domains (demonstrated by implementing full symmetric permutation groups). For further details see chapter 14 for a description of the Gaussian integers and rationals and chapter 5 for a list of all functions applicable to rings.

1.29 About Defining New Parametrized Domains

In this section we will show you an example that is slightly more complex than the example in the previous section. Namely we will demonstrate how one can implement parametrized domains. As an example we will implement symmetric permutation groups. This works similar to the implementation of a single domain. Therefore we can be very brief. Of course you should have read the previous section.

Note that everything defined here is already in the file GRPNAME/"permgrp.grp", so there is no need for you to type it in. You may however like to make a copy of this file and modify it.

In the example of the previous section we simply had a variable (GaussianIntegers), whose value was the domain. This can not work in this example, because there is not one symmetric permutation group. The solution is obvious. We simply define a function that takes the degree and returns the symmetric permutation group of this degree (as a domain).

    SymmetricPermGroup := function ( n )

local G; # symmetric group on n points, result

# make the group generated by (1,n), (2,n), .., (n-1,n)

        G := Group( List( [1..n-1], i -> (i,n) ), () );
        G.degree := n;

# give it the correct operations record

        G.operations := SymmetricPermGroupOps;

# return the symmetric group

        return G;
    end;

The key is of course to give the domains returned by SymmetricPermGroup a new operations record. This operations record will hold functions that are written especially for symmetric permutation groups. Note that all symmetric groups created by SymmetricPermGroup share one operations record.

Just as we inherited in the example in the previous section from the operations record RingOps, here we can inherit from the operations record PermGroupOps (after all, each symmetric permutation group is also a permutation group).

    SymmetricPermGroupOps := Copy( PermGroupOps );

We will now overlay some of the functions in this operations record with new functions that make use of the fact that the domain is a full symmetric permutation group. The first function that does this is the membership test function.

    SymmetricPermGroupOps.'in' := function ( g, G )
        return     IsPerm( g )
               and (   g = ()
                    or LargestMovedPointPerm( g ) <= G.degree);
    end;

The most important knowledge for a permutation group is a base and a strong generating set with respect to that base. It is not important that you understand at this point what this is mathematically. The important point here is that such a strong generating set with respect to an appropriate base is used by many generic permutation group functions, most of which we inherit for symmetric permutation groups. Therefore it is important that we are able to compute a strong generating set as fast as possible. Luckily it is possible to simply write down such a strong generating set for a full symmetric group. This is done by the following function.

    SymmetricPermGroupOps.MakeStabChain := function ( G, base )

local sgs, # strong generating system of G wrt. base last; # last point of the base

# remove all unwanted points from the base

        base := Filtered( base, i -> i <= G.degree );

# extend the base with those points not already in the base

        base := Concatenation( base, Difference( [1..G.degree], base ) );

# take the last point

        last := base[ Length(base) ];

# make the strong generating set

        sgs := List( [1..Length(base)-1], i -> ( base[i], last ) );

# make the stabilizer chain

MakeStabChainStrongGenerators( G, base, sgs );
end;

One of the things that are very easy for symmetric groups is the computation of centralizers of elements. The next function does this. Again it is not important that you understand this mathematically. The centralizer of an element g in the symmetric group is generated by the cycles c of g and an element x for each pair of cycles of g of the same length that maps one cycle to the other.

SymmetricPermGroupOps.Centralizer := function ( G, g )

local C, # centralizer of g in G, result sgs, # strong generating set of C gen, # one generator in sgs cycles, # cycles of g cycle, # one cycle from cycles lasts, # lasts[l] is the last cycle of length l last, # one cycle from lasts i; # loop variable

# handle special case

if IsPerm( g )  and g in G  then

# start with the empty strong generating system

sgs := [];

# compute the cycles and find for each length the last one

cycles := Cycles( g, [1..G.degree] );
lasts := [];
for cycle  in cycles  do
lasts[Length(cycle)] := cycle;
od;

# loop over the cycles

for cycle  in cycles  do

# add that cycle itself to the strong generators

if Length( cycle ) <> 1  then
    gen := [1..G.degree];
    for i  in [1..Length(cycle)-1]  do
        gen[cycle[i]] := cycle[i+1];
    od;
    gen[cycle[Length(cycle)]] := cycle[1];
    gen := PermList( gen );
    Add( sgs, gen );
fi;

# and it can be mapped to the last cycle of this length

if cycle <> lasts[ Length(cycle) ]  then

                    last := lasts[ Length(cycle) ];
                    gen := [1..G.degree];
                    for i  in [1..Length(cycle)]  do
                        gen[cycle[i]] := last[i];
                        gen[last[i]] := cycle[i];
                    od;
                    gen := PermList( gen );
                    Add( sgs, gen );
                fi;
            od;

# make the centralizer

            C := Subgroup( G, sgs );

# make the stabilizer chain

            MakeStabChainStrongGenerators( C, [1..G.degree], sgs );

# delegate general case

        else
            C := PermGroupOps.Centralizer( G, g );
        fi;

# return the centralizer

        return C;
    end;

Note that the definition C := Subgroup( G, sgs ); defines a subgroup of a symmetric permutation group. But this subgroup is usually not a full symmetric permutation group itself. Thus C must not have the operations record SymmetricPermGroupOps, instead it should have the operations record PermGroupOps. And indeed C will have this operations record. This is because Subgroup calls G.operations.Subgroup, and we inherited this function from PermGroupOps. Note also that we only handle one special case in the function above. Namely the computation of a centralizer of a single element. This function can also be called to compute the centralizer of a whole subgroup. In this case SymmetricPermGroupOps.Centralizer simply delegates the problem by calling PermGroupOps.Centralizer. The next function computes the conjugacy classes of elements in a symmetric group. This is very easy, because two elements are conjugated in a symmetric group when they have the same cycle structure. Thus we can simply compute the partitions of the degree, and for each degree we get one conjugacy class.

    SymmetricPermGroupOps.ConjugacyClasses := function ( G )

local classes, # conjugacy classes of G, result prt, # partition of G sum, # partial sum of the entries in prt rep, # representative of a conjugacy class of G i; # loop variable # loop over the partitions

        classes := [];
        for prt  in Partitions( G.degree )  do

# compute the representative of the conjugacy class

            rep := [2..G.degree];
            sum := 1;
            for i  in prt  do
                rep[sum+i-1] := sum;
                sum := sum + i;
            od;
            rep := PermList( rep );

# add the new class to the list of classes

            Add( classes, ConjugacyClass( G, rep ) );
        od;

# return the classes

        return classes;
    end;

This concludes this example. You have seen that the implementation of a parametrized domain is not much more difficult than the implementation of a single domain. You have also seen how functions that overlay generic functions may delegate problems back to the generic function. The library file for symmetric permutation groups contain some more functions for symmetric permutation groups.

1.30 About Defining New Group Elements

In this section we will show how one can add a new type of group elements to GAP. A lot of group elements in GAP are implemented this way, for example elements of generic factor groups, or elements of generic direct products.

We will use prime residue classes modulo an integer as our example. They have the advantage that the arithmetic is very simple, so that we can concentrate on the implementation without being carried away by mathematical details.

Note that everything we define is already in the library in the file LIBNAME/"numtheor.g", so there is no need for you to type it in. You may however like to make a copy of this file and modify it.

We will represent residue classes by records. This is absolutely typical, all group elements not built into the GAP kernel are realized by records.

To distinguish records representing residue classes from other records we require that residue class records have a component with the name isResidueClass and the value true. We also require that they have a component with the name isGroupElement and again the value true. Those two components are called the tag components.

Next each residue class record must of course have components that tell us which residue class this record represents. The component with the name representative contains the smallest nonnegative element of the residue class. The component with the name modulus contains the modulus. Those two components are called the identifying components.

Finally each residue class record must have a component with the name operations that contains an appropriate operations record (see below). In this way we can make use of the possibility to define operations for records (see 39.4 and 39.5).

Below is an example of a residue class record.

    r13mod43 := rec(
        isGroupElement := true,
        isResidueClass := true,
        representative := 13,
        modulus        := 43,
        domain         := GroupElements,
        operations     := ResidueClassOps )

The first function that we have to write is very simple. Its only task is to test whether an object is a residue class. It does this by testing for the tag component isResidueClass.

    IsResidueClass := function ( obj )
        return IsRec( obj )
               and IsBound( obj.isResidueClass )
               and obj.isResidueClass;
    end;

Our next function takes a representative and a modulus and constructs a new residue class. Again this is not very difficult.

    ResidueClass := function ( representative, modulus )
        local res;
        res := rec();
        res.isGroupElement  := true;
        res.isResidueClass  := true;
        res.representative  := representative mod modulus;
        res.modulus         := modulus;
        res.domain          := GroupElements;
        res.operations      := ResidueClassOps;
        return res;
    end;

Now we have to define the operations record for residue classes. Remember that this record contains a function for each binary operation, which is called to evaluate such a binary operation (see 39.4 and 39.5). The operations =, <, *, /, mod, ^, Comm, and Order are the ones that are applicable to all group elements. The meaning of those operations for group elements is described in 7.2 and 7.3.

Luckily we do not have to define everything. Instead we can inherit a lot of those functions from generic group elements. For example, for all group elements g/h should be equivalent to g*h^-1. So the function for / could simply be function(g,h) return g*h^-1; end. Note that this function can be applied to all group elements, independently of their type, because all the dependencies are in * and ^.

The operations record GroupElementOps contains such functions that can be used by all types of group elements. Note that there is no element that has GroupElementsOps as its operations record. This is impossible, because there is for example no generic method to multiply or invert group elements. Thus GroupElementsOps is only used to inherit general methods as is done below.

    ResidueClassOps := Copy( GroupElementOps );

Note that the copy is necessary, otherwise the following assignments would not only change

ResidueClassOps but also GroupElementOps.

The first function we are implementing is the equality comparison. The required operation is described simply enough. = should evaluate to true if the operands are equal and false otherwise. Two residue classes are of course equal if they have the same representative and the same modulus. One complication is that when this function is called either operand may not be a residue class. Of course at least one must be a residue class otherwise this function would not have been called at all.

    ResidueClassOps.'=' := function ( l, r )
        local   isEql;
        if IsResidueClass( l )  then
            if IsResidueClass( r )  then
                isEql :=    l.representative = r.representative
                        and l.modulus        = r.modulus;
            else
                isEql := false;
            fi;
        else
            if IsResidueClass( r )  then
                isEql := false;
            else
                Error("panic, neither <l> nor <r> is a residue class");
            fi;
        fi;
        return isEql;
    end;

Note that the quotes around the equal sign = are necessary, otherwise it would not be taken as a record component name, as required, but as the symbol for equality, which must not appear at this place.

Note that we do not have to implement a function for the inequality operator <>, because it is in the GAP kernel implemented by the equivalence l <> r is not l = r.

The next operation is the comparison. We define that one residue class is smaller than another residue class if either it has a smaller modulus or, if the moduli are equal, it has a smaller representative. We must also implement comparisons with other objects.

    ResidueClassOps.'<' := function ( l, r )
        local   isLess;
        if IsResidueClass( l )  then
            if IsResidueClass( r )  then
                isLess :=   l.representative < r.representative
                        or (l.representative = r.representative
                            and l.modulus    < r.modulus);

else
    isLess := not IsInt( r ) and not IsRat( r )
          and not IsCyc( r ) and not IsPerm( r )
          and not IsWord( r ) and not IsAgWord( r );
fi;
else
if IsResidueClass( r )  then
    isLess :=  IsInt( l ) or IsRat( l )
            or IsCyc( l ) or IsPerm( l )
            or IsWord( l ) or IsAgWord( l );
else
    Error("panic, neither <l> nor <r> is a residue class");
fi;
fi;
return isLess;
end;

The next operation that we must implement is the multiplication *. This function is quite complex because it must handle several different tasks. To make its implementation easier to understand we will start with a very simple–minded one, which only multiplies residue classes, and extend it in the following paragraphs.

ResidueClassOps.'*' := function ( l, r )

local prd; # product of l and r, result

if IsResidueClass( l )  then
if IsResidueClass( r )  then
    if l.modulus <> r.modulus  then
        Error("<l> and <r> must have the same modulus");
    fi;
    prd := ResidueClass(
                l.representative * r.representative,
                l.modulus );
else
    Error("product of <l> and <r> must be defined");
fi;
else
if IsResidueClass( r )  then
    Error("product of <l> and <r> must be defined");
else
    Error("panic, neither <l> nor <r> is a residue class");
fi;
fi;
return prd;
end;

This function correctly multiplies residue classes, but there are other products that must be implemented. First every group element can be multiplied with a list of group elements, and the result shall be the list of products (see 7.3 and 25.12). In such a case the above function would only signal an error, which is not acceptable. Therefore we must extend this definition.

    ResidueClassOps.'*' := function ( l, r )

local prd; # product of l and r, result

        if IsResidueClass( l )  then
            if IsResidueClass( r )  then
                if l.modulus <> r.modulus  then
                    Error( "<l> and <r> must have the same modulus" );
                fi;
                prd := ResidueClass(
                            l.representative * r.representative,
                            l.modulus );
            elif IsList( r )  then
                prd := List( r, x -> l * x );
            else
                Error("product of <l> and <r> must be defined");
            fi;
        elif IsList( l )  then
            if IsResidueClass( r )  then
                prd := List( l, x -> x * r );
            else
                Error("panic: neither <l> nor <r> is a residue class");
            fi;
        else
            if IsResidueClass( r )  then
                Error( "product of <l> and <r> must be defined" );
            else
                Error("panic, neither <l> nor <r> is a residue class");
            fi;
        fi;
        return prd;
    end;

This function is almost complete. However it is also allowed to multiply a group element with a subgroup and the result shall be a coset (see 7.79). The operations record of subgroups, which are of course also represented by records (see 7.111), contains a function that constructs such a coset. The problem is that in an expression like subgroup * residue-class, this function is not called. This is because the multiplication function in the operations record of the right operand is called if both operands have such a function (see 39.5). Now in the above case both operands have such a function. The left operand subgroup has the operations record GroupOps (or some refinement thereof), the right operand residue-class has the operations record ResidueClassOps. Thus ResidueClassOps.* is called. But it does not and also should not know how to construct a coset. The solution is simple. The multiplication function for residue classes detects this special case and simply calls the multiplication function of the left operand.

    ResidueClassOps.'*' := function ( l, r )

local prd; # product of l and r, result

        if IsResidueClass( l )  then
            if IsResidueClass( r )  then
                if l.modulus <> r.modulus  then

    Error( "<l> and <r> must have the same modulus" );
fi;
prd := ResidueClass(
            l.representative * r.representative,
            l.modulus );
elif IsList( r )  then
prd := List( r, x -> l * x );
else
Error("product of <l> and <r> must be defined");
fi;
elif IsList( l )  then
if IsResidueClass( r )  then
prd := List( l, x -> x * r );
else
Error("panic: neither <l> nor <r> is a residue class");
fi;
else
if IsResidueClass( r )  then
if IsRec( l )  and IsBound( l.operations )
    and IsBound( l.operations.'*' )
    and l.operations.'*' <> ResidueClassOps.'*'
then
    prd := l.operations.'*'( l, r );
else
    Error("product of <l> and <r> must be defined");
fi;
else
Error("panic, neither <l> nor <r> is a residue class");
fi;
fi;
return prd;
end;

Now we are done with the multiplication.

Next is the powering operation ^. It is not very complicated. The PowerMod function (see 5.25) does most of what we need, especially the inversion of elements with the Euclidean algorithm when the exponent is negative. Note however, that the definition of operations (see 7.3) requires that the conjugation is available as power of a residue class by another residue class. This is of course very easy since residue classes form an abelian group.

ResidueClassOps.'^' := function ( l, r )
local    pow;
if IsResidueClass( l )  then
if IsResidueClass( r )  then
if l.modulus <> r.modulus  then
    Error("<l> and <r> must have the same modulus");
fi;
if GcdInt( r.representative, r.modulus ) <> 1  then
    Error("<r> must be invertable");

                fi;
                pow := l;
            elif IsInt( r )  then
                pow := ResidueClass(
                            PowerMod( l.representative, r, l.modulus ),
                            l.modulus );
            else
                Error("power of <l> and <r> must be defined");
            fi;
        else
            if IsResidueClass( r )  then
                Error("power of <l> and <r> must be defined");
            else
                Error("panic, neither <l> nor <r> is a residue class");
            fi;
        fi;
        return pow;
    end;

The last function that we have to write is the printing function. This is called to print a residue class. It prints the residue class in the form ResidueClass( representative, modulus ). It is fairly typical to print objects in such a form. This form has the advantage that it can be read back, resulting in exactly the same element, yet it is very concise.

    ResidueClassOps.Print := function ( r )
        Print("ResidueClass( ",r.representative,", ",r.modulus," )");
    end;

Now we are done with the definition of residue classes as group elements. Try them. We can at this point actually create groups of such elements, and compute in them.

However, we are not yet satisfied. There are two problems with the code we have implemented so far. Different people have different opinions about which of those problems is the graver one, but hopefully all agree that we should try to attack those problems.

The first problem is that it is still possible to define objects via Group (see 7.9) that are not actually groups.

    gap> G := Group( ResidueClass(13,43), ResidueClass(13,41) );
    Group( ResidueClass(13,43), ResidueClass(13,41) )

The other problem is that groups of residue classes constructed with the code we have implemented so far are not handled very efficiently. This is because the generic group algorithms are used, since we have not implemented anything else. For example to test whether a residue class lies in a residue class group, all elements of the residue class group are computed by a Dimino algorithm, and then it is tested whether the residue class is an element of this proper set.

To solve the first problem we must first understand what happens with the above code if we create a group with Group( res1 , res2 ... ). Group tries to find a domain that contains all the elements res1 , res2 , etc. It first calls Domain( [ res1 , res2 ... ] ) (see 4.5). Domain looks at the residue classes and sees that they all are records and that they all have a component with the name domain. This is understood to be a domain in which the elements lie. And in fact res1 in GroupElements is true, because GroupElements accepts

all records with tag isGroupElement. So Domain returns GroupElements. Group then calls
GroupElements.operations.Group(GroupElements,[res1 ,res2 ...],id), where id is the

identity residue class, obtained by res1 ^0, and returns the result.

GroupElementsOps.Group is the function that actually creates the group. It does this by simply creating a record with its second argument as generators list, its third argument as identity, and the generic GroupOps as operations record. It ignores the first argument, which is passed only because convention dictates that a dispatcher passes the domain as first argument.

So to solve the first problem we must achieve that another function instead of the generic function GroupElementsOps.Group is called. This can be done by persuading Domain to return a different domain. And this will happen if the residue classes hold this other domain in their domain component.

The obvious choice for such a domain is the (yet to be written) domain ResidueClasses. So ResidueClass must be slightly changed.

    ResidueClass := function ( representative, modulus )
        local res;
        res := rec();
        res.isGroupElement  := true;
        res.isResidueClass  := true;
        res.representative  := representative mod modulus;
        res.modulus         := modulus;
        res.domain          := ResidueClasses;
        res.operations      := ResidueClassOps;
        return res;
    end;

The main purpose of the domain ResidueClasses is to construct groups, so there is very little we have to do. And in fact most of that can be inherited from GroupElements.

    ResidueClasses := Copy( GroupElements );
    ResidueClasses.name := "ResidueClasses";
    ResidueClassesOps := Copy( GroupElementsOps );
    ResidueClasses.operations := ResidueClassesOps;

So now we must implement ResidueClassesOps.Group, which should check whether the passed elements do in fact form a group. After checking it simply delegates to the generic function GroupElementsOps.Group to create the group as before.

    ResidueClassesOps.Group := function ( ResidueClasses, gens, id )

local g; # one generator from gens

        for g  in gens  do
            if g.modulus <> id.modulus  then
                Error("the generators must all have the same modulus");
            fi;
            if GcdInt( g.representative, g.modulus ) <> 1  then
              Error("the generators must all be prime residue classes");
            fi;
        od;

return GroupElementOps.Group( ResidueClasses, gens, id );
end;

This solves the first problem. To solve the second problem, i.e., to make operations with residue class groups more efficient, we must extend the function ResidueClassesOps.Group. It now enters a new operations record into the group. It also puts the modulus into the group record, so that it is easier to access.

ResidueClassesOps.Group := function ( ResidueClasses, gens, id )

local G, # group G, result

gen; # one generator from gens

for gen  in gens  do
    if gen.modulus <> id.modulus  then
        Error("the generators must all have the same modulus");
    fi;
    if GcdInt( gen.representative, gen.modulus ) <> 1  then
      Error("the generators must all be prime residue classes");
    fi;
od;
G := GroupElementsOps.Group( ResidueClasses, gens, id );
G.modulus    := id.modulus;
G.operations := ResidueClassGroupOps;
return G;
end;

Of course now we must build such an operations record. Luckily we do not have to implement all functions, because we can inherit a lot of functions from GroupOps. This is done by

copying GroupOps as we have done before for ResidueClassOps and ResidueClassesOps.
ResidueClassGroupOps := Copy( GroupOps );

Now the first function that we must write is the Subgroup function to ensure that not only groups constructed by Group have the correct operations record, but also subgroups of those groups created by Subgroup. As in Group we only check the arguments and then leave the

work to GroupOps.Subgroup.
ResidueClassGroupOps.Subgroup := function ( G, gens )

local S, # subgroup of G, result

gen; # one generator from gens

for gen  in gens  do
    if gen.modulus <> G.modulus  then
        Error("the generators must all have the same modulus");
    fi;
    if GcdInt( gen.representative, gen.modulus ) <> 1  then
      Error("the generators must all be prime residue classes");
    fi;
od;
S := GroupOps.Subgroup( G, gens );
S.modulus    := G.modulus;
S.operations := ResidueClassGroupOps;
return S;
end;

The first function that we write especially for residue class groups is SylowSubgroup. Since residue class groups are abelian we can compute a Sylow subgroup of such a group by simply taking appropriate powers of the generators.

    ResidueClassGroupOps.SylowSubgroup := function ( G, p )

local S, # Sylow subgroup of G, result gen, # one generator of G ord, # order of gen gens; # generators of S

        gens := [];
        for gen  in G.generators  do
            ord := OrderMod( gen.representative, G.modulus );
            while ord mod p = 0  do ord := ord / p;  od;
            Add( gens, gen ^ord );
        od;
        S := Subgroup( Parent( G ), gens );
        return S;
    end;

To allow the other functions that are applicable to residue class groups to work efficiently we now want to make use of the fact that residue class groups are direct products of cyclic groups and that we know what those factors are and how we can project onto those factors.

To do this we write ResidueClassGroupOps.MakeFactors that adds the components facts,

roots, sizes, and sgs to the group record G. This information, detailed below, will enable other functions to work efficiently with such groups. Creating such information is a fairly typical thing, for example for permutation groups the corresponding information is the stabilizer chain computed by MakeStabChain.

G.facts will be the list of prime power factors of G.modulus. Actually this is a little bit more complicated, because the residue class group modulo the largest power q of 2 that divides G.modulus need not be cyclic. So if q is a multiple of 4, G.facts[1] will be 4, corresponding to the projection of G into (Z/4Z)^{∗} (of size 2), furthermore if q is a multiple of 8, G.facts[2] will be q, corresponding to the projection of G into the subgroup generated by 5 in (Z/qZ)^{∗} (of size q/4).

G.roots will be a list of primitive roots, i.e., of generators of the corresponding factors in G.facts. G.sizes will be a list of the sizes of the corresponding factors in G.facts, i.e.,

G.sizes[i] = Phi( G.facts[i] ). (If G.modulus is a multiple of 8, G.roots[2] will

be 5, and G.sizes[2] will be q/4.)

Now we can represent each element g of the group G by a list e, called the exponent vector, of the length of G.facts, where e[i] is the logarithm of g.representative mod G.facts[i] with respect to G.roots[i]. The multiplication of elements of G corresponds to the componentwise addition of their exponent vectors, where we add modulo G.sizes[i] in the i-th component. (Again special consideration are necessary if G.modulus is divisible by 8.)

Next we compute the exponent vectors of all generators of G, and represent this information as a matrix. Then we bring this matrix into upper triangular form, with an algorithm that is very much like the ordinary Gaussian elimination, modified to account for the different sizes of the components. This upper triangular matrix of exponent vectors is the component G.sgs. This new matrix obviously still contains the exponent vectors of a generating system of G, but a much nicer one, which allows us to tackle problems one component at a time. (It is not necessary that you fully check this, the important thing here is not the mathematical side.)

ResidueClassGroupOps.MakeFactors := function ( G )

local p, q, # prime factor of modulus and largest power r, s, # two rows of the standard generating system g, # extended gcd of leading entries in r, s x, y, # two entries in r and s i, k, l; # loop variables

# find the factors of the direct product

G.facts := [];
G.roots := [];
G.sizes := [];
for p  in Set( Factors( G.modulus ) )  do
q := p;
while G.modulus mod (p*q) = 0  do q := p*q;  od;
if q mod 4 = 0  then
Add( G.facts, 4 );
Add( G.roots, 3 );
Add( G.sizes, 2 );
fi;
if q mod 8 = 0  then
Add( G.facts, q );
Add( G.roots, 5 );
Add( G.sizes, q/4 );
fi;
if p <> 2  then
Add( G.facts, q );
Add( G.roots, PrimitiveRootMod( q ) );
Add( G.sizes, (p-1)*q/p );
fi;
od;

# represent each generator in this factorization

G.sgs := [];
for k  in [ 1 .. Length( G.generators ) ]  do
G.sgs[k] := [];
for i  in [ 1 .. Length( G.facts ) ]  do
if G.facts[i] mod 8 = 0  then
    if G.generators[k].representative mod 4 = 1  then
        G.sgs[k][i] := LogMod(
            G.generators[k].representative,
            G.roots[i], G.facts[i] );
    else
        G.sgs[k][i] := LogMod(

                -G.generators[k].representative,
                G.roots[i], G.facts[i] );
        fi;
    else
        G.sgs[k][i] := LogMod(
                G.generators[k].representative,
                G.roots[i], G.facts[i] );
    fi;
od;
od;
for i  in [ Length( G.sgs ) + 1 .. Length( G.facts ) ]  do
G.sgs[i] := 0 * G.facts;
od;

# bring this matrix to diagonal form

for i  in [ 1 .. Length( G.facts ) ]  do
r := G.sgs[i];
for k  in [ i+1 .. Length( G.sgs ) ]  do
    s := G.sgs[k];
    g := Gcdex( r[i], s[i] );
    for l  in [ i .. Length( r ) ]  do
        x := r[l];  y := s[l];
        r[l] := (g.coeff1 * x + g.coeff2 * y) mod G.sizes[l];
        s[l] := (g.coeff3 * x + g.coeff4 * y) mod G.sizes[l];
    od;
od;
s := [];
x := G.sizes[i] / GcdInt( G.sizes[i], r[i] );
for l  in [ 1 .. Length( r ) ]  do
    s[l] := (x * r[l]) mod G.sizes[l];
od;
Add( G.sgs, s );
od;
end;

With the information computed by MakeFactors it is now of course very easy to compute the size of a residue class group. We just look at the G.sgs, and multiply the orders of the leading exponents of the nonzero exponent vectors.

ResidueClassGroupOps.Size := function ( G )

local s, # size of G, result

i; # loop variable

if not IsBound( G.facts )  then
G.operations.MakeFactors( G );
fi;
s := 1;
for i  in [ 1 .. Length( G.facts ) ]  do
s := s * G.sizes[i] / GcdInt( G.sizes[i], G.sgs[i][i] );

od;
return s;
end;

The membership test is a little bit more complicated. First we test that the first argument is really a residue class with the correct modulus. Then we compute the exponent vector of this residue class and reduce this exponent vector using the upper triangular matrix G.sgs.

ResidueClassGroupOps.'in' := function ( res, G )

local s, # exponent vector of res g, # extended gcd x, y, # two entries in s and G.sgs[i] i, l; # loop variables

if not IsResidueClass( res )
    or res.modulus <> G.modulus
    or GcdInt( res.representative, res.modulus ) <> 1
then
    return false;
fi;
if not IsBound( G.facts )  then
    G.operations.MakeFactors( G );
fi;
s := [];
for i  in [ 1 .. Length( G.facts ) ]  do
    if G.facts[i] mod 8 = 0  then
        if res.representative mod 4 = 1  then
            s[i] := LogMod( res.representative,
                            G.roots[i], G.facts[i] );
        else
            s[i] := LogMod( -res.representative,
                            G.roots[i], G.facts[i] );
        fi;
    else
        s[i] := LogMod( res.representative,
                        G.roots[i], G.facts[i] );
    fi;
od;
for i  in [ 1 .. Length( G.facts ) ]  do
    if s[i] mod GcdInt( G.sizes[i], G.sgs[i][i] ) <> 0  then
        return false;
    fi;
    g := Gcdex( s[i], G.sgs[i][i] );
    for l  in [ i .. Length( G.facts ) ]  do
        x := s[l];  y := G.sgs[i][l];
        s[l] := (g.coeff3 * x + g.coeff4 * y) mod G.sizes[l];
    od;
od;
return true;
end;

We also add a function Random that works by creating a random exponent as a random linear combination of the exponent vectors in G.sgs, and converts this exponent vector to a residue class. (The main purpose of this function is to allow you to create random test examples for the other functions.)

ResidueClassGroupOps.Random := function ( G )

local s, # exponent vector of random element r, # vector of remainders in each factor i, k, l; # loop variables

if not IsBound( G.facts )  then
    G.operations.MakeFactors( G );
fi;
s := 0 * G.facts;
for i  in [ 1 .. Length( G.facts ) ]  do
    l := G.sizes[i] / GcdInt( G.sizes[i], G.sgs[i][i] );
    k := Random( [ 0 .. l-1 ] );
    for l  in [ i .. Length( s ) ]  do
        s[l] := (s[l] + k * G.sgs[i][l]) mod G.sizes[l];
    od;
od;
r := [];
for l  in [ 1 .. Length( s ) ]  do
    r[l] := PowerModInt( G.roots[l], s[l], G.facts[l] );
    if G.facts[l] mod 8 = 0  and r[1] = 3  then
        r[l] := G.facts[l] - r[l];
    fi;
od;
return ResidueClass( ChineseRem( G.facts, r ), G.modulus );
end;

There are a lot more functions that would benefit from being implemented especially for residue class groups. We do not show them here, because the above functions already displayed how such functions can be written. To round things up, we finally add a function that constructs the full residue class group given a modulus m. This function is totally independent of the implementation of residue classes and residue class groups. It only has to find a (minimal) system of generators of the full prime residue classes group, and to call Group to construct this group. It also adds the information entry size to the group record, of course with the value ϕ(n).

PrimeResidueClassGroup := function ( m )

local G, # group Z/mZ, result gens, # generators of G p, q, # prime and prime power dividing m r, # primitive root modulo q g; # is = r mod q and = 1 mod m / q

# add generators for each prime power factor q of m

gens := [];
for p  in Set( Factors( m ) )  do
    q := p;

    while m mod (q * p) = 0  do q := q * p;  od;

# (Z/4Z)^{∗} =< 3 >

    if   q = 4  then
        r := 3;
        g := r + q * (((1/q mod (m/q)) * (1 - r)) mod (m/q));
        Add( gens, ResidueClass( g, m ) );

# (Z/8nZ)^{∗} =< 5, −1 > is not cyclic

    elif q mod 8 = 0  then
        r := q-1;
        g := r + q * (((1/q mod (m/q)) * (1 - r)) mod (m/q));
        Add( gens, ResidueClass( g, m ) );
        r := 5;
        g := r + q * (((1/q mod (m/q)) * (1 - r)) mod (m/q));
        Add( gens, ResidueClass( g, m ) );

# for odd q (Z/qZ)^{∗} is cyclic

    elif q <> 2  then
        r :=  PrimitiveRootMod( q );
        g := r + q * (((1/q mod (m/q)) * (1 - r)) mod (m/q));
        Add( gens, ResidueClass( g, m ) );
    fi;
od;

# return the group generated by gens

G := Group( gens, ResidueClass( 1, m ) );
G.size := Phi( n );
return G;
end;

There is one more thing that we can learn from this example. Mathematically a residue class is not only a group element, but a set as well. We can reflect this in GAP by turning residue classes into domains (see 4). Section 1.28 gives an example of how to implement a new domain, so we will here only show the code with few comments.

First we must change the function that constructs a residue class, so that it enters the necessary fields to tag this record as a domain. It also adds the information that residue classes are infinite.

ResidueClass := function ( representative, modulus )
local res;
res := rec();
res.isGroupElement  := true;
res.isDomain        := true;
res.isResidueClass  := true;
res.representative  := representative mod modulus;
res.modulus         := modulus;
res.isFinite        := false;

        res.size            := "infinity";
        res.domain          := ResidueClasses;
        res.operations      := ResidueClassOps;
        return res;
    end;

The initialization of the ResidueClassOps record must be changed too, because now we want to inherit both from GroupElementsOps and DomainOps. This is done by the function MergedRecord, which takes two records and returns a new record that contains all components from either record.

Note that the record returned by MergedRecord does not have those components that appear in both arguments. This forces us to explicitly write down from which record we want to inherit those functions, or to define them anew. In our example the components common to GroupElementOps and DomainOps are only the equality and ordering functions, which we have to define anyhow. (This solution for the problem of which definition to choose in the case of multiple inheritance is also taken by C++.)

With this function definition we can now initialize ResidueClassOps.

    ResidueClassOps := MergedRecord( GroupElementOps, DomainOps );

Now we add all functions to this record as described above.

Next we add a function to the operations record that tests whether a certain object is in a residue class.

    ResidueClassOps.'in' := function ( element, class )
        if IsInt( element )  then
            return (element mod class.modulus = class.representative);
        else
            return false;
        fi;
    end;

Finally we add a function to compute the intersection of two residue classes.

    ResidueClassOps.Intersection := function ( R, S )

local I, # intersection of R and S, result gcd; # gcd of the moduli

        if IsResidueClass( R )  then
            if IsResidueClass( S )  then
                gcd := GcdInt( R.modulus, S.modulus );
                if     R.representative mod gcd
                    <> S.representative mod gcd
                then
                    I := [];
                else
                    I := ResidueClass(
                            ChineseRem(
                                [ R.representative, S.representative ],
                                [ R.modulus,        S.modulus ] ),
                            Lcm(  R.modulus,        S.modulus ) );
                fi;

            else
                I := DomainOps.Intersection( R, S );
            fi;
        else
            I := DomainOps.Intersection( R, S );
        fi;
        return I;
    end;

There is one further thing that we have to do. When Group is called with a single argument that is a domain, it assumes that you want to create a new group such that there is a bijection between the original domain and the new group. This is not what we want here. We want that in this case we get the cyclic group that is generated by the single residue class. (This overloading of Group is probably a mistake, but so is the overloading of residue classes, which are both group elements and domains.) The following definition solves this problem.

    ResidueClassOps.Group := function ( R )
        return ResidueClassesOps.Group( ResidueClasses, [R], R^0 );
    end;

This concludes our example. There are however several further things that you could do. One is to add functions for the quotient, the modulus, etc. Another is to fix the functions so that they do not hang if asked for the residue class group mod 1. Also you might try to implement residue class rings analogous to residue class groups. Finally it might be worthwhile to improve the speed of the multiplication of prime residue classes. This can be done by doing some precomputation in ResidueClass and adding some information to the residue class record for prime residue classes ([Mon85]).

The Programming Language

This chapter describes the GAP programming language. It should allow you in principle to predict the result of each and every input. In order to know what we are talking about, we first have to look more closely at the process of interpretation and the various representations of data involved.

First we have the input to GAP, given as a string of characters. How those characters enter GAP is operating system dependent, e.g., they might be entered at a terminal, pasted with a mouse into a window, or read from a file. The mechanism does not matter. This representation of expressions by characters is called the external representation of the expression. Every expression has at least one external representation that can be entered to get exactly this expression.

The input, i.e., the external representation, is transformed in a process called reading to an internal representation. At this point the input is analyzed and inputs that are not legal external representations, according to the rules given below, are rejected as errors. Those rules are usually called the syntax of a programming language.

The internal representation created by reading is called either an expression or a statement. Later we will distinguish between those two terms, however now we will use them interchangeably. The exact form of the internal representation does not matter. It could be a string of characters equal to the external representation, in which case the reading would only need to check for errors. It could be a series of machine instructions for the processor on which GAP is running, in which case the reading would more appropriately be called compilation. It is in fact a tree–like structure.

After the input has been read it is again transformed in a process called evaluation or execution. Later we will distinguish between those two terms too, but for the moment we will use them interchangeably. The name hints at the nature of this process, it replaces an expression with the value of the expression. This works recursively, i.e., to evaluate an expression first the subexpressions are evaluated and then the value of the expression is computed according to rules given below from those values. Those rules are usually called the semantics of a programming language.

The result of the evaluation is, not surprisingly, called a value. The set of values is of course a much smaller set than the set of expressions; for every value there are several expressions that will evaluate to this value. Again the form in which such a value is represented internally does not matter. It is in fact a tree–like structure again.

The last process is called printing. It takes the value produced by the evaluation and creates an external representation, i.e., a string of characters again. What you do with this external representation is up to you. You can look at it, paste it with the mouse into another window, or write it to a file.

Lets look at an example to make this more clear. Suppose you type in the following string of 8 characters

    1 + 2 * 3;

GAP takes this external representation and creates a tree like internal representation, which we can picture as follows

       +
      / \
     1   *
        / \
       2   3

This expression is then evaluated. To do this GAP first evaluates the right subexpression 2*3. Again to do this GAP first evaluates its subexpressions 2 and 3. However they are so simple that they are their own value, we say that they are self–evaluating. After this has been done, the rule for * tells us that the value is the product of the values of the two subexpressions, which in this case is clearly 6. Combining this with the value of the left operand of the +, which is self–evaluating too gives us the value of the whole expression 7. This is then printed, i.e., converted into the external representation consisting of the single character 7.

In this fashion we can predict the result of every input when we know the syntactic rules that govern the process of reading and the semantic rules that tell us for every expression how its value is computed in terms of the values of the subexpressions. The syntactic rules are given in sections 2.1, 2.2, 2.3, 2.4, 2.5, and 2.20, the semantic rules are given in sections 2.6, 2.7, 2.8, 2.9, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, and the chapters describing the individual data types.

2.1 Lexical Structure

The input of GAP consists of sequences of the following characters.

Digits, uppercase and lowercase letters, space, tab, newline, and the special characters

    "       '       (       )       *       +       ,       _
    .       /       :       ;       <       =       >       ~
    [       \       ]       ^_{       }       #

Other characters will be signalled as illegal. Inside strings and comments the full character set supported by the computer is allowed.

2.2 Symbols

The process of reading, i.e., of assembling the input into expressions, has a subprocess, called scanning, that assembles the characters into symbols. A symbol is a sequence of characters that form a lexical unit. The set of symbols consists of keywords, identifiers, strings, integers, and operator and delimiter symbols.

A keyword is a reserved word consisting entirely of lowercase letters (see 2.4). An identifier is a sequence of letters and digits that contains at least one letter and is not a keyword (see 2.5). An integer is a sequence of digits (see 10). A string is a sequence of arbitrary characters enclosed in double quotes (see 28).

Operator and delimiter symbols are

    +       -       *       /       ^~
    =       <>      <       <=      >       >=
    :=      .       ..      ->      ,       ;
    [       ]       {       }       (       )

Note that during the process of scanning also all whitespace is removed (see 2.3).

2.3 Whitespaces

The characters space, tab, newline, and return are called whitespace characters. Whitespace is used as necessary to separate lexical symbols, such as integers, identifiers, or keywords. For example Thorondor is a single identifier, while Th or ondor is the keyword or between the two identifiers Th and ondor. Whitespace may occur between any two symbols, but not within a symbol. Two or more adjacent whitespaces are equivalent to a single whitespace. Apart from the role as separator of symbols, whitespaces are otherwise insignificant. Whitespaces may also occur inside a string, where they are significant. Whitespaces should also be used freely for improved readability.

A comment starts with the character #, which is sometimes called sharp or hatch, and continues to the end of the line on which the comment character appears. The whole comment, including # and the newline character is treated as a single whitespace. Inside a string, the comment character # looses its role and is just an ordinary character.

For example, the following statement

    if i<0 then a:=-i;else a:=i;fi;

is equivalent to

    if i < 0  then      # if i is negative
        a := -i;        #     take its inverse
    else                # otherwise
        a := i;         #     take itself
    fi;

(which by the way shows that it is possible to write superfluous comments). However the first statement is not equivalent to

    ifi<0thena:=-i;elsea:=i;fi;

since the keyword if must be separated from the identifier i by a whitespace, and similarly then and a, and else and a must be separated.

2.4 Keywords

Keywords are reserved words that are used to denote special operations or are part of statements. They must not be used as identifiers. The keywords are

    and       do        elif      else      end       fi
    for       function  if        in        local     mod
    not       od        or        repeat    return    then
    until     while     quit

Note that all keywords are written in lowercase. For example only else is a keyword; Else, eLsE, ELSE and so forth are ordinary identifiers. Keywords must not contain whitespace, for example el if is not the same as elif.

2.5 Identifiers

An identifier is used to refer to a variable (see 2.7). An identifier consists of letters, digits, and underscores , and must contain at least one letter or underscore. An identifier is terminated by the first character not in this class. Examples of valid identifiers are

    a                   foo                 aLongIdentifier
    hello               Hello               HELLO
    x100                100x                _100
    some_people_prefer_underscores_to_separate_words
    WePreferMixedCaseToSeparateWords

Note that case is significant, so the three identifiers in the second line are distinguished.

The backslash \ can be used to include other characters in identifiers; a backslash followed by a character is equivalent to the character, except that this escape sequence is considered to be an ordinary letter. For example G\(2\,5\) is an identifier, not a call to a function G.

An identifier that starts with a backslash is never a keyword, so for example \* and \mod are identifier.

The length of identifiers is not limited, however only the first 1023 characters are significant. The escape sequence \newline is ignored, making it possible to split long identifiers over multiple lines.

2.6 Expressions

An expression is a construct that evaluates to a value. Syntactic constructs that are executed to produce a side effect and return no value are called statements (see 2.11). Expressions appear as right hand sides of assignments (see 2.12), as actual arguments in function calls (see 2.8), and in statements.

Note that an expression is not the same as a value. For example 1 + 11 is an expression, whose value is the integer 12. The external representation of this integer is the character sequence 12, i.e., this sequence is output if the integer is printed. This sequence is another expression whose value is the integer 12. The process of finding the value of an expression is done by the interpreter and is called the evaluation of the expression.

Variables, function calls, and integer, permutation, string, function, list, and record literals (see 2.7, 2.8, 10, 19, 28, 2.18, 25, 39), are the simplest cases of expressions.

Expressions, for example the simple expressions mentioned above, can be combined with the operators to form more complex expressions. Of course those expressions can then be combined further with the operators to form even more complex expressions. The operators fall into three classes. The comparisons are =, <>, <=, >, >=, and in (see 2.9 and 25.13). The arithmetic operators are +, -, *, /, mod, and ^(see 2.10). The logical operators are not, and, and or (see 38.2).

gap> 2 * 2; # a very simple expression with value

    4
    gap> 2 * 2 + 9 = Fibonacci(7) and  Fibonacci(13) in Primes;

true # a more complex expression

2.7 Variables

A variable is a location in a GAP program that points to a value. We say the variable is bound to this value. If a variable is evaluated it evaluates to this value.

Initially an ordinary variable is not bound to any value. The variable can be bound to a value by assigning this value to the variable (see 2.12). Because of this we sometimes say that a variable that is not bound to any value has no assigned value. Assignment is in fact the only way by which a variable, which is not an argument of a function, can be bound to a value. After a variable has been bound to a value an assignment can also be used to bind the variable to another value.

A special class of variables are arguments of functions. They behave similarly to other variables, except they are bound to the value of the actual arguments upon a function call (see 2.8).

Each variable has a name that is also called its identifier. This is because in a given scope an identifier identifies a unique variable (see 2.5). A scope is a lexical part of a program text. There is the global scope that encloses the entire program text, and there are local scopes that range from the function keyword, denoting the beginning of a function definition, to the corresponding end keyword. A local scope introduces new variables, whose identifiers are given in the formal argument list and the local declaration of the function (see 2.18). Usage of an identifier in a program text refers to the variable in the innermost scope that has this identifier as its name. Because this mapping from identifiers to variables is done when the program is read, not when it is executed, GAP is said to have lexical scoping. The following example shows how one identifier refers to different variables at different points in the program text.

     g := 0;            # global variable g
     x := function ( a, b, c )
        local   y;
        g := c;         # c refers to argument c of function x
        y := function ( y )
            local  d, e, f;
            d := y;     # y refers to argument y of function y
            e := b;     # b refers to argument b of function x
            f := g;     # g refers to global variable g
            return d + e + f;
        end;

        return y( a );  # y refers to local y of function x
    end;

It is important to note that the concept of a variable in GAP is quite different from the concept of a variable in programming languages like PASCAL. In those languages a variable denotes a block of memory. The value of the variable is stored in this block. So in those languages two variables can have the same value, but they can never have identical values, because they denote different blocks of memory. (Note that PASCAL has the concept of a reference argument. It seems as if such an argument and the variable used in the actual function call have the same value, since changing the argument’s value also changes the value of the variable used in the actual function call. But this is not so; the reference argument is actually a pointer to the variable used in the actual function call, and it is the compiler that inserts enough magic to make the pointer invisible.) In order for this to work the compiler needs enough information to compute the amount of memory needed for each variable in a program, which is readily available in the declarations PASCAL requires for every variable. In GAP on the other hand each variable justs points to a value.

2.8 Function Calls

function-var() function-var( arg-expr {, arg-expr} ) The function call has the effect of calling the function function-var. The precise semantics are as follows. First GAP evaluates the function-var. Usually function-var is a variable, and GAP does nothing more than taking the value of this variable. It is allowed though that function-var is a more complex expression, namely it can for example be a selection of a list element list-var[int-expr], or a selection of a record component record-var.ident. In any case GAP tests whether the value is a function. If it is not, GAP signals an error. Next GAP checks that the number of actual arguments arg-exprs agrees with the number of formal arguments as given in the function definition. If they do not agree GAP signals an error. An exception is the case when there is exactly one formal argument with the name arg, in which case any number of actual arguments is allowed. Now GAP allocates for each formal argument and for each formal local a new variable. Remember that a variable is a location in a GAP program that points to a value. Thus for each formal argument and for each formal local such a location is allocated. Next the arguments arg-exprs are evaluated, and the values are assigned to the newly created variables corresponding to the formal arguments. Of course the first value is assigned to the new variable corresponding to the first formal argument, the second value is assigned to the new variable corresponding to the second formal argument, and so on. However, GAP does not make any guarantee about the order in which the arguments are evaluated. They might be evaluated left to right, right to left, or in any other order, but each argument is evaluated once. An exception again occurs if the function has only one formal argument with the name arg. In this case the values of all the actual arguments are stored in a list and this list is assigned to the new variable corresponding to the formal argument arg. The new variables corresponding to the formal locals are initially not bound to any value. So trying to evaluate those variables before something has been assigned to them will signal an error.

Now the body of the function, which is a statement, is executed. If the identifier of one of the formal arguments or formal locals appears in the body of the function it refers to the new variable that was allocated for this formal argument or formal local, and evaluates to the value of this variable.

If during the execution of the body of the function a return statement with an expression (see 2.19) is executed, execution of the body is terminated and the value of the function call is the value of the expression of the return. If during the execution of the body a return statement without an expression is executed, execution of the body is terminated and the function call does not produce a value, in which case we call this call a procedure call (see 2.13). If the execution of the body completes without execution of a return statement, the function call again produces no value, and again we talk about a procedure call.

    gap> Fibonacci( 11 );

# a call to the function Fibonacci with actual argument 11

    89
    gap> G.operations.RightCosets( G, Intersection( U, V ) );;
        # a call to the function in G.operations.RightCosets

# where the second actual argument is another function call

2.9 Comparisons

left-expr = right-expr left-expr <> right-expr

The operator = tests for equality of its two operands and evaluates to true if they are equal and to false otherwise. Likewise <> tests for inequality of its two operands. Note that any two objects can be compared, i.e., = and <> will never signal an error. For each type of objects the definition of equality is given in the respective chapter. Objects of different types are never equal, i.e., = evaluates in this case to false, and <> evaluates to true.

left-expr < right-expr left-expr > right-expr left-expr <= right-expr left-expr >= right-expr

< denotes less than, <= less than or equal, > greater than, and >= greater than or equal of its two operands. For each type of objects the definition of the ordering is given in the respective chapter. The ordering of objects of different types is as follows. Rationals are smallest, next are cyclotomics, followed by finite field elements, permutations, words, words in solvable groups, boolean values, functions, lists, and records are largest.

Comparison operators, which includes the operator in (see 25.13) are not associative, i.e., it is not allowed to write a = b <> c = d, you must use (a = b) <> (c = d) instead. The comparison operators have higher precedence than the logical operators (see 38.2), but lower precedence than the arithmetic operators (see 2.10). Thus, for example, a * b = c and d is interpreted, ((a * b) = c) and d).

gap> 2 * 2 + 9 = Fibonacci(7); # a comparison where the left true # operand is an expression

2.10 Operations

+ right-expr

- right-expr left-expr + right-expr left-expr - right-expr left-expr * right-expr left-expr / right-expr left-expr mod right-expr left-expr ^right-expr

The arithmetic operators are +, -, *, /, mod, and ^. The meanings (semantic) of those operators generally depend on the types of the operands involved, and they are defined in the various chapters describing the types. However basically the meanings are as follows.

+ denotes the addition, and - the subtraction of ring and field elements. * is the multiplication of group elements, / is the multiplication of the left operand with the inverse of the right operand. mod is only defined for integers and rationals and denotes the modulo operation. + and - can also be used as unary operations. The unary + is ignored and unary - is equivalent to multiplication by -1. ^denotes powering of a group element if the right operand is an integer, and is also used to denote operation if the right operand is a group element.

The precedence of those operators is as follows. The powering operator ^has the highest precedence, followed by the unary operators + and -, which are followed by the multiplicative operators *, /, and mod, and the additive binary operators + and - have the lowest precedence. That means that the expression -2 ^-2 * 3 + 1 is interpreted as (-(2 ^ (-2)) * 3) + 1. If in doubt use parentheses to clarify your intention.

The associativity of the arithmetic operators is as follows.^is not associative, i.e., it is illegal to write 2^3^4, use parentheses to clarify whether you mean (2^3) ^4 or 2 ^(3^4). The unary operators + and - are right associative, because they are written to the left of their operands. *, /, mod, +, and - are all left associative, i.e., 1-2-3 is interpreted as (1-2)-3 not as 1-(2-3). Again, if in doubt use parentheses to clarify your intentions.

The arithmetic operators have higher precedence than the comparison operators (see 2.9 and 25.13) and the logical operators (see 38.2). Thus, for example, a * b = c and d is interpreted, ((a * b) = c) and d.

    gap> 2 * 2 + 9;    # a very simple arithmetic expression
    13

2.11 Statements

Assignments (see 2.12), Procedure calls (see 2.13), if statements (see 2.14), while (see 2.15), repeat (see 2.16) and for loops (see 2.17), and the return statement (see 2.19) are called statements. They can be entered interactively or be part of a function definition. Every statement must be terminated by a semicolon.

Statements, unlike expressions, have no value. They are executed only to produce an effect. For example an assignment has the effect of assigning a value to a variable, a for loop has the effect of executing a statement sequence for all elements in a list and so on. We will talk about evaluation of expressions but about execution of statements to emphasize this difference.

It is possible to use expressions as statements. However this does cause a warning.

    gap> if i <> 0  then  k = 16/i;  fi;
    Syntax error: warning, this statement has no effect
    if i <> 0  then  k = 16/i;  fi;
                             ^

As you can see from the example this is useful for those users who are used to languages where = instead of := denotes assignment.

A sequence of one or more statements is a statement sequence, and may occur everywhere instead of a single statement. There is nothing like PASCAL’s BEGIN-END, instead each construct is terminated by a keyword. The most simple statement sequence is a single semicolon, which can be used as an empty statement sequence.

2.12 Assignments

var := expr;

The assignment has the effect of assigning the value of the expressions expr to the variable var.

The variable var may be an ordinary variable (see 2.7), a list element selection list-var[int-expr] (see 25.5) or a record component selection record-var.ident (see 39.2). Since a list element or a record component may itself be a list or a record the left hand side of an assignment may be arbitrarily complex.

Note that variables do not have a type. Thus any value may be assigned to any variable. For example a variable with an integer value may be assigned a permutation or a list or anything else.

If the expression expr is a function call then this function must return a value. If the function does not return a value an error is signalled and you enter a break loop (see 3.2). As usual you can leave the break loop with quit;. If you enter return return-expr; the value of the expression return-expr is assigned to the variable, and execution continues after the assignment.

    gap> S6 := rec( size := 720 );; S6;
    rec(
      size := 720 )
    gap> S6.generators := [ (1,2), (1,2,3,4,5) ];; S6;
    rec(
      size := 720,
      generators := [ (1,2), (1,2,3,4,5) ] )
    gap> S6.generators[2] := (1,2,3,4,5,6);; S6;
    rec(
      size := 720,
      generators := [ (1,2), (1,2,3,4,5,6) ] )

2.13 Procedure Calls

procedure-var(); procedure-var( arg-expr {, arg-expr} );

The procedure call has the effect of calling the procedure procedure-var. A procedure call is done exactly like a function call (see 2.8). The distinction between functions and procedures is only for the sake of the discussion, GAP does not distinguish between them.

A function does return a value but does not produce a side effect. As a convention the name of a function is a noun, denoting what the function returns, e.g., Length, Concatenation

and Order.

A procedure is a function that does not return a value but produces some effect. Procedures are called only for this effect. As a convention the name of a procedure is a verb, denoting what the procedure does, e.g., Print, Append and Sort.

    gap> Read( "myfile.g" );     # a call to the procedure Read
    gap> l := [ 1, 2 ];;
    gap> Append( l, [3,4,5] );    # a call to the procedure Append

2.14 If

if bool-expr1 then statements1 { elif bool-expr2 then statements2 } [ else statements3 ]

fi;

The if statement allows one to execute statements depending on the value of some boolean expression. The execution is done as follows.

First the expression bool-expr1 following the if is evaluated. If it evaluates to true the statement sequence statements1 after the first then is executed, and the execution of the if statement is complete.

Otherwise the expressions bool-expr2 following the elif are evaluated in turn. There may be any number of elif parts, possibly none at all. As soon as an expression evaluates to true the corresponding statement sequence statements2 is executed and execution of the if statement is complete.

If the if expression and all, if any, elif expressions evaluate to false and there is an else part, which is optional, its statement sequence statements3 is executed and the execution of the if statement is complete. If there is no else part the if statement is complete without executing any statement sequence.

Since the if statement is terminated by the fi keyword there is no question where an else part belongs, i.e., GAP has no dangling else. In if expr1 then if expr2 then stats1 else stats2 fi; fi; the else part belongs to the second if statement, whereas in

if expr1 then if expr2 then stats1 fi; else stats2 fi;

the else part belongs to the first if statement.

Since an if statement is not an expression it is not possible to write

    abs := if x  > 0  then  x;  else  -x;  fi;

which would, even if legal syntax, be meaningless, since the if statement does not produce a value that could be assigned to abs. If one expression evaluates neither to true nor to false an error is signalled and a break loop (see 3.2) is entered. As usual you can leave the break loop with quit;. If you enter return true;, execution of the if statement continues as if the expression whose evaluation failed had evaluated to true. Likewise, if you enter return false;, execution of the if statement continues as if the expression whose evaluation failed had evaluated to false.

    gap> i := 10;;
    gap> if 0 < i  then
    >        s := 1;
    >    elif i < 0  then
    >        s := -1;
    >    else
    >        s := 0;
    >    fi;
    gap> s;

1 # the sign of i

2.15 While

while bool-expr do statements od; The while loop executes the statement sequence statements while the condition bool-expr evaluates to true. First bool-expr is evaluated. If it evaluates to false execution of the while loop terminates and the statement immediately following the while loop is executed next. Otherwise if it evaluates to true the statements are executed and the whole process begins again. The difference between the while loop and the repeat until loop (see 2.16) is that the statements in the repeat until loop are executed at least once, while the statements in the while loop are not executed at all if bool-expr is false at the first iteration. If bool-expr does not evaluate to true or false an error is signalled and a break loop (see 3.2) is entered. As usual you can leave the break loop with quit;. If you enter return false;, execution continues with the next statement immediately following the while loop. If you enter return true;, execution continues at statements, after which the next evaluation of bool-expr may cause another error.

    gap> i := 0;;  s := 0;;
    gap> while s <= 200  do
    >        i := i + 1;  s := s + i^2;
    >    od;
    gap> s;

204 # first sum of the first i squares larger than 200

2.16 Repeat

repeat statements until bool-expr; The repeat loop executes the statement sequence statements until the condition bool-expr evaluates to true.

First statements are executed. Then bool-expr is evaluated. If it evaluates to true the repeat loop terminates and the statement immediately following the repeat loop is executed next. Otherwise if it evaluates to false the whole process begins again with the execution of the statements.

The difference between the while loop (see 2.15) and the repeat until loop is that the statements in the repeat until loop are executed at least once, while the statements in the while loop are not executed at all if bool-expr is false at the first iteration.

If bool-expr does not evaluate to true or false a error is signalled and a break loop (see 3.2) is entered. As usual you can leave the break loop with quit;. If you enter return true;, execution continues with the next statement immediately following the repeat loop. If you enter return false;, execution continues at statements, after which the next evaluation of bool-expr may cause another error.

    gap> i := 0;;  s := 0;;
    gap> repeat
    >        i := i + 1;  s := s + i^2;
    >    until s > 200;
    gap> s;

204 # first sum of the first i squares larger than 200

2.17 For

for simple-var in list-expr do statements od;

The for loop executes the statement sequence statements for every element of the list listexpr.

The statement sequence statements is first executed with simple-var bound to the first element of the list list, then with simple-var bound to the second element of list and so on. simple-var must be a simple variable, it must not be a list element selection list-var[int-expr] or a record component selection record-var.ident.

The execution of the for loop is exactly equivalent to the while loop

loop-list := list; loop-index := 1; while loop-index <= Length(loop-list) do variable := loop-list[loop-index]; statements loop-index := loop-index + 1;

    od;

with the exception that loop-list and loop-index are different variables for each for loop that do not interfere with each other.

The list list is very often a range. for variable in [from..to] do statements od; corresponds to the more common for variable from from to to do statements od; in other programming languages.

    gap> s := 0;;

gap> for i  in [1..100]  do
>        s := s + i;
> od;
gap> s;
5050

Note in the following example how the modification of the list in the loop body causes the loop body also to be executed for the new values

gap> l := [ 1, 2, 3, 4, 5, 6 ];;
gap> for i  in l  do
>        Print( i, " " );
>        if i mod 2 = 0  then Add( l, 3 * i / 2 );  fi;
> od;  Print( "\n" );
1 2 3 4 5 6 3 6 9 9
gap> l;
[ 1, 2, 3, 4, 5, 6, 3, 6, 9, 9 ]

Note in the following example that the modification of the variable that holds the list has no influence on the loop

gap> l := [ 1, 2, 3, 4, 5, 6 ];;
gap> for i  in l  do
>        Print( i, " " );
>        l := [];
> od;  Print( "\n" );
1 2 3 4 5 6
gap> l;
[  ]

2.18 Functions

function ( [ arg-ident {, arg-ident} ] )

[ local loc-ident {, loc-ident} ; ] statements

end

A function is in fact a literal and not a statement. Such a function literal can be assigned to a variable or to a list element or a record component. Later this function can be called as described in 2.8.

The following is an example of a function definition. It is a function to compute values of the Fibonacci sequence (see 40.21)

gap> fib := function ( n )
>         local  f1,  f2,  f3,  i;
>         f1 := 1;  f2 := 1;
>         for i  in [3..n]  do
>             f3 := f1 + f2;
>             f1 := f2;
>             f2 := f3;
>         od;

    >         return f2;
    >     end;;
    gap> List( [1..10], fib );
    [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]

Because for each of the formal arguments arg-ident and for each of the formal locals locident a new variable is allocated when the function is called (see 2.8), it is possible that a function calls itself. This is usually called recursion. The following is a recursive function that computes values of the Fibonacci sequence

    gap> fib := function ( n )
    >         if n < 3  then
    >             return 1;
    >         else
    >             return fib(n-1) + fib(n-2);
    >         fi;
    >     end;;
    gap> List( [1..10], fib );
    [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]

Note that the recursive version needs 2 * fib(n)-1 steps to compute fib(n), while the iterative version of fib needs only n-2 steps. Both are not optimal however, the library function Fibonacci only needs on the order of Log(n) steps.

arg-ident -> expr

This is a shorthand for

function ( arg-ident ) return expr; end.

arg-ident must be a single identifier, i.e., it is not possible to write functions of several arguments this way. Also arg is not treated specially, so it is also impossible to write functions that take a variable number of arguments this way.

The following is an example of a typical use of such a function

    gap> Sum( List( [1..100], x -> x^2 ) );
    338350

When a function fun1 definition is evaluated inside another function fun2 , GAP binds all the identifiers inside the function fun1 that are identifiers of an argument or a local of fun2 to the corresponding variable. This set of bindings is called the environment of the function fun1 . When fun1 is called, its body is executed in this environment. The following implementation of a simple stack uses this. Values can be pushed onto the stack and then later be popped off again. The interesting thing here is that the functions push and pop in the record returned by Stack access the local variable stack of Stack. When Stack is called a new variable for the identifier stack is created. When the function definitions of push and pop are then evaluated (as part of the return statement) each reference to stack is bound to this new variable. Note also that the two stacks A and B do not interfere, because each call of Stack creates a new variable for stack.

    gap> Stack := function ()
    >         local   stack;
    >         stack := [];
    >         return rec(
    >             push := function ( value )

>                 Add( stack, value );
>             end,
>             pop := function ()
>                 local   value;
>                 value := stack[Length(stack)];
>                 Unbind( stack[Length(stack)] );
>                 return value;
>             end
>         );
>    end;;
gap> A := Stack();;
gap> B := Stack();;
gap> A.push( 1 );  A.push( 2 );  A.push( 3 );
gap> B.push( 4 );  B.push( 5 );  B.push( 6 );
gap> A.pop();  A.pop();  A.pop();
3
2
1
gap> B.pop();  B.pop();  B.pop();
6
5
4

This feature should be used rarely, since its implementation in GAP is not very efficient.

2.19 Return

return;

In this form return terminates the call of the innermost function that is currently executing, and control returns to the calling function. An error is signalled if no function is currently executing. No value is returned by the function.

return expr;

In this form return terminates the call of the innermost function that is currently executing, and returns the value of the expression expr. Control returns to the calling function. An error is signalled if no function is currently executing.

Both statements can also be used in break loops (see 3.2). return; has the effect that the computation continues where it was interrupted by an error or the user hitting ctrC. return expr; can be used to continue execution after an error. What happens with the value expr depends on the particular error.

2.20 The Syntax in BNF

This section contains the definition of the GAP syntax in Backus-Naur form.

A BNF is a set of rules, whose left side is the name of a syntactical construct. Those names are enclosed in angle brackets and written in italics. The right side of each rule contains a possible form for that syntactic construct. Each right side may contain names of other syntactic constructs, again enclosed in angle brackets and written in italics, or character sequences that must occur literally; they are written in typewriter style.

Furthermore each righthand side can contain the following metasymbols written in boldface. If the right hand side contains forms separated by a pipe symbol (|) this means that one of the possible forms can occur. If a part of a form is enclosed in square brackets ([ ]) this means that this part is optional, i.e. might be present or missing. If part of the form is enclosed in curly braces ({ }) this means that the part may occur arbitrarily often, or possibly be missing.

Ident := a|...|z|A|...|Z| {a|...|z|A|...|Z|0|...|9| } Var := Ident

| Var . Ident | Var . ( Expr ) | Var [ Expr ] | Var { Expr } | Var ( [ Expr { , Expr } ] )

List := [ [ Expr ] {, [ Expr ] } ]

| [ Expr [, Expr ] .. Expr ]

Record := rec( [ Ident := Expr {, Ident := Expr } ] ) Permutation := ( Expr {, Expr } ) { ( Expr {, Expr } ) } Function := function ( [ Ident {, Ident } ] )

[ local Ident {, Ident } ; ]

Statements

   end

Char := ' any character ' String := " { any character } " Int := 0|1|...|9 { 0|1|...|9 } Atom := Int

| Var | ( Expr ) | Permutation | Char | String | Function | List | Record

Factor := {+|-} Atom [ ^{+|-} Atom ] Term := Factor { *|/|mod Factor } Arith := Term { +|- Term } Rel := { not } Arith { =|<>|<|>|<=|>=|in Arith } And := Rel { and Rel } Log := And { or And } Expr := Log

| Var [ -> Log ]

Statement := Expr | Var := Expr | if Expr then Statements

{ elif Expr then Statements } [ else Statements ] fi

| for Var in Expr do Statements od | while Expr do Statements od | repeat Statements until Expr | return [ Expr ]

| quit

Statements := { Statement ; }

| ;

182 CHAPTER 2. THE PROGRAMMING LANGUAGE

Environment

This chapter describes the interactive environment in which you use GAP.

The first sections describe the main read eval print loop and the break loop (see 3.1, 3.2, and 3.3).

The next section describes the commands you can use to edit the current input line (see 3.4).

The next sections describe the GAP help system (see 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11).

The next sections describe the input and output functions (see 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, and 3.19).

The next sections describe the functions that allow you to collect statistics about a computation (see 3.20, 3.21).

The last sections describe the functions that allow you to execute other programs as subprocesses from within GAP (see 3.22 and 3.23).

3.1 Main Loop

The normal interaction with GAP happens in the so–called read eval print loop. This means that you type an input, GAP first reads it, evaluates it, and prints the result. The exact sequence is as follows.

To show you that it is ready to accept your input, GAP displays the prompt gap> . When you see this, you know that GAP is waiting for your input.

Note that every statement must be terminated by a semicolon. You must also enter return before GAP starts to read and evaluate your input. Because GAP does not do anything until you enter return, you can edit your input to fix typos and only when everything is correct enter return and have GAP take a look at it (see 3.4). It is also possible to enter several statements as input on a single line. Of course each statement must be terminated by a semicolon.

It is absolutely acceptable to enter a single statement on several lines. When you have entered the beginning of a statement, but the statement is not yet complete, and you enter return, GAP will display the partial prompt > . When you see this, you know that GAP is waiting for the rest of the statement. This happens also when you forget the semicolon ; that terminates every GAP statement.

When you enter return, GAP first checks your input to see if it is syntactically correct (see chapter 2 for the definition of syntactically correct). If it is not, GAP prints an error message of the following form

    gap> 1 * ;
    Syntax error: expression expected
    1 * ;
        ^

The first line tells you what is wrong about the input, in this case the * operator takes two expressions as operands, so obviously the right one is missing. If the input came from a file (see 3.12), this line will also contain the filename and the line number. The second line is a copy of the input. And the third line contains a caret pointing to the place in the previous line where GAP realized that something is wrong. This need not be the exact place where the error is, but it is usually quite close.

Sometimes, you will also see a partial prompt after you have entered an input that is syntactically incorrect. This is because GAP is so confused by your input, that it thinks that there is still something to follow. In this case you should enter ;return repeatedly, ignoring further error messages, until you see the full prompt again. When you see the full prompt, you know that GAP forgave you and is now ready to accept your next – hopefully correct – input.

If your input is syntactically correct, GAP evaluates or executes it, i.e., performs the required computations (see chapter 2 for the definition of the evaluation).

If you do not see a prompt, you know that GAP is still working on your last input. Of course, you can type ahead, i.e., already start entering new input, but it will not be accepted by GAP until GAP has completed the ongoing computation.

When GAP is ready it will usually print the result of the computation, i.e., the value computed. Note that not all statements produce a value, for example, if you enter a for loop, nothing will be printed, because the for loop does not produce a value that could be printed.

Also sometimes you do not want to see the result. For example if you have computed a value and now want to assign the result to a variable, you probably do not want to see the value again. You can terminate statements by two semicolons to suppress the printing of the result.

If you have entered several statements on a single line GAP will first read, evaluate, and print the first one, then read evaluate, and print the second one, and so on. This means that the second statement will not even be checked for syntactical correctness until GAP has completed the first computation.

After the result has been printed GAP will display another prompt, and wait for your next input. And the whole process starts all over again. Note that a new prompt will only be printed after GAP has read, evaluated, and printed the last statement if you have entered several statements on a single line.

In each statement that you enter the result of the previous statement that produced a value is available in the variable last. The next to previous result is available in last2 and the result produced before that is available in last3.

    gap> 1; 2; 3;
    1
    2
    3
    gap> last3 + last2 * last;
    7

Also in each statement the time spent by the last statement, whether it produced a value or not, is available in the variable time. This is an integer that holds the number of milliseconds.

3.2 Break Loops

When an error has occurred or when you interrupt GAP, usually by hitting ctr-C, GAP enters a break loop, that is in most respects like the main read eval print loop (see 3.1). That is, you can enter statements, GAP reads them, evaluates them, and prints the result if any. However those evaluations happen within the context in which the error occurred. So you can look at the arguments and local variables of the functions that were active when the error happened and even change them. The prompt is changed from gap> to brk> to indicate that you are in a break loop.

There are two ways to leave a break loop.

The first is to quit the break loop and continue in the main loop. To do this you enter quit; or hit the eof (end of file) character, which is usually ctr-D. In this case control returns to the main loop, and you can enter new statements.

The other way is to return from a break loop. To do this you enter return; or return expr;. If the break loop was entered because you interrupted GAP, then you can continue by entering return;. If the break loop was entered due to an error, you usually have to return a value to continue the computation. For example, if the break loop was entered because a variable had no assigned value, you must return the value that this variable should have to continue the computation.

3.3 Error

Error( messages...  )

Error signals an error. First the messages messages are printed, this is done exactly as if Print (see 3.14) were called with these arguments. Then a break loop (see 3.2) is entered, unless the standard error output is not connected to a terminal. You can leave this break loop with return; to continue execution with the statement following the call to Error.

3.4 Line Editing

GAP allows you to edit the current input line with a number of editing commands. Those commands are accessible either as control keys or as escape keys. You enter a control key by pressing the ctr key, and, while still holding the ctr key down, hitting another key key. You enter an escape key by hitting esc and then hitting another key key. Below we denote control keys by ctr-key and escape keys by esc-key. The case of key does not matter, i.e., ctr-A and ctr-a are equivalent.

Characters not mentioned below always insert themselves at the current cursor position.

The first few commands allow you to move the cursor on the current line.

ctr-A move the cursor to the beginning of the line. esc-B move the cursor to the beginning of the previous word. ctr-B move the cursor backward one character. ctr-F move the cursor forward one character. esc-F move the cursor to the end of the next word. ctr-E move the cursor to the end of the line.

The next commands delete or kill text. The last killed text can be reinserted, possibly at a different position with the yank command.

ctr-H or del delete the character left of the cursor. ctr-D delete the character under the cursor. ctr-K kill up to the end of the line. esc-D kill forward to the end of the next word. esc-del kill backward to the beginning of the last word. ctr-X kill entire input line, and discard all pending input. ctr-Y insert (yank) a just killed text.

The next commands allow you to change the input.

ctr-T exchange (twiddle) current and previous character. esc-U uppercase next word. esc-L lowercase next word. esc-C capitalize next word.

The tab character, which is in fact the control key ctr-I, looks at the characters before the cursor, interprets them as the beginning of an identifier and tries to complete this identifier. If there is more than one possible completion, it completes to the longest common prefix of all those completions. If the characters to the left of the cursor are already the longest common prefix of all completions hitting tab a second time will display all possible completions.

tab complete the identifier before the cursor.

The next commands allow you to fetch previous lines, e.g., to correct typos, etc. This history is limited to about 8000 characters.

ctr-L insert last input line before current character. ctr-P redisplay the last input line, another ctr-P will redisplay the line before that, etc. If the cursor is not in the first column only the lines starting with the string to the left of the cursor are taken. ctr-N Like ctr-P but goes the other way round through the history. esc-< goes to the beginning of the history. esc-> goes to the end of the history. ctr-O accepts this line and perform a ctr-N.

Finally there are a few miscellaneous commands.

ctr-V enter next character literally, i.e., enter it even if it is one of the control keys. ctr-U execute the next command 4 times. esc-num execute the next command num times. esc-ctr-L repaint input line.

3.5 Help

This section describes together with the following sections the GAP help system. The help system lets you read the manual interactively.

?section

The help command ? displays the section with the name section on the screen. For example ?Help will display this section on the screen. You should not type in the single quotes, they are only used in help sections to delimit text that you should enter into GAP or that GAP prints in response. When the whole section has been displayed the normal GAP prompt gap> is shown and normal GAP interaction resumes.

The section 3.6 tells you what actions you can perform while you are reading a section. You command GAP to display this section by entering ?Reading Sections, without quotes. The section 3.7 describes the format of sections and the conventions used, 3.8 lists the commands you use to flip through sections, 3.9 describes how to read a section again, 3.10 tells you how to avoid typing the long section names, and 3.11 describes the the index command.

3.6 Reading Sections

If the section is longer than 24 lines GAP stops after 24 lines and displays

    -- <space> for more --

If you press space GAP displays the next 24 lines of the section and then stops again. This goes on until the whole section has been displayed, at which point GAP will return immediately to the main GAP loop. Pressing f has the same effect as space.

You can also press b or the key labeled del which will scroll back to the previous 24 lines of the section. If you press b or del when GAP is displaying the top of a section GAP will ring the bell.

You can also press q to quit and return immediately back to the main GAP loop without reading the rest of the section.

Actually the 24 is only a default, if you have a larger screen that can display more lines of text you may want to tell this to GAP with the -y rows option when you start GAP.

3.7 Format of Sections

This section describes the format of sections when they are displayed on the screen and the special conventions used.

As you can see GAP indents sections 4 spaces and prints a header line containing the name of the section on the left and the name of the chapter on the right.

<text>

Text enclosed in angle brackets is used for arguments in the descriptions of functions and for other placeholders. It means that you should not actually enter this text into GAP but replace it by an appropriate text depending on what you want to do. For example when we write that you should enter ?section to see the section with the name section, section servers as a placeholder, indicating that you can enter the name of the section that you want to see at this place. In the printed manual such text is printed in italics.

'text'

Text enclosed in single quotes is used for names of variables and functions and other text that you may actually enter into your computer and see on your screen. The text enclosed in single quotes may contain placeholders enclosed in angle brackets as described above. For example when the help text for IsPrime says that the form of the call is 'IsPrime( <n> )' this means that you should actually enter the IsPrime( and ), without the quotes, but replace the n with the number (or expression) that you want to test. In the printed manual this text is printed in a monospaced (all characters have the same width) typewriter font.

"text"

Text enclosed in double quotes is used for cross references to other parts of the manual. So the text inside the double quotes is the name of another section of the manual. This is used to direct you to other sections that describe a topic or a function used in this section. So for example 3.10 is a cross reference to the next section. In the printed manual the text is replaced by the number of the section.

_and ^

In mathematical formulas the underscore and the caret are used to denote subscription and superscription. Ordinarily they apply only to the very next character following, unless a whole expression enclosed in parentheses follows. So for example x_1^(i+1) denotes the variable x with subscript 1 raised to the i+1 power. In the printed manual mathematical formulas are typeset in italics (actually mathitalics) and subscripts and superscripts are actually lowered and raised.

Longer examples are usually paragraphs of their own that are indented 8 spaces from the left margin, i.e. 4 spaces further than the surrounding text. Everything on the lines with the prompts gap> and >, except the prompts themselves of course, is the input you have to type, everything else is GAP’s response. In the printed manual examples are also indented 4 spaces and are printed in a monospaced typewriter font.

    gap> ?Format of Sections
    Format of Sections ______________________________________Environment
    This section describes the format of sections when they are displayed
    on the screen and the special conventions used.
    ...

3.8 Browsing through the Sections

The help sections are organized like a book into chapters. This should not surprise you, since the same source is used both for the printed manual and the online help. Just as you can flip through the pages of a book there are special commands to browse through the help sections.

?>
?<

The two help commands ?< and ?> correspond to the flipping of pages. ?< takes you to the section preceding the current section and displays it, and ?> takes you to the section following the current section.

?<<
?>>

?<< is like ?<, only more so. It takes you back to the first section of the current chapter, which gives an overview of the sections described in this chapter. If you are already in this section ?<< takes you to the first section of the previous chapter. ?>> takes you to the first section of the next chapter.

?-
?+

GAP remembers the sections that you have read. ?- takes you to the one that you have read before the current one, and displays it again. Further ?- takes you further back in this history. ?+ reverses this process, i.e., it takes you back to the section that you have read after the current one. It is important to note, that ?- and ?+ do not alter the history like the other help commands.

3.9 Redisplaying a Section

?

The help command ? followed by no section name redisplays the last help section again. So if you reach the bottom of a long help section and already forgot what was mentioned at the beginning, or, for example, the examples do not seem to agree with your interpretation of the explanations, use ? to read the whole section again from the beginning.

When ? is used before any section has been read GAP displays the section Welcome to GAP.

3.10 Abbreviating Section Names

Upper and lower case in section are not distinguished, so typing either ?Abbreviating

Section Names or ?abbreviating section names will show this very section.

Each word in section may be abbreviated. So instead of typing ?abbreviating section names you may also type ?abb sec nam, or even ?a s n. You must not omit the spaces separating the words. For each word in the section name you must give at least the first character. As another example you may type ?oper for int instead of ?operations for integers, which is especially handy when you can not remember whether it was operations

or operators.

If an abbreviation matches multiple section names a list of all these section names is displayed.

3.11 Help Index

??topic

?? looks up topic in GAP’s index and prints all the index entries that contain the substring topic. Then you can decide which section is the one you are actually interested in and request this one.

    gap> ??help
        help ______________________________________________________Index
        Help

        Reading Sections (help!scrolling)
        Format of the Sections (help!format)
        Browsing through the Sections (help!browsing)
        Redisplaying a Section (help!redisplaying)
        Abbreviating Section Names (help!abbreviating)
        Help Index
    gap>

The first thing on each line is the name of the section. If the name of the section matches topic nothing more is printed. Otherwise the index entry that matched topic is printed in parentheses following the section name. For each section only the first matching index entry is printed. The order of the sections corresponds to their order in the GAP manual, so that related sections should be adjacent.

3.12 Read

Read( filename )

Read reads the input from the file with the filename filename, which must be a string.

Read first opens the file filename. If the file does not exist, or if GAP can not open it, e.g., because of access restrictions, an error is signalled.

Then the contents of the file are read and evaluated, but the results are not printed. The reading and printing happens exactly as described for the main loop (see 3.1).

If an input in the file contains a syntactical error, a message is printed, and the rest of this statement is ignored, but the rest of the file is read.

If a statement in the file causes an error a break loop is entered (see 3.2). The input for this break loop is not taken from the file, but from the input connected to the stderr output of GAP. If stderr is not connected to a terminal, no break loop is entered. If this break loop is left with quit (or ctr-D) the file is closed and GAP does not continue to read from it.

Note that a statement may not begin in one file and end in another, i.e., eof (end of file) is not treated as whitespace, but as a special symbol that must not appear inside any statement.

Note that one file may very well contain a read statement causing another file to be read, before input is again taken from the first file. There is an operating system dependent maximum on the number of files that may be open at once, usually it is 15.

The special file name "*stdin*" denotes the standard input, i.e., the stream through which the user enters commands to GAP. The exact behaviour of Read( "*stdin*") is operating system dependent, but usually the following happens. If GAP was started with no input redirection, statements are read from the terminal stream until the user enters the end of file character, which is usually ctr-D. Note that terminal streams are special, in that they may yield ordinary input after an end of file. Thus when control returns to the main read eval print loop the user can continue with GAP. If GAP was started with an input redirection, statements are read from the current position in the input file up to the end of the file. When control returns to the main read eval print loop the input stream will still return end of file, and GAP will terminate. The special file name "*errin*" denotes the stream connected with the stderr output. This stream is usually connected to the terminal, even if the standard input was redirected, unless the standard error stream was also redirected, in which case opening of "*errin*" fails, and Read will signal an error.

Read is implemented in terms of the function READ, which behaves exactly like Read, except that READ does not signal an error when it can not open the file. Instead it returns true or false to indicate whether opening the file was successful or not.

3.13 ReadLib

ReadLib( name )

ReadLib reads input from the library file with the name name. ReadLib prefixes name with the value of the variable LIBNAME and appends the string ".g" and calls Read (see 3.12) with this file name.

3.14 Print

Print( obj1 , obj2 ...  )

Print prints the objects obj1 , obj2 ... etc. to the standard output. The output looks exactly like the printed representation of the objects printed by the main loop. The exception are strings, which are printed without the enclosing quotes and a few other transformations (see 28). Note that no space or newline is printed between the objects. PrintTo can be used to print to a file (see 3.15).

    gap> for i  in [1..5]  do
    >     Print( i, " ", i^2, " ", i^3, "\n" );
    > od;
    1 1 1
    2 4 8
    3 9 27
    4 16 64
    5 25 125

3.15 PrintTo

PrintTo( filename, obj1 , obj2 ... )

PrintTo works like Print, except that the output is printed to the file with the name filename instead of the standard output. This file must of course be writable by GAP, otherwise an error is signalled. Note that PrintTo will overwrite the previous contents of this file if it already existed. AppendTo can be used to append to a file (see 3.16).

The special file name "*stdout*" can be used to print to the standard output. This is equivalent to a plain Print, except that a plain Print that is executed while evaluating an argument to a PrintTo call will also print to the output file opened by the last PrintTo call, while PrintTo( "*stdout*", obj1 , obj2 ... ) always prints to the standard output. The special file name "*errout*" can be used to print to the standard error output file, which is usually connected with the terminal, even if the standard output was redirected.

There is an operating system dependent maximum to the number of output files that may be open at once, usually this is 14.

3.16 AppendTo

AppendTo( filename, obj1 , obj2 ...  )

AppendTo works like PrintTo (see 3.15), except that the output does not overwrite the previous contents of the file, but is appended to the file.

3.17 LogTo

LogTo( filename )

LogTo causes the subsequent interaction to be logged to the file with the name filename, i.e., everything you see on your terminal will also appear in this file. This file must of course be writable by GAP, otherwise an error is signalled. Note that LogTo will overwrite the previous contents of this file if it already existed.

LogTo()

In this form LogTo stops logging again.

3.18 LogInputTo

LogInputTo( filename )

LogInputTo causes the subsequent input lines to be logged to the file with the name filename, i.e., every line you type will also appear in this file. This file must of course be writable by GAP, otherwise an error is signalled. Note that LogInputTo will overwrite the previous contents of this file if it already existed.

LogInputTo()

In this form LogInputTo stops logging again.

3.19 SizeScreen

SizeScreen()

In this form ScreeSize returns the size of the screen as a list with two entries. The first is the length of each line, the second is the number of lines.

SizeScreen( [ x, y ] )

In this form SizeScreen sets the size of the screen. x is the length of each line, y is the number of lines. Either value may be missing, to leave this value unaffected. Note that those parameters can also be set with the command line options -x x and -y y (see 47).

3.20 Runtime

Runtime()

Runtime returns the time spent by GAP in milliseconds as an integer. This is usually the cpu time, i.e., not the wall clock time. Also time spent by subprocesses of GAP (see 3.22) is not counted.

3.21 Profile

Profile( true )

In this form Profile turns the profiling on. Subsequent computations will record the time spent by each function and the number of times each function was called. Old profiling information is cleared.

Profile( false )

In this form Profile turns the profiling off again. Recorded information is still kept, so you can display it even after turning the profiling off.

Profile()

In this form Profile displays the collected information in the following format.

gap> Factors( 10^21+1 );; # make sure that the library is loaded

    gap> Profile( true );
    gap> Factors( 10^42+1 );
    [ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]
    gap> Profile( false );
    gap> Profile();
     count    time percent time/call child function
         4    1811      76     452    2324  FactorsRho
        18     171       7       9     237  PowerModInt
       127      94       3       0      94  GcdInt
        41      83       3       2     415  IsPrimeInt
        91      59       2       0      59  TraceModQF
       511      47       1       0      39  QuoInt
        22      23       0       1      23  Jacobi
       116      20       0       0      31  log
         3      20       0       6      70  SmallestRootInt
         1      19       0      19    2370  FactorsInt
        26      15       0       0      39  LogInt
         4       4       0       1       4  Concatenation
         5       4       0       0      20  RootInt
         7       0       0       0       0  Add
        26       0       0       0       0  Length
        13       0       0       0       0  NextPrimeInt
         4       0       0       0       0  AddSet
         4       0       0       0       0  IsList
         4       0       0       0       0  Sort
         8       0       0       0       0  Append
              2369     100                  TOTAL

The last column contains the name of the function. The first column contains the number of times each function was called. The second column contains the time spent in this function. The third column contains the percentage of the total time spent in this function. The fourth column contains the time per call, i.e., the quotient of the second by the first number. The fifth column contains the time spent in this function and all other functions called, directly or indirectly, by this function.

3.22 Exec

Exec( command )

Exec executes the command given by the string command in the operating system. How this happens is operating system dependent. Under UNIX, for example, a new shell is started and command is passed as a command to this shell.

    gap> Exec( "date" );
    Fri Dec 13 17:00:29 MET 1991

Edit (see 3.23) should be used to call an editor from within GAP.

3.23 Edit

Edit( filename )

Edit starts an editor with the file whose filename is given by the string filename, and reads the file back into GAP when you exit the editor again. You should set the GAP variable EDITOR to the name of the editor that you usually use, e.g., /usr/ucb/vi. This can for example be done in your .gaprc file (see the sections on operating system dependent features in chapter 47).

Domains

Domain is GAP’s name for structured sets. The ring of Gaussian integers Z[I] is an example of a domain, the group D_{12} of symmetries of a regular hexahedron is another. The GAP library predefines some domains. For example the ring of Gaussian integers is predefined as GaussianIntegers (see 14) and the field of rationals is predefined as Rationals (see 12). Most domains are constructed by functions, which are called domain constructors. For example the group D_{12} is constructed by the construction Group( (1,2,3,4,5,6), (2,6)(3,5) ) (see 7.9) and the finite field with 16 elements is constructed

by GaloisField( 16 ) (see 17.10).

The first place where you need domains in GAP is the obvious one. Sometimes you simply want to talk about a domain. For example if you want to compute the size of the group D_{12}, you had better be able to represent this group in a way that the Size function can understand. The second place where you need domains in GAP is when you want to be able to specify that an operation or computation takes place in a certain domain. For example suppose you want to factor 10 in the ring of Gaussian integers. Saying Factors( 10 ) will not do, because this will return the factorization in the ring of integers [ 2, 5 ]. To allow operations and computations to happen in a specific domain, Factors, and many other functions as well, accept this domain as optional first argument. Thus Factors( GaussianIntegers, 10 )

yields the desired result [ 1+E(4), 1-E(4), 2+E(4), 2-E(4) ].

Each domain in GAP belongs to one or more categories, which are simply sets of domains. The categories in which a domain lies determine the functions that are applicable to this domain and its elements. Examples of domains are rings (the functions applicable to a domain that is a ring are described in 5), fields (see 6), groups (see 7), vector spaces (see 9), and of course the category domains that contains all domains (the functions applicable to any domain are described in this chapter). This chapter describes how domains are represented in GAP (see 4.1), how functions that can be applied to different types of domains know how to solve a problem for each of those types (see 4.2, 4.3, and 4.4), how domains are compared (see 4.7), and the set theoretic functions that can be applied to any domain (see 4.6, 4.8, 4.9, 4.10, 4.11, 4.12, 4.13, 4.14, 4.16). The functions described in this chapter are implemented in the file LIBNAME/"domain.g".

4.1 Domain Records

Domains are represented by records (see 39), which are called domain records in the following. Which components need to be present, which may, and what those components hold, differs from category to category, and, to a smaller extent, from domain to domain. It is generally possible though to distinguish four types of components.

Each domain record has the component isDomain, which has the value true. Furthermore, most domains also have a component that specifies which category this domain belongs to. For example, each group has the component isGroup, holding the value true. Those components are called the category components of the domain record. A domain that only has the component isDomain is a member only of the category Domains and only the functions described in this chapter are applicable to such a domain.

Every domain record also contains enough information to identify uniquely the domain in the so called identification components. For example, for a group the domain record, called group record in this case, has a component called generators containing a system of generators (and also a component identity holding the identity element of the group, needed if the generator list is empty, as is the case for the trivial group).

Next the domain record holds all the knowledge GAP has about the domain, for example the size of the domain, in the so called knowledge components. Of course, the knowledge about a certain domain will usually increase as time goes by. For example, a group record may initially hold only the knowledge that the group is finite, but may end holding all kinds of knowledge, for example the derived series, the Sylow subgroups, etc.

Finally each domain record has a component, which is called its operations record (because it is the component with the name operations and it holds a record), that tells functions like Size how to compute this information for this domain. The exact mechanism is described later (see 4.2).

4.2 Dispatchers

In the previous section it was mentioned that domains are represented by domain records, and that each domain record has an operations record. This operations record is used by functions like Size to find out how to compute this information for the domain. Let us discuss this mechanism using the example of Size. Suppose you call Size with a domain D.

First Size tests whether D has a component called size, i.e., if D.size is bound. If it is, Size assumes that it holds the size of the domain and returns this value.

Let us suppose that this component has no assigned value. Then Size looks at the component D.operations, which must be a record. Size takes component D.operations.Size of this record, which must be a function. Size calls this function passing D as argument. If a domain record has no Size function in its operations record, an error is signalled.

Finally Size stores the value returned by D.operations.Size( D ) in the component D.size, where it is available for the next call of Size( D ).

Because functions like Size do little except dispatch to the function in the operations record they are called dispatcher functions.

Which function is called through this mechanism obviously depends on the domain and its operations record. In principle each domain could have its own Size function. In practice however this is not the case. For example all permutation groups share the operations record

PermGroupOps so they all use the same Size function PermGroupOps.Size.

Note that in fact domains of the same type not only share the functions, in fact they share the operations record. So for example all permutation groups have the same operations record. This means that changing such a function for a domain D in the following way D.operations.function := new-function; will also change this function for all domains of the same type, even those that do not yet exist at the moment of the assignment and will only be constructed later. This is usually not desirable, since supposedly new-function uses some special properties of the domain D to work efficiently. We suggest therefore, that you use the following assignments instead:

D.operations := Copy( D.operations );

D.operations.function := new-function;.

Some domains do not provide a special Size function, either because no efficient method is known or because the author that implemented the domain simply was too lazy to write one. In those cases the domain inherits the default function, which is DomainOps.Size. Such inheritance is uncommon for the Size function, but rather common for the Union function.

4.3 More about Dispatchers

Usually you need not care about the mechanism described in the previous section. You just call the dispatcher functions like Size. They will call the function in the operations record, which is hopefully implementing an algorithm that is well suited for their domain, by using the structure of this domain.

There are three reasons why you might want to avoid calling the dispatcher function and call the dispatched to function directly.

The first reason is efficiency. The dispatcher functions don’t do very much. They only check the types of their arguments, check if the requested information is already present, and dispatch to the appropriate function in the operations record. But sometimes, for example in the innermost loop of your algorithm, even this little is too much. In those cases you can avoid the overhead introduced by the dispatcher function by calling the function in the operations record directly. For example, you would use G.operations.Size(G) instead

of Size(G).

The second reason is flexibility. Sometimes you do not want to call the function in the operations record, but another function that performs the same task, using a different algorithm. In that case you will call this different function. For example, if G is a permutation group, and the orbit of p under G is very short, GroupOps.Orbit(G,p), which is the default function to compute an orbit, may be slightly more efficient than Orbit(G,p), which calls

G.operations.Orbit(G,p), which is the same as PermGroupOps.Orbit(G,p).

The third has to do with the fact that the dispatcher functions check for knowledge components like D.size or D.elements and also store their result in such components. For example, suppose you know that the result of a computation takes up quite some space, as is the case with Elements(D), and that you will never need the value again. In this case you would not want the dispatcher function to enter the value in the domain record, and therefore would call D.operations.Elements(D) directly. On the other hand you may not want to use the value in the domain record, because you mistrust it. In this case you should call the function in the operations record directly, e.g., you would use G.operations.Size(G) instead of Size(G) (and then compare the result with G.size).

4.4 An Example of a Computation in a Domain

This section contains an extended example to show you how a computation in a domain may use default and special functions to achieve its goal. Suppose you defined G, x, and y as follows.

    gap> G := SymmetricGroup( 8 );;
    gap> x := [ (2,7,4)(3,5), (1,2,6)(4,8) ];;
    gap> y := [ (2,5,7)(4,6), (1,5)(3,8,7) ];;

Now you ask for an element of G that conjugates x to y, i.e., a permutation on 8 points that

takes (2,7,4)(3,5) to (2,5,7)(4,6) and (1,2,6)(4,8) to (1,5)(3,8,7). This is done

as follows (see 8.23 and 8.1).

    gap> RepresentativeOperation( G, x, y, OnTuples );
    (1,8)(2,7)(3,4,5,6)

Let us look at what happens step by step. First RepresentativeOperation is called. After checking the arguments it calls the function G.operations.RepresentativeOperation,

which is the function SymmetricGroupOps.RepresentativeOperation, passing the argu-

ments G, x, y, and OnTuples.

SymmetricGroupOps.RepresentativeOperation handles a lot of cases specially, but the

operation on tuples of permutations is not among them. Therefore it delegates this problem

to the function that it overlays, which is PermGroupOps.RepresentativeOperation.

PermGroupOps.RepresentativeOperation also does not handle this special case, and delegates the problem to the function that it overlays, which is the default function called

GroupOps.RepresentativeOperation.

GroupOps.RepresentativeOperation views this problem as a general tuples problem, i.e., it does not care whether the points in the tuples are integers or permutations, and decides to solve it one step at a time. So first it looks for an element taking (2,7,4)(3,5) to

(2,5,7)(4,6) by calling RepresentativeOperation( G, (2,7,4)(3,5), (2,5,7)(4,6)
).
RepresentativeOperation calls G.operations.RepresentativeOperation next, which is
the function SymmetricGroupOps.RepresentativeOperation, passing the arguments G,
(2,7,4)(3,5), and (2,5,7)(4,6).
SymmetricGroupOps.RepresentativeOperation can handle this case. It knows that G

contains every permutation on 8 points, so it contains (3,4,7,5,6), which obviously does what we want, namely it takes x[1] to y[1]. We will call this element t.

Now GroupOps.RepresentativeOperation (see above) looks for an s in the stabilizer of

x[1] taking x[2] to y[2]^(t^-1), since then for r=s*t we have x[1]^r = (x[1]^s)^t
= x[1]^t = y[1] and also x[2]^r = (x[2]^s)^t = (y[2]^(t^-1))^t = y[2]. So the

next step is to compute the stabilizer of x[1] in G. To do this it calls Stabilizer( G,

(2,7,4)(3,5) ).

Stabilizer calls G.operations.Stabilizer, which is SymmetricGroupOps.Stabilizer,
passing the arguments G and (2,7,4)(3,5). SymmetricGroupOps.Stabilizer detects that

the second argument is a permutation, i.e., an element of the group, and calls Centralizer(

G, (2,7,4)(3,5) ). Centralizer calls the function G.operations.Centralizer, which
is SymmetricGroupOps.Centralizer, again passing the arguments G, (2,7,4)(3,5).

SymmetricGroupOps.Centralizer again knows how centralizers in symmetric groups look, and after looking at the permutation (2,7,4)(3,5) sharply for a short while returns the

centralizer as Subgroup( G, [ (1,6), (1,6,8), (2,7,4), (3,5) ] ), which we will call

S. Note that S is of course not a symmetric group, therefore SymmetricGroupOps.Subgroup gives it PermGroupOps as operations record and not SymmetricGroupOps.

As explained above GroupOps.RepresentativeOperation needs an element of S taking

x[2] ((1,2,6)(4,8)) to y[2]^(t^-1) ((1,7)(4,6,8)). So RepresentativeOperation(
S, (1,2,6)(4,8), (1,7)(4,6,8) ) is called. RepresentativeOperation in turn calls

the function S.operations.RepresentativeOperation, which is, since S is a permutation group, the function PermGroupOps.RepresentativeOperation, passing the arguments S,

(1,2,6)(4,8), and (1,7)(4,6,8).

PermGroupOps.RepresentativeOperation detects that the points are permutations and and performs a backtrack search through S. It finds and returns (1,8)(2,4,7)(3,5), which we call s.

Then GroupOps.RepresentativeOperation returns r = s*t = (1,8)(2,7)(3,6)(4,5),

and we are done.

In this example you have seen how functions use the structure of their domain to solve

a problem most efficiently, for example SymmetricGroupOps.RepresentativeOperation

but also the backtrack search in PermGroupOps.RepresentativeOperation, how they use

other functions, for example SymmetricGroupOps.Stabilizer called Centralizer, and

how they delegate cases which they can not handle more efficiently back to the func-

tion they overlaid, for example SymmetricGroupOps.RepresentativeOperation delegated

to PermGroupOps.RepresentativeOperation, which in turn delegated to to the function

GroupOps.RepresentativeOperation.

4.5 Domain

Domain( list )

Domain returns a domain that contains all the elements in list and that knows how to make the ring, field, group, or vector space that contains those elements.

Note that the domain returned by Domain need in general not be a ring, field, group, or vector space itself. For example if passed a list of elements of finite fields Domain will return the domain FiniteFieldElements. This domain contains all finite field elements, no matter of which characteristic. This domain has a function FiniteFieldElementsOps.Field that knows how to make a finite field that contains the elements in list. This function knows that all elements must have the same characteristic for them to lie in a common field.

    gap> D := Domain( [ Z(4), Z(8) ] );
    FiniteFieldElements
    gap> IsField( D );
    false

    gap> D.operations.Field( [ Z(4), Z(8) ] );
    GF(2^6)

Domain is the only function in the whole GAP library that knows about the various types of elements. For example, when Norm is confronted by a field element z, it does not know what to do with it. So it calls F := DefaultField( [ z ] ) to get a field in which z lies, because this field (more precisely F.operations.Norm) will know better. However, DefaultField also does not know what to do with z. So it calls D := Domain( [ z ] ) to get a domain in which z lies, because it (more precisely D.operations.DefaultField) will know how to make a default field in which z lies.

4.6 Elements

Elements( D )

Elements returns the set of elements of the domain D. The set is returned as a new proper set, i.e., as a new sorted list without holes and duplicates (see 26). D may also be a list, in which case the set of elements of this list is returned. An error is signalled if D is an infinite domain.

    gap> Elements( GaussianIntegers );
    Error, the ring <R> must be finite to compute its elements
    gap> D12 := Group( (2,6)(3,5), (1,2)(3,6)(4,5) );;
    gap> Elements( D12 );
    [ (), (2,6)(3,5), (1,2)(3,6)(4,5), (1,2,3,4,5,6), (1,3)(4,6),
      (1,3,5)(2,4,6), (1,4)(2,3)(5,6), (1,4)(2,5)(3,6), (1,5)(2,4),
      (1,5,3)(2,6,4), (1,6,5,4,3,2), (1,6)(2,5)(3,4) ]

Elements remembers the set of elements in the component D.elements and will return a shallow copy (see 39.12) next time it is called to compute the elements of D. If you want to avoid this, for example for a large domain, for which you know that you will not need the list of elements in the future, either unbind (see 39.10) D.elements or call

D.operation.Elements(D) directly.

Since there is no general method to compute the elements of a domain the default function DomainOps.Elements just signals an error. This default function is overlaid for each special finite domain. In fact, implementors of domains, must implement this function for new domains, since it is, together with IsFinite (see 4.9) the most basic function for domains, used by most of the default functions in the domain package.

In general functions that return a set of elements are free, in fact encouraged, to return a domain instead of the proper set of elements. For one thing this allows to keep the structure, for another the representation by a domain record is usually more space efficient. Elements must not do this, its only purpose is to create the proper set of elements.

4.7 Comparisons of Domains

D = E

D <> E

= evaluates to true if the two domains D and E are equal, to false otherwise. <> evaluates to true if the two domains D and E are different and to false if they are equal.

Two domains are considered equal if and only if the sets of their elements as computed by Elements (see 4.6) are equal. Thus, in general = behaves as if each domain operand were replaced by its set of elements. Except that = will also sometimes, but not always, work for infinite domains, for which it is of course difficult to compute the set of elements. Note that this implies that domains belonging to different categories may well be equal. As a special case of this, either operand may also be a proper set, i.e., a sorted list without holes or duplicates (see 26.2), and the result will be true if and only if the set of elements of the domain is, as a set, equal to the set. It is also possible to compare a domain with something else that is not a domain or a set, but the result will of course always be false in this case.

    gap> GaussianIntegers = D12;

false # GAP knows that those domains cannot be equal because # GaussianIntegers is infinite and D12 is finite

    gap> GaussianIntegers = Integers;

false # GAP knows how to compare those two rings

    gap> GaussianIntegers = Rationals;
    Error, sorry, cannot compare the infinite domains <D> and <E>
    gap> D12 = Group( (2,6)(3,5), (1,2)(3,6)(4,5) );
    true
    gap> D12 = [(),(2,6)(3,5),(1,2)(3,6)(4,5),(1,2,3,4,5,6),(1,3)(4,6),
    >           (1,3,5)(2,4,6),(1,4)(2,3)(5,6),(1,4)(2,5)(3,6),
    >           (1,5)(2,4),(1,5,3)(2,6,4),(1,6,5,4,3,2),(1,6)(2,5)(3,4)];
    true
    gap> D12 = [(1,6,5,4,3,2),(1,6)(2,5)(3,4),(1,5,3)(2,6,4),(1,5)(2,4),
    >           (1,4)(2,5)(3,6),(1,4)(2,3)(5,6),(1,3,5)(2,4,6),(1,3)(4,6),
    >           (1,2,3,4,5,6),(1,2)(3,6)(4,5),(2,6)(3,5),()];

false # since the left operand behaves as a set # while the right operand is not a set

The default function DomainOps.'=' checks whether both domains are infinite. If they are, an error is signalled. Otherwise, if one domain is infinite, false is returned. Otherwise the sizes (see 4.10) of the domains are compared. If they are different, false is returned. Finally the sets of elements of both domains are computed (see 4.6) and compared. This default function is overlaid by more special functions for other domains.

D < E

D <= E

D > E

D >= E

<, <=, >, and >= evaluate to true if the domain D is less than, less than or equal to, greater than, and greater than or equal to the domain E and to false otherwise.

A domain D is considered less than a domain E if and only if the set of elements of D is less than the set of elements of the domain E. Generally you may just imagine that each domain operand is replaced by the set of its elements, and that the comparison is performed on those sets (see 25.11). This implies that, if you compare a domain with an object that is not a list or a domain, this other object will be less than the domain, except if it is a record, in which case it is larger than the domain (see 2.9).

Note that < does not test whether the left domain is a subset of the right operand, even though it resembles the mathematical subset notation.

gap> GaussianIntegers < Rationals;
Error, sorry, cannot compare <E> with the infinite domain <D>
gap> Group( (1,2), (1,2,3,4,5,6) ) < D12;

true # since (5,6), the second element of the left operand,

# is less than (2,6)(3,5), the second element of D12.

gap> D12 < [(1,6,5,4,3,2),(1,6)(2,5)(3,4),(1,5,3)(2,6,4),(1,5)(2,4),
>           (1,4)(2,5)(3,6),(1,4)(2,3)(5,6),(1,3,5)(2,4,6),(1,3)(4,6),
>           (1,2,3,4,5,6),(1,2)(3,6)(4,5),(2,6)(3,5),()];

true # since (), the first element of D12, is less than

# (1,6,5,4,3,2), the first element of the right operand.

gap> 17 < D12;

true # objects that are not lists or records are smaller # than domains, which behave as if they were a set

The default function DomainOps.'<' checks whether either domain is infinite. If one is, an error is signalled. Otherwise the sets of elements of both domains are computed (see 4.6) and compared. This default function is only very seldom overlaid by more special functions for other domains. Thus the operators <, <=, >, and >= are quite expensive and their use should be avoided if possible.

4.8 Membership Test for Domains

elm in D

in returns true if the element elm, which may be an object of any type, lies in the domain D, and false otherwise.

gap> 13 in GaussianIntegers;
true
gap> GaussianIntegers in GaussianIntegers;
false
gap> (1,2) in D12;
false
gap> (1,2)(3,6)(4,5) in D12;
true

The default function for domain membership tests is DomainOps.'in', which computes the set of elements of the domain with the function Elements (see 4.6) and tests whether elm lies in this set. Special domains usually overlay this function with more efficient membership tests.

4.9 IsFinite

IsFinite( D )

IsFinite returns true if the domain D is finite and false otherwise. D may also be a proper set (see 26.2), in which case the result is of course always true.

gap> IsFinite( GaussianIntegers );
false
gap> IsFinite( D12 );
true

The default function DomainOps.IsFinite just signals an error, since there is no general method to determine whether a domain is finite or not. This default function is overlaid for each special domain. In fact, implementors of domains must implement this function for new domains, since it is, together with Elements (see 4.6), the most basic function for domains, used by most of the default functions in the domain package.

4.10 Size

Size( D )

Size returns the size of the domain D. If D is infinite, Size returns the string "infinity". D may also be a proper set (see 26.2), in which case the result is the length of this list. Size will, however, signal an error if D is a list that is not a proper set, i.e., that is not sorted, or has holes, or contains duplicates.

    gap> Size( GaussianIntegers );
    "infinity"
    gap> Size( D12 );
    12

The default function to compute the size of a domain is DomainOps.Size, which computes the set of elements of the domain with the function Elements (see 4.6) and returns the length of this set. This default function is overlaid in practically every domain.

4.11 IsSubset

IsSubset( D, E )

IsSubset returns true if the domain E is a subset of the domain D and false otherwise. E is considered a subset of D if and only if the set of elements of E is as a set a subset of the set of elements of D (see 4.6 and 26.9). That is IsSubset behaves as if implemented as IsSubsetSet( Elements(D), Elements(E) ), except that it will also sometimes, but not always, work for infinite domains, and that it will usually work much faster than the above definition. Either argument may also be a proper set.

    gap> IsSubset( GaussianIntegers, [1,E(4)] );
    true
    gap> IsSubset( GaussianIntegers, Rationals );
    Error, sorry, cannot compare the infinite domains <D> and <E>
    gap> IsSubset( Group( (1,2), (1,2,3,4,5,6) ), D12 );
    true
    gap> IsSubset( D12, [ (), (1,2)(3,4)(5,6) ] );
    false

The default function DomainOps.IsSubset checks whether both domains are infinite. If they are it signals an error. Otherwise if the E is infinite it returns false. Otherwise if D is infinite it tests if each element of E is in D (see 4.8). Otherwise it tests whether the proper set of elements of E is a subset of the proper set of elements of D (see 4.6 and 26.9).

4.12 Intersection

Intersection( D1 , D2 ...  )
Intersection( list )

In the first form Intersection returns the intersection of the domains D1 , D2 , etc. In the second form list must be a list of domains and Intersection returns the intersection of those domains. Each argument D or element of list respectively may also be an arbitrary list, in which case Intersection silently applies Set (see 26.2) to it first. The result of Intersection is the set of elements that lie in every of the domains D1 , D2 , etc. Functions called by the dispatcher function Intersection however, are encouraged to keep as much structure as possible. So if D1 and D2 are elements of a common category and if this category is closed under taking intersections, then the result should be a domain lying in this category too. So for example the intersection of permutation groups will again be a permutation group.

    gap> Intersection( CyclotomicField(9), CyclotomicField(12) );
    CF(3)  # CF is a shorthand for CyclotomicField

# this is one of the rare cases where the intersection # of two infinite domains works

    gap> Intersection( GaussianIntegers, Rationals );
    Error, sorry, cannot intersect infinite domains <D> and <E>
    gap> Intersection( D12, Group( (1,2), (1,2,3,4,5) ) );
    Group( (1,5)(2,4) )
    gap> Intersection( D12, [ (1,3)(4,6), (1,2)(3,4) ] );

[ (1,3)(4,6) ] # note that the second argument is not a set

    gap> Intersection( D12, [ (), (1,2)(3,4), (1,3)(4,6), (1,4)(5,6) ] );

[ (), (1,3)(4,6) ] # although the result is mathematically a # group it is returned as a proper set # because the second argument was not a group

    gap> Intersection( [2,4,6,8,10], [3,6,9,12,15], [5,10,15,20,25] );

[ ] # two or more domains or sets as arguments are legal

    gap> Intersection( [ [1,2,4], [2,3,4], [1,3,4] ] );

[ 4 ] # or a list of domains or sets

    gap> Intersection( [ ] );
    Error, List Element: <list>[1] must have a value

The dispatcher function (see 4.2) Intersection is slightly different from other dispatcher functions. It does not simply call the function in the operations record passings its arguments. Instead it loops over its arguments (or the list of domains or sets) and calls the function in the operations record repeatedly, and passes each time only two domains. This obviously makes writing the function for the operations record simpler. The default function DomainOps.Intersection checks whether both domains are infinite. If they are it signals an error. Otherwise, if one of the domains is infinite it loops over the elements of the other domain, and tests for each element whether it lies in the infinite domain. If both domains are finite it computes the proper sets of elements of both and intersects them (see 4.6 and 26.9). This default method is overlaid by more special functions for most other domains. Those functions usually are faster and keep the structure of the domains if possible.

4.13 Union

Union( D1 , D2 ...  )
Union( list )

In the first form Union returns the union of the domains D1 , D2 , etc. In the second form list must be a list of domains and Union returns the union of those domains. Each argument D or element of list respectively may also be an arbitrary list, in which case Union silently applies Set (see 26.2) to it first.

The result of Union is the set of elements that lie in any the domains D1 , D2 , etc. Functions called by the dispatcher function Union however, are encouraged to keep as much structure as possible. However, currently GAP does not support any category that is closed under taking unions except the category of all domains. So the only case that structure will be kept is when one argument D or element of list respectively is a superset of all the other arguments or elements of list.

    gap> Union( GaussianIntegers, Rationals );
    Error, sorry, cannot unite <E> with the infinite domain <D>
    gap> Union( D12, Group( (1,2), (1,2,3) ) );
    [ (), (2,3), (2,6)(3,5), (1,2), (1,2)(3,6)(4,5), (1,2,3),
      (1,2,3,4,5,6), (1,3,2), (1,3), (1,3)(4,6), (1,3,5)(2,4,6),
      (1,4)(2,3)(5,6), (1,4)(2,5)(3,6), (1,5)(2,4), (1,5,3)(2,6,4),
      (1,6,5,4,3,2), (1,6)(2,5)(3,4) ]
    gap> Union( [2,4,6,8,10], [3,6,9,12,15], [5,10,15,20,25] );
    [ 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 20, 25 ]

# two or more domains or sets as arguments are legal

    gap> Union( [ [1,2,4], [2,3,4], [1,3,4] ] );

[ 1, 2, 3, 4 ] # or a list of domains or sets

    gap> Union( [ ] );
    [  ]

The dispatcher function (see 4.2) Union is slightly different from other dispatcher functions. It does not simply call the function in the operations record passings its arguments. Instead it loops over its arguments (or the list of domains or sets) and calls the function in the operations record repeatedly, and passes each time only two domains. This obviously makes writing the function for the operations record simpler.

The default function DomainOps.Union checks whether either domain is infinite. If one is it signals an error. If both domains are finite it computes the proper sets of elements of both and unites them (see 4.6 and 26.9). This default method is overlaid by more special functions for some other domains. Those functions usually are faster.

4.14 Difference

Difference( D, E )

Difference returns the set difference of the domains D and E. Either argument may also be an arbitrary list, in which case Difference silently applies Set (see 26.2) to it first.

The result of Difference is the set of elements that lie in D but not in E. Note that E need not be a subset of D. The elements of E, however, that are not element of D play no role for the result.

    gap> Difference( D12, [(),(1,2,3,4,5,6),(1,3,5)(2,4,6),
    >                    (1,4)(2,5)(3,6),(1,6,5,4,3,2),(1,5,3)(2,6,4)] );
    [ (2,6)(3,5), (1,2)(3,6)(4,5), (1,3)(4,6), (1,4)(2,3)(5,6),

      (1,5)(2,4), (1,6)(2,5)(3,4) ]

The default function DomainOps.Difference checks whether D is infinite. If it is it signals an error. Otherwise Difference computes the proper sets of elements of D and E and returns the difference of those sets (see 4.6 and 26.8). This default function is currently not overlaid for any domain.

4.15 Representative

Representative( D )

Representative returns a representative of the domain D.

The existence of a representative, and the exact definition of what a representative is, depends on the category of D. The representative should be an element that, within a given context, identifies the domain D. For example if D is a cyclic group, its representative would be a generator of D, or if D is a coset, its representative would be an arbitrary element of the coset.

Note that Representative is pretty free in choosing a representative if there are several. It is not even guaranteed that Representative returns the same representative if it is called several times for one domain. Thus the main difference between Representative and Random (see 4.16) is that Representative is free to choose a value that is cheap to compute, while Random must make an effort to randomly distribute its answers.

    gap> C := Coset( Subgroup( G, [(1,4)(2,5)(3,6)] ), (1,6,5,4,3,2) );;
    gap> Representative( C );
    (1,3,5)(2,4,6)

Representative first tests whether the component D.representative is bound. If the field is bound it returns its value. Otherwise it calls D.operations.Representative( D ), remembers the returned value in D.representative, and returns it.

The default function called this way is DomainOps.Representative, which simply signals an error, since there is no default way to find a representative.

4.16 Random

Random( D )

Random returns a random element of the domain D. The distribution of elements returned by Random depends on the domain D. For finite domains all elements are usually equally likely. For infinite domains some reasonable distribution is used. See the chapters of the various domains to find out which distribution is being used.

    gap> Random( GaussianIntegers );
    1-4*E(4)
    gap> Random( GaussianIntegers );
    1+2*E(4)
    gap> Random( D12 );
    ()
    gap> Random( D12 );
    (1,4)(2,5)(3,6)

The default function for random selection is DomainOps.Random, which computes the set of elements using Elements and selects a random element of this list using RandomList (see 25.37 for a description of the pseudo random number generator used). This default function can of course only be applied to finite domains. It is overlaid by other functions for most other domains.

All random functions called this way rely on the low level random number generator provided by RandomList (see 25.37).

208 CHAPTER 4. DOMAINS

Rings

Rings are important algebraic domains. Mathematically a ring is a set R with two operations + and * called addition and multiplication. (R, +) must be an abelian group. The identity of this group is called 0_{R}. (R − {0_{R}}, ∗) must be a monoid. If this monoid has an identity element it is called 1_{R}.

Important examples of rings are the integers (see 10), the Gaussian integers (see 14), the integers of a cyclotomic field (see 15), and matrices (see 32).

This chapter contains sections that describe how to test whether a domain is a ring (see 5.1), and how to find the smallest and the default ring in which a list of elements lies (see 5.2 and 5.3).

The next sections describe the operations applicable to ring elements (see 5.4, 5.5, 5.6).

The next sections describe the functions that test whether a ring has certain properties (5.7, 5.8, 5.9, and 5.10).

The next sections describe functions that are related to the units of a ring (see 5.11, 5.12, 5.13, 5.14, and 5.15).

Then come the sections that describe the functions that deal with the irreducible and prime elements of a ring (see 5.16, 5.17, and 5.18).

Then come the sections that describe the functions that are applicable to elements of rings (see 5.19, 5.20, 5.21, 5.22, 5.24, 5.25, 5.26, 5.27, 5.28).

The last section describes how ring records are represented internally (see 5.29).

Because rings are a category of domains all functions applicable to domains are also applicable to rings (see chapter 4) .

All functions described in this chapter are in LIBNAME/"ring.g".

5.1 IsRing

IsRing( domain )

IsRing returns true if the object domain is a ring record, representing a ring (see 5.29), and false otherwise.

More precisely IsRing tests whether domain is a ring record (see 5.29). So for example a matrix group may in fact be a ring, yet IsRing would return false.

    gap> IsRing( Integers );
    true
    gap> IsRing( Rationals );

false # Rationals is a field record not a ring record

    gap> IsRing( rec( isDomain := true, isRing := true ) );

true # it is possible to fool IsRing

5.2 Ring

Ring( r, s...  )
Ring( list )

In the first form Ring returns the smallest ring that contains all the elements r, s... etc. In the second form Ring returns the smallest ring that contains all the elements in the list list. If any element is not an element of a ring or if the elements lie in no common ring an error is raised.

    gap> Ring( 1, -1 );
    Integers
    gap> Ring( [10..20] );
    Integers

Ring differs from DefaultRing (see 5.3) in that it returns the smallest ring in which the elements lie, while DefaultRing may return a larger ring if that makes sense.

5.3 DefaultRing

DefaultRing( r, s...  )
DefaultRing( list )

In the first form DefaultRing returns the default ring that contains all the elements r, s... etc. In the second form DefaultRing returns the default ring that contains all the elements in the list list. If any element is not an element of a ring or if the elements lie in no common ring an error is raised.

The ring returned by DefaultRing need not be the smallest ring in which the elements lie. For example for elements from cyclotomic fields DefaultRing may return the ring of integers of the smallest cyclotomic field in which the elements lie, which need not be the smallest ring overall, because the elements may in fact lie in a smaller number field which is not a cyclotomic field.

For the exact definition of the default ring of a certain type of elements read the chapter describing this type.

DefaultRing is used by the ring functions like Quotient, IsPrime, Factors, or Gcd if no explicit ring is given.

    gap> DefaultRing( 1, -1 );
    Integers
    gap> DefaultRing( [10..20] );
    Integers

Ring (see 5.2) differs from DefaultRing in that it returns the smallest ring in which the elements lie, while DefaultRing may return a larger ring if that makes sense.

5.4 Comparisons of Ring Elements

r = s

r <> s

The equality operator = evaluates to true if the two ring elements r and s are equal, and to false otherwise. The inequality operator <> evaluates to true if the two ring elements r and s are not equal, and to false otherwise. Note that any two ring elements can be compared, even if they do not lie in compatible rings. In this case they can, of course, never be equal. For each type of rings the equality of those ring elements is given in the respective chapter.

Ring elements can also be compared with objects of other types. Of course they are never equal.

r < s

r <= s

r > s

r >= s

The operators <, <=, >, and >= evaluate to true if the ring element r is less than, less than or equal to, greater than, or greater than or equal to the ring element s, and to false otherwise. For each type of rings the definition of the ordering of those ring elements is given in the respective chapter. The ordering of ring elements is as follows. Rationals are smallest, next are cyclotomics, followed by finite ring elements.

Ring elements can also be compared with objects of other types. They are smaller than everything else.

5.5 Operations for Ring Elements

The following operations are always available for ring elements. Of course the operands must lie in compatible rings, i.e., the rings must be equal, or at least have a common superring.

r + s

The operator + evaluates to the sum of the two ring elements r and s, which must lie in compatible rings.

r - s

The operator - evaluates to the difference of the two ring elements r and s, which must lie in compatible rings.

r * s

The operator * evaluates to the product of the two ring elements r and s, which must lie in compatible rings.

r ^n

The operator ^evaluates to the n-th power of the ring element r. If n is a positive integer then r^n is r*r*..*r (n factors). If n is a negative integer r^n is defined as 1/r^{−n}. If 0 is raised to a negative power an error is signalled. Any ring element, even 0, raised to the 0-th power yields 1.

For the precedence of the operators see 2.10.

Note that the quotient operator / usually performs the division in the quotient field of the ring. To compute a quotient in a ring use the function Quotient (see 5.6).

5.6 Quotient

Quotient( r, s )
Quotient( R, r, s )

In the first form Quotient returns the quotient of the two ring elements r and s in their default ring (see 5.3). In the second form Quotient returns the quotient of the two ring elements r and s in the ring R. It returns false if the quotient does not exist.

    gap> Quotient( 4, 2 );
    2
    gap> Quotient( Integers, 3, 2 );
    false
Quotient calls R.operations.Quotient( R, r, s ) and returns the value.

The default function called this way is RingOps.Quotient, which just signals an error, because there is no generic method to compute the quotient of two ring elements. Thus special categories of rings must overlay this default function with other functions.

5.7 IsCommutativeRing

IsCommutativeRing( R )

IsCommutativeRing returns true if the ring R is commutative and false otherwise.

A ring R is called commutative if for all elements r and s of R we have rs = sr.

    gap> IsCommutativeRing( Integers );
    true

IsCommutativeRing first tests whether the flag R.isCommutativeRing is bound. If the flag is bound, it returns this value. Otherwise it calls R.operations.IsCommutativeRing( R ), remembers the returned value in R.isCommutativeRing, and returns it.

The default function called this way is RingOps.IsCommutativeRing, which tests whether all the generators commute if the component R.generators is bound, and tests whether all elements commute otherwise, unless R is infinite. This function is seldom overlaid, because most rings already have the flag bound.

5.8 IsIntegralRing

IsIntegralRing( R )

IsIntegeralRing returns true if the ring R is integral and false otherwise.

A ring R is called integral if it is commutative and if for all elements r and s of R we have rs = 0_{R} implies that either r or s is 0_{R}.

    gap> IsIntegralRing( Integers );
    true

IsIntegralRing first tests whether the flag R.isIntegralRing is bound. If the flag is bound, it returns this value. Otherwise it calls R.operations.IsIntegralRing( R ), remembers the returned value in R.isIntegralRing, and returns it.

The default function called this way is RingOps.IsIntegralRing, which tests whether the product of each pair of nonzero elements is unequal to zero, unless R is infinite. This function is seldom overlaid, because most rings already have the flag bound.

5.9 IsUniqueFactorizationRing

IsUniqueFactorizationRing( R )

IsUniqueFactorizationRing returns true if R is a unique factorization ring and false otherwise.

A ring R is called a unique factorization ring if it is an integral ring, and every element has a unique factorization into irreducible elements, i.e., a unique representation as product of irreducibles (see 5.16). Unique in this context means unique up to permutations of the factors and up to multiplication of the factors by units (see 5.12).

    gap> IsUniqueFactorizationRing( Integers );
    true
IsUniqueFactorizationRing tests whether R.isUniqueFactorizationRing is bound. If

the flag is bound, it returns this value. If this flag has no assigned value it calls the func-

tion R.operations.IsUniqueFactorizationRing( R ), remembers the returned value in
R.isUniqueFactorizationRing, and returns it.

The default function called this way is RingOps.IsUniqueFactorizationRing, which just signals an error, since there is no generic method to test whether a ring is a unique factorization ring. Special categories of rings thus must either have the flag bound or overlay this default function.

5.10 IsEuclideanRing

IsEuclideanRing( R )

IsEuclideanRing returns true if the ring R is a Euclidean ring and false otherwise.

A ring R is called a Euclidean ring if it is an integral ring and there exists a function δ, called the Euclidean degree, from R − {0_{R}} to the nonnegative integers, such that for every pair r ∈ R and s ∈ R − {0_{R}} there exists an element q such that either r − qs = 0_{R} or δ(r − qs) < δ(s). The existence of this division with remainder implies that the Euclidean algorithm can be applied to compute a greatest common divisor of two elements, which in turn implies that R is a unique factorization ring.

    gap> IsEuclideanRing( Integers );
    true

IsEuclideanRing first tests whether the flag R.isEuclideanRing is bound. If the flag is bound, it returns this value. Otherwise it calls R.operations.IsEuclideanRing( R ), remembers the returned value in R.isEuclideanRing, and returns it.

The default function called this way is RingOps.IsEuclideanRing, which just signals an error, because there is no generic way to test whether a ring is a Euclidean ring. This function is seldom overlaid because most rings already have the flag bound.

5.11 IsUnit

IsUnit( r )
IsUnit( R, r )

In the first form IsUnit returns true if the ring element r is a unit in its default ring (see 5.3). In the second form IsUnit returns true if r is a unit in the ring R.

An element r is called a unit in a ring R, if r has an inverse in R.

    gap> IsUnit( Integers, 2 );
    false
    gap> IsUnit( Integers, -1 );
    true
IsUnit calls R.operations.IsUnit( R, r ) and returns the value.

The default function called this way is RingOps.IsUnit, which tries to compute the inverse of r with R.operations.Quotient( R, R.one, r ) and returns true if the result is not false, and false otherwise. Special categories of rings overlay this default function with more efficient functions.

5.12 Units

Units( R )

Units returns the group of units of the ring R. This may either be returned as a list or as a group described by a group record (see 7).

An element r is called a unit of a ring R, if r has an inverse in R. It is easy to see that the set of units forms a multiplicative group.

    gap> Units( Integers );
    [ -1, 1 ]

Units first tests whether the component R.units is bound. If the component is bound, it returns this value. Otherwise it calls R.operations.Units( R ), remembers the returned value in R.units, and returns it.

The default function called this way is RingOps.Units, which runs over all elements of R and tests for each whether it is a unit, provided that R is finite. Special categories of rings overlay this default function with more efficient functions.

5.13 IsAssociated

IsAssociated( r, s )
IsAssociated( R, r, s )

In the first form IsAssociated returns true if the two ring elements r and s are associated in their default ring (see 5.3) and false otherwise. In the second form IsAssociated returns true if the two ring elements r and s are associated in the ring R and false otherwise.

Two elements r and s of a ring R are called associates if there is a unit u of R such that ru = s.

    gap> IsAssociated( Integers, 2, 3 );
    false
    gap> IsAssociated( Integers, 17, -17 );
    true
IsAssociated calls R.operations.IsAssociated( R, r, s ) and returns the value.

The default function called this way is RingOps.IsAssociated, which tries to compute the quotient of r and s and returns true if the quotient exists and is a unit. Special categories of rings overlay this default function with more efficient functions.

5.14 StandardAssociate

StandardAssociate( r )
StandardAssociate( R, r )

In the first form StandardAssociate returns the standard associate of the ring element r in its default ring (see 5.3). In the second form StandardAssociate returns the standard associate of the ring element r in the ring R.

The standard associate of an ring element r of R is an associated element of r which is, in a ring dependent way, distinguished among the set of associates of r. For example, in the ring of integers the standard associate is the absolute value.

    gap> StandardAssociate( Integers, -17 );
    17
StandardAssociate calls R.operations.StandardAssociate( R, r ) and returns the

value.

The default function called this way is RingOps.StandardAssociate, which just signals an error, because there is no generic way even to define the standard associate. Thus special categories of rings must overlay this default function with other functions.

5.15 Associates

Associates( r )
Associates( R, r )

In the first form Associates returns the set of associates of the ring element r in its default ring (see 5.3). In the second form Associates returns the set of associates of r in the ring R.

Two elements r and s of a ring R are called associate if there is a unit u of R such that ru = s.

    gap> Associates( Integers, 17 );
    [ -17, 17 ]
Associates calls R.operations.Associates( R, r ) and returns the value.

The default function called this way is RingOps.Associates, which multiplies the set of units of R with the element r, and returns the set of those elements. Special categories of rings overlay this default function with more efficient functions.

5.16 IsIrreducible

IsIrreducible( r )
IsIrreducible( R, r )

In the first form IsIrreducible returns true if the ring element r is irreducible in its default ring (see 5.3) and false otherwise. In the second form IsIrreducible returns true if the ring element r is irreducible in the ring R and false otherwise.

An element r of a ring R is called irreducible if there is no nontrivial factorization of r in R, i.e., if there is no representation of r as product st such that neither s nor t is a unit (see 5.11). Each prime element (see 5.17) is irreducible.

    gap> IsIrreducible( Integers, 4 );
    false
    gap> IsIrreducible( Integers, 3 );
    true
IsIrreducible calls R.operations.IsIrreducible( R, r ) and returns the value.

The default function called this way is RingOps.IsIrreducible, which justs signals an error, because there is no generic way to test whether an element is irreducible. Thus special categories of rings must overlay this default function with other functions.

5.17 IsPrime

IsPrime( r )
IsPrime( R, r )

In the first form IsPrime returns true if the ring element r is a prime in its default ring (see 5.3) and false otherwise. In the second form IsPrime returns true if the ring element r is a prime in the ring R and false otherwise.

An element r of a ring R is called prime if for each pair s and t such that r divides st the element r divides either s or t. Note that there are rings where not every irreducible element (see 5.16) is a prime.

    gap> IsPrime( Integers, 4 );
    false
    gap> IsPrime( Integers, 3 );
    true
IsPrime calls R.operations.IsPrime( R, r ) and returns the value.

The default function called this way is RingOps.IsPrime, which just signals an error, because there is no generic way to test whether an element is prime. Thus special categories of rings must overlay this default function with other functions.

5.18 Factors

Factors( r )
Factors( R, r )

In the first form Factors returns the factorization of the ring element r in its default ring (see 5.3). In the second form Factors returns the factorization of the ring element r in the ring R. The factorization is returned as a list of primes (see 5.17). Each element in the list is a standard associate (see 5.14) except the first one, which is multiplied by a unit as necessary to have Product( Factors( R, r ) ) = r. This list is usually also sorted, thus smallest prime factors come first. If r is a unit or zero, Factors( R, r ) = [ r ].

    gap> Factors( -Factorial(6) );
    [ -2, 2, 2, 2, 3, 3, 5 ]
    gap> Set( Factors( Factorial(13)/11 ) );
    [ 2, 3, 5, 7, 13 ]
    gap> Factors( 2^63 - 1 );
    [ 7, 7, 73, 127, 337, 92737, 649657 ]
    gap> Factors( 10^42 + 1 );
    [ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]
Factors calls R.operations.Factors( R, r ) and returns the value.

The default function called this way is RingOps.Factors, which just signals an error, because there is no generic way to compute the factorization of ring elements. Thus special categories of ring elements must overlay this default function with other functions.

5.19 EuclideanDegree

EuclideanDegree( r )
EuclideanDegree( R, r )

In the first form EuclideanDegree returns the Euclidean degree of the ring element r in its default ring. In the second form EuclideanDegree returns the Euclidean degree of the ring element in the ring R. R must of course be an Euclidean ring (see 5.10).

A ring R is called a Euclidean ring, if it is an integral ring, and there exists a function δ, called the Euclidean degree, from R − {0_{R}} to the nonnegative integers, such that for every pair r ∈ R and s ∈ R − {0_{R}} there exists an element q such that either r − qs = 0_{R} or δ(r − qs) < δ(s). The existence of this division with remainder implies that the Euclidean algorithm can be applied to compute a greatest common divisors of two elements, which in turn implies that R is a unique factorization ring.

    gap> EuclideanDegree( Integers, 17 );
    17
    gap> EuclideanDegree( Integers, -17 );
    17
EuclideanDegree calls R.operations.EuclideanDegree( R, r ) and returns the value.

The default function called this way is RingOps.EuclideanDegree, which justs signals an error, because there is no default way to compute the Euclidean degree of an element. Thus Euclidean rings must overlay this default function with other functions.

5.20 EuclideanRemainder

EuclideanRemainder( r, m )
EuclideanRemainder( R, r, m )

In the first form EuclideanRemainder returns the remainder of the ring element r modulo the ring element m in their default ring. In the second form EuclideanRemainder returns the remainder of the ring element r modulo the ring element m in the ring R. The ring R must be a Euclidean ring (see 5.10) otherwise an error is signalled. A ring R is called a Euclidean ring, if it is an integral ring, and there exists a function δ, called the Euclidean degree, from R − {0_{R}} to the nonnegative integers, such that for every pair r ∈ R and s ∈ R − {0_{R}} there exists an element q such that either r − qs = 0_{R} or δ(r − qs) < δ(s). The existence of this division with remainder implies that the Euclidean algorithm can be applied to compute a greatest common divisors of two elements, which in turn implies that R is a unique factorization ring. EuclideanRemainder returns this remainder r − qs.

    gap> EuclideanRemainder( 16, 3 );
    1
    gap> EuclideanRemainder( Integers, 201, 11 );
    3
EuclideanRemainder calls R.operations.EuclideanRemainder( R, r, m ) in order to

compute the remainder and returns the value. The default function called this way uses QuotientRemainder in order to compute the remainder.

5.21 EuclideanQuotient

EuclideanQuotient( r, m )
EuclideanQuotient( R, r, m )

In the first form EuclideanQuotient returns the Euclidean quotient of the ring elements r and m in their default ring. In the second form EuclideanQuotient returns the Euclidean quotient of the ring elements rand m in the ring R. The ring R must be a Euclidean ring (see 5.10) otherwise an error is signalled. A ring R is called a Euclidean ring, if it is an integral ring, and there exists a function δ, called the Euclidean degree, from R − {0_{R}} to the nonnegative integers, such that for every pair r ∈ R and s ∈ R − {0_{R}} there exists an element q such that either r − qs = 0_{R} or δ(r − qs) < δ(s). The existence of this division with remainder implies that the Euclidean algorithm can be applied to compute a greatest common divisors of two elements, which in turn implies that R is a unique factorization ring. EuclideanQuotient returns the quotient q.

    gap> EuclideanQuotient( 16, 3 );
    5
    gap> EuclideanQuotient( Integers, 201, 11 );
    18
EuclideanQuotient calls R.operations.EuclideanQuotient( R, r, m ) and returns

the value. The default function called this way uses QuotientRemainder in order to compute the quotient.

5.22 QuotientRemainder

QuotientRemainder( r, m )
QuotientRemainder( R, r, m )

In the first form QuotientRemainder returns the Euclidean quotient and the Euclidean remainder of the ring elements r and m in their default ring as pair of ring elements. In the second form QuotientRemainder returns the Euclidean quotient and the Euclidean remainder of the ring elements r and m in the ring R. The ring R must be a Euclidean ring (see 5.10) otherwise an error is signalled.

A ring R is called a Euclidean ring, if it is an integral ring, and there exists a function δ, called the Euclidean degree, from R − {0_{R}} to the nonnegative integers, such that for every pair r ∈ R and s ∈ R − {0_{R}} there exists an element q such that either r − qs = 0_{R} or δ(r − qs) < δ(s). The existence of this division with remainder implies that the Euclidean algorithm can be applied to compute a greatest common divisors of two elements, which in turn implies that R is a unique factorization ring. QuotientRemainder returns this quotient q and the remainder r − qs.

    gap> qr := QuotientRemainder( 16, 3 );
    [ 5, 1 ]
    gap> 3 * qr[1] + qr[2];
    16
    gap> QuotientRemainder( Integers, 201, 11 );
    [ 18, 3 ]
QuotientRemainder calls R.operations.QuotientRemainder( R, r, m ) and returns

the value.

The default function called this way is RingOps.QuotientRemainder, which just signals an error, because there is no default function to compute the Euclidean quotient or remainder of one ring element modulo another. Thus Euclidean rings must overlay this default function with other functions.

5.23 Mod

Mod( r, m )
Mod( R, r, m )

Mod is a synonym for EuclideanRemainder and is obsolete, see 5.20.

5.24 QuotientMod

QuotientMod( r, s, m )
QuotientMod( R, r, s, m )

In the first form QuotientMod returns the quotient of the ring elements r and s modulo the ring element m in their default ring (see 5.3). In the second form QuotientMod returns the quotient of the ring elements r and s modulo the ring element m in the ring R. R must be a Euclidean ring (see 5.10) so that EuclideanRemainder (see 5.20) can be applied. If the modular quotient does not exist, false is returned.

The quotient q of r and s modulo m is an element of R such that qs = r modulo m, i.e., such that qs − r is divisable by m in R and that q is either 0 (if r is divisable by m) or the Euclidean degree of q is strictly smaller than the Euclidean degree of m.

    gap> QuotientMod( Integers, 13, 7, 11 );

    gap> QuotientMod( Integers, 13, 7, 21 );
    false
QuotientMod calls R.operations.QuotientMod( R, r, s, m ) and returns the value.

The default function called this way is RingOps.QuotientMod, which applies the Euclidean gcd algorithm to compute the gcd g of s and m, together with the representation of this gcd as linear combination in s and m, g = a * s + b * m. The modular quotient exists if and only if r is divisible by g, in which case the quotient is a * Quotient( R, r, g ). This default function is seldom overlaid, because there is seldom a better way to compute the quotient.

5.25 PowerMod

PowerMod( r, e, m )
PowerMod( R, r, e, m )

In the first form PowerMod returns the e-th power of the ring element r modulo the ring element m in their default ring (see 5.3). In the second form PowerMod returns the e-th power of the ring element r modulo the ring element m in the ring R. e must be an integer. R must be a Euclidean ring (see 5.10) so that EuclideanRemainder (see 5.20) can be applied to its elements.

If e is positive the result is r^{e} modulo m. If e is negative then PowerMod first tries to find the inverse of r modulo m, i.e., i such that ir = 1 modulo m. If the inverse does not exist an error is signalled. If the inverse does exist PowerMod returns PowerMod( R, i, -e, m

).

PowerMod reduces the intermediate values modulo m, improving performance drastically when e is large and m small.

    gap> PowerMod( Integers, 2, 20, 100 );

76 # 2^{20} = 1048576

    gap> PowerMod( Integers, 3, 2^32, 2^32+1 );

3029026160 # which proves that 2^{32} + 1 is not a prime

    gap> PowerMod( Integers, 3, -1, 22 );

15 # 3*15 = 45 = 1 modulo 22

PowerMod calls R.operations.PowerMod( R, r, e, m ) and returns the value.

The default function called this way is RingOps.PowerMod, which uses QuotientMod (see 5.24) if necessary to invert r, and then uses a right-to-left repeated squaring, reducing the intermediate results modulo m in each step. This function is seldom overlaid, because there is seldom a better way of computing the power.

5.26 Gcd

Gcd( r1 , r2 ...  )
Gcd( R, r1 , r2 ...  )

In the first form Gcd returns the greatest common divisor of the ring elements r1 , r2 ... etc. in their default ring (see 5.3). In the second form Gcd returns the greatest common divisor of the ring elements r1 , r2 ... etc. in the ring R. R must be a Euclidean ring (see 5.10) so that QuotientRemainder (see 5.22) can be applied to its elements. Gcd returns the standard associate (see 5.14) of the greatest common divisors.

A greatest common divisor of the elements r_{1}, r_{2}... etc. of the ring R is an element of largest Euclidean degree (see 5.19) that is a divisor of r_{1}, r_{2}... etc. We define gcd(r, 0_{R}) = gcd(0_{R}, r) = StandardAssociate(r) and gcd(0_{R}, 0_{R}) = 0_{R}.

    gap> Gcd( Integers, 123, 66 );
    3

Gcd calls R.operations.Gcd repeatedly, each time passing the result of the previous call and the next argument, and returns the value of the last call.

The default function called this way is RingOps.Gcd, which applies the Euclidean algorithm to compute the greatest common divisor. Special categories of rings overlay this default function with more efficient functions.

5.27 GcdRepresentation

GcdRepresentation( r1 , r2 ...  )
GcdRepresentation( R, r1 , r2 ...  )

In the first form GcdRepresentation returns the representation of the greatest common divisor of the ring elements r1 , r2 ... etc. in their default ring (see 5.3). In the second form GcdRepresentation returns the representation of the greatest common divisor of the ring elements r1 , r2 ... etc. in the ring R. R must be a Euclidean ring (see 5.10) so that Gcd (see 5.26) can be applied to its elements. The representation is returned as a list of ring elements.

The representation of the gcd g of the elements r_{1}, r_{2}... etc. of a ring R is a list of ring elements s_{1}, s_{2}... etc. of R, such that g = s_{1}r_{1} + s_{2}r_{2}.... That this representation exists can be shown using the Euclidean algorithm, which in fact can compute those coefficients.

    gap> GcdRepresentation( 123, 66 );

[ 7, -13 ] # 3 = 7*123 - 13*66

    gap> Gcd( 123, 66 ) = last * [ 123, 66 ];
    true
GcdRepresentation calls R.operations.GcdRepresentation repeatedly, each time pass-

ing the gcd result of the previous call and the next argument, and returns the value of the last call.

The default function called this way is RingOps.GcdRepresentation, which applies the Euclidean algorithm to compute the greatest common divisor and its representation. Special categories of rings overlay this default function with more efficient functions.

5.28 Lcm

Lcm( r1 , r2 ...  )
Lcm( R, r1 , r2 ...  )

In the first form Lcm returns the least common multiple of the ring elements r1 , r2 ... etc. in their default ring (see 5.3). In the second form Lcm returns the least common multiple of the ring elements r1 , r2 ,... etc. in the ring R. R must be a Euclidean ring (see 5.10) so that Gcd (see 5.26) can be applied to its elements. Lcm returns the standard associate (see 5.14) of the least common multiples.

A least common multiple of the elements r_{1}, r_{2}... etc. of the ring R is an element of smallest Euclidean degree (see 5.19) that is a multiple of r_{1}, r_{2}... etc. We define lcm(r, 0_{R}) = lcm(0_{R}, r) = StandardAssociate(r) and Lcm(0_{R}, 0_{R}) = 0_{R}.

Lcm uses the equality lcm(m, n) = m ∗ n/gcd(m, n) (see 5.26).

    gap> Lcm( Integers, 123, 66 );
    2706

Lcm calls R.operations.Lcm repeatedly, each time passing the result of the previous call and the next argument, and returns the value of the last call.

The default function called this way is RingOps.Lcm, which simply returns the product of r with the quotient of s and the greatest common divisor of r and s. Special categories of rings overlay this default function with more efficient functions.

5.29 Ring Records

A ring R is represented by a record with the following entries.

isDomain

is of course always the value true.

isRing

is of course always the value true.

isCommutativeRing

is true if the multiplication is known to be commutative, false if the multiplication is known to be noncommutative, and unbound otherwise.

isIntegralRing

is true if R is known to be a commutative domain with 1 without zero divisor, false if R is known to lack one of these properties, and unbound otherwise.

isUniqueFactorizationRing

is true if R is known to be a domain with unique factorization into primes, false if R is known to have a nonunique factorization, and unbound otherwise.

isEuclideanRing

is true if R is known to be a Euclidean domain, false if it is known not to be a Euclidean domain, and unbound otherwise.

zero

is the additive neutral element.

units

is the list of units of the ring if it is known.

size

is the size of the ring if it is known. If the ring is not finite this is the string ”infinity”.

one

is the multiplicative neutral element, if the ring has one.

integralBase

if the ring is, as additive group, isomorphic to the direct product of a finite number of copies of Z this contains a base.

As an example of a ring record, here is the definition of the ring record Integers.

rec(

# category components

isDomain                    := true,
isRing                      := true,

# identity components

generators                  := [ 1 ],
zero                        := 0,
one                         := 1,
name                        := "Integers",

# knowledge components

size                        := "infinity",
isFinite                    := false,
isCommutativeRing           := true,
isIntegralRing              := true,
isUniqueFactorizationRing   := true,
isEuclideanRing             := true,
units                       := [ -1, 1 ],

# operations record

operations                  := rec(
    ...
    IsPrime                 := function ( Integers, n )
        return IsPrimeInt( n );
    end,
    ...
    'mod'                   := function ( Integers, n, m )
        return n mod m;
    end,
    ... ) )

224 CHAPTER 5. RINGS

Fields

Fields are important algebraic domains. Mathematically a field is a commutative ring F (see chapter 5), such that every element except 0 has a multiplicative inverse. Thus F has two operations + and * called addition and multiplication. (F, +) must be an abelian group, whose identity is called 0_{F}. (F − {0_{F} }, ∗) must be an abelian group, whose identity element is called 1_{F}.

GAP supports the field of rationals (see 12), subfields of cyclotomic fields (see 15), and finite fields (see 17).

This chapter begins with sections that describe how to test whether a domain is a field (see 6.1), how to find the smallest field and the default field in which a list of elements lies (see 6.2 and 6.3), and how to view a field over a subfield (see 6.4).

The next sections describes the operation applicable to field elements (see 6.5 and 6.6).

The next sections describe the functions that are applicable to fields (see 6.7) and their elements (see 6.8, 6.9, 6.10, 6.11, and 6.12).

The following sections describe homomorphisms of fields (see 6.13, 6.14, 6.15, 6.16).

The last section describes how fields are represented internally (see 6.17).

Fields are domains, so all functions that are applicable to all domains are also applicable to fields (see chapter 4).

All functions for fields are in LIBNAME/"field.g".

6.1 IsField

IsField( D )

IsField returns true if the object D is a field and false otherwise.

More precisely IsField tests whether D is a field record (see 6.17). So, for example, a matrix group may in fact be a field, yet IsField would return false.

    gap> IsField( GaloisField(16) );
    true
    gap> IsField( CyclotomicField(9) );

    true
    gap> IsField( rec( isDomain := true, isField := true ) );

true # it is possible to fool IsField

    gap> IsField( AsRing( Rationals ) );

false # though this ring is, as a set, still Rationals

6.2 Field

Field( z,..  ) Field( list )

In the first form Field returns the smallest field that contains all the elements z,.. etc. In the second form Field returns the smallest field that contains all the elements in the list list. If any element is not an element of a field or the elements lie in no common field an error is raised.

    gap> Field( Z(4) );
    GF(2^2)
    gap> Field( E(9) );
    CF(9)
    gap> Field( [ Z(4), Z(9) ] );
    Error, CharFFE: <z> must be a finite field element, vector, or matrix
    gap> Field( [ E(4), E(9) ] );
    CF(36)

Field differs from DefaultField (see 6.3) in that it returns the smallest field in which the elements lie, while DefaultField may return a larger field if that makes sense.

6.3 DefaultField

DefaultField( z,..  ) DefaultField( list )

In the first form DefaultField returns the default field that contains all the elements z,.. etc. In the second form DefaultField returns the default field that contains all the elements in the list list. If any element is not an element of a field or the elements lie in no common field an error is raised.

The field returned by DefaultField need not be the smallest field in which the elements lie. For example for elements from cyclotomic fields DefaultField may return the smallest cyclotomic field in which the elements lie, which need not be the smallest field overall, because the elements may in fact lie in a smaller number field which is not a cyclotomic field.

For the exact definition of the default field of a certain type of elements read the chapter describing this type (see 17 and 15).

DefaultField is used by Conjugates, Norm, Trace, CharPol, and MinPol (see 6.8, 6.9,

6.10, 6.11, and 6.12) if no explicit field is given.

    gap> DefaultField( Z(4) );
    GF(2^2)
    gap> DefaultField( E(9) );
    CF(9)
    gap> DefaultField( [ Z(4), Z(9) ] );

    Error, CharFFE: <z> must be a finite field element, vector, or matrix
    gap> DefaultField( [ E(4), E(9) ] );
    CF(36)

Field (see 6.2) differs from DefaultField in that it returns the smallest field in which the elements lie, while DefaultField may return a larger field if that makes sense.

6.4 Fields over Subfields

F / G

The quotient operator / evaluates to a new field H . This field has the same elements as F , i.e., is a domain equal to F . However H is viewed as a field over the field G, which must be a subfield of F .

What subfield a field is viewed over determines its Galois group. As described in 6.7 the Galois group is the group of field automorphisms that leave the subfield fixed. It also influences the results of 6.9, 6.10, 6.11, and 6.12, because they are defined in terms of the Galois group.

    gap> F := GF(2^12);
    GF(2^12)
    gap> G := GF(2^2);
    GF(2^2)
    gap> Q := F / G;
    GF(2^12)/GF(2^2)
    gap> Norm( F, Z(2^6) );
    Z(2)^0
    gap> Norm( Q, Z(2^6) );
    Z(2^2)^2

The operator / calls G.operations./( F , G ).

The default function called this way is FieldOps./, which simply makes a copy of F and enters G into the record component F .field (see 6.17).

6.5 Comparisons of Field Elements

f = g

f <> g

The equality operator = evaluates to true if the two field elements f and g are equal, and to false otherwise. The inequality operator <> evaluates to true if the two field elements f and g are not equal, and to false otherwise. Note that any two field elements can be compared, even if they do not lie in compatible fields. In this case they cn, of course, never be equal. For each type of fields the equality of those field elements is given in the respective chapter.

Note that you can compare field elements with elements of other types; of course they are never equal.

f < g

f <= g

f > g

f >= g

The operators <, <=, >, and >= evaluate to true if the field element f is less than, less than or equal to, greater than, or greater than or equal to the field element g. For each type of fields the definition of the ordering of those field elements is given in the respective chapter. The ordering of field elements is as follows. Rationals are smallest, next are cyclotomics, followed by finite field elements.

Note that you can compare field elements with elements of other types; they are smaller than everything else.

6.6 Operations for Field Elements

The following operations are always available for field elements. Of course the operands must lie in compatible fields, i.e., the fields must be equal, or at least have a common superfield.

f + g

The operator + evaluates to the sum of the two field elements f and g, which must lie in compatible fields.

f - g

The operator - evaluates to the difference of the two field elements f and g, which must lie in compatible fields.

f * g

The operator * evaluates to the product of the two field elements f and g, which must lie in compatible fields.

f / g

The operator / evaluates to the quotient of the two field elements f and g, which must lie in compatible fields. If the divisor is 0 an error is signalled.

f ^n

The operator ^evaluates to the n-th power of the field element f . If n is a positive integer then f ^n is f *f *..*f (n factors). If n is a negative integer f ^n is defined as 1/f^{−n}. If 0 is raised to a negative power an error is signalled. Any field element, even 0, raised to the 0-th power yields 1.

For the precedence of the operators see 2.10.

6.7 GaloisGroup

GaloisGroup( F )

GaloisGroup returns the Galois group of the field F as a group (see 7) of field automorphisms (see 6.13).

The Galois group of a field F over a subfield F .field is the group of automorphisms of F that leave the subfield F .field fixed. This group can be interpreted as a permutation group permuting the zeroes of the characteristic polynomial of a primitive element of F . The degree of this group is equal to the number of zeroes, i.e., to the dimension of F as a vector space over the subfield F .field. It operates transitively on those zeroes. The normal divisors of the Galois group correspond to the subfields between F and F .field.

    gap> G := GaloisGroup( GF(4096)/GF(4) );;
    gap> Size( G );
    6
    gap> IsCyclic( G );

true # the Galois group of every finite field is

# generated by the Frobenius automorphism

    gap> H := GaloisGroup( CF(60) );;
    gap> Size( H );
    16
    gap> IsAbelian( H );
    true

The default function FieldOps.GaloisGroup just raises an error, since there is no general method to compute the Galois group of a field. This default function is overlaid by more specific functions for special types of domains (see 17.13 and 15.8).

6.8 Conjugates

Conjugates( z )
Conjugates( F , z )

In the first form Conjugates returns the list of conjugates of the field element z in its default field over its prime field (see 6.3). In the second form Conjugates returns the list of conjugates of the field element z in the field F over the subfield F .field. In either case the list may contain duplicates if z lies in a proper subfield of its default field, respectively of F .

The conjugates of an element z in a field F over a subfield S of are the images of z under the automorphisms in the Galois group of F over S (see 6.7), i.e., those automorphisms of F that leave S fixed. The number of different conjugates of z is given by the degree of the smallest extension of S in which z lies.

Norm (see 6.9) computes the product, Trace (see 6.10) the sum of all conjugates. CharPol (see 6.11) computes the polynomial that has precisely the conjugates with their corresponding multiplicities as roots, MinPol (see 6.12) the squarefree polynomial that has precisely the conjugates as roots.

    gap> Conjugates( Z(2^6) );
    [ Z(2^6), Z(2^6)^2, Z(2^6)^4, Z(2^6)^8, Z(2^6)^16, Z(2^6)^32 ]
    gap> Conjugates( GF(2^12), Z(2^6) );
    [ Z(2^6), Z(2^6)^2, Z(2^6)^4, Z(2^6)^8, Z(2^6)^16, Z(2^6)^32, Z(2^6),
      Z(2^6)^2, Z(2^6)^4, Z(2^6)^8, Z(2^6)^16, Z(2^6)^32 ]
    gap> Conjugates( GF(2^12)/GF(2^2), Z(2^6) );
    [ Z(2^6), Z(2^6)^4, Z(2^6)^16, Z(2^6), Z(2^6)^4, Z(2^6)^16 ]

The default function FieldOps.Conjugates applies the automorphisms of the Galois group of F (see 6.7) to z and returns the list of images. This is overlaid by more efficient functions for most fields.

6.9 Norm

Norm( z )
Norm( F , z )

In the first form Norm returns the norm of the field element z in its default field over its prime field (see 6.3). In the second form Norm returns the norm of z in the field F over the subfield F .field.

The norm of an element z in a field F over a subfield S is the product of all conjugates of z in F over S (see 6.8). As the set of conjugates is fixed under the Galois group of F over S (see 6.7), so is the product. Thus the norm lies in S. The norm is (−1)^{F.degree/S.degree} times the constant term of the characteristic polynomial of z (see 6.11).

    gap> Norm( Z(2^6) );
    Z(2)^0
    gap> Norm( GF(2^12), Z(2^6) );
    Z(2)^0
    gap> Norm( GF(2^12)/GF(2^2), Z(2^6) );
    Z(2^2)^2

The default function FieldOps.Norm multiplies the conjugates of z in F (see 6.8). This is seldom overlaid by more efficient functions for specific fields.

6.10 Trace

Trace( z )
Trace( F , z )

In the first form Trace returns the trace of the field element z in its default field over its prime field (see 6.3). In the second form Trace returns the trace of the element z in the field F over the subfield F .field.

The trace of an element z in a field F over a subfield S is the sum over all conjugates of z in F over S (see 6.8). Because the set of conjugates is fixed under the Galois group of F over S (see 6.7), so is the sum. Thus the trace lies in S. The trace is the negative of the coefficient of the second highest degree term of the characteristic polynomial of z (see 6.11).

    gap> Trace( Z(2^6) );
    0*Z(2)
    gap> Trace( GF(2^12), Z(2^6) );
    0*Z(2)
    gap> Trace( GF(2^12)/GF(2^2), Z(2^6) );
    0*Z(2)

The default function FieldOps.Trace adds the conjugates of z in F (see 6.8). This is seldom overlaid by more efficient functions for specific fields.

6.11 CharPol

CharPol( z )
CharPol( F , z )

In the first form CharPol returns the characteristic polynomial of the element z in its default field over its prime field (see 6.3). In the second form CharPol returns the characteristic polynomial of the element z in the field F over the subfield F .field. The characteristic polynomial is returned as a list of coefficients, the i-th entry is the coefficient of x^{i−1}.

The characteristic polynomial of an element z in a field F over a subfield S is the product of the factors x − c, where c ranges over all the conjugates of z in F over S (see 6.8). Therefore the characteristic polynomial of z in F over S is the monic polynomial whose roots are precisely the conjugates of z with the corresponding multiplicities. Because the set of conjugates is fixed under the Galois group of F over S (see 6.7), so is the polynomial. Thus all the coefficients of the characteristic polynomial lie in S. The constant term is (−1)^{F.degree/S.degree} times the norm of z (see 6.9), and the coefficient of the second highest degree term is the negative of the trace of z (see 6.10). The minimal polynomial of z (see 6.12) is the squarefree polynomial whose roots are precisely the conjugates of z.

    gap> CharPol( Z(2^6) );
    [ Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0 ]
    gap> CharPol( GF(2^12), Z(2^6) );
    [ Z(2)^0, 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2),
      Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0 ]
    gap> CharPol( GF(2^12)/GF(2^2), Z(2^6) );
    [ Z(2^2)^2, 0*Z(2), Z(2)^0, 0*Z(2), Z(2)^0, 0*Z(2), Z(2)^0 ]

The default function FieldOps.CharPol multiplies the binomials x − c with c ranging over the conjugates of z in F (see 6.8). This is seldom overlaid by more efficient functions for specific fields.

6.12 MinPol

MinPol( z )
MinPol( F , z )

In the first form MinPol returns the minimal polynomial of the element z in its default field over its prime field (see 6.3). In the second form MinPol returns the minimal polynomial of the element z in the field F over the subfield F .field. The minimal polynomial is returned as a list of coefficients, the i-th entry is the coefficient of x^{i−1}.

The minimal polynomial of an element z in a field F over a subfield S is the product of the factors x − c, where c ranges over the set of all conjugates of z in F over S (see 6.8). Therefore the minimal polynomial of z in F over S is the squarefree polynomial whose roots are precisely the conjugates of z. Because the set of conjugates is fixed under the Galois group of F over S (see 6.7), so is the polynomial. Thus all the coefficients of the minimal polynomial lie in S. The characteristic polynomial of z (see 6.11) is the polynomial whose roots are precisely the conjugates of z with the corresponding multiplicities.

    gap> MinPol( Z(2^6) );
    [ Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0 ]
    gap> MinPol( GF(2^12), Z(2^6) );
    [ Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0 ]
    gap> MinPol( GF(2^12)/GF(2^2), Z(2^6) );
    [ Z(2^2), Z(2)^0, Z(2)^0, Z(2)^0 ]

The default function FieldOps.MinPol multiplies the binomials x − c with c ranging over the set of conjugates of z in F (see 6.8). This is seldom overlaid by more efficient functions for specific fields.

6.13 Field Homomorphisms

Field homomorphisms are an important class of homomorphisms in GAP (see chapter 37).

A field homomorphism ϕ is a mapping that maps each element of a field F, called the source of ϕ, to an element of another field G, called the range of ϕ, such that for each pair x, y ∈ F we have (x + y)^{ϕ} = x^{ϕ} + y^{ϕ} and (xy)^{ϕ} = x^{ϕ}y^{ϕ}. We also require that ϕ maps the one of F to the one of G (that ϕ maps the zero of F to the zero of G is implied by the above relations).

An Example of a field homomorphism is the Frobinius automorphism of a finite field (see 17.11). Look under field homomorphisms in the index for a list of all available field homomorphisms.

Since field homomorphisms are just a special case of homomorphisms, all functions described in chapter 37 are applicable to all field homomorphisms, e.g., the function to test if a homomorphism is a an automorphism (see 37.6). More general, since field homomorphisms are just a special case of mappings all functions described in chapter 36 are also applicable, e.g., the function to compute the image of an element under a field homomorphism (see 36.8).

The following sections describe the functions that test whether a mapping is a field homomorphism (see 6.14), compute the kernel of a field homomorphism (see 6.15), and how the general mapping functions are implemented for field homomorphisms.

6.14 IsFieldHomomorphism

IsFieldHomomorphism( map )

IsFieldHomomorphism returns true if the mapping map is a field homomorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping map is a field homomorphism if its source F and range G are both fields and if for each pair of elements x, y ∈ F we have (x + y)^{map} = x^{map} + y^{map} and (xy)^{map} = x^{map}y^{map}. We also require that 1_{F}^{map} = 1_{G}.

    gap> f := GF( 16 );
    GF(2^4)
    gap> fun := FrobeniusAutomorphism( f );
    FrobeniusAutomorphism( GF(2^4) )
    gap> IsFieldHomomorphism( fun );
    true
IsFieldHomomorphism first tests if the flag map.isFieldHomomorphism is bound. If the

flag is bound, IsFieldHomomorphism returns its value. Otherwise it calls

map.source.operations.IsFieldHomomorphism( map ), remembers the returned value

in map.isFieldHomomorphism, and returns it. Note that of course all functions that create field homomorphism set the flag map.isFieldHomomorphism to true, so that no function is called for those field homomorphisms.

The default function called this way is MappingOps.IsFieldHomomorphism. It computes all the elements of the source of map and for each pair of elements x, y tests whether (x + y)^{map} = x^{map} + y^{map} and (xy)^{map} = x^{map}y^{map}. Look under IsHomomorphism in the index to see for which mappings this function is overlaid.

6.15 KernelFieldHomomorphism

KernelFieldHomomorphism( hom )

KernelFieldHomomorphism returns the kernel of the field homomorphism hom. Because the kernel must be a ideal in the source and it can not be the full source (because we require that the one of the source is mapped to the one of the range), it must be the trivial ideal. Therefor the kernel of every field homomorphism is the set containing only the zero of the source.

6.16 Mapping Functions for Field Homomorphisms

This section describes how the mapping functions defined in chapter 36 are implemented for field homomorphisms. Those functions not mentioned here are implemented by the default functions described in the respective sections.

IsInjective( hom )

Always returns true (see 6.15).

IsSurjective( hom )

The field homomorphism hom is surjective if the size of the image Size( Image( hom ) ) is equal to the size of the range Size( hom.range ).

hom1 = hom2 The two field homomorphism hom1 and hom2 are are equal if the have the same source and range and if the images of the generators of the source under hom1 and hom2 are equal.

Image( hom )
Image( hom, H )
Images( hom, H )

The image of a subfield under a field homomorphism is computed by computing the images of a set of generators of the subfield, and the result is the subfield generated by those images.

PreImage( hom )
PreImage( hom, H )
PreImages( hom, H )

The preimages of a subfield under a field homomorphism are computed by computing the preimages of all the generators of the subfield, and the result is the subfield generated by those elements. Look in the index under IsInjective, IsSurjective, equality, Image, Images, PreImage, and PreImages to see for which field homomorphisms these functions are overlaid.

6.17 Field Records

A field is represented by a record that contains important information about this field. The GAP library predefines some field records, for example Rationals (see 12). Field constructors construct others, for example Field (see 6.2), and GaloisField (see 17.10). Of course you may also create such a record by hand.

All field records contain the components isDomain, isField, char, degree, generators, zero, one, field, base, and dimension. They may also contain the optional components isFinite, size, galoisGroup. The contents of all components of a field F are described below.

isDomain

is always true. This indicates that F is a domain.

isField

is always true. This indicates that F is a field.

char

is the characteristic of F . For finite fields this is always a prime, for infinite fields this is 0.

degree

is the degree of F as extension of the prime field, not as extension of the subfield S. For finite fields the order of F is given by F .char^F .degree.

generators

a list of elements that together generate F . That is F is the smallest field over the prime field given by F .char that contains the elements of F .generators.

zero

is the additive neutral element of the finite field.

one

is the multiplicative neutral element of the finite field.

field

is the subfield S over which F was constructed. This is either a field record for S, or the same value as F .char, denoting the prime field (see 6.4).

base

is a list of elements of F forming a base for F as vector space over the subfield S.

dimension

is the dimension of F as vector space over the subfield S.

isFinite

if present this is true if the field F is finite and false otherwise.

size

if present this is the size of the field F . If F is infinite this holds the string ”infinity”.

galoisGroup

if present this holds the Galois group of F (see 6.7).

Groups

Finitely generated groups and their subgroups are important domains in GAP. They are represented as permutation groups, matrix groups, ag groups or even more complicated constructs as for instance automorphism groups, direct products or semi-direct products where the group elements are represented by records.

Groups are created using Group (see 7.9), they are represented by records that contain important information about the groups. Subgroups are created as subgroups of a given group using Subgroup, and are also represented by records. See 7.6 for details about the distinction between groups and subgroups.

Because this chapter is very large it is split into several parts. Each part consists of several sections.

Note that some functions will only work if the elements of a group are represented in an unique way. This is not true in finitely presented groups, see 22.3 for a list of functions applicable to finitely presented groups.

The first part describes the operations and functions that are available for group elements, e.g., Order (see 7.1). The next part tells your more about the distinction of parent groups and subgroups (see 7.6). The next parts describe the functions that compute subgroups, e.g., SylowSubgroup (7.14), and series of subgroups, e.g., DerivedSeries (see 7.34). The next part describes the functions that compute and test properties of groups, e.g., AbelianInvariants and IsSimple (see 7.43). The next parts describe conjugacy classes of elements and subgroups (see 7.63) and cosets (see 7.77). The next part describes the functions that create new groups, e.g., DirectProduct (see 7.91). The next part describes group homomorphisms, e.g., NaturalHomomorphism (see 7.99). The last part tells you more about the implementation of groups, e.g., it describes the format of group records (see 7.107).

The functions described in this chapter are implemented in the following library files. LIBNAME/"grpelms.g" contains the functions for group elements, LIBNAME/"group.g" contains the dispatcher and default group functions, LIBNAME/"grpcoset.g" contains the functions for cosets and factor groups, LIBNAME/"grphomom.g" implements the group homomorphisms, and LIBNAME/"grpprods.g" implements the group constructions.

7.1 Group Elements

The following sections describe the operations and functions available for group elements (see 7.2, 7.3, 7.4, and 7.5).

Note that group elements usually exist independently of a group, e.g., you can write down two permutations and compute their product without ever defining a group that contains them.

7.2 Comparisons of Group Elements

g = h

g <> h

The equality operator = evaluates to true if the group elements g and h are equal and to false otherwise. The inequality operator <> evaluates to true if the group elements g and h are not equal and to false otherwise.

You can compare group elements with objects of other types. Of course they are never equal. Standard group elements are permutations, ag words and matrices. For examples of generic group elements see for instance 7.92.

g < h

g <= h
g >= h

g > h

The operators <, <=, >= and > evaluate to true if the group element g is strictly less than, less than or equal to, greater than or equal to and strictly greater than the group element h. There is no general ordering on group elements.

Standard group elements may be compared with objects of other types while generic group elements may disallow such a comparison.

7.3 Operations for Group Elements

g * h g / h

The operators * and / evaluate to the product and quotient of the two group elements g and h. The operands must of course lie in a common parent group, otherwise an error is signaled.

g ^h

The operator ^evaluates to the conjugate h^{−1} ∗ g ∗ h of g under h for two group elements elements g and h. The operands must of course lie in a common parent group, otherwise an error is signaled.

g ^i The powering operator ^returns the i-th power of a group element g and an integer i. If i is zero the identity of a parent group of g is returned.

list * g g * list

In this form the operator * returns a new list where each entry is the product of g and the corresponding entry of list. Of course multiplication must be defined between g and each entry of list.

list / g

In this form the operator / returns a new list where each entry is the quotient of g and the corresponding entry of list. Of course division must be defined between g and each entry of list.

Comm( g, h )

Comm returns the commutator g^{−1} ∗h^{−1} ∗g ∗h of two group elements g and h. The operands must of course lie in a common parent group, otherwise an error is signaled.

LeftQuotient( g, h )

LeftQuotient returns the left quotient g^{−1}∗h of two group elements g and h. The operands must of course lie in a common parent group, otherwise an error is signaled.

7.4 IsGroupElement

IsGroupElement( obj )

IsGroupElement returns true if obj , which may be an object of arbitrary type, is a group element, and false otherwise. The function will signal an error if obj is an unbound variable.

    gap> IsGroupElement( 10 );
    false
    gap> IsGroupElement( (11,10) );
    true
    gap> IsGroupElement( IdWord );
    true

7.5 Order

Order( G, g )

Order returns the order of a group element g in the group G.

The order is the smallest positive integer i such that g^{i} is the identity. The order of the identity is one.

    gap> Order( Group( (1,2), (1,2,3,4) ), (1,2,3) );
    3
    gap> Order( Group( (1,2), (1,2,3,4) ), () );

7.6 More about Groups and Subgroups

GAP distinguishs between parent groups and subgroups of parent groups. Each subgroup belongs to a unique parent group. We say that this parent group is the parent of the subgroup. We also say that a parent group is its own parent. Parent groups are constructed by Group and subgroups are constructed by Subgroup. The first argument of Subgroup must be a parent group, i.e., it must not be a subgroup of a parent group, and this parent group will be the parent of the constructed subgroup. Those group functions that take more than one argument require that the arguments have a common parent. Take for instance Normalizer. It takes two arguments, a group G and a group H , and returns the normalizer of H in G. So either G is a parent group, and H is a subgroup of this parent group, or G and H are subgroups of a common parent group P.

gap> s4 := Group( (1,2), (1,2,3,4) );
Group( (1,2), (1,2,3,4) )
gap> c3 := Subgroup( s4, [ (1,2,3) ] );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3) ] )
gap> Normalizer( s4, c3 );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3), (2,3) ] )

# ok, c3 is a subgroup of the parent group s4

gap> a4 := Subgroup( s4, [ (1,2,3), (2,3,4) ] );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3), (2,3,4) ] )
gap> Normalizer( a4, c3 );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3) ] )

# also ok, c3 and a4 are subgroups of the parent group s4

gap> x3 := Group( (1,2,3) );
Group( (1,2,3) )
gap> Normalizer( s4, x3 );
Error, <G> and <H> must have the same parent group

# not ok, s4 is its own parent and x3 is its own parent Those functions that return new subgroups, as with Normalizer above, return this subgroup as a subgroup of the common parent of their arguments. Note especially that the normalizer of c3 in a4 is returned as a subgroup of their common parent group s4, not as a subgroup of a4. It can not be a subgroup of a4, because subgroups must be subgroups of parent groups, and a4 is not a parent group. Of course, mathematically the normalizer is a subgroup of

a4.

Note that a subgroup of a parent group need not be a proper subgroup, as can be seen in the following example.

gap> s4 := Group( (1,2), (1,2,3,4) );
Group( (1,2), (1,2,3,4) )
gap> x4 := Subgroup( s4, [ (1,2,3,4), (3,4) ] );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3,4), (3,4) ] )
gap> Index( s4, x4 );
1

One exception to the rule are functions that construct new groups such as DirectProduct. They accept groups with different parents. If you want rename the function DirectProduct

to OuterDirectProduct.

Another exception is Intersection (see 4.12), which allows groups with different parent groups, it computes the intersection in such cases as if the groups were sets of elements. This is because Intersection is not a group function, but a domain function, i.e., it accepts two (or more) arbitrary domains as arguments.

Whenever you have two subgroups which have different parent groups but have a common supergroup G you can use AsSubgroup (see 7.13) in order to construct new subgroups which have a common parent group G.

    gap> s4 := Group( (1,2), (1,2,3,4) );
    Group( (1,2), (1,2,3,4) )
    gap> x3 := Group( (1,2,3) );
    Group( (1,2,3) )
    gap> Normalizer( s4, x3 );
    Error, <G> and <H> must have the same parent group

# not ok, s4 is its own parent and x3 is its own parent

    gap> c3 := AsSubgroup( s4, x3 );
    Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3) ] )
    gap> Normalizer( s4, c3 );
    Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3), (2,3) ] )

The following sections describe the functions related to this concept (see 7.7, 7.8, 7.9, 7.10, 7.11, 7.12, 7.13).

7.7 IsParent

IsParent( G )

IsParent returns true if G is a parent group, and false otherwise (see 7.6).

7.8 Parent

Parent( U_{1}, ..., U_{n} )

Parent returns the common parent group of its subgroups and parent group arguments.

In case more than one argument is given, all groups must have the same parent group. Otherwise an error is signaled. This can be used to ensure that a collection of given subgroups have a common parent group.

7.9 Group

Group( U )

Let U be a parent group or a subgroup. Group returns a new parent group G which is isomorphic to U . The generators of G need not be the same elements as the generators of U . The default group function uses the same generators, while the ag group function may create new generators along with a new collector.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> s3 := Subgroup( s4, [ (1,2,3), (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,3), (1,2) ] )

gap> Group( s3 ); # same elements
Group( (1,2,3), (1,2) )
gap> s4.1 * s3.1;
(1,3,4,2)
gap> s4 := AgGroup( s4 );
Group( g1, g2, g3, g4 )
gap> a4 := DerivedSubgroup( s4 );
Subgroup( Group( g1, g2, g3, g4 ), [ g2, g3, g4 ] )
gap> a4 := Group( a4 ); # different elements
Group( g1, g2, g3 )
gap> s4.1 * a4.1;
Error, AgWord op: agwords have different groups
Group( list, id )

Group returns a new parent group G generated by group elements g_{1}, ..., g_{n} of list. id must be the identity of this group.

Group( g_{1}, ..., g_{n} )

Group returns a new parent group G generated by group elements g_{1}, ..., g_{n}.

The generators of this new parent group need not be the same elements as g_{1}, ..., g_{n}. The default group function however returns a group record with generators g_{1}, ..., g_{n} and identity id, while the ag group function may create new generators along with a new collector.

gap> s4 := Group( (1,2,3,4), (1,2) );
Group( (1,2,3,4), (1,2) )
gap> z4 := Group( s4.1 ); # same element
Group( (1,2,3,4) )
gap> s4.1 * z4.1;
(1,3)(2,4)
gap> s4 := AgGroup( s4 );
Group( g1, g2, g3, g4 )
gap> z4 := Group( s4.1 * s4.3 ); # different elements
Group( g1, g2 )
gap> s4.1 * z4.1;
Error, AgWord op: agwords have different groups

Let g_{i_{1}}, ..., g_{i_{m}} be the set of nontrivial generators in all four cases. Groups sets record components G.1, ..., G.m to these generators.

7.10 AsGroup

AsGroup( D )

Let D be a domain. AsGroup returns a group G such that the set of elements of D is the same as the set of elements of G if this is possible.

If D is a list of group elements these elements must form a group. Otherwise an error is signaled.

Note that this function returns a parent group or a subgroup of a parent group depending on D. In order to convert a subgroup into a parent group you must use Group (see 7.9).

    gap> s4 := AgGroup( Group( (1,2,3,4), (2,3) ) );
    Group( g1, g2, g3, g4 )
    gap> Elements( last );
    [ IdAgWord, g4, g3, g3*g4, g2, g2*g4, g2*g3, g2*g3*g4, g2^2, g2^2*g4,
      g2^2*g3, g2^2*g3*g4, g1, g1*g4, g1*g3, g1*g3*g4, g1*g2, g1*g2*g4,
      g1*g2*g3, g1*g2*g3*g4, g1*g2^2, g1*g2^2*g4, g1*g2^2*g3,
      g1*g2^2*g3*g4 ]
    gap> AsGroup( last );
    Group( g1, g2, g3, g4 )

The default function GroupOps.AsGroup for a group D returns a copy of D. If D is a subgroup then a subgroup is returned. The default function GroupElementsOps.AsGroup expects a list D of group elements forming a group and uses successively Closure in order to compute a reduced generating set.

7.11 IsGroup

IsGroup( obj )

IsGroup returns true if obj , which can be an object of arbitrary type, is a parent group or a subgroup and false otherwise. The function will signal an error if obj is an unbound variable.

    gap> IsGroup( Group( (1,2,3) ) );
    true
    gap> IsGroup( 1/2 );
    false

7.12 Subgroup

Subgroup( G, L )

Let G be a parent group and L be a list of elements g_{1}, ..., g_{n} of G. Subgroup returns the subgroup U generated by g_{1}, ..., g_{n} with parent group G.

Note that this function is the only group function in which the name Subgroup does not refer to the mathematical terms subgroup and supergroup but to the implementation of groups as subgroups and parent groups. IsSubgroup (see 7.59) is not the negation of IsParent (see 7.7) but decides subgroup and supergroup relations.

Subgroup always binds a copy of L to U.generators, so it is safe to modify L after calling Subgroup because this will not change the entries in U.

Let g_{i_{1}}, ..., g_{i_{m}} be the nontrivial generators. Subgroups binds these generators to U .1, ...,

U .m.
    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> v4 := Subgroup( s4, [ (1,2), (1,2)(3,4) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2), (1,2)(3,4) ] )
    gap> IsParent( v4 );
    false

7.13 AsSubgroup

AsSubgroup( G, U )

Let G be a parent group and U be a parent group or a subgroup with a possibly different parent group, such that the generators g_{1}, ..., g_{n} of U are elements of G. AsSubgroup returns a new subgroup S such that S has parent group G and is generated by g_{1}, ..., g_{n}.

    gap> d8 := Group( (1,2,3,4), (1,2)(3,4) );
    Group( (1,2,3,4), (1,2)(3,4) )
    gap> z := Centre( d8 );
    Subgroup( Group( (1,2,3,4), (1,2)(3,4) ), [ (1,3)(2,4) ] )
    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> Normalizer( s4, AsSubgroup( s4, z ) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (2,4), (1,2,3,4), (1,3)(2,4)
     ] )

7.14 Subgroups

The following sections describe functions that compute certain subgroups of a given group, e.g., SylowSubgroup computes a Sylow subgroup of a group (see 7.16, 7.17, 7.18, 7.19, 7.20, 7.21, 7.22, 7.23, 7.24, 7.25, 7.26, 7.27, 7.28, 7.29, 7.30).

They return group records as described in 7.111 for the computed subgroups. Some functions may not terminate if the given group has an infinite set of elements, while other functions may signal an error in such cases.

Here the term “subgroup” is used in a mathematical sense. But in GAP, every group is either a parent group or a subgroup of a unique parent group. If you compute a Sylow subgroup S of a group U with parent group G then S is a subgroup of U but its parent group is G (see 7.6).

Further sections describe functions that return factor groups of a given group (see 7.31 and 7.33).

7.15 Agemo

Agemo( G, p )

G must be a p-group. Agemo returns the subgroup of G generated by the p.th powers of the elements of G.

    gap> d8 := Group( (1,3)(2,4), (1,2) );
    Group( (1,3)(2,4), (1,2) )
    gap> Agemo( d8, 2 );
    Subgroup( Group( (1,3)(2,4), (1,2) ), [ (1,2)(3,4) ] )

The default function GroupOps.Agemo computes the subgroup of G generated by the p.th powers of the generators of G if G is abelian. Otherwise the function computes the normal closure of the p.th powers of the representatives of the conjugacy classes of G.

7.16 Centralizer

Centralizer( G, x )

Centralizer returns the centralizer of an element x in G where x must be an element of the parent group of G.

The centralizer of an element x in G is defined as the set C of elements c of G such that c and x commute.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> v4 := Centralizer( s4, (1,2) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (3,4), (1,2) ] )

The default function GroupOps.Centralizer uses Stabilizer (see 8.22) in order to compute the centralizer of x in G acting by conjugation.

Centralizer( G, U )

Centralizer returns the centralizer of a group U in G as group record. Note that G and U must have a common parent group.

The centralizer of a group U in G is defined as the set C of elements c of C such c commutes with every element of U .

If G is the parent group of U then Centralizer will set and test the record component

U .centralizer.
    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> v4 := Centralizer( s4, (1,2) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (3,4), (1,2) ] )
    gap> c2 := Subgroup( s4, [ (1,3) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3) ] )
    gap> Centralizer( v4, c2 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [  ] )

The default function GroupOps.Centralizer uses Stabilizer in order to compute successively the stabilizer of the generators of U .

7.17 Centre

Centre( G )

Centre returns the centre of G.

The centre of a group G is defined as the centralizer of G in G.

Note that Centre sets and tests the record component G.centre.

    gap> d8 := Group( (1,2,3,4), (1,2)(3,4) );
    Group( (1,2,3,4), (1,2)(3,4) )
    gap> Centre( d8 );
    Subgroup( Group( (1,2,3,4), (1,2)(3,4) ), [ (1,3)(2,4) ] )

The default group function GroupOps.Centre uses Centralizer (see 7.16) in order to compute the centralizer of G in G.

7.18 Closure

Closure( U , g )

Let U be a group with parent group G and let g be an element of G. Then Closure returns the closure C of U and g as subgroup of G. The closure C of U and g is the subgroup generated by U and g.

    gap> s4 := Group( (1,2,3,4), (1,2 ) );
    Group( (1,2,3,4), (1,2) )
    gap> s2 := Subgroup( s4, [ (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2) ] )
    gap> Closure( s2, (3,4) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2), (3,4) ] )

The default function GroupOps.Closure returns U if U is a parent group, or if g or its inverse is a generator of U , or if the set of elements is known and g is in this set, or if g is trivial. Otherwise the function constructs a new subgroup C which is generated by the generators of U and the element g.

Note that if the set of elements of U is bound to U .elements then GroupOps.Closure computes the set of elements for C and binds it to C.elements.

If U is known to be non-abelian or infinite so is C. If U is known to be abelian the function checks whether g commutes with every generator of U .

Closure( U , S )

Let U and S be two group with a common parent group G. Then Closure returns the subgroup of G generated by U and S.

    gap> s4 := Group( (1,2,3,4), (1,2 ) );
    Group( (1,2,3,4), (1,2) )
    gap> s2 := Subgroup( s4, [ (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2) ] )
    gap> z3 := Subgroup( s4, [ (1,2,3) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,3) ] )
    gap> Closure( z3, s2 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,3), (1,2) ] )

The default function GroupOps.Closure returns the parent of U and S if U or S is a parent group. Otherwise the function computes the closure of U under all generators of S.

Note that if the set of elements of U is bound to U .elements then GroupOps.Closure computes the set of elements for the closure C and binds it to C.elements.

7.19 CommutatorSubgroup

CommutatorSubgroup( G, H )

Let G and H be groups with a common parent group. CommutatorSubgroup returns the commutator subgroup [G, H].

The commutator subgroup of G and H is the group generated by all commutators [g, h] with g ∈ G and h ∈ H .

See also DerivedSubgroup (7.22).
    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> d8 := Group( (1,2,3,4), (1,2)(3,4) );
    Group( (1,2,3,4), (1,2)(3,4) )
    gap> CommutatorSubgroup( s4, AsSubgroup( s4, d8 ) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3)(2,4), (1,3,2) ] )

Let G be generated by g_{1}, ..., g_{n} and H be generated by h_{1}, ..., h_{m}. The normal closure of the subgroup S generated by Comm(g_{i}, h_{j}) for 1 ≤ i ≤ n and 1 ≤ j ≤ m under G and H is the commutator subgroup of G and H (see [Hup67]). The default function GroupOps.CommutatorSubgroup returns the normal closure of S under the closure of G and H .

7.20 ConjugateSubgroup

ConjugateSubgroup( U , g )

ConjugateSubgroup returns the subgroup U^{g} conjugate to U under g, which must be an element of the parent group of G.

If present, the flags U .isAbelian, U .isCyclic, U .isElementaryAbelian, U .isFinite,
U .isNilpotent, U .isPerfect, U .isSimple, U .isSolvable, and U .size are copied to

U^{g}.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> c2 := Subgroup( s4, [ (1,2)(3,4) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2)(3,4) ] )
    gap> ConjugateSubgroup( c2, (1,3) );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,4)(2,3) ] )

The default function GroupOps.ConjugateSubgroup returns U if the set of elements of U is known and g is an element of this set or if g is a generator of U . Otherwise it conjugates the generators of U with g.

If the set of elements of U is known the default function also conjugates and binds it to the conjugate subgroup.

7.21 Core

Core( S, U )

Let S and U be groups with a common parent group G. Then Core returns the core of U under conjugation of S.

The core of a group U under a group S Core_{S}(U ) is the intersection ⋂_{s∈S} U^{s} of all groups conjugate to U under conjugation by elements of S.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> s4.name := "s4";;
    gap> d8 := Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] );

    Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] )
    gap> Core( s4, d8 );
    Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] )
    gap> Core( d8, s4 );
    s4

The default function GroupOps.Core starts with U and replaces U with the intersection of U and a conjugate subgroup of U under a generator of G until the subgroup is normalized by G.

7.22 DerivedSubgroup

DerivedSubgroup( G )

DerivedSubgroup returns the derived subgroup G′ = [G, G] of G.

The derived subgroup of G is the group generated by all commutators [g, h] with g, h ∈ G.

Note that DerivedSubgroup sets and tests G.derivedSubgroup. CommutatorSubgroup

(see 7.19) allows you to compute the commutator group of two subgroups.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> DerivedSubgroup( s4 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3,2), (2,4,3) ] )

Let G be generated by g_{1}, ..., g_{n}. Then the default function GroupOps.DerivedSubgroup returns the normal closure of S under G where S is the subgroup of G generated by Comm(g_{i}, g_{j}) for 1 ≤ j < i ≤ n.

7.23 FittingSubgroup

FittingSubgroup( G )

FittingSubgroup returns the Fitting subgroup of G.

The Fitting subgroup of a group G is the biggest nilpotent normal subgroup of G.

    gap> s4;
    Group( (1,2,3,4), (1,2) )
    gap> FittingSubgroup( s4 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2)(3,4), (1,4)(2,3) ] )
    gap> IsNilpotent( last );
    true

Let G be a finite group. Then the default group function GroupOps.FittingSubgroup computes the subgroup of G generated by the cores of the Sylow subgroups in G.

7.24 NormalClosure

NormalClosure( S, U )

Let S and U be groups with a common parent group G. Then NormalClosure returns the normal closure of U under S as a subgroup of G.

The normal closure N of a group U under the action of a group S is the smallest subgroup in G that contains U and is invariant under conjugation by elements of S. Note that N is independent of G.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> s4.name := "s4";;
    gap> d8 := Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] );
    Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] )
    gap> NormalClosure( s4, d8 );
    Subgroup( s4, [ (1,2,3,4), (1,2)(3,4), (1,3,4,2) ] )
    gap> last = s4;
    true

7.25 NormalIntersection

NormalIntersection( N , U )

Let N and U be two subgroups with a common parent group. NormalIntersection returns the intersection in case U normalizes N .

Depending on the domain this may be faster than the general intersection algorithm (see

4.12). The default function GroupOps.NormalIntersection however uses Intersection.

7.26 Normalizer

Normalizer( S, U )

Let S and U be groups with a common parent group G. Then Normalizer returns the normalizer of U in S.

The normalizer N_{S}(U ) of U in S is the biggest subgroup of S which leaves U invariant under conjugation.

If S is the parent group of U then Normalizer sets and tests U .normalizer.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> c2 := Subgroup( s4, [ (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2) ] )
    gap> Normalizer( s4, c2 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (3,4), (1,2) ] )

The default function GroupOps.Normalizer uses Stabilizer (see 8.22) in order to compute the stabilizer of U in S acting by conjugation (see 7.20).

7.27 PCore

PCore( G, p )

PCore returns the p-core of the finite group G for a prime p.

The p-core is the largest normal subgroup whose size is a power of p. This is the core of the Sylow-p-subgroups (see 7.21 and 7.29).

Note that PCore sets and tests G.pCores[ p ].

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> PCore( s4, 2 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3)(2,4), (1,4)(2,3) ] )
    gap> PCore( s4, 3 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [  ] )

The default function GroupOps.PCore computes the p-core as the core of a Sylow-p-subgroup (see 7.21 and 7.29).

7.28 Radical

Radical( G )

Radical returns the radical of the finite group G. The radical is the largest normal solvable subgroup of G.

    gap> g := Group( (1,5), (1,5,6,7,8)(2,3,4) );
    Group( (1,5), (1,5,6,7,8)(2,3,4) )
    gap> Radical( g );
    Subgroup( Group( (1,5), (1,5,6,7,8)(2,3,4) ), [ (2,3,4) ] )

The default function GroupOps.Radical tests if G is solvable and signals an error if not.

7.29 SylowSubgroup

SylowSubgroup( G, p )

SylowSubgroup returns a Sylow-p-subgroup of the finite group G for a prime p. Let p be a prime and G be a finite group of order p^{n}m where m is relative prime to p. Then by Sylow’s theorem there exists at least one subgroup S of G of order p^{n}.

Note that SylowSubgroup sets and tests G.sylowSubgroups[ p ].
    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> SylowSubgroup( s4, 2 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (2,3), (1,4)(2,3), (1,2)(3,4)
     ] )
    gap> SylowSubgroup( s4, 3 );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (2,3,4) ] )

The default function GroupOps.SylowSubgroup computes the set of elements of p power order of G, starts with such an element of maximal order and computes the closure (see 7.18) with normalizing elements of p power order until a Sylow group is found.

7.30 TrivialSubgroup

TrivialSubgroup( U )

Let U be a group with parent group G. Then TrivialSubgroup returns the trivial subgroup T of U . Note that the parent group of T is G not U (see 7.14). The default function GroupOps.TrivialSubgroup binds the set of elements of U , namely [U .identity], to T.elements,

7.31 FactorGroup

FactorGroup( G, N )

FactorGroup returns the factor group G/N where N must be a normal subgroup of G (see 7.55). This is the same as G / N (see 7.110).

NaturalHomomorphism returns the natural homomorphism from G (or a subgroup thereof) onto the factor group (see 7.103).

It is not specified how the factor group N is represented.

    gap> a4 := Group( (1,2,3), (2,3,4) );;  a4.name := "a4";
    "a4"
    gap> v4 := Subgroup(a4,[(1,2)(3,4),(1,3)(2,4)]);;  v4.name := "v4";
    "v4"
    gap> f := FactorGroup( a4, v4 );
    (a4 / v4)
    gap> Size( f );
    3
    gap> Elements( f );
    [ FactorGroupElement( v4, () ), FactorGroupElement( v4, (2,3,4) ),
      FactorGroupElement( v4, (2,4,3) ) ]

If G is the parent group of N , FactorGroup first checks for the knowledge component N .factorGroup. If this component is bound, FactorGroup returns its value. Otherwise,

FactorGroup calls G.operations.FactorGroup( G, N ), remembers the returned value

in N .factorGroup, and returns it. If G is not the parent group of N , FactorGroup calls

G.operations.FactorGroup( G, N ) and returns this value.

The default function called this way is GroupOps.FactorGroup. It returns the factor group as a group of factor group elements (see 7.32). Look under FactorGroup in the index to see for which groups this function is overlaid.

7.32 FactorGroupElement

FactorGroupElement( N , g )

FactorGroupElement returns the coset N * g as a group element. It is not tested whether g normalizes N , but g must be an element of the parent group of N .

Factor group elements returned by FactorGroupElement are represented by records. Those records contain the following components.

isGroupElement

contains true.

isFactorGroupElement

contains true.

element

contains a right coset of N (see 7.79).

domain
     contains FactorGroupElements (see 4.5).

operations

contains the operations record FactorGroupElementOps.

All operations for group elements (see 7.3) are available for factor group elements, e.g., two factor group elements can be multiplied (provided that they have the same subgroup N ).

    gap> a4 := Group( (1,2,3), (2,3,4) );;  a4.name := "a4";;
    gap> v4 := Subgroup(a4,[(1,2)(3,4),(1,3)(2,4)]);;  v4.name := "v4";;
    gap> x := FactorGroupElement( v4, (1,2,3) );
    FactorGroupElement( v4, (2,4,3) )
    gap> y := FactorGroupElement( v4, (2,3,4) );
    FactorGroupElement( v4, (2,3,4) )
    gap> x * y;
    FactorGroupElement( v4, () )

7.33 CommutatorFactorGroup

CommutatorFactorGroup( G )

CommutatorFactorGroup returns a group isomorphic to G/G′ where G′ is the derived subgroup of G (see 7.22).

    gap> s4 := AgGroup( Group( (1,2,3,4), (1,2) ) );
    Group( g1, g2, g3, g4 )
    gap> CommutatorFactorGroup( s4 );
    Group( g1 )
The default group function GroupOps.CommutatorFactorGroup uses DerivedSubgroup (see

7.22) and FactorGroup (see 7.31) in order to compute the commutator factor group.

7.34 Series of Subgroups

The following sections describe functions that compute and return series of subgroups of a given group (see 7.35, 7.39, 7.41, and 7.42). The series are returned as lists of subgroups of the group (see 7.6).

These functions print warnings if the argument is an infinite group, because they may run forever.

7.35 DerivedSeries

DerivedSeries( G )

DerivedSeries returns the derived series of G.

The derived series is the series of iterated derived subgroups. The group G is solvable if and only if this series reaches {1} after finitely many steps.

Note that this function does not terminate if G is an infinite group with derived series of infinite length.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> DerivedSeries( s4 );

[ Group( (1,2,3,4), (1,2) ), Subgroup( Group( (1,2,3,4), (1,2) ),
    [ (1,3,2), (2,4,3) ] ), Subgroup( Group( (1,2,3,4), (1,2) ),
    [ (1,4)(2,3), (1,2)(3,4) ] ),
  Subgroup( Group( (1,2,3,4), (1,2) ), [  ] ) ]

The default function GroupOps.DerivedSeries uses DerivedSubgroup (see 7.22) in order to compute the derived series of G.

7.36 CompositionSeries

CompositionSeries( G )

CompositionSeries returns a composition series of G as list of subgroups.

gap> s4 := SymmetricGroup( 4 );
Group( (1,4), (2,4), (3,4) )
gap> s4.name := "s4";;
gap> CompositionSeries( s4 );
[ Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4), (1,2,4), (1,4) ] ),
  Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4), (1,2,4) ] ),
  Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] ),
  Subgroup( s4, [ (1,2)(3,4) ] ), Subgroup( s4, [  ] ) ]
gap> d8 := SylowSubgroup( s4, 2 );
Subgroup( s4, [ (1,2), (3,4), (1,3)(2,4) ] )
gap> CompositionSeries( d8 );
[ Subgroup( s4, [ (3,4), (1,2), (1,3)(2,4) ] ),
  Subgroup( s4, [ (3,4), (1,2) ] ), Subgroup( s4, [ (3,4) ] ),
  Subgroup( s4, [  ] ) ]

Note that there is no default function. GroupOps.CompositionSeries signals an error if called.

7.37 ElementaryAbelianSeries

ElementaryAbelianSeries( G )

Let G be a solvable group (see 7.58). Then the functions returns a normal series G = E_{0}, E_{1}, ..., E_{n} = {1} of G such that the factor groups E_{i}/E_{i+1} are elementary abelian groups.

gap> s5 := SymmetricGroup( 5 );; s5.name := "s5";;
gap> s4 := Subgroup( s5, [ (2,3,4,5), (2,3) ] );
Subgroup( s5, [ (2,3,4,5), (2,3) ] )
gap> ElementaryAbelianSeries( s4 );
[ Subgroup( s5, [ (2,3)(4,5), (2,5)(3,4), (2,3,5), (2,3,4,5) ] ),
  Subgroup( s5, [ (2,3)(4,5), (2,5)(3,4), (2,3,5) ] ),
  Subgroup( s5, [ (2,3)(4,5), (2,5)(3,4) ] ), Subgroup( s5, [  ] ) ]
The default function GroupOps.ElementaryAbelianSeries uses AgGroup (see 24.26) in

order to convert G into an isomorphic ag group and computes the elementary abelian series in this group. (see 24.9).

7.38 JenningsSeries

JenningsSeries( G, p )

JenningsSeries returns the Jennings series of a p-group G. The Jennings series of a p-group G is defined as follows. S_{1} = G and S_{n} = [S_{n−1}, G]S_{i}^{p} where i is the smallest integer equal or greater than n/p. The length l of S is the smallest integer such that S_{l} = {1}. Note that S_{n} = S_{n+1} is possible.

    gap> G := CyclicGroup( AgWords, 27 );
    Group( c27_1, c27_2, c27_3 )
    gap> G.name := "G";;
    gap> JenningsSeries( G );
    [ G, Subgroup( G, [ c27_2, c27_3 ] ),
      Subgroup( G, [ c27_2, c27_3 ] ), Subgroup( G, [ c27_3 ] ),
      Subgroup( G, [ c27_3 ] ), Subgroup( G, [ c27_3 ] ),
      Subgroup( G, [ c27_3 ] ), Subgroup( G, [ c27_3 ] ),
      Subgroup( G, [ c27_3 ] ), Subgroup( G, [  ] ) ]

7.39 LowerCentralSeries

LowerCentralSeries( G )

LowerCentralSeries returns the lower central series of G as a list of group records. The lower central series is the series defined by S_{1} = G and S_{i} = [G, S_{i−1}]. The group G is nilpotent if this series reaches {1} after finitely many steps. Note that this function may not terminate if G is an infinite group. LowerCentralSeries sets and tests the record component G.lowerCentralSeries in the group record of G.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> LowerCentralSeries( s4 );
    [ Group( (1,2,3,4), (1,2) ), Subgroup( Group( (1,2,3,4), (1,2) ),
        [ (1,3,2), (2,4,3) ] ) ]
The default group function GroupOps.LowerCentralSeries uses CommutatorSubgroup (see

7.19) in order to compute the lower central series of G.

7.40 PCentralSeries

PCentralSeries( G, p )

PCentralSeries returns the p-central series of a group G for a prime p. The p-central series of a group G is defined as follows. S_{1} = G and S_{i+1} is set to [G, S_{i}] ∗ S_{i}^{p}. The length of this series is n, where n = max{i; S_{i} > S_{i+1}}.

    gap> s4 := Group( (1,2,3,4), (1,2) );; s4.name := "s4";;
    gap> PCentralSeries( s4, 3 );
    [ s4 ]
    gap> PCentralSeries( s4, 2 );
    [ s4, Subgroup( s4, [ (1,2,3), (2,3,4) ] ) ]

7.41 SubnormalSeries

SubnormalSeries( G, U )

Let U be a subgroup of G, then SubnormalSeries returns a subnormal series G = G_{1} > ... > G_{n} of groups such that U is contained in G_{n} and there exists no proper subgroup V between G_{n} and U which is normal in G_{n}. G_{n} is equal to U if and only if U is subnormal in G. Note that this function may not terminate if G is an infinite group.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> c2 := Subgroup( s4, [ (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2) ] )
    gap> SubnormalSeries( s4, c2 );
    [ Group( (1,2,3,4), (1,2) ) ]
    gap> IsSubnormal( s4, c2 );
    false
    gap> c2 := Subgroup( s4, [ (1,2)(3,4) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2)(3,4) ] )
    gap> SubnormalSeries( s4, c2 );
    [ Group( (1,2,3,4), (1,2) ), Subgroup( Group( (1,2,3,4), (1,2) ),
        [ (1,2)(3,4), (1,4)(2,3) ] ),
      Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2)(3,4) ] ) ]
    gap> IsSubnormal( s4, c2 );
    true

The default function GroupOps.SubnormalSeries constructs the subnormal series as follows. G_{1} = G and G_{i+1} is set to the normal closure (see 7.24) of U under G_{i}. The length of the series is n, where n = max{i; G_{i} > G_{i+1}}.

7.42 UpperCentralSeries

UpperCentralSeries( G )

UpperCentralSeries returns the upper central series of G as a list of subgroups. The upper central series is the series S_{n}, ..., S_{0} defined by S_{0} = {1} < G and S_{i}/S_{i−1} = Z(G/S_{i−1}) where n = min{i; S_{i} = S_{i+1}} Note that this function may not terminate if G is an infinite group. UpperCentralSeries sets and tests G.upperCentralSeries in the group record of G.

    gap> d8 := AgGroup( Group( (1,2,3,4), (1,2)(3,4) ) );
    Group( g1, g2, g3 )
    gap> UpperCentralSeries( d8 );
    [ Group( g1, g2, g3 ), Subgroup( Group( g1, g2, g3 ), [ g3 ] ),
      Subgroup( Group( g1, g2, g3 ), [  ] ) ]

7.43 Properties and Property Tests

The following sections describe the functions that computes or test properties of groups (see 7.44, 7.47, 7.48, 7.49, 7.50, 7.51, 7.52, 7.53, 7.54, 7.55, 7.56, 7.57, 7.58, 7.59, 7.60, 7.61).

All tests expect a parent group or subgroup and return true if the group has the property and false otherwise. Some functions may not terminate if the given group has an infinite set of elements. A warning may be printed in such cases.

In addition the set theoretic functions Elements, Size and IsFinite, which are described in chapter 4, can be used for groups. Size (see 4.10) returns the order of a group, this is either a positive integer or the string “infinity”. IsFinite (see 4.9) returns true if a group is finite and false otherwise.

7.44 AbelianInvariants

AbelianInvariants( G )

Let G be an abelian group. Then AbelianInvariants returns the abelian invariants of G as a list of integers.

Let G be a finitely generated abelian group. Then there exist n nontrivial subgroups A_{i} of prime power order p_{i}^{e_{i}} and m infinite cyclic subgroups Z_{j} such that G = A_{1} × ... × A_{n} × Z_{1}... × Z_{m}. The invariants of G are the integers p_{1}^{e_{1}}, ..., p_{n}^{e_{n}} together with m zeros.

Note that AbelianInvariants tests and sets G.abelianInvariants.
    gap> AbelianInvariants( AbelianGroup( AgWords, [2,3,4,5,6,9] ) );
    [ 2, 2, 3, 3, 4, 5, 9 ]

The default function GroupOps.AbelianInvariants needs a finite abelian group G.

Let G be a finite abelian group of order p_{1}^{e_{1}}...p_{n}^{e_{n}} where p_{i} are distinct primes. The default function constructs for every prime p_{i} the series G, G^{p_{i}}, G^{p_{i}^{2}}, ... and computes the abelian invariants using the indices of these groups.

7.45 DimensionsLoewyFactors

DimensionsLoewyFactors( G )

Let G be p-group. Then DimensionsLoewyFactors returns the dimensions c_{i} of the Loewy factors of F_{p}G.

The Loewy series of F_{p}G is defined as follows. Let R be the Jacobson radical of the group ring F_{p}G. The series R^{0} = F_{p}G > R^{1} > ... > R^{l+1} = {1} is the Loewy series. The dimensions c_{i} are the dimensions of R^{i}/R^{i+1}.

    gap> f6 := FreeGroup( 6, "f6" );;
    gap> g := f6 / [ f6.1^3, f6.2^3, f6.3^3, f6.4^3, f6.5^3, f6.6^3,
    >          Comm(f6.3,f6.2)/f6.6^2,  Comm(f6.3,f6.1)/(f6.6*f6.5),
    >          Comm(f6.2,f6.1)/(f6.5*f6.4^2) ];;
    gap> a := AgGroupFpGroup(g);
    Group( f6.1, f6.2, f6.3, f6.4, f6.5, f6.6 )
    gap> DimensionsLoewyFactors(a);
    [ 1, 3, 9, 16, 30, 42, 62, 72, 87, 85, 87, 72, 62, 42, 30, 16, 9,
      3, 1 ]

The default function GroupOps.DimensionsLoewyFactors computes the Jennings series of G and uses Jennings thereom in order to calculate the dimensions of the Loewy factors.

Let G = X_{1} ≥ X_{2} ≥ ... ≥ X_{l} > X_{l+1} = {1} be the Jennings series of G (see 7.38) and let d_{i} be the dimensions of X_{i}/X_{i+1}. Then the Jennings polynomial is

∑_{i=0}^{l} c_{i}x^{i} = ∏_{k=1}^{l}(1 + x^{k} + x^{2k} + ... + x^{(p−1)k})^{d_{k}}.

7.46 Exponent

Exponent( G )

Let G be a finite group. Then Exponent returns the exponent of G. Note that Exponent tests and sets G.exponent.

    gap> Exponent( Group( (1,2,3,4), (1,2) ) );
    12

The default function GroupOps.Exponent computes all elements of G and their orders.

7.47 Factorization

Factorization( G, g )

Let G be a group with generators g_{1}, ..., g_{n} and let g be an element of G. Factorization returns a representation of g as word in the generators of G. The group record of G must have a component G.abstractGenerators which contains a list of n abstract words h_{1}, ..., h_{n}. Otherwise a list of n abstract generators is bound to G.abstractGenerators. The function returns an abstract word h = h_{i_{1}}^{e_{1}} ∗ ... ∗ h_{i_{m}}^{e_{m}} such that g_{i_{1}}^{e_{1}} ∗ ... ∗ g_{i_{m}}^{e_{m}} = g.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> Factorization( s4, (1,2,3) );
    x1^3*x2*x1*x2
    gap> (1,2,3,4)^3 * (1,2) * (1,2,3,4) * (1,2);
    (1,2,3)

The default group function GroupOps.Factorization needs a finite group G. It computes the set of elements of G using a Dimino algorithm, together with a representation of these elements as words in the generators of G.

7.48 Index

Index( G, U )

Let U be a subgroup of G. Then Index returns the index of U in G as an integer. Note that Index sets and checks U .index if G is the parent group of U .

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> Index( s4, DerivedSubgroup( s4 ) );
    2

The default function GroupOps.Index needs a finite group G. It returns the quotient of

Size( G ) and Size( U ).

7.49 IsAbelian

IsAbelian( G )

IsAbelian returns true if the group G is abelian and false otherwise.

A group G is abelian if and only if for every g, h ∈ G the equation g ∗ h = h ∗ g holds.

Note that IsAbelian sets and tests the record component G.isAbelian. If G is abelian it also sets G.centre.

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> IsAbelian( s4 );
    false
    gap> IsAbelian( Subgroup( s4, [ (1,2) ] ) );
    true

The default group function GroupOps.IsAbelian returns true for a group G generated by g_{1}, ..., g_{n} if g_{i} commutes with g_{j} for i > j.

7.50 IsCentral

IsCentral( G, U )

IsCentral returns true if the group G centralizes the group U and false otherwise.

A group G centralizes a group U if and only if for all g ∈ G and for all u ∈ U the equation g ∗ u = u ∗ g holds. Note that U need not to be a subgroup of G but they must have a common parent group.

Note that IsCentral sets and tests U .isCentral if G is the parent group of U .

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> d8 := Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] );;
    gap> c2 := Subgroup( s4, [ (1,3)(2,4) ] );;
    gap> IsCentral( s4, c2 );
    false
    gap> IsCentral( d8, c2 );
    true

The default function GroupOps.IsCentral tests whether G centralizes U by testing whether the generators of G commutes with the generators of U .

7.51 IsConjugate

IsConjugate( G, x, y )

Let x and y be elements of the parent group of G. Then IsConjugate returns true if x is conjugate to y under an element g of G and false otherwise.

    gap> s5 := Group( (1,2,3,4,5), (1,2) );
    Group( (1,2,3,4,5), (1,2) )
    gap> a5 := Subgroup( s5, [ (1,2,3), (2,3,4), (3,4,5) ] );
    Subgroup( Group( (1,2,3,4,5), (1,2) ), [ (1,2,3), (2,3,4), (3,4,5) ] )
    gap> IsConjugate( a5, (1,2,3,4,5), (1,2,3,4,5)^2 );

    false
    gap> IsConjugate( s5, (1,2,3,4,5), (1,2,3,4,5)^2 );
    true

The default function GroupOps.IsConjugate uses Representative (see 4.15) in order to check whether x is conjugate to y under G.

7.52 IsCyclic

IsCyclic( G )

IsCyclic returns true if G is cyclic and false otherwise.

A group G is cyclic if and only if there exists an element g ∈ G such that G is generated by g.

Note that IsCyclic sets and tests the record component G.isCyclic.

    gap> z6 := Group( (1,2,3), (4,5) );;
    gap> IsCyclic( z6 );
    true
    gap> z36 := AbelianGroup( AgWords, [ 9, 4 ] );;
    gap> IsCyclic( z36 );
    true

The default function GroupOps.IsCyclic returns false if G is not an abelian group. Otherwise it computes the abelian invariants (see 7.44) if G is infinite. If G is finite of order p_{1}^{e_{1}}...p_{n}^{e_{n}}, where p_{i} are distinct primes, then G is cyclic if and only if each G^{p_{i}} has index p_{i} in G.

7.53 IsElementaryAbelian

IsElementaryAbelian( G )

IsElementaryAbelian returns true if the group G is an elementary abelian p-group for a prime p and false otherwise.

A p-group G is elementary abelian if and only if for every g, h ∈ G the equations g ∗ h = h ∗ g and g^{p} = 1 hold.

Note that the IsElementaryAbelian sets and tests G.isElementaryAbelian.
    gap> z4 := Group( (1,2,3,4) );;
    gap> IsElementaryAbelian( z4 );
    false
    gap> v4 := Group( (1,2)(3,4), (1,3)(2,4) );;
    gap> IsElementaryAbelian( v4 );
    true

The default function GroupOps.IsElementaryAbelian returns true if G is abelian and for some prime p each generator is of order p.

7.54 IsNilpotent

IsNilpotent( G )

IsNilpotent returns true if the group G is nilpotent and false otherwise.

A group G is nilpotent if and only if the lower central series of G is of finite length and reaches {1}.

Note that IsNilpotent sets and tests the record component G.isNilpotent.

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> IsNilpotent( s4 );
    false
    gap> v4 := Group( (1,2)(3,4), (1,3)(2,4) );;
    gap> IsNilpotent( v4 );
    true

The default group function GroupOps.IsNilpotent computes the lower central series using LowerCentralSeries (see 7.39) in order to check whether G is nilpotent.

If G has an infinite set of elements a warning is given, as this function does not stop if G has a lower central series of infinite length.

7.55 IsNormal

IsNormal( G, U )

IsNormal returns true if the group G normalizes the group U and false otherwise.

A group G normalizes a group U if and only if for every g ∈ G and u ∈ U the element u^{g} is a member of U . Note that U need not be a subgroup of G but they must have a common parent group.

Note that IsNormal tests and sets U .isNormal if G is the parent group of U .

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> d8 := Subgroup( s4, [ (1,2,3,4), (1,2)(3,4) ] );;
    gap> c2 := Subgroup( s4, [ (1,3)(2,4) ] );;
    gap> IsNormal( s4, c2 );
    false
    gap> IsNormal( d8, c2 );
    true

Let G be a finite group. Then the default function GroupOps.IsNormal checks whether the conjugate of each generator of U under each generator of G is an element of U .

If G is an infinite group, then the default function GroupOps.IsNormal checks whether the conjugate of each generator of U under each generator of G and its inverse is an element of U .

7.56 IsPerfect

IsPerfect( G )

IsPerfect returns true if G is a perfect group and false otherwise.

A group G is perfect if G is equal to its derived subgroup. See 7.22.

Note that IsPerfect sets and tests G.isPerfect.

    gap> a4 := Group( (1,2,3), (2,3,4) );
    Group( (1,2,3), (2,3,4) )
    gap> IsPerfect( a4 );
    false
    gap> a5 := Group( (1,2,3), (2,3,4), (3,4,5) );
    Group( (1,2,3), (2,3,4), (3,4,5) )
    gap> IsPerfect( a5 );
    true

The default group function GroupOps.IsPerfect checks for a finite group G the index of G′ (see 7.22) in G. For an infinite group it computes the abelian invariants of the commutator factor group (see 7.44 and 7.33).

7.57 IsSimple

IsSimple( G )

IsSimple returns true if G is simple and false otherwise.

A group G is simple if and only if G and the trivial subgroup are the only normal subgroups of G.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> IsSimple( DerivedSubgroup( s4 ) );
    false
    gap> s5 := Group( (1,2,3,4,5), (1,2) );
    Group( (1,2,3,4,5), (1,2) )
    gap> IsSimple( DerivedSubgroup( s5 ) );
    true

7.58 IsSolvable

IsSolvable( G )

IsSolvable returns true if the group G is solvable and false otherwise.

A group G is solvable if and only if the derived series of G is of finite length and reaches {1}.

Note that IsSolvable sets and tests G.isSolvable.

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> IsSolvable( s4 );
    true

The default function GroupOps.IsSolvable computes the derived series using the function DerivedSeries (see 7.35) in order to see whether G is solvable.

If G has an infinite set of elements a warning is given, as this function does not stop if G has a derived series of infinite length.

7.59 IsSubgroup

IsSubgroup( G, U )

IsSubgroup returns true if U is a subgroup of G and false otherwise. Note that G and U must have a common parent group. This function returns true if and only if the set of elements of U is a subset of the set of elements of G, it is not the inverse of IsParent (see 7.7).

    gap> s6  := Group( (1,2,3,4,5,6), (1,2) );;
    gap> s4 := Subgroup( s6, [ (1,2,3,4), (1,2) ] );;
    gap> z2 := Subgroup( s6, [ (5,6) ] );;
    gap> IsSubgroup( s4, z2 );
    false
    gap> v4 := Subgroup( s6, [ (1,2)(3,4), (1,3)(2,4) ] );;
    gap> IsSubgroup( s4, v4 );
    true

If the elements of G are known, then the default function GroupOps.IsSubgroup checks whether the set of generators of U is a subset of the set of elements of G. Otherwise the function checks whether each generator of U is an element of G using in.

7.60 IsSubnormal

IsSubnormal( G, U )

IsSubnormal returns true if the subgroup U of G is subnormal in G and false otherwise. A subgroup U of G is subnormal if and only if there exists a series of subgroups G = G_{0} > G_{1} > ... > G_{n} = U such that G_{i} is normal in G_{i−1} for all i ∈ {1, ..., n}. Note that U must be a subgroup of G. The function sets and checks U .isSubnormal if G is the parent group of G.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> c2 := Subgroup( s4, [ (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2) ] )
    gap> IsSubnormal( s4, c2 );
    false
    gap> c2 := Subgroup( s4, [ (1,2)(3,4) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2)(3,4) ] )
    gap> IsSubnormal( s4, c2 );
    true

The default function GroupOps.IsSubnormal uses SubnormalSeries (see 7.41) in order to check if U is subnormal in G.

7.61 IsTrivial for Groups

GroupOps.IsTrivial( G )

GroupOps.IsTrivial returns true if G is the trivial group and false otherwise. Note that G is trivial if and only if the component generators of the group record of G is the empty list. It is faster to check this than to call IsTrivial.

7.62 PermutationCharacter

PermutationCharacter( G, U )

computes the permutation character of the operation of G on the cosets of U . The permutation character is returned as list of integers such that the i.th position contains the value of the permutation character on the i.th conjugacy class of G (see 7.64).

The value of the permutation character of U in G on a class c of G is the number of right cosets invariant under the action of an element of c.

    gap> G := SymmetricPermGroup(5);;
    gap> PermutationCharacter( G, SylowSubgroup(G,2) );
    [ 15, 3, 3, 0, 0, 1, 0 ]

For small groups the default function GroupOps.PermutationCharacter calculates the permutation character by inducing the trivial character of U . For large groups it counts the fixed points by examining double cosets of U and the subgroup generated by a class element.

7.63 Conjugacy Classes

The following sections describe how one can compute conjugacy classes of elements and subgroups in a group (see 7.64 and 7.69). Further sections describe how conjugacy classes of elements are created (see 7.65 and 7.66), and how they are implemented (see 7.67 and 7.68). Further sections describe how classes of subgroups are created (see 7.71 and 7.72), and how they are implemented (see 7.73 and 7.74). Another section describes the function that returns a conjugacy class of subgroups as a list of subgroups (see 7.76).

7.64 ConjugacyClasses

ConjugacyClasses( G )

ConjugacyClasses returns a list of the conjugacy classes of elements of the group G. The elements in the list returned are conjugacy class domains as created by ConjugacyClass (see 7.65). Because conjugacy classes are domains, all set theoretic functions can be applied to them (see 4).

    gap> a5 := Group( (1,2,3), (3,4,5) );;  a5.name := "a5";;
    gap> ConjugacyClasses( a5 );
    [ ConjugacyClass( a5, () ), ConjugacyClass( a5, (3,4,5) ),
      ConjugacyClass( a5, (2,3)(4,5) ), ConjugacyClass( a5, (1,2,3,4,5) ),
      ConjugacyClass( a5, (1,2,3,5,4) ) ]

ConjugacyClasses first checks if G.conjugacyClasses is bound. If the component is bound, it returns that value. Otherwise it calls G.operations.ConjugacyClasses( G ), remembers the returned value in G.conjugacyClasses, and returns it.

The default function called this way is GroupOps.ConjugacyClasses. This function takes random elements in G and tests whether such a random element g lies in one of the already known classes. If it does not it adds the new class ConjugacyClass( G, g ) (see 7.65). Also after adding a new class it tests whether any power of the representative gives rise to a new class. It returns the list of classes when the sum of the sizes is equal to the size of G.

7.65 ConjugacyClass

ConjugacyClass( G, g )

ConjugacyClass returns the conjugacy class of the element g in the group G. Signals an error if g is not an element in G. The conjugacy class is returned as a domain, so that all set theoretic functions are applicable (see 4).

gap> a5 := Group( (1,2,3), (3,4,5) );;  a5.name := "a5";;
gap> c := ConjugacyClass( a5, (1,2,3,4,5) );
ConjugacyClass( a5, (1,2,3,4,5) )
gap> Size( c );
12
gap> Representative( c );
(1,2,3,4,5)
gap> Elements( c );
[ (1,2,3,4,5), (1,2,4,5,3), (1,2,5,3,4), (1,3,5,4,2), (1,3,2,5,4),
  (1,3,4,2,5), (1,4,3,5,2), (1,4,5,2,3), (1,4,2,3,5), (1,5,4,3,2),
  (1,5,2,4,3), (1,5,3,2,4) ]
ConjugacyClass calls G.operations.ConjugacyClass( G, g ) and returns that value.

The default function called this way is GroupOps.ConjugacyClass, which creates a conjugacy class record (see 7.68) with the operations record ConjugacyClassOps (see 7.67). Look in the index under ConjugacyClass to see for which groups this function is overlaid.

7.66 IsConjugacyClass

IsConjugacyClass( obj )

IsConjugacyClass returns true if obj is a conjugacy class as created by ConjugacyClass (see 7.65) and false otherwise.

gap> a5 := Group( (1,2,3), (3,4,5) );;  a5.name := "a5";;
gap> c := ConjugacyClass( a5, (1,2,3,4,5) );
ConjugacyClass( a5, (1,2,3,4,5) )
gap> IsConjugacyClass( c );
true
gap> IsConjugacyClass(
>       [ (1,2,3,4,5), (1,2,4,5,3), (1,2,5,3,4), (1,3,5,4,2),
>         (1,3,2,5,4), (1,3,4,2,5), (1,4,3,5,2), (1,4,5,2,3),
>         (1,4,2,3,5), (1,5,4,3,2), (1,5,2,4,3), (1,5,3,2,4) ] );

false # even though this is as a set equal to c

7.67 Set Functions for Conjugacy Classes

As mentioned above, conjugacy classes are domains, so all domain functions are applicable to conjugacy classes (see 4). This section describes the functions that are implemented especially for conjugacy classes. Functions not mentioned here inherit the default functions mentioned in the respective sections.

In the following let C be the conjugacy class of the element g in the group G.

Elements( C )

The elements of the conjugacy class C are computed as the orbit of g under G, where G operates by conjugation.

Size( C )

The size of the conjugacy class C is computed as the index of the centralizer of g in G.

h in C

To test whether an element h lies in C , in tests whether there is an element of G that takes h to g. This is done by calling RepresentativeOperation(G,h,g) (see 8.23).

Random( C )

A random element of the conjugacy class C is computed by conjugating g with a random element of G.

7.68 Conjugacy Class Records

A conjugacy class C of an element g in a group G is represented by a record with the following components.

isDomain

always true.

isConjugacyClass

always true.

group

holds the group G.

representative

holds the representative g.

The following component is optional. It is computed and assigned when the size of a conjugacy class is computed.

centralizer

holds the centralizer of g in G.

7.69 ConjugacyClassesSubgroups

ConjugacyClassesSubgroups( G )

ConjugacyClassesSubgroups returns a list of all conjugacy classes of subgroups of the group G. The elements in the list returned are conjugacy class domains as created by ConjugacyClassSubgroups (see 7.71). Because conjugacy classes are domains, all set theoretic functions can be applied to them (see 4).

In fact, ConjugacyClassesSubgroups computes much more than it returns, for it calls (indi-

rectly via the function G.operations.ConjugacyClassesSubgroups( G )) the Lattice

command (see 7.70), constructs the whole subgroup lattice of G, stores it in the record component G.lattice, and finally returns the list G.lattice.classes. This means, in particular, that it will fail if G is non-solvable and its maximal perfect subgroup is not in the built-in catalogue of perfect groups (see the description of the Lattice command 7.70 for details).

gap> # Conjugacy classes of subgroups of S4
gap> s4 := Group( (1,2,3,4), (1,2) );;
gap> s4.name := "s4";;
gap> cl := ConjugacyClassesSubgroups( s4 );
[ ConjugacyClassSubgroups( s4, Subgroup( s4, [  ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (3,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (1,2)(3,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (2,3,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (3,4), (1,2) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4)
     ] ) ), ConjugacyClassSubgroups( s4, Subgroup( s4,
    [ (1,2)(3,4), (1,3,2,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4, [ (2,3,4), (3,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4,
    [ (3,4), (1,2), (1,2)(3,4), (1,3)(2,4) ] ) ),
  ConjugacyClassSubgroups( s4, Subgroup( s4,
    [ (1,2)(3,4), (1,3)(2,4), (2,3,4) ] ) ),
  ConjugacyClassSubgroups( s4, s4 ) ]

Each entry of the resulting list is a domain. As an example, let us take the seventh class in the above list of conjugacy classes of S_{4}.

gap> # Conjugacy classes of subgroups of S4 (continued)
gap> class7 := cl[7];;
gap> # Print the class representative subgroup.
gap> rep7 := Representative( class7 );
Subgroup( s4, [ (1,2)(3,4), (1,3,2,4) ] )
gap> # Print the order of the class representative subgroup.
gap> Size( rep7 );
4
gap> # Print the number of conjugates.
gap> Size( class7 );
3

In the record representing the domain there is an additional component class7 .conjugands which contains a list of conjugating elements.

gap> # Conjugacy classes of subgroups of S4 (continued)
gap> # Print a list of the subgroups in class7.
gap> List( class7.conjugands, c -> rep7^c );
[ Subgroup( s4, [ (1,2)(3,4), (1,3,2,4) ] ),
  Subgroup( s4, [ (1,4)(2,3), (1,2,4,3) ] ),
  Subgroup( s4, [ (1,3)(2,4), (1,4,3,2) ] ) ]

7.70 Lattice

Lattice( G )

Lattice returns the lattice of subgroups of the group G in the form of a record L, say, which contains certain lists with some appropriate information on the subgroups of G and their conjugacy classes. In particular, in its component L.classes, L provides the same list of all conjugacy classes of all subgroups of G as is returned by the ConjugacyClassesSubgroups command (see 7.69). The construction of the subgroup lattice record L of a group G may be very time consuming. Therefore, as soon as L has been computed for the first time, it will be saved as a component G.lattice in the group record G to avoid any duplication of that effort. The underlying routines are a reimplementation of the subgroup lattice routines which have been developed since 1958 by several people in Kiel and Aachen under the supervision of Joachim Neubüser. Their final version, written by Volkmar Felsch in 1984, has been available since then in Cayley (see [BC92]) and has also been used in SOGOS (see [Leh89a]). The current implementation in GAP by Jürgen Mnich is described in [Mni92], a summary of the method and references to all predecessors can be found in [FS84]. The Lattice command invokes the following procedure. In a first step, the solvable residuum P, say, of G is computed and looked up in a built-in catalogue of perfect groups which is given in the file LIBNAME/"lattperf.g". A list of subgroups is read off from that catalogue which contains just one representative of each conjugacy class of perfect subgroups of P and hence at least one representative of each conjugacy class of perfect subgroups of G. Then, starting from the identity subgroup and the conjugacy classes of perfect subgroups, the so called cyclic extension method is used to compute the non-perfect subgroups of G by forming for each class representative all its not yet involved cyclic extensions of prime number index and adding their conjugacy classes to the list. It is clear that this procedure cannot work if the catalogue of perfect groups does not contain a group isomorphic to P. At present, it contains only all perfect groups of order less than 5000 and, in addition, the groups P SL(3, 3), M_{11}, and A_{8}. If the Lattice command is called for a group G with a solvable residuum P not in the catalogue, it will provide an error message. As an example we handle the group SL(2, 19) of order 6840.

    gap> s := [ [4,0], [0,5] ] * Z( 19 )^0;;
    gap> t := [ [4,4], [-9,-4] ] * Z(19)^0;;
    gap> G := Group( s, t );;
    gap> Size( G );
    6840
    gap> Lattice( G );
    Error, sorry, can' t identify the group's solvable residuum

However, if you know the perfect subgroups of G, you can use the Lattice command to compute the whole subgroup lattice of G even if the solvable residuum of G is not in the catalogue. All you have to do in such a case is to create a list of subgroups of G which contains at least one representative of each conjugacy class of proper perfect subgroups of G, attach this list to the group record as a new component G.perfectSubgroups, and then call the Lattice command. The existence of that record component will prevent GAP from looking up the solvable residuum of G in the catalogue. Instead, it will insert the given subgroups into the lattice, leaving it to you to guarantee that in fact all conjugacy classes of proper perfect subgroups are involved. If you miss classes, the resulting lattice will be incomplete, but you will not get any warning. As long as you are aware of this fact, you may use this possibility to compute a sublattice of the subgroup lattice of G without getting the above mentioned error message even if the solvable residuum of G is not in the catalogue. In particular, you will get at least the classes of all proper solvable subgroups of G if you define G.perfectSubgroups to be an empty list. As an example for the computation of the complete lattice of subgroups of a group which is not covered by the catalogue, we handle the Mathieu group M_{12}.

gap> # Define the Mathieu group M12.
gap> a := (2,3,5,7,11,9,8,12,10,6,4);;
gap> b := (3,6)(5,8)(9,11)(10,12);;
gap> c := (1,2)(3,4)(5,9)(6,8)(7,12)(10,11);;
gap> M12 := Group( a, b, c );;
gap> Print( "#I  M12 has order ", Size( M12 ), "\n" );
#I  M12 has order 95040
gap> # Define a list of proper perfect subgroups of M_12 and attach
gap> # it to the group record M12 as component M12.perfectSubgroups.
gap> L2_11a := Subgroup( M12, [ a, b ] );;
gap> M11a   := Subgroup( M12, [ a, b, c*a^-1*b*a*c ] );;
gap> M11b   := Subgroup( M12, [ a, b, c*a*b*a^-1*c ] );;
gap> x      := a*b*a^2;;
gap> y      := a*c*a^-1*b*a*c*a^6;;
gap> A6a    := Subgroup( M12, [ x, y ] );;
gap> A5c    := Subgroup( M12, [ x*y, x^3*y^2*x^2*y ] );;
gap> x      := a^2*b*a;;
gap> y      := a^6*c*a*b*a^-1*c*a;;
gap> A6b    := Subgroup( M12, [ x, y ] );;
gap> A5d    := Subgroup( M12, [ x*y, x^3*y^2*x^2*y ] );;
gap> x      := a;;
gap> y      := b*c*b;;
gap> z      := c;;
gap> L2_11b := Subgroup( M12, [ x, y, z ] );;
gap> A5b    := Subgroup( M12, [ y, x*z ] );;
gap> x      := c;;
gap> y      := b*a^-1*c*a*b;;
gap> z      := a^2*b*a^-1*c*a*b*a^-2;;
gap> A5a    := Subgroup( M12, [ (x*z)^2, (y*z)^2 ] );;
gap> M12.perfectSubgroups := [
>   L2_11a, L2_11b, M11a, M11b, A6a, A6b, A5a, A5b, A5c, A5d ];;
gap> # Now compute the subgroup lattice of M12.
gap> lat := Lattice( M12 );
Lattice( Group( ( 2, 3, 5, 7,11, 9, 8,12,10, 6, 4), ( 3, 6)( 5, 8)
( 9,11)(10,12), ( 1, 2)( 3, 4)( 5, 9)( 6, 8)( 7,12)(10,11) ) )

The Lattice command returns a record which represents a very complicated structure.

gap> # Subgroup lattice of M12 (continued)
gap> RecFields( lat );
[ "group", "classes", "queue", "externalGroups", "calculatedGroups",
  "extensionGroups", "queueGroups", "classGroups", "method",
  "statistics", "isLattice", "isSubgroupLattice", "operations",

  "printLevel" ]

Probably the most important component of the lattice record is the list lat.classes. Its elements are domains. They are described in section 7.69. We can use this list, for instance, to print the number of conjugacy classes of subgroups and the number of subgroups of M_{12}.

gap> # Subgroup lattice of M12 (continued)
gap> n1 := Length( lat.classes );;
gap> n2 := Sum( [ 1 .. n1 ], i -> Size( lat.classes[i] ) );;
gap> Print( "#I  M12 has ", n1, " classes of altogether ", n2,
>   " subgroups\n" );
#I  M12 has 147 classes of altogether 214871 subgroups

It would not make sense to get all components of a subgroup lattice record printed in full detail whenever we ask GAP to print the lattice. Therefore, as you can see in the above example, the default printout is just an expression of the form ”Lattice( group )”. However, you can ask GAP to display some additional information in any subsequent printout of the lattice by increasing its individual print level. This print level is stored (in the form of a list of several print flags) in the lattice record and can be changed by an appropriate call of the SetPrintLevel command described below.

The following example demonstrates the effect of the subgroup lattice print level.

gap> # Subgroup lattice of S4
gap> s4 := Group( (1,2,3,4), (1,2) );;
gap> lat := Lattice( s4 );
Lattice( Group( (1,2,3,4), (1,2) ) )

The default subgroup lattice print level is 0. In this case, the print command provides just the expression mentioned above.

gap> # Subgroup lattice of S4 (continued)
gap> SetPrintLevel( lat, 1 );
gap> lat;
#I  Class number   1, Length    1, Order 1
#I  Class number   2, Length    6, Order 2
#I  Class number   3, Length    3, Order 2
#I  Class number   4, Length    4, Order 3
#I  Class number   5, Length    3, Order 4
#I  Class number   6, Length    1, Order 4
#I  Class number   7, Length    3, Order 4
#I  Class number   8, Length    4, Order 6
#I  Class number   9, Length    3, Order 8
#I  Class number  10, Length    1, Order 12
#I  Class number  11, Length    1, Order 24
Lattice( Group( (1,2,3,4), (1,2) ) )

If the print level is set to a value greater than 0, you get, in addition, for each class a kind of heading line. This line contains the position number and the length of the respective class as well as the order of the subgroups in the class.

gap> # Subgroup lattice of S4 (continued)
gap> SetPrintLevel( lat, 2 );
gap> lat;

#I  Class number   1, Length    1, Order 1
#I    Representative [  ]
#I  Class number   2, Length    6, Order 2
#I    Representative [ (3,4) ]
#I    Max  [1,1]
#I  Class number   3, Length    3, Order 2
#I    Representative [ (1,2)(3,4) ]
#I    Max  [1,1]
#I  Class number   4, Length    4, Order 3
#I    Representative [ (2,3,4) ]
#I    Max  [1,1]
#I  Class number   5, Length    3, Order 4
#I    Representative [ (3,4), (1,2) ]
#I    Max  [2,1] [2,4] [3,1]
#I  Class number   6, Length    1, Order 4
#I    Representative [ (1,2)(3,4), (1,3)(2,4) ]
#I    Max  [3,1] [3,2] [3,3]
#I  Class number   7, Length    3, Order 4
#I    Representative [ (1,2)(3,4), (1,3,2,4) ]
#I    Max  [3,1]
#I  Class number   8, Length    4, Order 6
#I    Representative [ (2,3,4), (3,4) ]
#I    Max  [2,1] [2,2] [2,5] [4,1]
#I  Class number   9, Length    3, Order 8
#I    Representative [ (3,4), (1,2), (1,2)(3,4), (1,3)(2,4) ]
#I    Max  [5,1] [6,1] [7,1]
#I  Class number  10, Length    1, Order 12
#I    Representative [ (1,2)(3,4), (1,3)(2,4), (2,3,4) ]
#I    Max  [4,1] [4,2] [4,3] [4,4] [6,1]
#I  Class number  11, Length    1, Order 24
#I    Representative [ (1,2,3,4), (1,2) ]
#I    Max  [8,1] [8,2] [8,3] [8,4] [9,1] [9,2] [9,3] [10,1]
Lattice( Group( (1,2,3,4), (1,2) ) )
gap> PrintClassSubgroupLattice( lat, 8 );
#I  Class number   8, Length    4, Order 6
#I    Representative [ (2,3,4), (3,4) ]
#I    Max  [2,1] [2,2] [2,5] [4,1]

If the subgroup lattice print level is at least 2, GAP prints, in addition, for each class representative subgroup a set of generators and a list of its maximal subgroups, where each maximal subgroup is represented by a pair of integers consisting of its class number and its position number in that class. As this information blows up the output, it may be convenient to restrict it to a particular class. We can do this by calling the PrintClassSubgroupLattice command described below.

gap> # Subgroup lattice of S4 (continued)
gap> SetPrintLevel( lat, 3 );
gap> PrintClassSubgroupLattice( lat, 8 );
#I  Class number   8, Length    4, Order 6

#I    Representative [ (2,3,4), (3,4) ]
#I    Max  [2,1] [2,2] [2,5] [4,1]
#I    Conjugate 2 by (1,4,3,2) is [ (1,2,3), (2,3) ]
#I    Conjugate 3 by (1,2) is [ (1,3,4), (3,4) ]
#I    Conjugate 4 by (1,3)(2,4) is [ (1,2,4), (1,2) ]

If the subgroup lattice print level has been set to at least 3, GAP displays, in addition, for each non-representative subgroup of a class its number in the class, an element which transforms the class representative subgroup into that subgroup, and a set of generators.

gap> # Subgroup lattice of S4 (continued)
gap> SetPrintLevel( lat, 4 );
gap> PrintClassSubgroupLattice( lat, 8 );
#I  Class number   8, Length    4, Order 6
#I    Representative [ (2,3,4), (3,4) ]
#I    Max  [2,1] [2,2] [2,5] [4,1]
#I    Conjugate 2 by (1,4,3,2) is [ (1,2,3), (2,3) ]
#I    Max  [2,2] [2,3] [2,4] [4,2]
#I    Conjugate 3 by (1,2) is [ (1,3,4), (3,4) ]
#I    Max  [2,1] [2,3] [2,6] [4,3]
#I    Conjugate 4 by (1,3)(2,4) is [ (1,2,4), (1,2) ]
#I    Max  [2,4] [2,5] [2,6] [4,4]

A subgroup lattice print level value of at least 4 causes GAP to list the maximal subgroups not only for the class representatives, but also for the other subgroups.

gap> # Subgroup lattice of S4 (continued)
gap> SetPrintLevel( lat, 5 );
gap> PrintClassSubgroupLattice( lat, 8 );
#I  Class number   8, Length    4, Order 6
#I    Representative [ (2,3,4), (3,4) ]
#I    Min  [11,1]
#I    Max  [2,1] [2,2] [2,5] [4,1]
#I    Conjugate 2 by (1,4,3,2) is [ (1,2,3), (2,3) ]
#I    Min  [11,1]
#I    Max  [2,2] [2,3] [2,4] [4,2]
#I    Conjugate 3 by (1,2) is [ (1,3,4), (3,4) ]
#I    Min  [11,1]
#I    Max  [2,1] [2,3] [2,6] [4,3]
#I    Conjugate 4 by (1,3)(2,4) is [ (1,2,4), (1,2) ]
#I    Min  [11,1]
#I    Max  [2,4] [2,5] [2,6] [4,4]

The maximal valid value of the subgroup lattice print level is 5. If it is set, GAP displays not only the maximal subgroups, but also the minimal supergroups of each subgroup. This is the most extensive output of a subgroup lattice record which you can get with the Print command, but of course you can use the RecFields command (see 39.13) to list all record components and then print them out individually in full detail.

If the computation of some subgroup lattice is very time consuming (as in the above example of the Mathieu group M_{12}), you might wish to see some intermediate printout which informs you about the progress of the computation. In fact, you can get such messages by activating a print mechanism which has been inserted into the subgroup lattice routines for diagnostic purposes. All you have to do is to replace the call

    lat := Lattice( M12 );

by the three calls

    InfoLattice1 := Print;
    lat := Lattice( M12 );
    InfoLattice1 := Ignore;

Note, however, that the final numbering of the conjugacy classes of subgroups will differ from the order in which they occur in the intermediate listing because they will be reordered by increasing subgroup orders at the end of the construction.

PrintClassSubgroupLattice( lattice, n )

PrintClassSubgroupLattice prints information on the nth conjugacy class of subgroups in the subgroup lattice lattice. The amount of this information depends on the current value of the subgroup lattice print level of lattice. Note that the default of that print level is zero which means that you will not get any output from the PrintClassSubgroupLattice command without increasing it (see SetPrintLevel below). Examples are given in the above description of the Lattice command.

SetPrintLevel( lattice, level )

SetPrintLevel changes the subgroup lattice print level of the subgroup lattice lattice to the specified value level by an appropriate alteration of the list of print flags which is stored in lattice.printLevel. The argument level is expected to be an integer between 0 and 5.

Examples of the effect of the subgroup lattice print level are given in the above description of the Lattice command.

7.71 ConjugacyClassSubgroups

ConjugacyClassSubgroups( G, U )

ConjugacyClassSubgroups returns the conjugacy class of the subgroup U in the group G. Signals an error if U is not a subgroup of G. The conjugacy class is returned as a domain, so all set theoretic functions are applicable (see 4).

    gap> s5 := Group( (1,2), (1,2,3,4,5) );;  s5.name := "s5";;
    gap> a5 := DerivedSubgroup( s5 );
    Subgroup( s5, [ (1,2,3), (2,3,4), (3,4,5) ] )
    gap> C := ConjugacyClassSubgroups( s5, a5 );
    ConjugacyClassSubgroups( s5, Subgroup( s5,
    [ (1,2,3), (2,3,4), (3,4,5) ] ) )
    gap> Size( C );
    1

Another example of such domains is given in section 7.69.

ConjugacyClassSubgroups calls
G.operations.ConjugacyClassSubgroups( G, U ) and returns this value.

The default function called is GroupOps.ConjugacyClassSubgroups, which creates a conjugacy class record (see 7.74) with the operations record ConjugacyClassSubgroupsOps (see 7.73). Look in the index under ConjugacyClassSubgroups to see for which groups this function is overlaid.

7.72 IsConjugacyClassSubgroups

IsConjugacyClassSubgroups( obj )

IsConjugacyClassSubgroups returns true if obj is a conjugacy class of subgroups as cre-

ated by ConjugacyClassSubgroups (see 7.71) and false otherwise.
    gap> s5 := Group( (1,2), (1,2,3,4,5) );;  s5.name := "s5";;
    gap> a5 := DerivedSubgroup( s5 );
    Subgroup( s5, [ (1,2,3), (2,3,4), (3,4,5) ] )
    gap> c := ConjugacyClassSubgroups( s5, a5 );
    ConjugacyClassSubgroups( s5, Subgroup( s5,
    [ (1,2,3), (2,3,4), (3,4,5) ] ) )
    gap> IsConjugacyClassSubgroups( c );
    true
    gap> IsConjugacyClassSubgroups( [ a5 ] );

false # even though this is as a set equal to c

7.73 Set Functions for Subgroup Conjugacy Classes

As mentioned above, conjugacy classes of subgroups are domains, so all set theoretic functions are also are applicable to conjugacy classes (see 4). This section describes the functions that are implemented especially for conjugacy classes. Functions not mentioned here inherit the default functions mentioned in the respective sections.

Elements( C )

The elements of the conjugacy class C with representative U in the group G are computed by first finding a right transversal of the normalizer of U in G and by computing the conjugates of U with the elements in the right transversal.

V in C

Membership of a group V is tested by comparing the set of contained cyclic subgroups of prime power order of V with those of the groups in C .

Size( C )

The size of the conjugacy class C with representative U in the group G is computed as the index of the normalizer of U in G.

7.74 Subgroup Conjugacy Class Records

Each conjugacy class of subgroups C is represented as a record with at least the following components.

isDomain

always true, because conjugacy classes of subgroups are domains.

isConjugacyClassSubgroups

as well, this entry is always set to true.

group

The group in which the members of this conjugacy class lie. This is not necessarily a parent group; it may also be a subgroup.

representative

The representative of the conjugacy class of subgroups as domain.

The following components are optional and may be bound by some functions which compute or make use of their value.

normalizer
     The normalizer of C .representative in C .group.
normalizerLattice

A special entry that is used when the conjugacy classes of subgroups are computed by ConjugacyClassesSubgroups. It determines the normalizer of the subgroup C .representative. It is a list of length 2. The first element is another conjugacy class D (in the same group), the second is an element g in C .group. The

     normalizer of C .representative is then D.representative ^g.
conjugands

A right transversal of the normalizer of C .representative in C .group. Thus the elements of the conjugacy class C can be computed by conjugating C .representative with those elements. this list, regarding each entry as a conjugating element.

7.75 NormalSubgroups

NormalSubgroups( G )

NormalSubgroups returns a list of all normal subgroups of G. The subgroups are sorted according to their sizes.

    gap> s4 := Group( (1,2,3,4), (1,2) );; s4.name := "s4";;
    gap> NormalSubgroups( s4 );
    [ Subgroup( s4, [  ] ), Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3) ] ),
      Subgroup( s4, [ (2,3,4), (1,3,4) ] ),
      Subgroup( s4, [ (3,4), (1,4), (1,2) ] ) ]

The default function GroupOps.NormalSubgroups uses the conjugacy classes of G and normal closures in order to compute the normal subgroups.

7.76 ConjugateSubgroups

ConjugateSubgroups( G, U )

ConjugateSubgroups returns the orbit of U under G acting by conjugation (see 7.20) as list of subgroups. U and G must have a common parent group.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )

    gap> s3 := Subgroup( s4, [ (1,2,3), (1,2) ] );
    Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,3), (1,2) ] )
    gap> ConjugateSubgroups( s4, s3 );
    [ Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,3), (1,2) ] ),
      Subgroup( Group( (1,2,3,4), (1,2) ), [ (2,3,4), (2,3) ] ),
      Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3,4), (3,4) ] ),
      Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,2,4), (1,4) ] ) ]

7.77 Cosets of Subgroups

The following sections describe how one can compute the right, left, and double cosets of subgroups (see 7.78, 7.83, 7.86). Further sections describe how cosets are created (see 7.79, 7.80, 7.84, 7.85, 7.87, and 7.88), and how they are implemented (see 7.81, 7.82, 7.89, and 7.90).

A coset is a GAP domain, which is different from a group. Altough the set of elements of a group and its trivial coset are equal, the group functions do not take trivial cosets as arguments. A trivial coset must be convert into a group using AsGroup (see 7.10) in order to be used as group.

7.78 RightCosets

Cosets( G, U )
RightCosets( G, U )

Cosets and RightCosets return a list of the right cosets of the subgroup U in the group G. The list is not sorted, i.e., the right cosets may appear in any order. The right cosets are domains as constructed by RightCoset (see 7.79).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> G.name := "G";;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> RightCosets( G, U );
    [ (Subgroup( G, [ (1,2), (3,4) ] )*()),
      (Subgroup( G, [ (1,2), (3,4) ] )*(2,4,3)),
      (Subgroup( G, [ (1,2), (3,4) ] )*(2,3)),
      (Subgroup( G, [ (1,2), (3,4) ] )*(1,2,4,3)),
      (Subgroup( G, [ (1,2), (3,4) ] )*(1,2,3)),
      (Subgroup( G, [ (1,2), (3,4) ] )*(1,3)(2,4)) ]

If G is the parent of U , the dispatcher RightCosets first checks whether U has a component rightCosets. If U has this component, it returns that value. Otherwise it calls

G.operations.RightCosets(G,U ), remembers the returned value in U .rightCosets

and returns it. If G is not the parent of U , RightCosets directly calls the function

G.operations.RightCosets(G,U ) and returns that value.

The default function called this way is GroupOps.RightCosets, which calls Orbit( G, RightCoset( U ), OnRight ). Look up RightCosets in the index, to see for which groups this function is overlaid.

7.79 RightCoset

U * u

Coset( U , u )
RightCoset( U , u )
Coset( U )
RightCoset( U )

The first three forms return the right coset of the subgroup U with the representative u. u must lie in the parent group of U , otherwise an error is signalled. In the last two forms the right coset of U with the identity element of the parent of U as representative is returned. In each case the right coset is returned as a domain, so all domain functions are applicable to right cosets (see chapter 4 and 7.81).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> U * (1,2,3);
    (Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2), (3,4) ] )*(1,2,3))

RightCosets (see 7.78) computes the set of all right cosets of a subgroup in a group. LeftCoset (see 7.84) constructs left cosets.

RightCoset calls U .operations.RightCoset( U , u ) and returns that value.

The default function called this way is GroupOps.RightCoset, which creates a right coset record (see 7.82) with the operations record RightCosetGroupOps (see 7.81). Look up the entries for RightCoset in the index to see for which groups this function is overlaid.

7.80 IsRightCoset

IsRightCoset( obj )
IsCoset( obj )

IsRightCoset and IsCoset return true if the object obj is a right coset, i.e., a record with the component isRightCoset with value true, and false otherwise. Will signal an error if obj is an unbound variable.

    gap> C := Subgroup( Group( (1,2), (1,2,3) ), [ (1,2,3) ] ) * (1,2);;
    gap> IsRightCoset( C );
    true
    gap> D := (1,2) * Subgroup( Group( (1,2), (1,2,3) ), [ (1,2,3) ] );;
    gap> IsCoset( D );

false # note that D is a left coset record,

    gap> C = D;

true # though as a set, it is of course also a right coset

    gap> IsCoset( 17 );
    false

7.81 Set Functions for Right Cosets

Right cosets are domains, thus all set theoretic functions are applicable to cosets (see chapter 4). The following describes the functions that are implemented especially for right cosets.

Functions not mentioned here inherit the default function mentioned in the respective sections.

More technically speaking, all right cosets of generic groups have the operations record RightCosetGroupOps, which inherits its functions from DomainOps and overlays the components mentioned below with more efficient functions.

In the following let C be the coset U * u.

Elements( C )

To compute the proper set of elements of a right coset C the proper set of elements of the subgroup U is computed, each element is multiplied by u, and the result is sorted.

IsFinite( C )

This returns the result of applying IsFinite to the subgroup U .

Size( C )

This returns the result of applying Size to the subgroup U .

C = D

If C and D are both right cosets of the same subgroup, = returns true if the quotient of the representatives lies in the subgroup U , otherwise the test is delegated to DomainOps.=.

h in U

If h is an element of the parent group of U , this returns true if the quotient h / u lies in the subgroup U , otherwise the test is delegated to DomainOps.in.

Intersection( C , D )

If C and D are both right cosets of subgroups U and V with the same parent group the result is a right coset of the intersection of U and V . The representative is found by a random search for a common element. In other cases the computation of the intersection is

delegated to DomainOps.Intersection.

Random( C )

This takes a random element of the subgroup U and returns the product of this element by the representative u.

Print( C )

A right coset C is printed as (U * u) (the parenthesis are used to avoid confusion about the precedence, which could occur if the coset is part of a larger object).

C * v If v is an element of the parent group of the subgroup U , the result is a new right coset of U with representative u * v. Otherwise the result is obtained by multiplying the proper set of elements of C with the element v, which may signal an error.

v * C

The result is obtained by multiplying the proper set of elements of the coset C with the element v, which may signal an error.

7.82 Right Cosets Records

A right coset is represented by a domain record with the following tag components.

isDomain

always true.

isRightCoset

always true.

The right coset is determined by the following identity components, which every right coset record has.

group

the subgroup U of which this right coset is a right coset.

representative

an element of the right coset. It is unspecified which element.

In addition, a right coset record may have the following optional information components.

elements

if present the proper set of elements of the coset.

isFinite

if present this is true if the coset is finite, and false if the coset is infinite. If not present it is not known whether the coset is finite or infinite.

size

if present the size of the coset. Is ”infinity” if the coset is infinite. If not present the size of the coset is not known.

7.83 LeftCosets

LeftCosets( G, U )

LeftCosets returns a list of the left cosets of the subgroup U in the group G. The list is not sorted, i.e., the left cosets may appear in any order. The left cosets are domains as constructed by LeftCosets (see 7.83).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> G.name := "G";;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> LeftCosets( G, U );
    [ ((3,4)*Subgroup( G, [ (1,2), (3,4) ] )),
      ((2,4)*Subgroup( G, [ (1,2), (3,4) ] )),

      ((1,2,4,3)*Subgroup( G, [ (1,2), (3,4) ] )),
      ((1,3,4,2)*Subgroup( G, [ (1,2), (3,4) ] )),
      ((1,3)*Subgroup( G, [ (1,2), (3,4) ] )),
      ((1,3,2,4)*Subgroup( G, [ (1,2), (3,4) ] )) ]

If G is the parent of U , the dispatcher LeftCosets first checks whether U has a component leftCosets. If U has this component, it returns that value. Otherwise LeftCosets calls

G.operations.LeftCosets(G,U ), remembers the returned value in U .leftCosets and

returns it. If G is not the parent of U , LeftCosets calls G.operations.LeftCosets(G,U ) directly and returns that value.

The default function called this way is GroupOps.LeftCosets, which calls RightCosets( G, U ) and turns each right coset U * u into the left coset u^-1 * U . Look up the entries for LeftCosets in the index, to see for which groups this function is overlaid.

7.84 LeftCoset

u * U

LeftCoset( U , u )
LeftCoset( U )

LeftCoset is exactly like RightCoset, except that it constructs left cosets instead of right cosets. So everything that applies to RightCoset applies also to LeftCoset, with right replaced by left (see 7.79, 7.81, 7.82).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> (1,2,3) * U;
    ((1,2,3)*Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2), (3,4) ] ))

LeftCosets (see 7.83) computes the set of all left cosets of a subgroup in a group.

7.85 IsLeftCoset

IsLeftCoset( obj )

IsLeftCoset returns true if the object obj is a left coset, i.e., a record with the component isLeftCoset with value true, and false otherwise. Will signal an error if obj is an unbound variable.

    gap> C := (1,2) * Subgroup( Group( (1,2), (1,2,3) ), [ (1,2,3) ] );;
    gap> IsLeftCoset( C );
    true
    gap> D := Subgroup( Group( (1,2), (1,2,3) ), [ (1,2,3) ] ) * (1,2);;
    gap> IsLeftCoset( D );

false # note that D is a right coset record,

    gap> C = D;

true # though as a set, it is of course also a left coset

    gap> IsLeftCoset( 17 );
    false

IsRightCoset (see 7.80) tests if an object is a right coset.

7.86 DoubleCosets

DoubleCosets( G, U , V )

DoubleCosets returns a list of the double cosets of the subgroups U and V in the group G. The list is not sorted, i.e., the double cosets may appear in any order. The double cosets are domains as constructed by DoubleCoset (see 7.87).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;  U.name := "U";;
    gap> DoubleCosets( G, U, U );
    [ DoubleCoset( U, (), U ), DoubleCoset( U, (2,3), U ),
      DoubleCoset( U, (1,3)(2,4), U ) ]
DoubleCosets calls G.operations.DoubleCoset( G, U , V ) and returns that value.

The default function called this way is GroupOps.DoubleCosets, which takes random elements from G, tests if this element lies in one of the already found double cosets, adds the double coset if this is not the case, and continues this until the sum of the sizes of the found double cosets equals the size of G. Look up DoubleCosets in the index, to see for which groups this function is overlaid.

7.87 DoubleCoset

DoubleCoset( U , u, V )

DoubleCoset returns the double coset with representative u and left group U and right group V . U and V must have a common parent and u must lie in this parent, otherwise an error is signaled. Double cosets are domains, so all domain function are applicable to double cosets (see chapter 4 and 7.89).

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> D := DoubleCoset( U, (1,2,3), U );
    DoubleCoset( Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2), (3,4) ] ),
    (1,2,3), Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2), (3,4) ] ) )
    gap> Size( D );
    16

DoubleCosets (see 7.86) computes the set of all double cosets of two subgroups in a group.

DoubleCoset calls U .operations.DoubleCoset(U ,u,V ) and returns that value.

The default function called this way is GroupOps.DoubleCoset, which creates a double coset record (see 7.90) with the operations record DoubleCosetGroupOps (see 7.89). Look up DoubleCosets in the index to see for which groups this function is overlaid.

7.88 IsDoubleCoset

IsDoubleCoset( obj )

IsDoubleCoset returns true if the object obj is a double coset, i.e., a record with the component isDoubleCoset with value true, and false otherwise. Will signal an error if obj is an unbound variable.

    gap> G := Group( (1,2), (1,2,3,4) );;
    gap> U := Subgroup( G, [ (1,2), (3,4) ] );;
    gap> D := DoubleCoset( U, (1,2,3), U );;
    gap> IsDoubleCoset( D );
    true

7.89 Set Functions for Double Cosets

Double cosets are domains, thus all set theoretic functions are applicable to double cosets (see chapter 4). The following describes the functions that are implemented especially for double cosets. Functions not mentioned here inherit the default functions mentioned in the respective sections.

More technically speaking, double cosets of generic groups have the operations record DoubleCosetGroupOps, which inherits its functions from DomainOps and overlays the components mentioned below with more efficient functions.

Most functions below use the component D.rightCosets that contains a list of right cosets of the left group U whose union is this double coset. If this component is unbound they will compute it by computing the orbit of the right group V on the right coset U * u, where u is the representative of the double coset (see 7.90).

Elements( D )

To compute the proper set of elements the union of the right cosets D.rightCosets is computed.

IsFinite( D )

This returns the result of IsFinite( U ) and IsFinite( V ).

Size( D )

This returns the size of the left group U times the number of cosets in D.rightCosets.

C = D

If C and D are both double cosets with the same left and right groups this returns the result of testing whether the representative of C lies in D. In other cases the test is delegated to

DomainOps.=.

g in D

If g is an element of the parent group of the left and right group of D, this returns true if g lies in one of the right cosets in D.rightCosets. In other cases the the test is delegated

to DomainOps.in.

Intersection( C , D )

If C and D are both double cosets that are equal, this returns C . If C and D are both double cosets with the same left and right groups that are not equal, this returns []. In all other cases the computation is delegated to DomainsOps.Intersection.

Random( D )

This takes a random right coset from D.rightCosets and returns the result of applying Random to this right coset.

Print( D )

This prints the double coset in the form DoubleCoset( U , u, V ).

D * g g * D

Those returns the result of multiplying the proper set of element of D with the element g, which may signal an error.

7.90 Double Coset Records

A double coset is represented by a domain record with the following tag components.

isDomain

always true.

isDoubleCoset

always true.

The double coset is determined by the following identity components, which every double coset must have.

leftGroup

the left subgroup U .

rightGroup

the right subgroup V .

representative

an element of the double coset. It is unspecified which element.

In addition, a double coset record may have the following optional information components.

rightCosets

a list of disjoint right cosets of the left subgroup U , whose union is the double coset.

elements

if present the proper set of elements of the double coset.

isFinite

if present this is true if the double coset is finite and false if the double coset is infinite. If not present it is not known whether the double coset is finite or infinite.

size

if present the size of the double coset. Is ”infinity” if the coset is infinite. If not present the size of the double coset is not known.

7.91 Group Constructions

The following functions construct new parent groups from given groups (see 7.92, 7.94, 7.96 and 7.97).

7.92 DirectProduct

DirectProduct( G_{1}, ..., G_{n} )

DirectProduct returns a group record of the direct product D of the groups G_{1}, ...., G_{n} which need not to have a common parent group, it is even possible to construct the direct product of an ag group with a permutation group.

Note that the elements of the direct product may be just represented as records. But more complicate constructions, as for instance installing a new collector, may be used. The choice of method strongly depends on the type of group arguments.

Embedding( U , D, i )

Let U be a subgroup of G_{i}. Embedding returns a homomorphism of U into D which describes the embedding of U in D.

Projection( D, U , i )

Let U be a supergroup of G_{i}. Projection returns a homomorphism of D into U which describes the projection of D onto G_{i}.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> S4 := AgGroup( s4 );
    Group( g1, g2, g3, g4 )
    gap> D := DirectProduct( s4, S4 );
    Group( DirectProductElement( (1,2,3,4), IdAgWord ),
      DirectProductElement( (1,2), IdAgWord ),
      DirectProductElement( (), g1 ),
      DirectProductElement( (), g2 ),
      DirectProductElement( (), g3 ),
      DirectProductElement( (), g4 ) )
    gap> pr := Projection( D, s4, 1 );;
    gap> Image( pr );
    Group( (1,2,3,4), (1,2) )

7.93 DirectProduct for Groups

GroupOps.DirectProduct( L )

Let L be a list of groups G_{1}, ..., G_{n}. Then a group element g of the direct product D is represented as record containing the following components.

element

a list g_{1} ∈ G_{1}, ..., g_{n} ∈ G_{n} describing g.

domain
     contains GroupElements.
isGroupElement

contains true.

isDirectProductElement

contains true.

operations

contains the operations record DirectProductElementOps (see 4.5).

7.94 SemidirectProduct

SemidirectProduct( G, a, H )

SemidirectProduct returns the semidirect product of G with H . a must be a homomorphism that from G onto a group A that operates on H via the caret (^) operator. A may either be a subgroup of the parent group of H that normalizes H , or a subgroup of the automorphism group of H , i.e., a group of automorphisms (see 7.99).

The semidirect product of G and H is a the group of pairs (g, h) with g ∈ G and h ∈ H, where the product of (g_{1}, h_{1})(g_{2}, h_{2}) is defined as (g_{1}g_{2}, h_{1}^{g_{2}^{a}}h_{2}). Note that the elements (1_{G}, h) form a normal subgroup in the semidirect product.

Embedding( U , S, 1 )

Let U be a subgroup of G. Embedding returns the homomorphism of U into the semidirect product S where u is mapped to (u,1).

Embedding( U , S, 2 )

Let U be a subgroup of H . Embedding returns the homomorphism of U into the semidirect product S where u is mapped to (1,u).

Projection( S, G, 1 )

Projection returns the homomorphism of S onto G, where (g,h) is mapped to g.

Projection( S, H , 2 )

Projection returns the homomorphism of S onto H , where (g,h) is mapped to h.

It is not specified how the elements of the semidirect product are represented. Thus Embedding and Projection are the only general possibility to relate G and H with the semidirect product.

    gap> s4 := Group( (1,2), (1,2,3,4) );;  s4.name := "s4";;
    gap> s3 := Subgroup( s4, [ (1,2), (1,2,3) ] );; s3.name := "s3";;
    gap> a4 := Subgroup( s4, [ (1,2,3), (2,3,4) ] );;  a4.name := "a4";;
    gap> a := IdentityMapping( s3 );;
    gap> s := SemidirectProduct( s3, a, a4 );
    Group( SemidirectProductElement( (1,2), (1,2), () ),
      SemidirectProductElement( (1,2,3), (1,2,3), () ),
      SemidirectProductElement( (), (), (1,2,3) ),

      SemidirectProductElement( (), (), (2,3,4) ) )
    gap> Size( s );
    72

Note that the three arguments of SemidirectProductElement are the element g, its image under a, and the element h.

SemidirectProduct calls the function G.operations.SemidirectProduct with the argu-

ments G, a, and H , and returns the result.

The default function called this way is GroupOps.SemidirectProduct. This function constructs the semidirect product as a group of semidirect product elements (see 7.95). Look in the index under SemidirectProduct to see for which groups this function is overlaid.

7.95 SemidirectProduct for Groups

The function GroupOps.SemidirectProduct constructs the semidirect product as a group of semidirect product elements. In the following let G, a, and H be the arguments of

SemidirectProduct.

Each such element (g,h) is represented by a record with the following components.

element

the list [ g, h ].

automorphism

contains the image of g under a.

isGroupElement

always true.

isSemidirectProductElement

always true.

domain
     contains GroupElements.
operations

contains the operations record SemidirectProductOps.

The operations of semidirect product elements in done in the obvious way.

7.96 SubdirectProduct

SubdirectProduct( G1 , G2 , h1 , h2 )

SubdirectProduct returns the subdirect product of the groups G1 and G2 . h1 and h2 must be homomorphisms from G1 and G2 into a common group H .

The subdirect product of G_{1} and G_{2} is the subgroup of the direct product of G_{1} and G_{2} of those elements (g_{1}, g_{2}) with g_{1}^{h_{1}} = g_{2}^{h_{2}}. This subgroup is generated by the elements (g_{1}, x_{g_{1}}), where g_{1} loops over the generators of G_{1} and x_{g_{1}} ∈ G_{2} is an arbitrary element such that g_{1}^{h_{1}} = x_{g_{1}}^{h_{2}} together with the element (1_{G}, k_{2}) where k_{2} loops over the generators of the kernel of h_{2}.

Projection( S, G1 , 1 )

Projection returns the projection of S onto G1 , where (g1 ,g2 ) is mapped to g1 .

Projection( S, G2 , 2 )

Projection returns the projection of S onto G2 , where (g1 ,g2 ) is mapped to g2 .

It is not specified how the elements of the subdirect product are represented. Therefor Projection is the only general possibility to relate G1 and G2 with the subdirect product.

gap> s3 := Group( (1,2,3), (1,2) );;
gap> c3 := Subgroup( s3, [ (1,2,3) ] );;
gap> x1 := Operation( s3, Cosets( s3, c3 ), OnRight );;
gap> h1 := OperationHomomorphism( s3, x1 );;
gap> d8 := Group( (1,2,3,4), (2,4) );;
gap> c4 := Subgroup( d8, [ (1,2,3,4) ] );;
gap> x2 := Operation( d8, Cosets( d8, c4 ), OnRight );;
gap> h2 := OperationHomomorphism( d8, x2 );;
gap> s := SubdirectProduct( s3, d8, h1, h2 );
Group( (1,2,3), (1,2)(5,7), (4,5,6,7) )
gap> Size( s );
24
SubdirectProduct calls the function G1 .operations.SubdirectProduct with the argu-

ments G1 , G2 , h1 , and h2 .

The default function called this way is GroupOps.SubdirectProduct. This function constructs the subdirect product as a subgroup of the direct product. The generators for this subgroup are computed as described above.

7.97 WreathProduct

WreathProduct( G, H )
WreathProduct( G, H , α )

In the first form of WreathProduct the right regular permutation representation of H on its elements is used as the homomorphism α. In the second form α must be a homomorphism of H into a permutation group. Let d be the degree of the range of α. Then WreathProduct returns the wreath product of G by H with respect to α, that is the semi-direct product of the direct product of d copies of G which are permuted by H through application of α to H .

gap> s3 := Group( (1,2,3), (1,2) );
Group( (1,2,3), (1,2) )
gap> z2 := CyclicGroup( AgWords, 2 );
Group( c2 )
gap> f := IdentityMapping( s3 );
IdentityMapping( Group( (1,2,3), (1,2) ) )
gap> w := WreathProduct( z2, s3, f );
Group( WreathProductElement( c2, IdAgWord, IdAgWord, (), () ),
  WreathProductElement( IdAgWord, c2, IdAgWord, (), () ),
  WreathProductElement( IdAgWord, IdAgWord, c2, (), () ),
  WreathProductElement( IdAgWord, IdAgWord, IdAgWord, (1,2,3), (1,2,3)
   ),

      WreathProductElement( IdAgWord, IdAgWord, IdAgWord, (1,2), (1,2) ) )
    gap> Factors( Size( w ) );
    [ 2, 2, 2, 2, 3 ]

7.98 WreathProduct for Groups

GroupOps.WreathProduct( G, H , α )

Let d be the degree of α.range. A group element of the wreath product W is represented as a record containing the following components.

element

a list of d elements of G followed by an element h of H .

permutation

the image of h under α.

domain
     contains GroupElements.
isGroupElement

contains true.

isWreathProductElement

contains true.

operations

contains the operations record WreathProductElementOps (see 4.5).

7.99 Group Homomorphisms

Since groups is probably the most important category of domains in GAP group homomorphisms are probably the most important homomorphisms (see chapter 37) A group homomorphism ϕ is a mapping that maps each element of a group G, called the source of ϕ, to an element of another group H, called the range of ϕ, such that for each pair x, y ∈ G we have (xy)^{ϕ} = x^{ϕ}y^{ϕ}. Examples of group homomorphisms are the natural homomorphism of a group into a factor group (see 7.103) and the homomorphism of a group into a symmetric group defined by an operation (see 8.19). Look under group homomorphisms in the index for a list of all available group homomorphisms. Since group homomorphisms are just a special case of homomorphisms, all functions described in chapter 37 are applicable to all group homomorphisms, e.g., the function to test if a homomorphism is an automorphism (see 37.6). More general, since group homomorphisms are just a special case of mappings all functions described in chapter 36 are also applicable, e.g., the function to compute the image of an element under a group homomorphism (see 36.8). The following sections describe the functions that test whether a mapping is a group homomorphism (see 7.100), compute the kernel of a group homomorphism (see 7.101), how the general mapping functions are implemented for group homomorphisms (see 7.102), the natural homomorphism of a group onto a factor group (see 7.103), homomorphisms by conjugation (see 7.104, 7.105), and the most general group homomorphism, which is defined by simply specifying the images of a set of generators (see 7.106).

7.100 IsGroupHomomorphism

IsGroupHomomorphism( map )

IsGroupHomomorphism returns true if the function map is a group homomorphism and false otherwise. Signals an error if map is a multi value mapping.

A mapping map is a group homomorphism if its source G and range H are both groups and if for every pair of elements x, y ∈ G it holds that (xy)^{map} = x^{map}y^{map}.

    gap> s4 := Group( (1,2), (1,2,3,4) );;
    gap> v4 := Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] );;
    gap> phi := NaturalHomomorphism( s4, s4/v4 );;
    gap> IsGroupHomomorphism( phi );
    true
    gap> IsHomomorphism( phi );

true # since the source is a group this is equivalent to the above

    gap> IsGroupHomomorphism( FrobeniusAutomorphism( GF(16) ) );

false # it is a field automorphism

IsGroupHomomorphism first tests if the flag map.isGroupHomomorphism is bound. If the

flag is bound, IsGroupHomomorphism returns its value. Otherwise it calls

map.source.operations.IsGroupHomomorphism( map ), remembers the returned value

in map.isGroupHomomorphism, and returns it. Note that of course all functions that create group homomorphisms set the flag map.isGroupHomomorphism to true, so that no function is called for those group homomorphisms.

The default function called this way is MappingOps.IsGroupHomomorphism. It computes all the elements of the source of map and for each such element x and each generator y tests whether (xy)^{map} = x^{map}y^{map}. Look under IsHomomorphism in the index to see for which mappings this function is overlaid.

7.101 KernelGroupHomomorphism

KernelGroupHomomorphism( hom )

KernelGroupHomomorphism returns the kernel of the group homomorphism hom as a subgroup of the group hom.source.

The kernel of a group homomorphism hom is the subset of elements x of the source G that are mapped to the identity of the range H, i.e., x^{hom} = H.identity.

    gap> s4 := Group( (1,2), (1,2,3,4) );;
    gap> v4 := Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] );;
    gap> phi := NaturalHomomorphism( s4, s4/v4 );;
    gap> KernelGroupHomomorphism( phi );
    Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2)(3,4), (1,3)(2,4) ] )
    gap> Kernel( phi );
    Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2)(3,4), (1,3)(2,4) ] )

# since the source is a group this is equivalent to the above

    gap> rho := GroupHomomorphismByImages( s4, Group( (1,2) ),
    >                          [ (1,2), (1,2,3,4) ], [ (1,2), (1,2) ] );;
    gap> Kernel( rho );

    Subgroup( Group( (1,2), (1,2,3,4) ), [ (2,3,4), (1,3,4) ] )
KernelGroupHomomorphism first tests if hom.kernelGroupHomomorphism is bound. If it is

bound, KernelGroupHomomorphisms returns that value. Otherwise it calls

hom.operations.KernelGroupHomomorphism( hom ), remembers the returned value in
hom.kernelGroupHomomorphism, and returns it.

The default function for this is MappingOps.KernelGroupHomomorphism, which simply tries random elements of the source of hom, until the subgroup generated by those that map to the identity has the correct size, i.e., Size( hom.source ) / Size( Image( hom ) ). Note that this implies that the image of hom and its size are computed. Look under Kernel in the index to see for which group homomorphisms this function is overlaid.

7.102 Mapping Functions for Group Homomorphisms

This section describes how the mapping functions defined in chapter 36 are implemented for group homomorphisms. Those functions not mentioned here are implemented by the default functions described in the respective sections.

IsInjective( hom )

The group homomorphism hom is injective if the kernel of hom KernelGroupHomomorphism( hom ) (see 7.101) is trivial.

IsSurjective( hom )

The group homomorphism hom is surjective if the size of the image Size( Image( hom ) ) (see 36.8 and below) is equal to the size of the range Size( hom.range ).

hom1 = hom2 The two group homomorphisms hom1 and hom2 are equal if the have the same source and range and if the images of the generators of the source under hom1 and hom2 are equal.

hom1 < hom2 By definition hom1 is smaller than hom2 if either the source of hom1 is smaller than the source of hom2 , or, if the sources are equal, if the range of hom1 is smaller than the range of hom2 , or, if sources and ranges are equal, the image of the smallest element x of the source for that the images are not equal under hom1 is smaller than the image under hom2 . Therefor GroupHomomorphismOps.< first compares the sources and the ranges. For group homomorphisms with equal sources and ranges only the images of the smallest irredundant generating system are compared. A generating system g_{1}, g_{2}, ..., g_{n} is called irredundant if no g_{i} lies in the subgroup generated by g_{1}, ..., g_{i−1}. The smallest irredundant generating system is simply the smallest such generating system with respect to the lexicographical ordering.

Image( hom )
Image( hom, H )
Images( hom, H )

The image of a subgroup under a group homomorphism is computed by computing the images of a set of generators of the subgroup, and the result is the subgroup generated by those images.

PreImages( hom, elm )

The preimages of an element under a group homomorphism are computed by computing a representative with PreImagesRepresentative( hom, elm ) and the result is the coset of Kernel( hom ) containing this representative.

PreImage( hom )
PreImage( hom, H )
PreImages( hom, H )

The preimages of a subgroup under a group homomorphism are computed by computing representatives of the preimages of all the generators of the subgroup, adding the generators of the kernel of hom, and the result is the subgroup generated by those elements.

Look under IsInjective, IsSurjective, equality, ordering, Image, Images, PreImage, and PreImages in the index to see for which group homomorphisms these functions are overlaid.

7.103 NaturalHomomorphism

NaturalHomomorphism( G, F )

NaturalHomomorphism returns the natural homomorphism of the group G into the factor group F . F must be a factor group, i.e., the result of FactorGroup(H ,N ) (see 7.31) or H /N (see 7.110), and G must be a subgroup of H .

Mathematically the factor group H/N consists of the cosets of N, and the natural homomorphism ϕ maps each element h of H to the coset Nh. Note that in GAP the representation of factor group elements is unspecified, but they are never cosets (see 7.80), because cosets are domains and not group elements in GAP. Thus the natural homomorphism is the only connection between a group and one of its factorgroups.

G is the source of the natural homomorphism ϕ, F is its range. Note that because G may be a proper subgroup of the group H of which F is a factor group ϕ need not be surjective, i.e., the image of ϕ may be a proper subgroup of F. The kernel of ϕ is of course the intersection of N and G.

    gap> s4 := Group( (1,2), (1,2,3,4) );;
    gap> v4 := Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] );;
    gap> v4.name := "v4";;
    gap> phi := NaturalHomomorphism( s4, s4/v4 );;
    gap> (1,2,3) ^phi;
    FactorGroupElement( v4, (2,4,3) )
    gap> PreImages( phi, last );
    (v4*(2,4,3))
    gap> (1,2,3) in last;
    true

    gap> rho :=
    >  NaturalHomomorphism( Subgroup( s4, [ (1,2), (1,2,3) ] ), s4/v4 );;
    gap> Kernel( rho );
    Subgroup( Group( (1,2), (1,2,3,4) ), [  ] )
    gap> IsIsomorphism( rho );
    true
NaturalHomomorphism calls
F .operations.NaturalHomomorphism( G, F ) and returns that value.

The default function called this way is GroupOps.NaturalHomomorphism. The homomorphism constructed this way has the operations record NaturalHomomorphismOps. It computes the image of an element g of G by calling FactorGroupElement( N , g ), the preim-

ages of an factor group element f as Coset( Kernel(phi), f .element.representative

), and the kernel by computing Intersection( G, N ). Look under NaturalHomomorphism in the index to see for which groups this function is overlaid.

7.104 ConjugationGroupHomomorphism

ConjugationGroupHomomorphism( G, H , x )

ConjugationGroupHomomorphism returns the homomorphism from G into H that takes each element g in G to the element g ^x. G and H must have a common parent group P and x must lie in this parent group. Of course G ^x must be a subgroup of H .

    gap> d12 := Group( (1,2,3,4,5,6), (2,6)(3,5) );; d12.name := "d12";;
    gap> c2 := Subgroup( d12, [ (2,6)(3,5) ] );
    Subgroup( d12, [ (2,6)(3,5) ] )
    gap> v4 := Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6) ] );
    Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6) ] )
    gap> x := ConjugationGroupHomomorphism( c2, v4, (1,3,5)(2,4,6) );
    ConjugationGroupHomomorphism( Subgroup( d12,
    [ (2,6)(3,5) ] ), Subgroup( d12, [ (1,2)(3,6)(4,5), (1,4)(2,5)(3,6)
     ] ), (1,3,5)(2,4,6) )
    gap> IsSurjective( x );
    false
    gap> Image( x );
    Subgroup( d12, [ (1,5)(2,4) ] )
ConjugationGroupHomomorphism calls
G.operations.ConjugationGroupHomomorphism( G, H , x ) and returns that value.

The default function called is GroupOps.ConjugationGroupHomomorphism. It just creates a homomorphism record with range G, source H , and the component element with the value x. It computes the image of an element g of G as g ^x. If the sizes of the range and the source are equal the inverse of such a homomorphism is computed as a conjugation homomorphism from H to G by x^-1. To multiply two such homomorphisms their elements are multiplied. Look under ConjugationGroupHomomorphism in the index to see for which groups this default function is overlaid.

7.105 InnerAutomorphism

InnerAutomorphism( G, g )

InnerAutomorphism returns the automorphism on the group G that takes each element h to h ^g. g must be an element in the parent group of G (but need not actually be in G) that normalizes G.

gap> s5 := Group( (1,2), (1,2,3,4,5) );;  s5.name := "s5";;
gap> i := InnerAutomorphism( s5, (1,2) );
InnerAutomorphism( s5, (1,2) )
gap> (1,2,3,4,5) ^i;
(1,3,4,5,2)
InnerAutomorphism( G, g ) calls ConjugationGroupHomomorphism( G, G, g ) (see

7.104).

7.106 GroupHomomorphismByImages

GroupHomomorphismByImages( G, H , gens, imgs )

GroupHomomorphismByImages returns the group homomorphism with source G and range H that is defined by mapping the list gens of generators of G to the list imgs of images in H .

gap> g := Group( (1,2,3,4), (1,2) );;
gap> h := Group( (2,3), (1,2) );;
gap> m := GroupHomomorphismByImages(g,h,g.generators,h.generators);
GroupHomomorphismByImages( Group( (1,2,3,4), (1,2) ), Group( (2,3),
(1,2) ), [ (1,2,3,4), (1,2) ], [ (2,3), (1,2) ] )
gap> Image( m, (1,3,4) );
(1,3,2)
gap> Kernel( m );
Subgroup( Group( (1,2,3,4), (1,2) ), [ (1,3)(2,4), (1,4)(2,3) ] )

Note that the result need not always be a single value mapping, even though the name seems to imply this. Namely if the elements in imgs do not satisfy all relations that hold for the generators gens, no element of G has a unique image under the mapping. This is demonstrated in the following example.

gap> g := Group( (1,2,3,4,5,6,7,8,9,10) );;
gap> h := Group( (1,2,3,4,5,6) );;
gap> m := GroupHomomorphismByImages(g,h,g.generators,h.generators);
GroupHomomorphismByImages( Group( ( 1, 2, 3, 4, 5, 6, 7, 8, 9,10
 ) ), Group( (1,2,3,4,5,6) ), [ ( 1, 2, 3, 4, 5, 6, 7, 8, 9,10) ],
[ (1,2,3,4,5,6) ] )
gap> IsMapping( m );
false
gap> Images( m, () );
(Subgroup( Group( (1,2,3,4,5,6) ), [ (1,5,3)(2,6,4) ] )*())
gap> g.1^10;

() # the generator of g satisfies this relation

gap> h.1^10;

(1,5,3)(2,6,4) # but its image does not The set of images of the identity returned by Images is the set of elements h.1^n such that g.1^n is the identity in g.

The test whether a mapping constructed by GroupHomomorphismByImages is a single valued mapping, is usually quite expensive. Note that this test is automatically performed the first time that you apply a function that expects a single valued mapping, e.g., Image or Images. There are two possibilities to avoid this test. When you know that the mapping constructed is really a single valued mapping, you can set the flag map.isMapping to true. Then the functions assume that map is indeed a mapping and do not test it again. On the other hand if you are not certain whether the mapping is single valued, you can use

ImagesRepresentative instead of Image (see 36.10). ImagesRepresentative returns just

one possible image, without testing whether there might actually be more than one possible image.

GroupHomomorphismByImages calls
G.operations.GroupHomomorphismByImages( G, H , gens, imgs )

and returns this value. The default function called this way is GroupOps.GroupHomomorphismByImages. Below we describe how the mapping functions are implemented for such a mapping. The functions not mentioned below are implemented by the default functions described in 7.102. All the function below first compute the list of elements of G with an orbit algorithm, sorts this list, and stores this list in hom.elements. In parallel they computes and sort a list of images, and store this list in hom.images.

IsMapping( map )

The mapping constructed by GroupHomomorphismByImages is a single valued mapping if for each i and for each k the following equation holds

map.images[Position(map.elements,map.elements[i]*gens[k])]
= map.images[i] * imgs[k].
Image( map, elm )

If the mapping map is a single valued mapping, the image of an element elm is computed

as map.images[ Position(map.elements,elm) ].
ImagesRepresentative( map, elm )

The representative of the images of an element elm under the mapping map is computed as

map.images[ Position(map.elements,elm) ].
InverseMapping( map )

The inverse of the mapping map is constructed as GroupHomomorphismByImages( H , G, imgs, gens ).

CompositionMapping( map1 , map2 )

If map2 is a mapping constructed by GroupHomomorphismByImages the composition is constructed by making a copy of map2 and replacing every element in map2 .images with its image under map1 . Look under GroupHomomorphismByImages in the index to see for which groups this function is overlaid.

7.107 Set Functions for Groups

As already mentioned in the introduction of the chapter, groups are domains. Thus all set theoretic functions, for example Intersection and Size can be applied to groups. This and the following sections give further comments on the definition and implementations of those functions for groups. All set theoretic functions not mentioned here not treated specially for groups. The last section describes the format of the records that describe groups (see 7.111).

Elements( G )

The elements of a group G are constructed using a Dimino algorithm. See 7.108.

IsSubset( G, H )

If G and H are groups then IsSubset tests whether the generators of H are elements of G.

Otherwise DomainOps.IsSubset is used.

Intersection( G, H )

The intersection of groups G and H is computed using an orbit algorithm. See 7.109.

7.108 Elements for Groups

GroupOps.Elements( G )

GroupOps.Elements returns the sets of elements of G (see 4.6). The function starts with the trivial subgroup of G, for which the set of elements is known and constructs the successive closures with the generators of G using GroupOps.Closure (see 7.18).

Note that this function neither checks nor sets the record component G.elements. It recomputes the set of elements even it is bound to G.elements.

7.109 Intersection for Groups

GroupOps.Intersection( G, H )

GroupOps.Intersection returns the intersection of G and H either as set of elements or as a group record (see 4.12).

If one argument, say G, is a set and the other a group, say H , then GroupOps.Intersection returns the subset of elements of G which lie in H .

If G and H have different parent groups then GroupOps.Intersection uses the function DomainOps.Intersection in order to compute the intersection.

Otherwise GroupOps.Intersection computes the stabilizer of the trivial coset of the bigger group in the smaller group using Stabilizer and Coset.

7.110 Operations for Groups

G ^s

The operator ^evaluates to the subgroup conjugate to G under a group element s of the parent group of G. See 7.20.

    gap> s4 := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> s4.name := "s4";;
    gap> v4 := Subgroup( s4, [ (1,2), (1,2)(3,4) ] );
    Subgroup( s4, [ (1,2), (1,2)(3,4) ] )
    gap> v4 ^(2,3);
    Subgroup( s4, [ (1,3), (1,3)(2,4) ] )
    gap> v4 ^(2,5);
    Error, <g> must be an element of the parent group of <G>

s in G

The operator in evaluates to true if s is an element of G and false otherwise. s must be an element of the parent group of G.

    gap> (1,2,3,4) in v4;
    false
    gap> (2,4) in v4^(2,3);
    true

G * s

The operator * evaluates to the right coset of G with representative s. s must be an element of the parent group of G. See 7.79 for details about right cosets.

s * G

The operator * evaluates to the left coset of G with representative s. s must be an element of the parent group of G. See 7.84 for details about left cosets.

    gap> v4 * (1,2,3,4);
    (Subgroup( s4, [ (1,2), (1,2)(3,4) ] )*(1,2,3))
    gap> (1,2,3,4) * v4;
    ((1,2,3,4)*Subgroup( s4, [ (1,2), (1,2)(3,4) ] ))

G / N

The operator / evaluates to the factor group G/N where N must be a normal subgroup of G. This is the same as FactorGroup(G,N ) (see 7.31).

7.111 Group Records

As for all domains (see 4 and 4.1) groups and their subgroups are represented by records that contain important information about groups. Most of the following functions return such records. Of course it is possible to create a group record by hand but generally Group (see 7.9) and Subgroup (see 7.12) should be used for such tasks.

Once a group record is created you may add record components to it but you must not alter informations already present, especially not generators and identity.

Group records must always contain the components generators, identity, isDomain and isGroup. Subgroups contain an additional component parent. The contents of all components of a group G are described below.

The following two components are the so-called category components used to identify the category this domain belongs to.

isDomain

is always true as a group is a domain.

isGroup

is of course true as G is a group.

The following three components determine a group domain. These are the so-called identification components.

generators

is a list group generators. Duplicate generators are allowed but none of the generators may be the group identity. The group G is the trivial group if and only if generators is the empty list. Note that once created this entry must never be changed, as most of the other entries depend on generators.

identity

is the group identity of G.

parent

if present this contains the group record of the parent group of a subgroup G, otherwise G itself is a parent group.

The following components are optional and contain knowledge about the group G.

abelianInvariants

a list of integers containing the abelian invariants of an abelian group G.

centralizer

contains the centralizer of G in its parent group.

centre

contains the centre of G. See 7.17.

commutatorFactorGroup

contains the commutator factor group of G. See 7.33 for details.

conjugacyClasses

contains a list of the conjugacy classes of G. See 7.64 for details.

core

contains the core of G under the action of its parent group. See 7.21 for details.

derivedSubgroup

contains the derived subgroup of G. See 7.22.

elements

is the set of all elements of G. See 4.6.

fittingSubgroup

contains the Fitting subgroup of G. See 7.23.

frattiniSubgroup

contains the Frattini subgroup of G.

index

contains the index of G in its parent group. See 7.48.

lowerCentralSeries

contains the lower central series of G as list of subgroups. See 7.39.

normalizer

contains the normalizer of G in its parent group. See 7.26 for details.

normalClosure

contains the normal closure of G in its parent group. See 7.24 for details.

upperCentralSeries

contains the upper central series of G as list of subgroups. See 7.42.

subnormalSeries

contains a subnormal series from the parent of G down to G. See 7.41 for details.

sylowSubgroups

contains a list of Sylow subgroups of G. See 7.29 for details.

size

is either an integer containing the size of a finite group or the string “infinity” if the group is infinite. See 4.10.

perfectSubgroups

contains the a list of subgroups which includes at least one representative of each class of conjugate proper perfect subgroups of G. See 7.70.

lattice

contains the subgroup lattice of G. See 7.70.

conjugacyClassesSubgroups

identical to the list G.lattice.classes, contains the conjugacy classes of subgroups of G. See 7.69.

tableOfMarks

contains the table of narks of G. See 41.4.

The following components are true if the group G has the property, false if not, and are not present if it is unknown whether the group has the property or not.

isAbelian

is true if the group G is abelian. See 7.49.

isCentral

is true if the group G is central in its parent group. See 7.50.

isCyclic

is true if the group G is cyclic. See 7.52.

isElementaryAbelian

is true if the group G is elementary abelian. See 7.53.

isFinite

is true if the group G is finite. If you know that a group for which you want to use the generic low level group functions is infinite, you should set this component to false. This will avoid attempts to compute the set of elements.

isNilpotent

is true if the group G is nilpotent. See 7.54.

isNormal

is true if the group G is normal in its parent group. See 7.55.

isPerfect

is true if the group G is perfect. See 7.56.

isSimple

is true if the group G is simple. See 7.57.

isSolvable

is true if the group G is solvable. See 7.58.

isSubnormal

is true if the group G is subnormal in its parent group. See 7.60.

The component operations contains the operations record (see 4.1 and 4.2).

Operations of Groups

One of the most important tools in group theory is the operation or action of a group on a certain set. We say that a group G operates on a set D if we have a function that takes each d ∈ D and each g ∈ G to another element d^{g} ∈ D, which we call the image of d under g, such that d^{identity} = d and (d^{g})^{h} = d^{gh} for each d ∈ D and g, h ∈ G. This is equivalent to saying that an operation is a homomorphism of the group G into the full symmetric group on D. We usually call D the domain of the operation and its elements points. An example of the usage of the functions in this package can be found in the introduction to GAP (see 1.19). In GAP group elements usually operate through the power operator, which is denoted by the caret ^. It is possible however to specify other operations (see 8.1). First this chapter describes the functions that take a single element of the group and compute cycles of this group element and related information (see 8.2, 8.3, 8.4, and 8.5), and the function that describes how a group element operates by a permutation that operates the same way on [1..n] (see 8.6). Next come the functions that test whether an orbit has minimal or maximal length and related functions (see 8.7, 8.8, 8.9, 8.10, and 8.11). Next this chapter describes the functions that take a group and compute orbits of this group and related information (see 8.14, 8.15, 8.16, and 8.17). Next are the functions that compute the permutation group P that operates on [ 1 .. Length(D) ] in the same way that G operates on D, and the corresponding homomorphism from G to P (see 8.18, 8.19). Next is the functions that compute block systems, i.e., partitions of D such that G operates on the sets of the partition (see 8.20), and the function that tests whether D has such a nontrivial partitioning under the operation of G (see 8.21). Finally come the functions that relate an orbit of G on D with the subgroup of G that fixes the first point in the orbit (see 8.22), and the cosets of this subgroup in G (see 8.23 and 8.24). All functions described in this chapter are in LIBNAME/"operatio.g".

8.1 Other Operations

The functions in the operation package generally compute with the operation of group elements defined by the canonical operation that is denoted with the caret (^) in GAP. However they also allow you to specify other operations. Such operations are specified by functions, which are accepted as optional argument by all the operations package functions.

This function must accept two arguments. The first argument will be the point and the second will be the group element. The function must return the image of the point under the group element.

As an example, the function OnPairs that specifies the operation on pairs could be defined as follows

    OnPairs := function ( pair, g )
        return [ pair[1] ^g, pair[2] ^g ];
    end;

The following operations are predefined.

OnPoints

specifies the canonical default operation. Passing this function is equivalent to specifying no operation. This function exists because there are places where the operation in not an option.

OnPairs

specifies the componentwise operation of group elements on pairs of points, which are represented by lists of length 2.

OnTuples

specifies the componentwise operation of group elements on tuples of points, which are represented by lists. OnPairs is the special case of OnTuples for tuples with two elements.

OnSets

specifies the operation of group elements on sets of points, which are represented by sorted lists of points without duplicates (see 26).

OnRight

specifies that group elements operate by multiplication from the right.

OnLeft

specifies that group elements operate by multiplication from the left.

OnRightCosets

specifies that group elements operate by multiplication from the right on sets of points, which are represented by sorted lists of points without duplicates (see 26).

OnLeftCosets

specifies that group elements operate by multiplication from the left on sets of points, which are represented by sorted lists of points without duplicates (see 26).

OnLines

specifies that group elements, which must be matrices, operate on lines, which are represented by vectors with first nonzero coefficient one. That is, OnLines multiplies the vector by the group element and then divides the vector by the first nonzero coefficient.

Note that it is your responsibility to make sure that the elements of the domain D on which you are operating are already in normal form. The reason is that all functions will compare points using the = operation. For example, if you are operating on sets with OnSets, you will get an error message it not all elements of the domain are sets.

    gap> Cycle( (1,2), [2,1], OnSets );
    Error, OnSets: <tuple> must be a set

8.2 Cycle

Cycle( g, d )

Cycle( g, d, operation )

Cycle returns the orbit of the point d, which may be an object of arbitrary type, under the group element g as a list of points.

The points e in the cycle of d under the group element g are those for which a power g^{i} exists such that d^{g^{i}} = e.

The first point in the list returned by Cycle is the point d itself, the ordering of the other points is such that each point is the image of the previous point.

Cycle accepts a function operation of two arguments d and g as optional third argument, which specifies how the element g operates (see 8.1).

    gap> Cycle( (1,5,3,8)(4,6,7), 3 );
    [ 3, 8, 1, 5 ]
    gap> Cycle( (1,5,3,8)(4,6,7), [3,4], OnPairs );
    [ [ 3, 4 ], [ 8, 6 ], [ 1, 7 ], [ 5, 4 ], [ 3, 6 ], [ 8, 7 ],
      [ 1, 4 ], [ 5, 6 ], [ 3, 7 ], [ 8, 4 ], [ 1, 6 ], [ 5, 7 ] ]
Cycle calls
Domain([g]).operations.Cycle( g, d, operation )

and returns the value. Note that the third argument is not optional for the functions called this way.

The default function called this way is GroupElementsOps.Cycle, which starts with d and applies g to the last point repeatedly until d is reached again. Special categories of group elements overlay this default function with more efficient functions.

8.3 CycleLength

CycleLength( g, d )
CycleLength( g, d, operation )

CycleLength returns the length of the orbit of the point d, which may be an object of arbitrary type, under the group elements g. See 8.2 for the definition of cycles.

CycleLength accepts a function operation of two arguments d and g as optional third argument, which specifies how the group element g operates (see 8.1).

    gap> CycleLength( (1,5,3,8)(4,6,7), 3 );
    4
    gap> CycleLength( (1,5,3,8)(4,6,7), [3,4], OnPairs );

CycleLength calls
Domain([g]).operations.CycleLength( g, d, operation )

and returns the value. Note that the third argument is not optional for the functions called this way.

The default function called this way is GroupElementsOps.CycleLength, which starts with d and applies g to the last point repeatedly until d is reached again. Special categories of group elements overlay this default function with more efficient functions.

8.4 Cycles

Cycles( g, D )

Cycles( g, D, operation )

Cycles returns the set of cycles of the group element g on the domain D, which must be a list of points of arbitrary type, as a set of lists of points. See 8.2 for the definition of cycles.

It is allowed that D is a proper subset of a domain, i.e., that D is not invariant under the operation of g. In this case D is silently replaced by the smallest superset of D which is invariant.

The first point in each cycle is the smallest point of D in this cycle. The ordering of the other points is such that each point is the image of the previous point. If D is invariant under g, then because Cycles returns a set of cycles, i.e., a sorted list, and because cycles are compared lexicographically, and because the first point in each cycle is the smallest point in that cycle, the list returned by Cycles is in fact sorted with respect to the smallest point in the cycles.

Cycles accepts a function operation of two arguments d and g as optional third argument, which specifies how the element g operates (see 8.1).

    gap> Cycles( (1,5,3,8)(4,6,7), [3,5,7] );
    [ [ 3, 8, 1, 5 ], [ 7, 4, 6 ] ]
    gap> Cycles( (1,5,3,8)(4,6,7), [[1,3],[4,6]], OnPairs );
    [ [ [ 1, 3 ], [ 5, 8 ], [ 3, 1 ], [ 8, 5 ] ],
      [ [ 4, 6 ], [ 6, 7 ], [ 7, 4 ] ] ]
Cycles calls
Domain([g]).operations.Cycles( g, D, operation )

and returns the value. Note that the third argument is not optional for the functions called this way.

The default function called this way is GroupElementsOps.Cycles, which takes elements from D, computes their orbit, removes all points in the orbit from D, and repeats this until D has been emptied. Special categories of group elements overlay this default function with more efficient functions.

8.5 CycleLengths

CycleLengths( g, D )
CycleLengths( g, D, operation )

CycleLengths returns a list of the lengths of the cycles of the group element g on the domain D, which must be a list of points of arbitrary type. See 8.2 for the definition of cycles.

It is allowed that D is a proper subset of a domain, i.e., that D is not invariant under the operation of g. In this case D is silently replaced by the smallest superset of D which is invariant.

The ordering of the lengths of cycles in the list returned by CycleLengths corresponds to the list of cycles returned by Cycles, which is ordered with respect to the smallest point in each cycle.

CycleLengths accepts a function operation of two arguments d and g as optional third argument, which specifies how the element g operates (see 8.1).

    gap> CycleLengths( (1,5,3,8)(4,6,7), [3,5,7] );
    [ 4, 3 ]
    gap> CycleLengths( (1,5,3,8)(4,6,7), [[1,3],[4,6]], OnPairs );
    [ 4, 3 ]
CycleLengths calls
Domain([g]).operations.CycleLengths( g, D, operation )

and returns the value. Note that the third argument is not optional for the functions called this way.

The default function called this way is GroupElementsOps.CycleLengths, which takes elements from D, computes their orbit, removes all points in the orbit from D, and repeats this until D has been emptied. Special categories of group elements overlay this default function with more efficient functions.

8.6 Permutation

Permutation( g, D )
Permutation( g, D, operation )

Permutation returns a permutation that operates on the points [1..Length(D)] in the same way that the group element g operates on the domain D, which may be a list of arbitrary type.

It is not allowed that D is a proper subset of a domain, i.e., D must be invariant under the element g.

Permutation accepts a function operation of two arguments d and g as optional third argument, which specifies how the element g operates (see 8.1).

    gap> Permutation( (1,5,3,8)(4,6,7), [4,7,6] );
    (1,3,2)
    gap> D := [ [1,4], [1,6], [1,7], [3,4], [3,6], [3,7],
    >           [4,5], [5,6], [5,7], [4,8], [6,8], [7,8] ];;
    gap> Permutation( (1,5,3,8)(4,6,7), D, OnSets );
    ( 1, 8, 6,10, 2, 9, 4,11, 3, 7, 5,12)
Permutation calls
Domain([g]).operations.Permutation( g, D, operation )

and returns the value. Note that the third argument is not optional for the functions called this way.

The default function called this way is GroupElementsOps.Permutation, which simply applies g to all the points of D, finds the position of the image in D, and finally applies PermList (see 19.9) to the list of those positions. Actually this is not quite true. Because finding the position of an image in a sorted list is so much faster than finding it in D, GroupElementsOps.Permutation first sorts a copy of D and remembers how it had to rearrange the elements of D to achieve this. Special categories of group elements overlay this default function with more efficient functions.

8.7 IsFixpoint

IsFixpoint( G, d )
IsFixpoint( G, d, operation )

IsFixpoint returns true if the point d is a fixpoint under the operation of the group G.

We say that d is a fixpoint under the operation of G if every element g of G maps d to itself. This is equivalent to saying that each generator of G maps d to itself.

As a special case it is allowed that the first argument is a single group element, though this does not make a lot of sense, since in this case IsFixpoint simply has to test d^g = d.

IsFixpoint accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> IsFixpoint( g, 1 );
    false
    gap> IsFixpoint( g, [6,7,8], OnSets );
    true

IsFixpoint is so simple that it does all the work by itself, and, unlike the other functions described in this chapter, does not dispatch to another function.

8.8 IsFixpointFree

IsFixpointFree( G, D )
IsFixpointFree( G, D, operation )

IsFixpointFree returns true if the group G operates without a fixpoint (see 8.7) on the domain D, which must be a list of points of arbitrary type.

We say that G operates fixpoint free on the domain D if each point of D is moved by at least one element of G. This is equivalent to saying that each point of D is moved by at least one generator of G. This definition also applies in the case that D is a proper subset of a domain, i.e., that D is not invariant under the operation of G.

As a special case it is allowed that the first argument is a single group element.

IsFixpointFree accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> IsFixpointFree( g, [1..8] );
    true
    gap> sets := Combinations( [1..8], 3 );;  Length( sets );

56 # a list of all three element subsets of [1..8]

    gap> IsFixpointFree( g, sets, OnSets );

    false
IsFixpointFree calls
G.operations.IsFixpointFree( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way. The default function called this way is GroupOps.IsFixpointFree, which simply loops over the elements of D and applies to each all generators of G, and tests whether each is moved by at least one generator. This function is seldom overlaid, because it is very difficult to improve it.

8.9 DegreeOperation

DegreeOperation( G, D )
DegreeOperation( G, D, operation )

DegreeOperation returns the degree of the operation of the group G on the domain D, which must be a list of points of arbitrary type. The degree of the operation of G on D is defined as the number of points of D that are properly moved by at least one element of G. This definition also applies in the case that D is a proper subset of a domain, i.e., that D is not invariant under the operation of G. DegreeOperation accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> DegreeOperation( g, [1..10] );
    8
    gap> sets := Combinations( [1..8], 3 );;  Length( sets );

56 # a list of all three element subsets of [1..8]

    gap> DegreeOperation( g, sets, OnSets );
    55
DegreeOperation calls
G.operations.DegreeOperation( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way. The default function called this way is GroupOps.DegreeOperation, which simply loops over the elements of D and applies to each all generators of G, and counts those that are moved by at least one generator. This function is seldom overlaid, because it is very difficult to improve it.

8.10 IsTransitive

IsTransitive( G, D )
IsTransitive( G, D, operation )

IsTransitive returns true if the group G operates transitively on the domain D, which must be a list of points of arbitrary type. We say that a group G acts transitively on a domain D if and only if for every pair of points d and e there is an element g of G such that d^{g} = e. An alternative characterization of this property is to say that D as a set is equal to the orbit of every single point.

It is allowed that D is a proper subset of a domain, i.e., that D is not invariant under the operation of G. In this case IsTransitive checks whether for every pair of points d, e of D there is an element g of G, such that d^{g} = e. This can also be characterized by saying that D is a subset of the orbit of every single point.

IsTransitive accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> IsTransitive( g, [1..8] );
    false
    gap> IsTransitive( g, [1,6] );

false # note that the domain need not be invariant

    gap> sets := Combinations( [1..5], 3 );;  Length( sets );

10 # a list of all three element subsets of [1..5]

    gap> IsTransitive( g, sets, OnSets );
    true
IsTransitive calls
G.operations.IsTransitive( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.IsTransitive, which tests whether D is a subset of the orbit of the first point in D. This function is seldom overlaid, because it is difficult to improve it.

8.11 Transitivity

Transitivity( G, D )
Transitivity( G, D, operation )

Transitivity returns the degree of transitivity of the group G on the domain D, which must be a list of points of arbitrary type. If G does not operate transitively on D then

Transitivity returns 0.

The degree of transitivity of the operation of G on D is the largest k such that G operates k-fold transitively on D. We say that G operates k-fold transitively on D if it operates transitively on D (see 8.10) and the stabilizer of one point d of D operates k-1-fold transitively on Difference(D,[d]). Because the stabilizers of the points of D are conjugate this is equivalent to saying that the stabilizer of each point d of D operates k-1-fold transitively on Difference(D,[d]).

It is not allowed that D is a proper subset of a domain, i.e., D must be invariant under the operation of G.

Transitivity accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> Transitivity( g, [1..8] );
    0
    gap> Transitivity( g, [1..5] );

    gap> sets := Combinations( [1..5], 3 );;  Length( sets );

10 # a list of all three element subsets of [1..5]

    gap> Transitivity( g, sets, OnSets );
    1
Transitivity calls
G.operations.Transitivity( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.Transitivity, which first tests whether G operates transitively on D. If so, it returns

Transitivity(Stabilizer(G,Difference(D,[D[1]]),operation)+1;

if not, it simply returns 0. Special categories of groups overlay this default function with more efficient functions.

8.12 IsRegular

IsRegular( G, D ) IsRegular( G, D, operation )

IsRegular returns true if the group G operates regularly on the domain D, which must be a list of points of arbitrary type, and false otherwise.

A group G operates regularly on a domain D if it operates transitively and no element of G other than the idenity leaves a point of D fixed. An equal characterisation is that G operates transitively on D and the stabilizer of any point of D is trivial. Yet another characterisation is that the operation of G on D is equivalent to the operation of G on its elements by multiplication from the right.

It is not allowed that D is a proper subset of a domain, i.e., D must be invariant under the operation of G.

IsRegular accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> IsRegular( g, [1..5] );
    false
    gap> IsRegular( g, Elements(g), OnRight );
    true
    gap> g := Group( (1,2,3), (3,4,5) );;
    gap> IsRegular( g, Orbit( g, [1,2,3], OnTuples ), OnTuples );
    true
IsRegular calls
G.operations.IsRegular( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.IsRegular, which tests if G operates transitively and semiregularly on D (see 8.10 and 8.13).

8.13 IsSemiRegular

IsSemiRegular( G, D )
IsSemiRegular( G, D, operation )

IsSemiRegular returns true if the group G operates semiregularly on the domain D, which must be a list of points of arbitrary type, and false otherwise.

A group G operates semiregularly on a domain D if no element of G other than the idenity leaves a point of D fixed. An equal characterisation is that the stabilizer of any point of D is trivial. Yet another characterisation is that the operation of G on D is equivalent to multiple copies of the operation of G on its elements by multiplication from the right.

It is not allowed that D is a proper subset of a domain, i.e., D must be invariant under the operation of G.

IsSemiRegular accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2)(3,4)(5,7)(6,8), (1,3)(2,4)(5,6)(7,8) );;
    gap> IsSemiRegular( g, [1..8] );
    true
    gap> g := Group( (1,2)(3,4)(5,7)(6,8), (1,3)(2,4)(5,6,7,8) );;
    gap> IsSemiRegular( g, [1..8] );
    false
    gap> IsSemiRegular( g, Orbit( g, [1,5], OnSets ), OnSets );
    true
IsSemiRegular calls
G.operations.IsSemiRegular( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.IsSemiRegular, which computes a permutation group P that operates on [1..Length(D)] in the same way that G operates on D (see 8.18) and then checks if this permutation group operations semiregularly. This of course only works because this default function is overlaid for permutation groups (see 20.21).

8.14 Orbit

Orbit( G, d )

Orbit( G, d, operation )

Orbit returns the orbit of the point d, which may be an object of arbitrary type, under the group G as a list of points.

The points e in the orbit of d under the group G are those points for which a group element g of G exists such that d^{g} = e.

Suppose G has n generators. First we order the words of the free monoid with n abstract generators according to length and for words with equal length lexicographically. So if G has two generators called a and b the ordering is identity, a, b, a^{2}, ab, ba, b^{2}, a^{3}, .... Next we order the elements of G that can be written as a product of the generators, i.e., without inverses of the generators, according to the first occurence of a word representing the element in the above ordering. Then the ordering of points in the orbit returned by Orbit is according to the order of the first representative of each point e, i.e., the smallest g such that d^{g} = e. Note that because the orbit is finite there is for every point in the orbit at least one representative that can be written as a product in the generators of G. Orbit accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> Orbit( g, 1 );
    [ 1, 2, 3, 4, 5 ]
    gap> Orbit( g, 2 );
    [ 2, 3, 1, 4, 5 ]
    gap> Orbit( g, [1,6], OnPairs );
    [ [ 1, 6 ], [ 2, 7 ], [ 3, 6 ], [ 2, 8 ], [ 1, 7 ], [ 4, 6 ],
      [ 3, 8 ], [ 2, 6 ], [ 1, 8 ], [ 4, 7 ], [ 5, 6 ], [ 3, 7 ],
      [ 5, 8 ], [ 5, 7 ], [ 4, 8 ] ]
Orbit calls
G.operations.Orbit( G, d, operation )

and returns the value. Note that the third argument is not optional for functions called this way. The default function called this way is GroupOps.Orbit, which performs an ordinary orbit algorithm. Special categories of groups overlay this default function with more efficient functions.

8.15 OrbitLength

OrbitLength( G, d )
OrbitLength( G, d, operation )

OrbitLength returns the length of the orbit of the point d, which may be an object of arbitrary type, under the group G. See 8.14 for the definition of orbits. OrbitLength accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> OrbitLength( g, 1 );
    5
    gap> OrbitLength( g, 10 );
    1
    gap> OrbitLength( g, [1,6], OnPairs );
    15
OrbitLength calls
G.operations.OrbitLength( G, d, operation )

and returns the value. Note that the third argument is not optional for functions called this way. The default function called this way is GroupOps.OrbitLength, which performs an ordinary orbit algorithm. Special categories of groups overlay this default function with more efficient functions.

8.16 Orbits

Orbits( G, D )

Orbits( G, D, operation )

Orbits returns the orbits of the group G on the domain D, which must be a list of points of arbitrary type, as a set of lists of points. See 8.14 for the definition of orbits.

It is allowed that D is a proper subset of a domain, i.e., that D is not invariant under the operation of G. In this case D is silently replaced by the smallest superset of D which is invariant.

The first point in each orbit is the smallest point, the other points of each orbit are ordered in the standard order defined for orbits (see 8.14). Because Orbits returns a set of orbits, i.e., a sorted list, and because those orbits are compared lexicographically, and because the first point in each orbit is the smallest point in that orbit, the list returned by Orbits is in fact sorted with respect to the smallest points the orbits.

Orbits accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> Orbits( g, [1..8] );
    [ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ]
    gap> Orbits( g, [1,6] );

[ [ 1, 2, 3, 4, 5 ], [ 6, 7, 8 ] ] # the domain is not invariant

    gap> sets := Combinations( [1..8], 3 );; Length( sets );

56 # a list of all three element subsets of [1..8]

    gap> Orbits( g, sets, OnSets );
    [ [ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 2, 3, 4 ], [ 1, 2, 5 ], [ 1, 3, 4 ],
          [ 2, 4, 5 ], [ 2, 3, 5 ], [ 1, 4, 5 ], [ 3, 4, 5 ], [ 1, 3, 5 ]
         ],
      [ [ 1, 2, 6 ], [ 2, 3, 7 ], [ 1, 3, 6 ], [ 2, 4, 8 ], [ 1, 2, 7 ],
          [ 1, 4, 6 ], [ 3, 4, 8 ], [ 2, 5, 7 ], [ 2, 3, 6 ],
          [ 1, 2, 8 ], [ 2, 4, 7 ], [ 1, 5, 6 ], [ 1, 4, 8 ],
          [ 4, 5, 7 ], [ 3, 5, 6 ], [ 2, 3, 8 ], [ 1, 3, 7 ],
          [ 2, 4, 6 ], [ 3, 4, 6 ], [ 2, 5, 8 ], [ 1, 5, 7 ],
          [ 4, 5, 6 ], [ 3, 5, 8 ], [ 1, 3, 8 ], [ 3, 4, 7 ],
          [ 2, 5, 6 ], [ 1, 4, 7 ], [ 1, 5, 8 ], [ 4, 5, 8 ], [ 3, 5, 7 ]
         ],
      [ [ 1, 6, 7 ], [ 2, 6, 7 ], [ 1, 6, 8 ], [ 3, 6, 7 ], [ 2, 6, 8 ],
          [ 2, 7, 8 ], [ 4, 6, 8 ], [ 3, 7, 8 ], [ 3, 6, 8 ],
          [ 4, 7, 8 ], [ 5, 6, 7 ], [ 1, 7, 8 ], [ 4, 6, 7 ],
          [ 5, 7, 8 ], [ 5, 6, 8 ] ], [ [ 6, 7, 8 ] ] ]
Orbits calls
G.operations.Orbits( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.Orbits, which takes an element from D, computes its orbit, removes all points in the orbit from D, and repeats this until D has been emptied. Special categories of groups overlay this default function with more efficient functions.

8.17 OrbitLengths

OrbitLengths( G, D )
OrbitLengths( G, D, operation )

OrbitLengths returns a list of the lengths of the orbits of the group G on the domain D, which may be a list of points of arbitrary type. See 8.14 for the definition of orbits.

It is allowed that D is proper subset of a domain, i.e., that D is not invariant under the operation of G. In this case D is silently replaced by the smallest superset of D which is invariant.

The ordering of the lengths of orbits in the list returned by OrbitLengths corresponds to the list of cycles returned by Orbits, which is ordered with respect to the smallest point in each orbit.

OrbitLengths accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> OrbitLengths( g, [1..8] );
    [ 5, 3 ]
    gap> sets := Combinations( [1..8], 3 );; Length( sets );

56 # a list of all three element subsets of [1..8]

    gap> OrbitLengths( g, sets, OnSets );
    [ 10, 30, 15, 1 ]
OrbitLengths calls
G.operations.OrbitLenghts( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.OrbitLengths, which takes an element from D, computes its orbit, removes all points in the orbit from D, and repeats this until D has been emptied. Special categories of groups overlay this default function with more efficient functions.

8.18 Operation

Operation( G, D )
Operation( G, D, operation )

Operation returns a permutation group with the same number of generators as G, such that each generator of the permutation group operates on the set [1..Length(D)] in the same way that the corresponding generator of the group G operates on the domain D, which may be a list of arbitrary type.

It is not allowed that D is a proper subset of a domain, i.e., D must be invariant under the element g.

Operation accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

The function OperationHomomorphism (see 8.19) can be used to compute the homomorphism that maps G onto the new permutation group. Of course if you are only interested in mapping single elements of G into the new permutation group you may also use Permutation (see 8.6).

gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
gap> Operation( g, [1..5] );
Group( (1,2,3), (3,4,5) )
gap> Operation( g, Orbit( g, [1,6], OnPairs ), OnPairs );
Group( ( 1, 2, 3, 5, 8,12)( 4, 7, 9)( 6,10)(11,14), ( 2, 4)( 3, 6,11)
( 5, 9)( 7,10,13,12,15,14) )
Operation calls
G.operations.Operation( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.Operation, which simply applies each generator of G to all the points of D, finds the position of the image in D, and finally applies PermList (see 19.9) to the list of those positions. Actually this is not quite true. Because finding the position on an image in a sorted list is so much faster than finding it in D, GroupElementsOps.Operation first sorts a copy of D and remembers how it had to rearrange the elements of D to achieve this. Special categories of groups overlay this default function with more efficient functions.

8.19 OperationHomomorphism

OperationHomomorphism( G, P )

OperationHomomorphism returns the group homomorphism (see 7.99) from the group G to the permutation group P, which must be the result of a prior call to Operation (see 8.18) with G or a group of which G is a subgroup (see 7.59) as first argument.

gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
gap> h := Operation( g, [1..5] );
Group( (1,2,3), (3,4,5) )
gap> p := OperationHomomorphism( g, h );
OperationHomomorphism( Group( (1,2,3)(6,7), (3,4,5)(7,8) ), Group(
(1,2,3), (3,4,5) ) )
gap> (1,4,2,5,3)(6,7,8) ^p;
(1,4,2,5,3)
gap> h := Operation( g, Orbit( g, [1,6], OnPairs ), OnPairs );
Group( ( 1, 2, 3, 5, 8,12)( 4, 7, 9)( 6,10)(11,14), ( 2, 4)( 3, 6,11)
( 5, 9)( 7,10,13,12,15,14) )
gap> p := OperationHomomorphism( g, h );;
gap> s := SylowSubgroup( g, 2 );
Subgroup( Group( (1,2,3)(6,7), (3,4,5)(7,8) ),
[ (7,8), (7,8), (2,5)(3,4), (2,3)(4,5) ] )
gap> Images( p, s );
Subgroup( Group( ( 1, 2, 3, 5, 8,12)( 4, 7, 9)( 6,10)(11,14), ( 2, 4)
( 3, 6,11)( 5, 9)( 7,10,13,12,15,14) ),

    [ ( 2, 4)( 5, 9)( 7,12)(10,15)(13,14),
      ( 2, 4)( 5, 9)( 7,12)(10,15)(13,14),
      ( 2,14)( 3, 6)( 4,13)( 7,15)( 8,11)(10,12),
      ( 2,12)( 3, 8)( 4, 7)( 6,11)(10,14)(13,15) ] )
    gap> OperationHomomorphism( g, Group( (1,2,3), (3,4,5) ) );
    Error, <P> must be an operation group in
    OperationHomomorphism( g, Group( (1,2,3), (3,4,5) ) ) called from
    main loop
OperationHomomorphism calls
P.operations.OperationHomomorphism( G, P )

and returns the value. The default function called this way is GroupOps.OperationHomomorphism, which uses the

fields P.operationGroup, P.operationDomain, and P.operationOperation (the argu-

ments to the Operation call that created P) to construct a generic homomorphism h. This homomorphism uses

Permutation(g,h.range.operationDomain,h.range.operationOperation)

to compute the image of an element g of G under h. It uses Representative to compute the preimages of an element p of P under h. And it computes the kernel by intersecting the cores (see 7.21) of the stabilizers (see 8.22) of representatives of the orbits of G. Look under OperationHomomorphism in the index to see for which groups and operations this function is overlaid.

8.20 Blocks

Blocks( G, D, seed )

Blocks( G, D, seed, operation ) In this form Blocks returns a block system of the domain D, which may be a list of points of arbitrary type, under the group G, such that the points in the list seed all lie in the same block. If no such nontrivial block system exists, Blocks returns [ D ]. G must operate transitively on D, otherwise an error is signalled.

Blocks( G, D )

Blocks( G, D, operation ) In this form Blocks returns a minimal block system of the domain D, which may be a list of points of arbitrary type, under the group G. If no nontrivial block system exists, Blocks returns [ D ]. G must operate transitively on D, otherwise an error is signalled. A block system B is a list of blocks with the following properties. Each block b of B is a subset of D. The blocks are pairwise disjoint. The union of blocks is D. The image of each block under each element g of G is as a set equal to some block of the block system. Note that this implies that all blocks contain the same number of elements as G operates transitive on D. Put differently a block system B of D is a partition of D such that G operates with OnSets (see 8.1) on B. The block system that consists of only singleton sets and the block system consisting only of D are called trivial. A block system B is called minimal if there is no nontrivial block system whose blocks are all subsets of the blocks of B and whose number of blocks is larger than the number of blocks of B. Blocks accepts a function operation of two arguments d and g as optional third, resp. fourth, argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> Blocks( g, [1..5] );
    [ [ 1 .. 5 ] ]
    gap> Blocks( g, Orbit( g, [1,2], OnPairs ), OnPairs );
    [ [ [ 1, 2 ], [ 3, 2 ], [ 4, 2 ], [ 5, 2 ] ],
      [ [ 1, 3 ], [ 2, 3 ], [ 4, 3 ], [ 5, 3 ] ],
      [ [ 1, 4 ], [ 2, 4 ], [ 3, 4 ], [ 5, 4 ] ],
      [ [ 1, 5 ], [ 2, 5 ], [ 3, 5 ], [ 4, 5 ] ],
      [ [ 2, 1 ], [ 3, 1 ], [ 4, 1 ], [ 5, 1 ] ] ]
Blocks calls
G.operations.Blocks( G, D, seed, operation )

and returns the value. If no seed was given as argument to Blocks it passes the empty list. Note that the fourth argument is not optional for functions called this way.

The default function called this way is GroupOps.Blocks, which computes a permutation group P that operates on [1..Length(D)] in the same way that G operates on D (see 8.18) and leaves it to this permutation group to find the blocks. This of course works only because this default function is overlaid for permutation groups (see 20.21).

8.21 IsPrimitive

IsPrimitive( G, D )
IsPrimitive( G, D, operation )

IsPrimitive returns true if the group G operates primitively on the domain D, which may be a list of points of arbitrary type, and false otherwise.

A group G operates primitively on a domain D if and only if D operates transitively (see 8.10) and has only the trivial block systems (see 8.20).

IsPrimitive accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> IsPrimitive( g, [1..5] );
    true
    gap> IsPrimitive( g, Orbit( g, [1,2], OnPairs ), OnPairs );
    false
IsPrimitive calls
G.operations.IsPrimitive( G, D, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.IsPrimitive, which simply calls Blocks( G, D, operation ) and tests whether the returned block system is [ D ]. This function is seldom overlaid, because all the important work is done in Blocks.

8.22 Stabilizer

Stabilizer( G, d )
Stabilizer( G, d, operation )

Stabilizer returns the stabilizer of the point d under the operation of the group G.

The stabilizer S of d in G is the subgroup of those elements g of G that fix d, i.e., for which d^{g} = d. The right cosets of S correspond in a canonical way to the points p in the orbit O of d under G; namely all elements from a right coset Sg map d to the same point d^{g} ∈ O, and elements from different right cosets Sg and Sh map d to different points d^{g} and d^{h}. Thus the index of the stabilizer S in G is equal to the length of the orbit O. RepresentativesOperation (see 8.24) computes a system of representatives of the right cosets of S in G.

Stabilizer accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> g.name := "G";;
    gap> Stabilizer( g, 1 );
    Subgroup( G, [ (3,4,5)(7,8), (2,5,3)(6,7) ] )
    gap> Stabilizer( g, [1,2,3], OnSets );
    Subgroup( G, [ (7,8), (6,8), (2,3)(4,5)(6,7,8), (1,2)(4,5)(6,7,8) ] )
Stabilizer calls
G.operations.Stabilizer( G, d, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.Stabilizer, which computes the orbit of d under G, remembers a representative r_{e} for each point e in the orbit, and uses Schreier’s theorem, which says that the stabilizer is generated by the elements r_{e}gr_{e^{g}}^{−1}. Special categories of groups overlay this default function with more efficient functions.

8.23 RepresentativeOperation

RepresentativeOperation( G, d, e )
RepresentativeOperation( G, d, e, operation )

RepresentativeOperation returns a representative of the point e in the orbit of the point d under the group G. If d = e then RepresentativeOperation returns G.identity, otherwise it is not specified which group element RepresentativeOperation will return if there are several that map d to e. If e is not in the orbit of d under G, RepresentativeOperation returns false.

An element g of G is called a representative for the point e in the orbit of d under G if g maps d to e, i.e., d^{g} = e. Note that the set of such representatives that map d to e forms a right coset of the stabilizer of d in G (see 8.22).

RepresentativeOperation accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> RepresentativeOperation( g, 1, 5 );
    (1,5,4,3,2)(6,8,7)
    gap> RepresentativeOperation( g, 1, 6 );
    false
    gap> RepresentativeOperation( g, [1,2,3], [3,4,5], OnSets );

    (1,3,5,2,4)
    gap> RepresentativeOperation( g, [1,2,3,4], [3,4,5,2], OnTuples );
    false
RepresentativeOperation calls
G.operations.RepresentativeOperation( G, d, e, operation )

and returns the value. Note that the fourth argument is not optional for functions called this way.

The default function called this way is GroupOper.RepresentativeOperation, which starts a normal orbit calculation to compute the orbit of d under G, and remembers for each point how it was obtained, i.e., which generator of G took which orbit point to this new point. When the point e appears this information can be traced back to write down the representative of e as a word in the generators. Special categories of groups overlay this default function with more efficient functions.

8.24 RepresentativesOperation

RepresentativesOperation( G, d )
RepresentativesOperation( G, d, operation )

RepresentativesOperation returns a list of representatives of the points in the orbit of the point d under the group G.

The ordering of the representatives corresponds to the ordering of the points in the orbit

as returned by Orbit (see 8.14). Therefore List( RepresentativesOperation(G,d),
r->d^r ) = Orbit(G,d).

An element g of G is called a representative for the point e in the orbit of d under G if g maps d to e, i.e., d^{g} = e. Note that the set of such representatives that map d to e forms a right coset of the stabilizer of d in G (see 8.22). The set of all representatives of the orbit of d under G thus forms a system of representatives of the right cosets of the stabilizer of d in G.

RepresentativesOperation accepts a function operation of two arguments d and g as optional third argument, which specifies how the elements of G operate (see 8.1).

    gap> g := Group( (1,2,3)(6,7), (3,4,5)(7,8) );;
    gap> RepresentativesOperation( g, 1 );
    [ (), (1,2,3)(6,7), (1,3,2), (1,4,5,3,2)(7,8), (1,5,4,3,2) ]
    gap> Orbit( g, [1,2], OnSets );
    [ [ 1, 2 ], [ 2, 3 ], [ 1, 3 ], [ 2, 4 ], [ 1, 4 ], [ 3, 4 ],
      [ 2, 5 ], [ 1, 5 ], [ 4, 5 ], [ 3, 5 ] ]
    gap> RepresentativesOperation( g, [1,2], OnSets );
    [ (), (1,2,3)(6,7), (1,3,2), (1,2,4,5,3)(6,8,7), (1,4,5,3,2)(7,8),
      (1,3,2,4,5)(6,8), (1,2,5,4,3)(6,7), (1,5,4,3,2), (1,4,3,2,5)(6,7,8),
      (1,3,2,5,4) ]
RepresentativesOperation calls
G.operations.RepresentativesOperation( G, d, operation )

and returns the value. Note that the third argument is not optional for functions called this way.

The default function called this way is GroupOps.RepresentativesOperation, which computes the orbit of d with the normal algorithm, but remembers for each point e in the orbit a representative r_{e}. When a generator g of G takes an old point e to a point f not yet in the orbit, the representative r_{f} for f is computed as r_{e}g. Special categories of groups overlay this default function with more efficient functions.

8.25 IsEquivalentOperation

IsEquivalentOperation( G, D, H , E )
IsEquivalentOperation( G, D, H , E, operationH )
IsEquivalentOperation( G, D, operationG, H , E )
IsEquivalentOperation( G, D, operationG, H , E, operationH )

IsEquivalentOperation returns true if G operates on D in like H operates on E, and false otherwise.

The operations of G on D and H on E are equivalent if they have the same number of generators and there is a permutation F of the elements of E such that for every generator g of G and the corresponding generator h of H we have P osition(D, D_{i}^{g}) = P osition(F, F_{i}^{h}). Note that this assumes that the mapping defined by mapping G.generators to H.generators is a homomorphism (actually an isomorphism of factor groups of G and H represented by the respective operation).

IsEquivalentOperation accepts functions operationG and operationH of two arguments d and g as optional third and sixth arguments, which specify how the elements of G and H operate (see 8.1).

    gap> g := Group( (1,2)(4,5), (1,2,3)(4,5,6) );;
    gap> h := Group( (2,3)(4,5), (1,2,3)(4,5,6) );;
    gap> IsEquivalentOperation( g, [1..6], h, [1..6] );
    true
    gap> h := Group( (1,2), (1,2,3) );;
    gap> IsEquivalentOperation(g,[[1,4],[2,5],[3,6]],OnPairs,h,[1..3]);
    true
    gap> h := Group( (1,2), (1,2,3)(4,5,6) );;
    gap> IsEquivalentOperation( g, [1..6], h, [1..6] );
    false
    gap> h := Group( (1,2,3)(4,5,6), (1,2)(4,5) );;
    gap> IsEquivalentOperation( g, [1..6], h, [1..6] );

false # the generators must correspond

IsEquivalentOperation calls
G.operations.IsEquivalentOperation(G,D,oprG,H ,E,oprH ) and returns the value.

Note that the third and sixth argument are not optional for functions called this way.

The default function called this way is GroupOps.IsEquivalentOperation, which tries to rearrange E so that the above condition is satisfied. This is done one orbit of G at a time, and for each such orbit all the orbits of H of the same length are tried to see if there is one which can be rearranged as necessary. Special categories of groups overlay this function with more efficient ones.

316 CHAPTER 8. OPERATIONS OF GROUPS

Vector Spaces

The material described in this chapter is subject to change.

Vector spaces form another important domain in GAP. They may be given in any representation whenever the underlying set of elements forms a vector space in terms of linear algebra. Thus, for example, one may construct a vector space by defining generating matrices over a field or by using the base of a field extension as generators. More complex constructions may fake elements of a vector space by specifying records with appropriate operations. A special type of vector space, that is implemented in the GAP library, handles the case where the elements are lists over a field. This type is the so called RowSpace (see 31 for details).

General vector spaces are created using the function VectorSpace (see 9.1) and they are represented as records that contain all necessary information to deal with the vector space. The components listed in 9.3 are common for all vector spaces, but special types of vector spaces, such as the row spaces, may use additional entries to store specific data.

The following sections contain descriptions of functions and operations defined for vector spaces.

The next sections describe functions to compute a base (see 9.6) and the dimension (see 9.8) of a vector space over its field.

The next sections describe how to calculate linear combinations of the elements of a base (see 9.9) and how to find the coefficients of an element of a vector space when expressed as a linear combination in the current base (see 9.10).

The functions described in this chapter are implemented in the file LIBNAME/"vecspace.g".

9.1 VectorSpace

VectorSpace( generators, field )

Let generators be a list of objects generating a vector space over the field field. Then VectorSpace returns this vector space represented as a GAP record.

    gap> f := GF( 3^2 );
    GF(3^2)
    gap> m := [ [ f.one, f.one ], [ f.zero, f.zero ] ];

    [ [ Z(3)^0, Z(3)^0 ], [ 0*Z(3), 0*Z(3) ] ]
    gap> n := [ [ f.one, f.zero ], [ f.zero, f.one ] ];
    [ [ Z(3)^0, 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ]
    gap> VectorSpace( [ m, n ], f );
    VectorSpace( [ [ [ Z(3)^0, Z(3)^0 ], [ 0*Z(3), 0*Z(3) ] ],
      [ [ Z(3)^0, 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ] ], GF(3^2) )

VectorSpace( generators, field, zero )

VectorSpace returns the vector space generated by generators over the field field having zero as the uniquely determined neutral element. This call of VectorSpace always is requested if generators is the empty list.

    gap> VectorSpace( [], f, [ [ f.zero, f.zero ], [ f.zero, f.zero ] ] );
    VectorSpace( [  ], GF(3^2), [ [ 0*Z(3), 0*Z(3) ], [ 0*Z(3), 0*Z(3) ]
     ] )

9.2 IsVectorSpace

IsVectorSpace( obj )

IsVectorSpace returns true if obj , which can be an object of arbitrary type, is a vector space and false otherwise.

9.3 Vector Space Records

A vector space is represented as a GAP record having several entries to hold some necessary information about the vector space.

Basically a vector space record is constructed using the function VectorSpace although one may create such a record by hand. Furthermore vector space records may be returned by functions described here or somewhere else in this manual.

Once a vector space record is created you are free to add components, but you should never alter existing entries, especially generators, field and zero.

The following list mentions all components that are requested for a vector space V .

generators

a list of elements generating the vector space V .

field

the field over which the vector space V is written.

zero

the zero element of the vector space.

isDomain

always true, because vector spaces are domains.

isVectorSpace

always true, for obvious reasons.

There are as well some optional components for a vector space record.

base

a base for V , given as a list of elements of V .

dimension

the dimension of V which is the length of a base of V .

9.4 Set Functions for Vector Spaces

As mentioned before, vector spaces are domains. So all functions that exist for domains may also be applied to vector spaces. This and the following chapters give further information on the implementation of these functions for vector spaces, as far as they differ in their implementation from the general functions.

Elements( V )

The elements of a vector space V are computed by producing all linear combinations of the generators of V .

Size( V )

The size of a vector space V is determined by calculating the dimension of V and looking at the field over which it is written.

IsFinite( V )

A vector space in GAP is finite if it contains only its zero element or if the field over which it is written is finite. This characterisation is true here, as in GAP all vector spaces have a finite dimension.

Intersection( V , W )

The intersection of vector spaces is computed by finding a base for the intersection of the sets of their elements. One may consider the algorithm for finding a base of a vector space V as another way to write Intersection( V , V ).

9.5 IsSubspace

IsSubspace( V , W )

IsSubspace tests whether the vector space W is a subspace of V . It returns true if W lies in V and false if it does not. The answer to the question is obtained by testing whether all the generators of W lie in V , so that, for the general case of vector space handling, a list of all the elements of V is constructed.

9.6 Base

Base( V )

Base computes a base of the given vector space V . The result is returned as a list of elements of the vector space V .

The base of a vector space is defined to be a minimal generating set. It can be shown that for a given vector space V each base has the same number of elements, which is called the dimension of V (see 9.8).

Unfortunately, no better algorithm is known to compute a base in general than to browse through the list of all elements of the vector space. So be careful when using this command on plain vector spaces.

gap> f := GF(3);
GF(3)
gap> m1 := [[ f.one, f.one, f.zero, f.zero ]];
[ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ]
gap> m2 := [[ f.one, f.one, f.one, f.zero ]];
[ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ]
gap> V := VectorSpace( [ m1, m2, m1+m2 ], GF(3) );
VectorSpace( [ [ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ],
  [ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ],
  [ [ Z(3), Z(3), Z(3)^0, 0*Z(3) ] ] ], GF(3) )
gap> Base( V );
[ [ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ],
  [ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ] ]
gap> Dimension( V );
2

9.7 AddBase

AddBase( V , base )

AddBase attaches a user-supplied base for the vector space V to the record that represents V .

Most of the functions for vector spaces make use of a base (see 9.9, 9.10). These functions get access to a base using the function Base, which normally computes a base for the vector space using an appropriate algorithm. Once a base is computed it will always be reused, no matter whether there is a more interesting base available or not.

AddBase installs a given base for V by overwriting any other base of the vector space that has been installed before. So after AddBase has successfully been used, base will be used whenever Base is called with V as argument.

Calling AddBase with a base which is not a base for V might produce unpredictable results in following computations.

gap> f := GF(3);
GF(3)
gap> m1 := [[ f.one, f.one, f.zero, f.zero ]];
[ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ]
gap> m2 := [[ f.one, f.one, f.one, f.zero ]];
[ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ]
gap> V := VectorSpace( [ m1, m2, m1+m2 ], GF(3) );
VectorSpace( [ [ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ],
  [ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ],

  [ [ Z(3), Z(3), Z(3)^0, 0*Z(3) ] ] ], GF(3) )
gap> Base( V );
[ [ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ],
  [ [ Z(3)^0, Z(3)^0, Z(3)^0, 0*Z(3) ] ] ]
gap> AddBase( V, [ m1, m1+m2 ] );
gap> Base( V );
[ [ [ Z(3)^0, Z(3)^0, 0*Z(3), 0*Z(3) ] ],
  [ [ Z(3), Z(3), Z(3)^0, 0*Z(3) ] ] ]

9.8 Dimension

Dimension( V )

Dimension computes the dimension of the given vector space V over its field. The dimension of a vector space V is defined to be the length of a minimal generating set of V , which is called a base of V (see 9.6). The implementation of Dimension strictly follows its above definition, so that this function will always determine a base of V .

gap> f := GF( 3^4 );
GF(3^4)
gap> f.base;
[ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ]
gap> V := VectorSpace( f.base, GF( 3 ) );
VectorSpace( [ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ], GF(3) )
gap> Dimension( V );
4

9.9 LinearCombination

LinearCombination( V , cf )

LinearCombination computes the linear combination of the base elements of the vector space V with coefficients cf . cf has to be a list of elements of V .field, the field over which the vector space is written. Its length must be equal to the dimension of V to make sure that one coefficient is specified for each element of the base. LinearCombination will use that base of V which is returned when applying the function Base to V (see 9.6). To perform linear combinations of different bases use AddBase to specify which base should be used (see 9.7).

gap> f := GF( 3^4 );
GF(3^4)
gap> f.base;
[ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ]
gap> V := VectorSpace( f.base, GF( 3 ) );
VectorSpace( [ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ], GF(3) )
gap> LinearCombination( V, [ Z(3), Z(3)^0, Z(3), 0*Z(3) ] );
Z(3^4)^16
gap> Coefficients( V, f.root ^16 );
[ Z(3), Z(3)^0, Z(3), 0*Z(3) ]

9.10 Coefficients

Coefficients( V , v )

Coefficients computes the coefficients that have to be used to write v as a linear combination in the base of V .

To make sure that this function produces the correct result, v has to be an element of V . If v does not lie in V the result is unpredictable.

The result of Coefficients is returned as a list of elements of the field over which the vector space V is written. Of course, the length of this list equals the dimension of V .

gap> f := GF( 3^4 );
GF(3^4)
gap> f.base;
[ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ]
gap> V := VectorSpace( f.base, GF( 3 ) );
VectorSpace( [ Z(3)^0, Z(3^4), Z(3^4)^2, Z(3^4)^3 ], GF(3) )
gap> Dimension( V );
4
gap> Coefficients( V, f.root ^16 );
[ Z(3), Z(3)^0, Z(3), 0*Z(3) ]

Integers

One of the most fundamental datatypes in every programming language is the integer type. GAP is no exception.

GAP integers are entered as a sequence of digits optionally preceded by a + sign for positive integers or a - sign for negative integers. The size of integers in GAP is only limited by the amount of available memory, so you can compute with integers having thousands of digits.

    gap> -1234;
    -1234
    gap> 123456789012345678901234567890123456789012345678901234567890;
    123456789012345678901234567890123456789012345678901234567890

The first sections in this chapter describe the operations applicable to integers (see 10.1, 10.2, 10.3 and 10.4).

The next sections describe the functions that test whether an object is an integer (see 10.5) and convert objects of various types to integers (see 10.6).

The next sections describe functions related to the ordering of integers (see 10.7, 10.8).

The next section describes the function that computes a Chinese remainder (see 10.9).

The next sections describe the functions related to the ordering of integers, logarithms, and roots (10.10, 10.11, 10.12).

The GAP object Integers is the ring domain of all integers. So all set theoretic functions are also applicable to this domain (see chapter 4 and 10.13). The only serious use of this however seems to be the generation of random integers.

Since the integers form a Euclidean ring all the ring functions are applicable to integers (see chapter 5, 10.14, 10.15, 10.16, 10.17, 10.18, 10.19, 10.20, 10.21, 10.22, 10.23, and 10.24).

Since the integers are naturally embedded in the field of rationals all the field functions are applicable to integers (see chapter 6 and 12.7).

Many more functions that are mainly related to the prime residue group of integers modulo an integer are described in chapter 11.

The external functions are in the file LIBNAME/"integer.g".

10.1 Comparisons of Integers

n1 = n2 n1 <> n2 The equality operator = evaluates to true if the integer n1 is equal to the integer n2 and false otherwise. The inequality operator <> evaluates to true if n1 is not equal to n2 and false otherwise. Integers can also be compared to objects of other types; of course, they are never equal.

    gap> 1 = 1;
    true
    gap> 1 <> 0;
    true
    gap> 1 = (1,2);  # (1,2) is a permutation
    false

n1 < n2 n1 <= n2 n1 > n2 n1 >= n2 The operators <, <=, >, and => evaluate to true if the integer n1 is less than, less than or equal to, greater than, or greater than or equal to the integer n2 , respectively. Integers can also be compared to objects of other types, they are considered smaller than any other object, except rationals, where the ordering reflects the ordering of the rationals (see 12.4).

    gap> 1 < 2;
    true
    gap> 1 < -1;
    false
    gap> 1 < 3/2;
    true
    gap> 1 < false;
    true

10.2 Operations for Integers

n1 + n2 The operator + evaluates to the sum of the two integers n1 and n2 . n1 - n2 The operator - evaluates to the difference of the two integers n1 and n2 . n1 * n2 The operator * evaluates to the product of the two integers n1 and n2 . n1 / n2 The operator / evaluates to the quotient of the two integers n1 and n2 . If the divisor does not divide the dividend the quotient is a rational (see 12). If the divisor is 0 an error is signalled. The integer part of the quotient can be computed with QuoInt (see 10.3). n1 mod n2

The operator mod evaluates to the smallest positive representative of the residue class of the left operand modulo the right, i.e., i mod k is the unique m in the range [0 .. AbsInt(k)-1] such that k divides i - m. If the right operand is 0 an error is signalled. The remainder of the division can be computed with RemInt (see 10.4).

n1 ^n2

The operator ^evaluates to the n2 -th power of the integer n1 . If n2 is a positive integer then n1 ^n2 is n1 *n1 *..*n1 (n2 factors). If n2 is a negative integer n1 ^n2 is defined as 1/n1^{−n2} . If 0 is raised to a negative power an error is signalled. Any integer, even 0, raised to the zeroth power yields 1.

Since integers embed naturally into the field of rationals all the rational operations are available for integers too (see 12.5).

For the precedence of the operators see 2.10.

    gap> 2 * 3 + 1;
    7

10.3 QuoInt

QuoInt( n1 , n2 )

QuoInt returns the integer part of the quotient of its integer operands.

If n1 and n2 are positive QuoInt( n1 , n2 ) is the largest positive integer q such that q*n2 <= n1 . If n1 or n2 or both are negative the absolute value of the integer part of the quotient is the quotient of the absolute values of n1 and n2 , and the sign of it is the product of the signs of n1 and n2 .

RemInt (see 10.4) can be used to compute the remainder.

    gap> QuoInt(5,2);  QuoInt(-5,2);  QuoInt(5,-2);  QuoInt(-5,-2);
    2
    -2
    -2
    2

10.4 RemInt

RemInt( n1 , n2 )

RemInt returns the remainder of its two integer operands.

If n2 is not equal to zero RemInt( n1 , n2 ) = n1 - n2 *QuoInt( n1 , n2 ). Note that the rules given for QuoInt (see 10.3) imply that RemInt( n1 , n2 ) has the same sign as n1 and its absolute value is strictly less than the absolute value of n2 . Dividing by 0 signals an error.

    gap> RemInt(5,2);  RemInt(-5,2);  RemInt(5,-2);  RemInt(-5,-2);
    1
    -1
    1

10.5 IsInt

IsInt( obj )

IsInt returns true if obj , which can be an arbitrary object, is an integer and false otherwise. IsInt will signal an error if obj is an unbound variable.

    gap> IsInt( 1 );
    true
    gap> IsInt( IsInt );

false # IsInt is a function, not an integer

10.6 Int

Int( obj )

Int converts an object obj to an integer. If obj is an integer Int will simply return obj .

If obj is a rational number (see 12) Int returns the unique integer that has the same sign as obj and the largest absolute value not larger than the absolute value of obj .

If obj is an element of the prime field of a finite field F , Int returns the least positive integer n such that n*F .one = obj (see 17.8).

If obj is not of one of the above types an error is signalled.

    gap> Int( 17 );
    17
    gap> Int( 17 / 3 );
    5
    gap> Int( Z(5^3)^62 );

4 # Z(5^{3})^{62} = (Z(5^{3})^{124/4})^{2} = Z(5)^{2} = P rimitiveRoot(5)^{2} = 2^{2}

10.7 AbsInt

AbsInt( n )

AbsInt returns the absolute value of the integer n, i.e., n if n is positive, -n if n is negative and 0 if n is 0 (see 10.8).

    gap> AbsInt( 33 );
    33
    gap> AbsInt( -214378 );
    214378
    gap> AbsInt( 0 );
    0

10.8 SignInt

SignInt( obj )

SignInt returns the sign of the integer obj , i.e., 1 if obj is positive, -1 if obj is negative and 0 if obj is 0 (see 10.7).

    gap> SignInt( 33 );

    1
    gap> SignInt( -214378 );
    -1
    gap> SignInt( 0 );
    0

10.9 ChineseRem

ChineseRem( moduli, residues )

ChineseRem returns the combination of the residues modulo the moduli, i.e., the unique integer c from [0..Lcm(moduli)-1] such that c = residues[i] modulo moduli[i] for all i, if it exists. If no such combination exists ChineseRem signals an error.

Such a combination does exist if and only if residues[i]=residues[k] mod Gcd(moduli[i],moduli[k]) for every pair i, k. Note that this implies that such a combination exists if the moduli are pairwise relatively prime. This is called the Chinese remainder theorem.

    gap> ChineseRem( [ 2, 3, 5, 7 ], [ 1, 2, 3, 4 ] );
    53
    gap> ChineseRem( [ 6, 10, 14 ], [ 1, 3, 5 ] );
    103
    gap> ChineseRem( [ 6, 10, 14 ], [ 1, 2, 3 ] );
    Error, the residues must be equal modulo 2

10.10 LogInt

LogInt( n, base )

LogInt returns the integer part of the logarithm of the positive integer n with respect to the positive integer base, i.e., the largest positive integer exp such that base^{exp} <= n. LogInt will signal an error if either n or base is not positive.

    gap> LogInt( 1030, 2 );

10 # 2^{10} = 1024

    gap> LogInt( 1, 10 );
    0

10.11 RootInt

RootInt( n )
RootInt( n, k )

RootInt returns the integer part of the kth root of the integer n. If the optional integer argument k is not given it defaults to 2, i.e., RootInt returns the integer part of the square root in this case.

If n is positive RootInt returns the largest positive integer r such that r^{k} <= n. If n is negative and k is odd RootInt returns -RootInt( -n, k ). If n is negative and k is even RootInt will cause an error. RootInt will also cause an error if k is 0 or negative.

    gap> RootInt( 361 );

19
gap> RootInt( 2 * 10^12 );
1414213
gap> RootInt( 17000, 5 );

7 # 7^{5} = 16807

10.12 SmallestRootInt

SmallestRootInt( n )

SmallestRootInt returns the smallest root of the integer n. The smallest root of an integer n is the integer r of smallest absolute value for which a positive integer k exists such that n = r^{k}.

gap> SmallestRootInt( 2^30 );
2
gap> SmallestRootInt( -(2^30) );

-4 # note that (−2)^{30} = +(2^{30})

gap> SmallestRootInt( 279936 );
6
gap> LogInt( 279936, 6 );
7
gap> SmallestRootInt( 1001 );
1001

SmallestRootInt can be used to identify and decompose powers of primes as is demonstrated in the following example (see 10.17)

p := SmallestRootInt( q );  n := LogInt( q, p );
if not IsPrimeInt(p) then Error("GF: <q> must be a primepower"); fi;

10.13 Set Functions for Integers

As already mentioned in the first section of this chapter, Integers is the domain of all integers. Thus in principle all set theoretic functions, for example Intersection, Size, and so on can be applied to this domain. This seems generally of little use.

gap> Intersection( Integers, [ 0, 1/2, 1, 3/2 ] );
[ 0, 1 ]
gap> Size( Integers );
"infinity"
Random( Integers )

This seems to be the only useful domain function that can be applied to the domain Integers. It returns pseudo random integers between -10 and 10 distributed according to a binomial distribution.

gap> Random( Integers );
1
gap> Random( Integers );
-4

To generate uniformly distributed integers from a range, use the construct Random( [ low .. high ] ).

10.14 Ring Functions for Integers

As was already noted in the introduction to this chapter the integers form a Euclidean ring, so all ring functions (see chapter 5) are applicable to the integers. This section comments on the implementation of those functions for the integers and tells you how you can call the corresponding functions directly, for example to save time.

IsPrime( Integers, n )

This is implemented by IsPrimeInt, which you can call directly to save a little bit of time (see 10.16).

Factors( Integers, n )

This is implemented as by FactorsInt, which you can call directly to save a little bit of time (see 10.20).

EuclideanDegree( Integers, n )

The Euclidean degree of an integer is of course simply the absolute value of the integer. Calling AbsInt directly will be a little bit faster.

EuclideanRemainder( Integers, n, m )

This is implemented as RemInt( n, m ), which you can use directly to save a lot of time.

EuclideanQuotient( Integers, n, m )

This is implemented as QuoInt( n, m ), which you can use directly to save a lot of time.

QuotientRemainder( Integers, n, m )

This is implemented as [ QuoInt(n,m), RemInt(n,m) ], which you can use directly to save a lot of time.

QuotientMod( Integers, n1 , n2 , m )

This is implemented as (n1 / n2 ) mod m, which you can use directly to save a lot of time.

PowerMod( Integers, n, e, m )

This is implemented by PowerModInt, which you can call directly to save a little bit of time. Note that using n ^e mod m will generally be slower, because it can not reduce intermediate results like PowerMod.

Gcd( Integers, n1 , n2 ..  )

This is implemented by GcdInt, which you can call directly to save a lot of time. Note that GcdInt takes only two arguments, not several as Gcd does.

Gcdex( n1 , n2 )

Gcdex returns a record. The component gcd is the gcd of n1 and n2 . The components coeff1 and coeff2 are integer cofactors such that

g.gcd = g.coeff1*n1 + g.coeff2*n2 .

If n1 and n2 both are nonzero, AbsInt( g.coeff1 ) is less than or equal to AbsInt(n2 )

/ (2*g.gcd) and AbsInt( g.coeff2 ) is less than or equal to AbsInt(n1 ) / (2*g.gcd).

The components coeff3 and coeff4 are integer cofactors such that

0 = g.coeff3*n1 + g.coeff4*n2 .

If n1 or n2 or are both nonzero coeff3 is -n2 / g.gcd and coeff4 is n1 / g.gcd.

The coefficients always form a unimodular matrix, i.e., the determinant

g.coeff1*g.coeff4 - g.coeff3*g.coeff2

is 1 or -1.

    gap> Gcdex( 123, 66 );
    rec(
      gcd := 3,
      coeff1 := 7,
      coeff2 := -13,
      coeff3 := -22,
      coeff4 := 41 )

# 3 = 7*123 - 13*66, 0 = -22*123 + 41*66

    gap> Gcdex( 0, -3 );
    rec(
      gcd := 3,
      coeff1 := 0,
      coeff2 := -1,
      coeff3 := 1,
      coeff4 := 0 )
    gap> Gcdex( 0, 0 );
    rec(
      gcd := 0,
      coeff1 := 1,
      coeff2 := 0,
      coeff3 := 0,
      coeff4 := 1 )
Lcm( Integers, n1 , n2 ..  )

This is implemented as LcmInt, which you can call directly to save a little bit of time. Note that LcmInt takes only two arguments, not several as Lcm does.

10.15 Primes

Primes[ n ]

Primes is a set, i.e., a sorted list, of the 168 primes less than 1000.

Primes is used in IsPrimeInt (see 10.16) and FactorsInt (see 10.20) to cast out small prime divisors quickly.

    gap> Primes[1];
    2
    gap> Primes[100];
    541

10.16 IsPrimeInt

IsPrimeInt( n )

IsPrimeInt returns true if the integer n is a prime and false otherwise. By conven-

tion IsPrimeInt( 0 ) = IsPrimeInt( 1 ) = false and we define IsPrimeInt( -n ) =
IsPrimeInt( n ).

Note that IsPrimeInt implements a probabilistic primality test. If IsPrimeInt returns false then its argument definitely is not a prime. However if IsPrimeInt returns true there is a small chance that n is composite. This does not happen for integers smaller than 25 ∗ 10^{9} and happens with probability less than 0.1% for larger integers.

The time taken by IsPrimeInt is approximately proportional to the third power of the number of digits of n. Testing numbers with several hundreds digits is quite feasible.

    gap> IsPrimeInt( 2^31 - 1 );
    true
    gap> IsPrimeInt( 10^42 + 1 );
    false

10.17 IsPrimePowerInt

IsPrimePowerInt( n )

IsPrimePowerInt returns true if the integer n is a prime power and false otherwise.

n is a prime power if there exists a prime p and a positive integer i such that p^{i} = n. If n is negative the condition is that there must exist a negative prime p and an odd positive integer i such that p^{i} = n. 1 and -1 are not prime powers.

Note that IsPrimePowerInt uses SmallestRootInt (see 10.12) and a probabilistic primality test (see 10.16).

    gap> IsPrimePowerInt( 31^5 );
    true
    gap> IsPrimePowerInt( 2^31-1 );

true # 2^{31} − 1 is actually a prime

    gap> IsPrimePowerInt( 2^63-1 );
    false
    gap> Filtered( [-10..10], IsPrimePowerInt );
    [ -8, -7, -5, -3, -2, 2, 3, 4, 5, 7, 8, 9 ]

10.18 NextPrimeInt

NextPrimeInt( n )

NextPrimeInt returns the smallest prime which is strictly larger than the integer n.

Note that NextPrimeInt uses a probabilistic test (see 10.16).

    gap> NextPrimeInt( 541 );
    547
    gap> NextPrimeInt( -1 );
    2

10.19 PrevPrimeInt

PrevPrimeInt( n )

PrevPrimeInt returns the largest prime which is strictly smaller than the integer n.

Note that PrevPrimeInt uses a probabilistic test (see 10.16).

    gap> PrevPrimeInt( 541 );
    523
    gap> PrevPrimeInt( 1 );
    -2

10.20 FactorsInt

FactorsInt( n )

FactorsInt returns a list of the prime factors of the integer n. If the ith power of a prime divides n this prime appears i times. The list is sorted, that is the smallest prime factors come first. The first element has the same sign as n, the others are positive. For any integer

n it holds that Product( FactorsInt( n ) ) = n.

Note that FactorsInt uses a probabilistic primality test (see 10.16). Thus FactorsInt might return a list which contains composite integers.

The time taken by FactorsInt is approximately proportional to the square root of the second largest prime factor of n, which is the last one that FactorsInt has to find, since the largest factor is simply what remains when all others have been removed. Thus the time is roughly bounded by the fourth root of n. FactorsInt is guaranteed to find all factors less than 10^{6} and will find most factors less than 10^{10}. If n contains multiple factors larger than that FactorsInt may not be able to factor n and will then signal an error.

    gap> FactorsInt( -Factorial(6) );
    [ -2, 2, 2, 2, 3, 3, 5 ]
    gap> Set( FactorsInt( Factorial(13)/11 ) );
    [ 2, 3, 5, 7, 13 ]
    gap> FactorsInt( 2^63 - 1 );
    [ 7, 7, 73, 127, 337, 92737, 649657 ]
    gap> FactorsInt( 10^42 + 1 );
    [ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]

10.21 DivisorsInt

DivisorsInt( n )

DivisorsInt returns a list of all positive divisors of the integer n. The list is sorted, so it starts with 1 and ends with n. We define DivisorsInt( -n ) = DivisorsInt( n ). Since the set of divisors of 0 is infinite calling DivisorsInt( 0 ) causes an error.

DivisorsInt calls FactorsInt (see 10.20) to obtain the prime factors. Sigma (see 10.22) computes the sum, Tau (see 10.23) the number of positive divisors.

    gap> DivisorsInt( 1 );
    [ 1 ]
    gap> DivisorsInt( 20 );
    [ 1, 2, 4, 5, 10, 20 ]
    gap> DivisorsInt( 541 );
    [ 1, 541 ]

10.22 Sigma

Sigma( n )

Sigma returns the sum of the positive divisors (see 10.21) of the integer n.

Sigma is a multiplicative arithmetic function, i.e., if n and m are relatively prime we have σ(nm) = σ(n)σ(m). Together with the formula σ(p^{e}) = (p^{e+1} − 1)/(p − 1) this allows you to compute σ(n).

Integers n for which σ(n) = 2n are called perfect. Even perfect integers are exactly of the form 2^{n−1}(2^{n} − 1) where 2^{n} − 1 is prime. Primes of the form 2^{n} − 1 are called Mersenne primes, the known ones are obtained for n = 2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, and 756839. It is not known whether odd perfect integers exist, however [BC89] show that any such integer must have at least 300 decimal digits.

Sigma usually spends most of its time factoring n (see 10.20).

    gap> Sigma( 0 );
    Error, Sigma: <n> must not be 0
    gap> Sigma( 1 );
    1
    gap> Sigma( 1009 );

1010 # thus 1009 is a prime

    gap> Sigma( 8128 ) = 2*8128;

true # thus 8128 is a perfect number

10.23 Tau

Tau( n )

Tau returns the number of the positive divisors (see 10.21) of the integer n.

Tau is a multiplicative arithmetic function, i.e., if n and m are relatively prime we have τ(nm) = τ(n)τ(m). Together with the formula τ(p^{e}) = e + 1 this allows us to compute τ(n).

Tau usually spends most of its time factoring n (see 10.20).

    gap> Tau( 0 );
    Error, Tau: <n> must not be 0
    gap> Tau( 1 );
    1
    gap> Tau( 1013 );

2 # thus 1013 is a prime

    gap> Tau( 8128 );
    14
    gap> Tau( 36 );

9 # τ(n) is odd if and only if n is a perfect square

10.24 MoebiusMu

MoebiusMu( n )

MoebiusMu computes the value of the Moebius function for the integer n. This is 0 for integers which are not squarefree, i.e., which are divisible by a square r^{2}. Otherwise it is 1 if n has an even number and -1 if n has an odd number of prime factors.

The importance of μ stems from the so called inversion formula. Suppose f(n) is a function defined on the positive integers and let g(n) = ∑_{d|n} f(d). Then f(n) = ∑_{d|n} μ(d)g(n/d). As a special case we have ϕ(n) = ∑_{d|n} μ(d)n/d since n = ∑_{d|n} ϕ(d) (see 11.2).

MoebiusMu usually spends all of its time factoring n (see 10.20).

    gap> MoebiusMu( 60 );
    0
    gap> MoebiusMu( 61 );
    -1
    gap> MoebiusMu( 62 );

Number Theory

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. This chapter describes the functions that deal with this group.

The first section describes the function that computes the set of representatives of the group (see 11.1).

The next sections describe the functions that compute the size and the exponent of the group (see 11.2 and 11.3).

The next section describes the function that computes the order of an element in the group (see 11.4).

The next section describes the functions that test whether a residue generates the group or computes a generator of the group, provided it is cyclic (see 11.5, 11.6).

The next section describes the functions that test whether an element is a square in the group (see 11.7 and 11.8).

The next sections describe the functions that compute general roots in the group (see 11.9 and 11.10).

All these functions are in the file LIBNAME/"numtheor.g".

11.1 PrimeResidues

PrimeResidues( m )

PrimeResidues returns the set of integers from the range 0..Abs(m) − 1 that are relatively prime to the integer m.

Abs(m) must be less than 2^{28}, otherwise the set would probably be too large anyhow.

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. ϕ(m) (see 11.2) is the order of this group, λ(m) (see 11.3) the exponent. If and only if m is 2, 4, an odd prime power p^{e}, or twice an odd prime power 2p^{e}, this group is cyclic. In this case the generators of the group, i.e., elements of order ϕ(m), are called primitive roots (see 11.5, 11.6).

    gap> PrimeResidues( 0 );

    [  ]
    gap> PrimeResidues( 1 );
    [ 0 ]
    gap> PrimeResidues( 20 );
    [ 1, 3, 7, 9, 11, 13, 17, 19 ]

11.2 Phi

Phi( m )

Phi returns the value of the Euler totient function ϕ(m) for the integer m. ϕ(m) is defined as the number of positive integers less than or equal to m that are relatively prime to m.

Suppose that m = p_{1}^{e_{1}}p_{2}^{e_{2}}...p_{k}^{e_{k}}. Then ϕ(m) is p_{1}^{e_{1}−1}(p_{1} − 1)p_{2}^{e_{2}−1}(p_{2} − 1)...p_{k}^{e_{k}−1}(p_{k} − 1). It follows that m is a prime if and only if ϕ(m) = m − 1.

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. It can be computed with PrimeResidues (see 11.1). ϕ(m) is the order of this group, λ(m) (see 11.3) the exponent. If and only if m is 2, 4, an odd prime power p^{e}, or twice an odd prime power 2p^{e}, this group is cyclic. In this case the generators of the group, i.e., elements of order ϕ(m), are called primitive roots (see 11.5, 11.6).

Phi usually spends most of its time factoring m (see 10.20).

    gap> Phi( 12 );
    4
    gap> Phi( 2^13-1 );

8190 # which proves that 2^{13} − 1 is a prime

    gap> Phi( 2^15-1 );
    27000

11.3 Lambda

Lambda( m )

Lambda returns the exponent of the group of relatively prime residues modulo the integer m.

λ(m) is the smallest positive integer l such that for every a relatively prime to m we have a^{l} = 1 mod m. Fermat’s theorem asserts a^{ϕ(m)} = 1 mod m, thus λ(m) divides ϕ(m) (see 11.2).

Carmichael’s theorem states that λ can be computed as follows λ(2) = 1, λ(4) = 2 and λ(2^{e}) = 2^{e−2} if 3 <= e, λ(p^{e}) = (p − 1)p^{e−1} (= ϕ(p^{e})) if p is an odd prime, and λ(nm) = Lcm(λ(n), λ(m)) if n, m are relatively prime.

Composites for which λ(m) divides m − 1 are called Carmichaels. If 6k + 1, 12k + 1 and 18k+1 are primes their product is such a number. It is believed but unproven that there are infinitely many Carmichaels. There are only 1547 Carmichaels below 10^{10} but 455052511 primes.

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. It can be computed with PrimeResidues (see 11.1). ϕ(m) (see 11.2) is the order of this group, λ(m) the exponent. If and only if m is 2, 4, an odd prime power p^{e}, or twice an odd prime power 2p^{e}, this group is cyclic. In this case the generators of the group, i.e., elements of order ϕ(m), are called primitive roots (see 11.5, 11.6).

Lambda usually spends most of its time factoring m (see 10.20).

    gap> Lambda( 10 );
    4
    gap> Lambda( 30 );
    4
    gap> Lambda( 561 );

80 # 561 is the smallest Carmichael number

11.4 OrderMod

OrderMod( n, m )

OrderMod returns the multiplicative order of the integer n modulo the positive integer m. If n is less than 0 or larger than m it is replaced by its remainder. If n and m are not relatively prime the order of n is not defined and OrderMod will return 0.

If n and m are relatively prime the multiplicative order of n modulo m is the smallest positive integer i such that n^{i} = 1 mod m. Elements of maximal order are called primitive roots (see 11.2).

OrderMod usually spends most of its time factoring m and ϕ(m) (see 10.20).

    gap> OrderMod( 2, 7 );
    3
    gap> OrderMod( 3, 7 );

6 # 3 is a primitive root modulo 7

11.5 IsPrimitiveRootMod

IsPrimitiveRootMod( r, m )

IsPrimitiveRootMod returns true if the integer r is a primitive root modulo the positive integer m and false otherwise. If r is less than 0 or larger than m it is replaced by its remainder.

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. It can be computed with PrimeResidues (see 11.1). ϕ(m) (see 11.2) is the order of this group, λ(m) (see 11.3) the exponent. If and only if m is 2, 4, an odd prime power p^{e}, or twice an odd prime power 2p^{e}, this group is cyclic. In this case the generators of the group, i.e., elements of order ϕ(m), are called primitive roots (see also 11.6).

    gap> IsPrimitiveRootMod( 2, 541 );
    true
    gap> IsPrimitiveRootMod( -539, 541 );

true # same computation as above

    gap> IsPrimitiveRootMod( 4, 541 );
    false
    gap> ForAny( [1..29], r -> IsPrimitiveRootMod( r, 30 ) );

false # there does not exist a primitive root modulo 30

11.6 PrimitiveRootMod

PrimitiveRootMod( m )
PrimitiveRootMod( m, start )

PrimitiveRootMod returns the smallest primitive root modulo the positive integer m and false if no such primitive root exists. If the optional second integer argument start is given PrimitiveRootMod returns the smallest primitive root that is strictly larger than start.

The integers relatively prime to m form a group under multiplication modulo m, called the prime residue group. It can be computed with PrimeResidues (see 11.1). ϕ(m) (see 11.2) is the order of this group, λ(m) (see 11.3) the exponent. If and only if m is 2, 4, an odd prime power p^{e}, or twice an odd prime power 2p^{e}, this group is cyclic. In this case the generators of the group, i.e., elements of order ϕ(m), are called primitive roots (see also 11.5).

    gap> PrimitiveRootMod( 409 );

21 # largest primitive root for a prime less than 2000

    gap> PrimitiveRootMod( 541, 2 );
    10
    gap> PrimitiveRootMod( 337, 327 );

false # 327 is the largest primitive root mod 337

    gap> PrimitiveRootMod( 30 );

false # the exists no primitive root modulo 30

11.7 Jacobi

Jacobi( n, m )

Jacobi returns the value of the Jacobi symbol of the integer n modulo the integer m.

Suppose that m = p_{1}p_{2}..p_{k} as a product of primes, not necessarily distinct. Then for n relatively prime to m the Jacobi symbol is defined by J(n/m) = L(n/p_{1})L(n/p_{2})..L(n/p_{k}), where L(n/p) is the Legendre symbol (see 11.8). By convention J(n/1) = 1. If the gcd of n and m is larger than 1 we define J(n/m) = 0.

If n is an quadratic residue modulo m, i.e., if there exists an r such that r^{2} = n mod m then J(n/m) = 1. However J(n/m) = 1 implies the existence of such an r only if m is a prime.

Jacobi is very efficient, even for large values of n and m, it is about as fast as the Euclidean algorithm (see 5.26).

    gap> Jacobi( 11, 35 );

1 # 9^{2} = 11 mod 35

    gap> Jacobi( 6, 35 );

-1 # thus there is no r such that r^{2} = 6 mod 35

    gap> Jacobi( 3, 35 );

1 # even though there is no r with r^{2} = 3 mod 35

11.8 Legendre

Legendre( n, m )

Legendre returns the value of the Legendre symbol of the integer n modulo the positive integer m.

The value of the Legendre symbol L(n/m) is 1 if n is a quadratic residue modulo m, i.e., if there exists an integer r such that r^{2} = n mod m and -1 otherwise.

If a root of n exists it can be found by RootMod (see 11.9).

While the value of the Legendre symbol usually is only defined for m a prime, we have extended the definition to include composite moduli too. The Jacobi symbol (see 11.7) is another generalization of the Legendre symbol for composite moduli that is much cheaper to compute, because it does not need the factorization of m (see 10.20).

    gap> Legendre( 5, 11 );

1 # 4^{2} = 5 mod 11

    gap> Legendre( 6, 11 );

-1 # thus there is no r such that r^{2} = 6 mod 11

    gap> Legendre( 3, 35 );

-1 # thus there is no r such that r^{2} = 3 mod 35

11.9 RootMod

RootMod( n, m )
RootMod( n, k, m )

In the first form RootMod computes a square root of the integer n modulo the positive integer m, i.e., an integer r such that r^{2} = n mod m. If no such root exists RootMod returns false.

A root of n exists only if Legendre(n,m) = 1 (see 11.8). If m has k different prime factors then there are 2^{k} different roots of n mod m. It is unspecified which one RootMod returns. You can, however, use RootsUnityMod (see 11.10) to compute the full set of roots.

In the second form RootMod computes a kth root of the integer n modulo the positive integer m, i.e., an integer r such that r^{k} = n mod m. If no such root exists RootMod returns false.

In the current implementation k must be a prime.

RootMod is efficient even for large values of m, actually most time is usually spent factoring m (see 10.20).

    gap> RootMod( 64, 1009 );

1001 # note RootMod does not return 8 in this case but -8

    gap> RootMod( 64, 3, 1009 );
    518
    gap> RootMod( 64, 5, 1009 );
    656
    gap> List( RootMod( 64, 1009 ) * RootsUnityMod( 1009 ),
    >          x -> x mod 1009 );

[ 1001, 8 ] # set of all square roots of 64 mod 1009

11.10 RootsUnityMod

RootsUnityMod( m )
RootsUnityMod( k, m )

In the first form RootsUnityMod computes the square roots of 1 modulo the integer m, i.e., the set of all positive integers r less than n such that r^{2} = 1 mod m.

In the second form RootsUnityMod computes the kth roots of 1 modulo the integer m, i.e., the set of all positive integers r less than n such that r^{k} = 1 mod m.

In general there are k^{n} such roots if the modulus m has n different prime factors p such that p = 1 mod k. If k^{2} divides m then there are k^{n+1} such roots; and especially if k = 2 and 8 divides m there are 2^{n+2} such roots.

If you are interested in the full set of roots of another number instead of 1 use RootsUnityMod together with RootMod (see 11.9).

In the current implementation k must be a prime.

RootsUnityMod is efficient even for large values of m, actually most time is usually spent factoring m (see 10.20).

    gap> RootsUnityMod(7*31);
    [ 1, 92, 125, 216 ]
    gap> RootsUnityMod(3,7*31);
    [ 1, 25, 32, 36, 67, 149, 156, 191, 211 ]
    gap> RootsUnityMod(5,7*31);
    [ 1, 8, 64, 78, 190 ]
    gap> List( RootMod( 64, 1009 ) * RootsUnityMod( 1009 ),
    >          x -> x mod 1009 );

[ 1001, 8 ] # set of all square roots of 64 mod 1009

Rationals

The rationals form a very important field. On the one hand it is the quotient field of the integers (see 10). On the other hand it is the prime field of the fields of characteristic zero (see 15).

The former comment suggests the representation actually used. A rational is represented as a pair of integers, called numerator and denominator. Numerator and denominator are reduced, i.e., their greatest common divisor is 1. If the denominator is 1, the rational is in fact an integer and is represented as such. The numerator holds the sign of the rational, thus the denominator is always positive.

Because the underlying integer arithmetic can compute with arbitrary size integers, the rational arithmetic is always exact, even for rationals whose numerators and denominators have thousands of digits.

    gap> 2/3;
    2/3
    gap> 66/123;

22/41 # numerator and denominator are made relatively prime

    gap> 17/-13;

-17/13 # the numerator carries the sign

    gap> 121/11;

11 # rationals with denominator 1 (after cancelling) are integers

The first sections of this chapter describe the functions that test whether an object is a rational (see 12.1), and select the numerator and denominator of a rational (see 12.2, 12.3).

The next sections describe the rational operations (see 12.4, and 12.5).

The GAP object Rationals is the field domain of all rationals. All set theoretic functions are applicable to this domain (see chapter 4 and 12.6). Since Rationals is a field all field functions are also applicable to this domain and its elements (see chapter 6 and 12.7).

All external functions are defined in the file "LIBNAME/rational.g".

12.1 IsRat

IsRat( obj )

IsRat returns true if obj , which can be an arbitrary object, is a rational and false otherwise. Integers are rationals with denominator 1, thus IsRat returns true for integers. IsRat will signal an error if obj is an unbound variable or a procedure call.

gap> IsRat( 2/3 );
true
gap> IsRat( 17/-13 );
true
gap> IsRat( 11 );
true
gap> IsRat( IsRat );

false # IsRat is a function, not a rational

12.2 Numerator

Numerator( rat )

Numerator returns the numerator of the rational rat. Because the numerator holds the sign of the rational it may be any integer. Integers are rationals with denominator 1, thus Numerator is the identity function for integers.

gap> Numerator( 2/3 );
2
gap> Numerator( 66/123 );

22 # numerator and denominator are made relatively prime

gap> Numerator( 17/-13 );

-17 # the numerator holds the sign of the rational

gap> Numerator( 11 );

11 # integers are rationals with denominator 1

Denominator (see 12.3) is the counterpart to Numerator.

12.3 Denominator

Denominator( rat )

Denominator returns the denominator of the rational rat. Because the numerator holds the sign of the rational the denominator is always a positive integer. Integers are rationals with the denominator 1, thus Denominator returns 1 for integers.

gap> Denominator( 2/3 );
3
gap> Denominator( 66/123 );

41 # numerator and denominator are made relatively prime

gap> Denominator( 17/-13 );

13 # the denominator holds the sign of the rational

gap> Denominator( 11 );

1 # integers are rationals with denominator 1

Numerator (see 12.2) is the counterpart to Denominator.

12.4 Comparisons of Rationals

q1 = q2 q1 <> q2

The equality operator = evaluates to true if the two rationals q1 and q2 are equal and to false otherwise. The inequality operator <> evaluates to true if the two rationals q1 and q2 are not equal and to false otherwise.

    gap> 2/3 = -4/-6;
    true
    gap> 66/123 <> 22/41;
    false
    gap> 17/13 = 11;
    false

q1 < q2 q1 <= q2 q1 > q2 q1 >= q2

The operators <, <=, >, and => evaluate to true if the rational q1 is less than, less than or equal to, greater than, and greater than or equal to the rational q2 and to false otherwise.

One rational q_{1} = n_{1}/d_{1} is less than another q_{2} = n_{2}/d_{2} if and only if n_{1}d_{2} < n_{2}d_{2}. This definition is of course only valid because the denominator of rationals is always defined to be positive. This definition also extends to the comparison of rationals with integers, which are interpreted as rationals with denominator 1. Rationals can also be compared with objects of other types. They are smaller than objects of any other type by definition.

    gap> 2/3 < 22/41;
    false
    gap> -17/13 < 11;
    true

12.5 Operations for Rationals

q1 + q2 q1 - q2 q1 * q2 q1 / q2

The operators +, -, * and / evaluate to the sum, difference, product, and quotient of the two rationals q1 and q2 . For the quotient / q2 must of course be nonzero, otherwise an error is signalled. Either operand may also be an integer i, which is interpreted as a rational with denominator 1. The result of those operations is always reduced. If, after the reduction, the denominator is 1, the rational is in fact an integer, and is represented as such.

    gap> 2/3 + 4/5;
    22/15
    gap> 7/6 * 2/3;

7/9 # note how the result is cancelled

    gap> 67/6 - 1/6;

11 # the result is an integer

q ^i

The powering operator ^returns the i-th power of the rational q. i must be an integer. If the exponent i is zero, q^i is defined as 1; if i is positive, q^i is defined as the i-fold product q*q*..*q; finally, if i is negative, q^i is defined as (1/q)^-i. In this case q must of course be nonzero.

    gap> (2/3) ^3;
    8/27
    gap> (-17/13) ^-1;

-13/17 # note how the sign switched

    gap> (1/2) ^-2;
    4

12.6 Set Functions for Rationals

As was already mentioned in the introduction of this chapter the GAP object Rationals is the domain of all rationals. All set theoretic functions, e.g., Intersection and Size, are applicable to this domain.

    gap> Intersection( Rationals, [ E(4)^0, E(4)^1, E(4)^2, E(4)^3 ] );

[ -1, 1 ] # E(4) is the complex square root of -1

    gap> Size( Rationals );
    "infinity"

This does not seem to be very useful.

12.7 Field Functions for Rationals

As was already mentioned in the introduction of this chapter the GAP object Rationals is the field of all rationals. All field functions, e.g., Norm and MinPol are applicable to this domain and its elements. However, since the field of rationals is the prime field, all those functions are trivial. Therefore, Conjugates( Rationals, q ) returns [ q ], Norm(

Rationals, q ) and Trace( Rationals, q ) return q, and CharPol( Rationals, q )
and MinPol( Rationals, q ) both return [ -q, 1 ].

Cyclotomics

GAP allows computations in abelian extension fields of the rational field Q, i.e., fields with abelian Galois group over Q. These fields are described in chapter 15. They are subfields of cyclotomic fields Q_{n} = Q(e_{n}) where e_{n} = e^{\frac{2πi}{n}} is a primitive n–th root of unity. Their elements are called cyclotomics.

The internal representation of a cyclotomic does not refer to the smallest number field but the smallest cyclotomic field containing it (the so–called conductor). This is because it is easy to embed two cyclotomic fields in a larger one that contains both, i.e., there is a natural way to get the sum or the product of two arbitrary cyclotomics as element of a cyclotomic field. The disadvantage is that the arithmetical operations are too expensive to do arithmetics in number fields, e.g., calculations in a matrix ring over a number field. But it suffices to deal with irrationalities in character tables (see 42). (And in fact, the comfortability of working with the natural embeddings is used there in many situations which did not actually afford it …)

All functions that take a field extension as —possibly optional— argument, e.g., Trace or Coefficients (see chapter 6), are described in chapter 15.

This chapter informs about the representation of cyclotomics in GAP (see 13.1), access to the internal data (see 13.7, 13.8) integral elements of number fields (see 13.2, 13.3, 13.4), characteristic functions (see 13.5, 13.6), comparison and arithmetical operations of cyclotomics (see 13.9, 13.10), functions concerning Galois conjugacy of cyclotomics (see 13.11, 13.13), or lists of them (see 13.15, 13.16), some special cyclotomics, as defined in [CCN^{+}85] (see 13.12, 13.14)

The external functions are in the file LIBNAME/"cyclotom.g".

13.1 More about Cyclotomics

Elements of number fields (see chapter 15), cyclotomics for short, are arithmetical objects like rationals and finite field elements; they are not implemented as records —like groups— or e.g. with respect to a character table (although character tables may be the main interest for cyclotomic arithmetics).

E( n )

returns the primitive n-th root of unity e_{n} = e^{\frac{2πi}{n}}. Cyclotomics are usually entered as (and irrational cyclotomics are always displayed as) sums of roots of unity with rational coefficients. (For special cyclotomics, see 13.12.)

    gap> E(9); E(9)^3; E(6); E(12) / 3;

-E(9)^4-E(9)^7 # the root needs not to be an element of the base

    E(3)
    -E(3)^2
    -1/3*E(12)^7

For the representation of cyclotomics one has to recall that the cyclotomic field Q_{n} = Q(e_{n}) is a vector space of dimension φ(n) over the rationals where φ denotes Euler’s phi-function (see 11.2).

Note that the set of all n-th roots of unity is linearly dependent for n > 1, so multiplication is not the multiplication of the group ring Q⟨e_{n}⟩; given a Q-basis of Q_{n} the result of the multiplication (computed as multiplication of polynomials in e_{n}, using (e_{n})^{n} = 1) will be converted to the base.

    gap> E(5) * E(5)^2; ( E(5) + E(5)^4 ) * E(5)^2;
    E(5)^3
    E(5)+E(5)^3
    gap> ( E(5) + E(5)^4 ) * E(5);
    -E(5)-E(5)^3-E(5)^4

Cyclotomics are always represented in the smallest cyclotomic field they are contained in. Together with the choice of a fixed base this means that two cyclotomics are equal if and only if they are equally represented.

Addition and multiplication of two cyclotomics represented in Q_{n} and Q_{m}, respectively, is computed in the smallest cyclotomic field containing both: Q_{Lcm(n,m)}. Conversely, if the result is contained in a smaller cyclotomic field the representation is reduced to the minimal such field.

The base, the base conversion and the reduction to the minimal cyclotomic field are described in [Zum89], more about the base can be found in 15.9.

Since n must be a short integer, the maximal cyclotomic field implemented in GAP is not really the field Q^{ab}. The biggest allowed (though not very useful) n is 65535.

There is a global variable Cyclotomics in GAP, a record that stands for the domain of all cyclotomics (see chapter 15).

13.2 Cyclotomic Integers

A cyclotomic is called integral or cyclotomic integer if all coefficients of its minimal polynomial are integers. Since the base used is an integral base (see 15.9), the subring of cyclotomic integers in a cyclotomic field is formed by those cyclotomics which have not only rational but integral coefficients in their representation as sums of roots of unity. For example, square roots of integers are cyclotomic integers (see 13.12), any root of unity is a cyclotomic integer, character values are always cyclotomic integers, but all rationals which are not integers are not cyclotomic integers. (See 13.6)

gap> ER( 5 ); # The square root of 5 is a cyclotomic E(5)-E(5)^2-E(5)^3+E(5)^4 # integer, it has integral coefficients. gap> 1/2 * ER( 5 ); # This is not a cyclotomic integer, …

1/2*E(5)-1/2*E(5)^2-1/2*E(5)^3+1/2*E(5)^4
gap> 1/2 * ER( 5 ) - 1/2;  # . . . but this is one.
E(5)+E(5)^4

13.3 IntCyc

IntCyc( z )

returns the cyclotomic integer (see 13.2) with Zumbroich base coefficients (see 15.9) List( zumb, x -> Int( x ) ) where zumb is the vector of Zumbroich base coefficients of the cyclotomic z; see also 13.4.

gap> IntCyc( E(5)+1/2*E(5)^2 ); IntCyc( 2/3*E(7)+3/2*E(4) );
E(5)
E(4)

13.4 RoundCyc

RoundCyc( z )

returns the cyclotomic integer (see 13.2) with Zumbroich base coefficients (see 15.9) List( zumb, x -> Int( x+1/2 ) ) where zumb is the vector of Zumbroich base coefficients of the cyclotomic z; see also 13.3.

gap> RoundCyc( E(5)+1/2*E(5)^2 ); RoundCyc( 2/3*E(7)+3/2*E(4) );
E(5)+E(5)^2
-2*E(28)^3+E(28)^4-2*E(28)^11-2*E(28)^15-2*E(28)^19-2*E(28)^23
 -2*E(28)^27

13.5 IsCyc

IsCyc( obj )

returns true if obj is a cyclotomic, and false otherwise. Will signal an error if obj is an unbound variable.

gap> IsCyc( 0 ); IsCyc( E(3) ); IsCyc( 1/2 * E(3) ); IsCyc( IsCyc );
true
true
true
false

IsCyc is an internal function.

13.6 IsCycInt

IsCycInt( obj )

returns true if obj is a cyclotomic integer (see 13.2), false otherwise. Will signal an error if obj is an unbound variable.

    gap> IsCycInt( 0 ); IsCycInt( E(3) ); IsCycInt( 1/2 * E(3) );
    true
    true
    false

IsCycInt is an internal function.

13.7 NofCyc

NofCyc( z )
NofCyc( list )

returns the smallest positive integer n for which the cyclotomic z is resp. for which all cyclotomics in the list list are contained in Q_{n} = Q(e^{\frac{2πi}{n}}) = Q(E(n)).

    gap> NofCyc( 0 ); NofCyc( E(10) ); NofCyc( E(12) );
    1
    5
    12

NofCyc is an internal function.

13.8 CoeffsCyc

CoeffsCyc( z, n )

If z is a cyclotomic which is contained in Q_{n}, CoeffsCyc( z, n ) returns a list cfs of length n where the entry at position i is the coefficient of E(n)^{i−1} in the internal representation of z as element of the cyclotomic field Q_{n} (see 13.1, 15.9): z = cfs[1] + cfs[2] E(n)^{1} + … + cfs[n] E(n)^{n−1}.

Note that all positions which do not belong to base elements of Q_{n} contain zeroes.

    gap> CoeffsCyc( E(5), 5 ); CoeffsCyc( E(5), 15 );
    [ 0, 1, 0, 0, 0 ]
    [ 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0 ]
    gap> CoeffsCyc( 1+E(3), 9 ); CoeffsCyc( E(5), 7 );
    [ 0, 0, 0, 0, 0, 0, -1, 0, 0 ]
    Error, no representation of <z> in 7th roots of unity

CoeffsCyc calls the internal function COEFFSCYC:

COEFFSCYC( z )

is equivalent to CoeffsCyc( z, NofCyc( z ) ), see 13.7.

13.9 Comparisons of Cyclotomics

To compare cyclotomics, the operators <, <=, =, >=, > and <> can be used, the result will be true if the first operand is smaller, smaller or equal, equal, larger or equal, larger, or inequal, respectively, and false otherwise.

Cyclotomics are ordered as follows: The relation between rationals is as usual, and rationals are smaller than irrational cyclotomics. For two irrational cyclotomics z1 , z2 which lie in different minimal cyclotomic fields, we have z1 < z2 if and only if NofCyc(z1 ) < NofCyc(z2 )); if NofCyc(z1 ) = NofCyc(z2 )), that one is smaller that has the smaller coefficient vector, i.e., z1 ≤ z2 if and only if COEFFSCYC(z1 ) ≤ COEFFSCYC(z2 ). You can compare cyclotomics with objects of other types; all objects which are not cyclotomics are larger than cyclotomics.

gap> E(5) < E(6); # the latter value lies in Q_{3}

false

gap> E(3) < E(3)^2; # both lie in Q_{3}, so compare coefficients

false
gap> 3 < E(3); E(5) < E(7);
true
true
gap> E(728) < (1,2);
true

13.10 Operations for Cyclotomics

The operators +, -, *, / are used for addition, subtraction, multiplication and division of two cyclotomics; note that division by 0 causes an error. + and - can also be used as unary operators; ^is used for exponentiation of a cyclotomic with an integer; this is in general not equal to Galois conjugation.

gap> E(5) + E(3); (E(5) + E(5)^4) ^2; E(5) / E(3); E(5) * E(3);
-E(15)^2-2*E(15)^8-E(15)^11-E(15)^13-E(15)^14
-2*E(5)-E(5)^2-E(5)^3-2*E(5)^4
E(15)^13
E(15)^8

13.11 GaloisCyc

GaloisCyc( z, k )

returns the cyclotomic obtained on raising the roots of unity in the representation of the cyclotomic z to the k-th power. If z is represented in the field Q_{n} and k is a fixed integer relative prime to n, GaloisCyc( ., k ) acts as a Galois automorphism of Q_{n} (see 15.8); to get Galois automorphisms as functions, use 6.7 GaloisGroup.

gap> GaloisCyc( E(5) + E(5)^4, 2 );
E(5)^2+E(5)^3
gap> GaloisCyc( E(5), -1 );  # the complex conjugate
E(5)^4
gap> GaloisCyc( E(5) + E(5)^4, -1 ); # this value is real
E(5)+E(5)^4
gap> GaloisCyc( E(15) + E(15)^4, 3 );
E(5)+E(5)^4

GaloisCyc is an internal function.

13.12 ATLAS irrationalities

EB( N ), EC( N ), . . ., EH( N ),
EI( N ), ER( N ),
EJ( N ), EK( N ), EL( N ), EM( N ),
EJ( N , d ), EK( N , d ), EL( N , d ), EM( N , d ),
ES( N ), ET( N ), . . ., EY( N ),
ES( N , d ), ET( N , d ), . . ., EY( N , d ),
NK( N , k, d )

For N a positive integer, let z = E(N ) = e^{2πi/N}. The following so-called atomic irrationalities (see [CCN^{+}85, Chapter 7, Section 10]) can be entered by functions (Note that the values are not necessary irrational.):

EB(N ) = b_{N} = \frac{1}{2} ∑_{j=1}^{N−1} z^{j^{2}} (N ≡ 1 mod 2)
EC(N ) = c_{N} = \frac{1}{3} ∑_{j=1}^{N−1} z^{j^{3}} (N ≡ 1 mod 3)
ED(N ) = d_{N} = \frac{1}{4} ∑_{j=1}^{N−1} z^{j^{4}} (N ≡ 1 mod 4)
EE(N ) = e_{N} = \frac{1}{5} ∑_{j=1}^{N−1} z^{j^{5}} (N ≡ 1 mod 5)
EF(N ) = f_{N} = \frac{1}{6} ∑_{j=1}^{N−1} z^{j^{6}} (N ≡ 1 mod 6)
EG(N ) = g_{N} = \frac{1}{7} ∑_{j=1}^{N−1} z^{j^{7}} (N ≡ 1 mod 7)
EH(N ) = h_{N} = \frac{1}{8} ∑_{j=1}^{N−1} z^{j^{8}} (N ≡ 1 mod 8)

(Note that in c_{N}, …, h_{N}, N must be a prime.)

\begin{aligned}ER(N ) & = & \sqrt{N} \\ EI(N ) & = & i\sqrt{N} & = & \sqrt{−N} \end{aligned}

From a theorem of Gauss we know that

b_{N} = \{ \begin{matrix}\frac{1}{2}(−1 + \sqrt{N}) & if & N ≡ 1 & mod4 \\ \frac{1}{2}(−1 + i\sqrt{N}) & if & N ≡ −1 & mod4 \end{matrix} ,

so \sqrt{N} can be (and in fact is) computed from b_{N}.

For given N , let n_{k} = n_{k}(N) be the first integer with multiplicative order exactly k modulo N , chosen in the order of preference

1, −1, 2, −2, 3, −3, 4, −4, ….

We have

EY(N ) = y_{n} = z + z^{n}  (n = n_{2})
EX(N ) = x_{n} = z + z^{n} + z^{n^{2}}  (n = n_{3})
EW(N ) = w_{n} = z + z^{n} + z^{n^{2}} + z^{n^{3}}  (n = n_{4})
EV(N ) = v_{n} = z + z^{n} + z^{n^{2}} + z^{n^{3}} + z^{n^{4}} (n = n_{5})
EU(N ) = u_{n} = z + z^{n} + z^{n^{2}} + … + z^{n^{5}} (n = n_{6})
ET(N ) = t_{n} = z + z^{n} + z^{n^{2}} + … + z^{n^{6}} (n = n_{7})
ES(N ) = s_{n} = z + z^{n} + z^{n^{2}} + … + z^{n^{7}} (n = n_{8})

EM(N ) = m_{n} = z − z^{n}  (n = n_{2})
EL(N ) = l_{n} = z − z^{n} + z^{n^{2}} − z^{n^{3}} (n = n_{4})
EK(N ) = k_{n} = z − z^{n} + … − z^{n^{5}} (n = n_{6})
EJ(N ) = j_{n} = z − z^{n} + … − z^{n^{7}} (n = n_{8})

Let n_{k}^{(d)} = n_{k}^{(d)}(N) be the d + 1-th integer with multiplicative order exactly k modulo N , chosen in the order of preference defined above; we write n_{k} = n_{k}^{(0)}, n′_{k} = n_{k}^{(1)}, n′′_{k} = n_{k}^{(2)} and so on. These values can be computed as NK(N ,k,d)= n_{k}^{(d)}(N); if there is no integer with the required multiplicative order, NK will return false.

The algebraic numbers

y′_{N} = y_{N}^{(1)}, y′′_{N} = y_{N}^{(2)}, …, x′_{N}, x′′_{N}, …, j′_{N}, j′′_{N}, …

are obtained on replacing n_{k} in the above definitions by n′_{k}, n′′_{k}, …; they can be entered as

EY(N , d) = y_{N}^{(d)}
EX(N , d) = x_{N}^{(d)}
⋮
EJ(N , d) = j_{n}^{(d)}

    gap> EW(16,3); EW(17,2); ER(3); ER(-3); EY(5); EB(9);
    0
    E(17)+E(17)^4+E(17)^13+E(17)^16
    -E(12)^7+E(12)^11
    E(3)-E(3)^2
    E(5)+E(5)^4
    1

13.13 StarCyc

StarCyc( z )

If z is an irrational element of a quadratic number field (i.e. if z is a quadratic irrationality), StarCyc( z ) returns the unique Galois conjugate of z that is different from z; this is often called z∗ (see 42.34). Otherwise false is returned.

    gap> StarCyc( EB(5) ); StarCyc( E(5) );
    E(5)^2+E(5)^3
    false

13.14 Quadratic

Quadratic( z )

If z is a cyclotomic integer that is contained in a quadratic number field over the rationals, it can be written as z = \frac{a+b\sqrt{n}}{d} with integers a, b, n and d, where d is either 1 or 2. In this case Quadratic( z ) returns a record with fields a, b, root, d and ATLAS where the first four mean the integers mentioned above, and the last one is a string that is a (not necessarily shortest) representation of z by b_{m}, i_{m} or r_{m} for m = |root| (see 13.12).

If z is not a quadratic irrationality or not a cyclotomic integer, false is returned.

gap> Quadratic( EB(5) ); Quadratic( EB(27) );
rec(
a := -1,
b := 1,
root := 5,
d := 2,
ATLAS := "b5" )
rec(
a := -1,
b := 3,
root := -3,
d := 2,
ATLAS := "1+3b3" )
gap> Quadratic(0); Quadratic( E(5) );
rec(
a := 0,
b := 0,
root := 1,
d := 1,
ATLAS := "0" )
false

13.15 GaloisMat

GaloisMat( mat )

mat must be a matrix of cyclotomics (or possibly unknowns, see 16.1). The conjugate of a row in mat under a particular Galois automorphism is defined pointwise. If mat consists of full orbits under this action then the Galois group of its entries acts on mat as a permutation group, otherwise the orbits must be completed before.

GaloisMat( mat ) returns a record with fields mat, galoisfams and generators:
mat

a list with initial segment mat (not a copy of mat); the list consists of full orbits under the action of the Galois group of the entries of mat defined above. The last entries are those rows which had to be added to complete the orbits; so if they were already complete, mat and mat have identical entries.

galoisfams

a list that has the same length as mat; its entries are either 1, 0, -1 or lists: galoisfams[i] = 1 means that mat[i] consists of rationals, i.e. [mat[i]] forms an orbit. galoisfams[i] = −1 means that mat[i] contains unknowns; in this case [mat[i]] is regarded as an orbit, too, even if mat[i] contains irrational entries. If galoisfams[i] = [l_{1}, l_{2}] is a list then mat[i] is the first element of its orbit in mat; l_{1} is the list of positions of rows which form the orbit, and l_{2} is the list of corresponding Galois automorphisms (as exponents, not as functions); so we have mat[l_{1}[j]][k] = GaloisCyc(mat[i][k], l_{2}[j]). galoisfams[i] = 0 means that mat[i] is an element of a nontrivial orbit but not the first element of it.

generators

a list of permutations generating the permutation group corresponding to the action of the Galois group on the rows of mat.

Note that mat should be a set, i.e. no two rows should be equal. Otherwise only the first row of some equal rows is considered for the permutations, and a warning is printed.

    gap> GaloisMat( [ [ E(3), E(4) ] ] );
    rec(
      mat := [ [ E(3), E(4) ], [ E(3), -E(4) ], [ E(3)^2, E(4) ],
          [ E(3)^2, -E(4) ] ],
      galoisfams := [ [ [ 1, 2, 3, 4 ], [ 1, 7, 5, 11 ] ], 0, 0, 0 ],
      generators := [ (1,2)(3,4), (1,3)(2,4) ] )
    gap> GaloisMat( [ [ 1, 1, 1 ], [ 1, E(3), E(3)^2 ] ] );
    rec(
      mat := [ [ 1, 1, 1 ], [ 1, E(3), E(3)^2 ], [ 1, E(3)^2, E(3) ] ],
      galoisfams := [ 1, [ [ 2, 3 ], [ 1, 2 ] ], 0 ],
      generators := [ (2,3) ] )

13.16 RationalizedMat

RationalizedMat( mat )

returns the set of rationalized rows of mat, i.e. the set of sums over orbits under the action of the Galois group of the elements of mat (see 13.15).

This may be viewed as a kind of trace operation for the rows.

Note that mat should be a set, i.e. no two rows should be equal.

    gap> mat:= CharTable( "A5" ).irreducibles;
    [ [ 1, 1, 1, 1, 1 ], [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]
    gap> RationalizedMat( mat );
    [ [ 1, 1, 1, 1, 1 ], [ 6, -2, 0, 1, 1 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]

354 CHAPTER 13. CYCLOTOMICS

Gaussians

If we adjoin a square root of -1, usually denoted by i, to the field of rationals we obtain a field that is an extension of degree 2. This field is called the Gaussian rationals and its ring of integers is called the Gaussian integers, because C.F. Gauss was the first to study them.

In GAP Gaussian rationals are written in the form a + b*E(4), where a and b are rationals, because E(4) is GAP’s name for i. Because 1 and i form an integral base the Gaussian integers are written in the form a + b*E(4), where a and b are integers.

The first sections in this chapter describe the operations applicable to Gaussian rationals (see 14.1 and 14.2).

The next sections describe the functions that test whether an object is a Gaussian rational or integer (see 14.3 and 14.4).

The GAP object GaussianRationals is the field domain of all Gaussian rationals, and the object GaussianIntegers is the ring domain of all Gaussian integers. All set theoretic functions are applicable to those two domains (see chapter 4 and 14.5).

The Gaussian rationals form a field so all field functions, e.g., Norm, are applicable to the domain GaussianRationals and its elements (see chapter 6 and 14.6).

The Gaussian integers form a Euclidean ring so all ring functions, e.g., Factors, are applicable to GaussianIntegers and its elements (see chapter 5, 14.7, and 14.8).

The field of Gaussian rationals is just a special case of cyclotomic fields, so everything that applies to those fields also applies to it (see chapters 13 and 15).

All functions are in the library file LIBNAME/"gaussian.g".

14.1 Comparisons of Gaussians

x = y

x <> y

The equality operator evaluates to true if the two Gaussians x and y are equal, and to false otherwise. The inequality operator <> evaluates to true if the two Gaussians x and y are not equal, and to false otherwise. It is also possible to compare a Gaussian with an object of another type, of course they are never equal.

Two Gaussians a + b*E(4) and c + d*E(4) are considered equal if a = c and b = d.

    gap> 1 + E(4) = 2 / (1 - E(4));
    true
    gap> 1 + E(4) = 1 - E(4);
    false
    gap> 1 + E(4) = E(6);
    false

x <y

x <= y

x > y

x >= y

The operators <, <=, >, and >= evaluate to true if the Gaussian x is less than, less than or equal to, greater than, and greater than or equal to the Gaussian y, and to false otherwise. Gaussians can also be compared to objects of other types, they are smaller than anything else, except other cyclotomics (see 13.9).

A Gaussian a + b*E(4) is considered less than another Gaussian c + d*E(4) if a is less than c, or if a is equal to c and b is less than d.

    gap> 1 + E(4) < 2 + E(4);
    true
    gap> 1 + E(4) < 1 - E(4);
    false
    gap> 1 + E(4) < 1/2;
    false

14.2 Operations for Gaussians

x + y x - y x * y x / y

The operators +, -, *, and / evaluate to the sum, difference, product, and quotient of the two Gaussians x and y. Of course either operand may also be an ordinary rational (see 12), because the rationals are embedded into the Gaussian rationals. On the other hand the Gaussian rationals are embedded into other cyclotomic fields, so either operand may also be a cyclotomic (see 13). Division by 0 is as usual an error.

x ^n

The operator ^evaluates to the n-th power of the Gaussian rational x. If n is positive, the power is defined as the n-fold product x*x*...x; if n is negative, the power is defined as (1/x)^(-n); and if n is zero, the power is 1, even if x is 0.

    gap> (1 + E(4)) * (E(4) - 1);

14.3 IsGaussRat

IsGaussRat( obj )

IsGaussRat returns true if obj , which may be an object of arbitrary type, is a Gaussian rational and false otherwise. Will signal an error if obj is an unbound variable.

    gap> IsGaussRat( 1/2 );
    true
    gap> IsGaussRat( E(4) );
    true
    gap> IsGaussRat( E(6) );
    false
    gap> IsGaussRat( true );
    false

IsGaussInt can be used to test whether an object is a Gaussian integer (see 14.4).

14.4 IsGaussInt

IsGaussInt( obj )

IsGaussInt returns true if obj , which may be an object of arbitrary type, is a Gaussian integer, and false otherwise. Will signal an error if obj is an unbound variable.

    gap> IsGaussInt( 1 );
    true
    gap> IsGaussInt( E(4) );
    true
    gap> IsGaussInt( 1/2 + 1/2*E(4) );
    false
    gap> IsGaussInt( E(6) );
    false

IsGaussRat can be used to test whether an object is a Gaussian rational (see 14.3).

14.5 Set Functions for Gaussians

As already mentioned in the introduction of this chapter the objects GaussianRationals and GaussianIntegers are the domains of Gaussian rationals and integers respectively. All set theoretic functions, i.e., Size and Intersection, are applicable to these domains and their elements (see chapter 4). There does not seem to be an important use of this however. All functions not mentioned here are not treated specially, i.e., they are implemented by the default function mentioned in the respective section.

in

The membership test for Gaussian rationals is implemented via IsGaussRat (14.3). The membership test for Gaussian integers is implemented via IsGaussInt (see 14.4).

Random

A random Gaussian rational a + b*E(4) is computed by combining two random rationals a and b (see 12.6). Likewise a random Gaussian integer a + b*E(4) is computed by combining two random integers a and b (see 10.13).

    gap> Size( GaussianRationals );
    "infinity"
    gap> Intersection( GaussianIntegers, [1,1/2,E(4),-E(6),E(4)/3] );
    [ 1, E(4) ]

14.6 Field Functions for Gaussian Rationals

As already mentioned in the introduction of this chapter, the domain of Gaussian rationals is a field. Therefore all field functions are applicable to this domain and its elements (see chapter 6). This section gives further comments on the definitions and implementations of those functions for the the Gaussian rationals. All functions not mentioned here are not treated specially, i.e., they are implemented by the default function mentioned in the respective section.

Conjugates

The field of Gaussian rationals is an extension of degree 2 of the rationals, its prime field. Therefore there is one further conjugate of every element a + b*E(4), namely a - b*E(4).

Norm, Trace

According to the definition of conjugates above, the norm of a Gaussian rational a + b*E(4) is a^2 + b^2 and the trace is 2*a.

14.7 Ring Functions for Gaussian Integers

As already mentioned in the introduction to this chapter, the ring of Gaussian integers is a Euclidean ring. Therefore all ring functions are applicable to this ring and its elements (see chapter 5). This section gives further comments on the definitions and implementations of those functions for the Gaussian integers. All functions not mentioned here are not treated specially, i.e., they are implemented by the default function mentioned in the respective section.

IsUnit, Units, IsAssociated, Associates
The units of GaussianIntegers are [ 1, E(4), -1, -E(4) ].

StandardAssociate

The standard associate of a Gaussian integer x is the associated element y of x that lies in the first quadrant of the complex plane. That is y is that element from x * [1,-1,E(4),-E(4)] that has positive real part and nonnegative imaginary part.

EuclideanDegree

The Euclidean degree of a Gaussian integer x is the product of x and its complex conjugate.

EuclideanRemainder

Define the integer part i of the quotient of x and y as the point of the lattice spanned by 1 and E(4) that lies next to the rational quotient of x and y, rounding towards the origin if there are several such points. Then EuclideanRemainder( x, y ) is defined as x - i * y. With this definition the ordinary Euclidean algorithm for the greatest common divisor works, whereas it does not work if you always round towards the origin.

EuclideanQuotient

The Euclidean quotient of two Gaussian integers x and y is the quotient of w and y, where w is the difference between x and the Euclidean remainder of x and y.

QuotientRemainder
QuotientRemainder uses EuclideanRemainder and EuclideanQuotient.

IsPrime, IsIrreducible

Since the Gaussian integers are a Euclidean ring, primes and irreducibles are equivalent. The primes are the elements 1 + E(4) and 1 - E(4) of norm 2, the elements a + b*E(4) and a - b*E(4) of norm p = a^2 + b^2 with p a rational prime congruent to 1 mod 4, and the elements p of norm p^2 with p a rational prime congruent to 3 mod 4.

Factors

The list returned by Factors is sorted according to the norms of the primes, and among those of equal norm with respect to <. All elements in the list are standard associates, except the first, which is multiplied by a unit as necessary.

The above characterization already shows how one can factor a Gaussian integer. First compute the norm of the element, factor this norm over the rational integers and then split 2 and the primes congruent to 1 mod 4 with TwoSquares (see 14.8).

    gap> Factors( GaussianIntegers, 30 );
    [ -1-E(4), 1+E(4), 3, 1+2*E(4), 2+E(4) ]

14.8 TwoSquares

TwoSquares( n )

TwoSquares returns a list of two integers x <= y such that the sum of the squares of x and y is equal to the nonnegative integer n, i.e., n = x^{2} + y^{2}. If no such representation exists TwoSquares will return false. TwoSquares will return a representation for which the gcd of x and y is as small as possible. If there are several such representations, it is not specified which one TwoSquares returns.

Let a be the product of all maximal powers of primes of the form 4k + 3 dividing n. A representation of n as a sum of two squares exists if and only if a is a perfect square. Let b be the maximal power of 2 dividing n, or its half, whichever is a perfect square. Then the minimal possible gcd of x and y is the square root c of ab. The number of different minimal representations with x <= y is 2^{l−1}, where l is the number of different prime factors of the form 4k + 1 of n.

gap> TwoSquares( 5 );
[ 1, 2 ]
gap> TwoSquares( 11 );

false # no representation exists

gap> TwoSquares( 16 );
[ 0, 4 ]
gap> TwoSquares( 45 );

[ 3, 6 ] # 3 is the minimal possible gcd because 9 divides 45

gap> TwoSquares( 125 );

[ 2, 11 ] # not [ 5, 10 ] because this has not minimal gcd

gap> TwoSquares( 13*17 );

[ 5, 14 ] # [10,11] would be the other possible representation

gap> TwoSquares( 848654483879497562821 );

[ 6305894639, 28440994650 ] # 848654483879497562821 is prime

Subfields of Cyclotomic Fields

The only number fields that GAP can handle at the moment are subfields of cyclotomic fields, e.g., Q(\sqrt{5}) is a number field that is not cyclotomic but contained in the cyclotomic field Q_{5} = Q(e^{\frac{2πi}{5}} ). Although this means that GAP does not know arbitrary algebraic number fields but only those with abelian Galois group, here we call these fields number fields for short. The elements of number fields are called cyclotomics (see chapter 13). Thus number fields are the domains (see chapter 4) related to cyclotomics; they are special field records (see 6.17) which are needed to specify the field extension with respect to which e.g. the trace of a cyclotomic shall be computed.

In many situations cyclotomic fields need not be treated in a special way, except that there may be more efficient algorithms for them than for arbitrary number fields. For that, there are the global variables NumberFieldOps and CyclotomicFieldOps, both records which contain the field operations stored in FieldOps (see chapter 6) and in which some functions are overlaid (see 15.13). If all necessary information about a function is already given in chapter 6, this function is not described here; this is the case e.g. for Conjugates and related functions, like Trace and CharPol. Some functions, however, need further explanation, e.g., 15.12 tells more about Coefficients for number fields.

There are some functions which are different for cyclotomic fields and other number fields, e.g., the field constructors CF resp. NF. In such a situation, the special case is described in a section immediately following the section about the general case.

Besides the single number fields, there is another domain in GAP related to number fields, the domain Cyclotomics of all cyclotomics. Although this is an abstract field, namely the field

Q^{ab}, Cyclotomics is not a field record. It is used by DefaultField, DefaultRing, Domain,

Field and Ring (see 6.3, 5.3, 4.5, 6.2, 5.2) which are mainly interested in the corresponding entries of Cyclotomics.operations since these functions know how to create fields resp. integral rings generated by some cyclotomics.

This chapter informs about characteristic functions (see 15.1, 15.2), field constructors (see 15.3, 15.4), (default) fields of cyclotomics (see 15.5), and (default) rings of cyclotomic integers (see 15.6), Galois groups of number fields (see 15.7, 15.8), vector space bases (see 15.9, 15.10, 15.11) and coefficients (see 15.12) and overlaid functions in the operations records (see 15.13).

The external functions are in the file LIBNAME/"numfield.g"

15.1 IsNumberField

IsNumberField( obj )

returns true if obj is a field record (see 6.1, 6.17) of a field of characteristic zero where F .generators is a list of cyclotomics (see chapter 13), and false else.

    gap> IsNumberField( CF(9) ); IsNumberField( NF( [ ER(3) ] ) );
    true
    true
    gap> IsNumberField( GF( 2 ) );
    false

15.2 IsCyclotomicField

IsCyclotomicField( obj )

returns true if obj is a number field record (see 15.1) where obj .isCyclotomicField =

true, and false else.
    gap> IsCyclotomicField( CF(9) );
    true
    gap> IsCyclotomicField( NF( [ ER(-3) ] ) );
    true
    gap> IsCyclotomicField( NF( [ ER(3) ] ) );
    false

15.3 Number Field Records

NumberField( gens )
NumberField( n, stab )
NumberField( subfield, poly )
NumberField( subfield, base )

NumberField may be abbreviated NF; it returns number fields, namely

NumberField( gens ):

the number field generated by the cyclotomics in the list gens,

NumberField( n, stab ):

the fixed field of the prime residues in the list stab inside the cyclotomic field Q_{n} (see 15.4),

NumberField( subfield, poly ):

the splitting field of the polynomial poly (which must have degree at most 2) over the number field subfield; subfield = 0 is equivalent to subfield = Rationals,

NumberField( subfield, base ):

the extension field of the number field subfield which is as vector space generated by the elements of the list base of cyclotomics; that means, base must be or at least contain a vector space base of this extension, if base is a base it will be assigned to the base field of the cyclotomic field (see 15.12). subfield = 0 is equivalent to subfield = Rationals.

    gap> NF( [ EB(7), ER(3) ] );
    NF(84,[ 1, 11, 23, 25, 37, 71 ])
    gap> NF( 7, [ 1 ] );
    CF(7)
    gap> NF( NF( [ EB(7) ] ), [ 1, 1, 1 ] );
    NF(NF(7,[ 1, 2, 4 ]),[ 1, E(3) ])
    gap> F:= NF( 0, [ 1, E(4) ] ); G:= NF( 0, NormalBaseNumberField( F ) );
    GaussianRationals
    CF( Rationals,[ 1/2-1/2*E(4), 1/2+1/2*E(4) ])
    gap> G.base; G.basechangemat; Coefficients( G, 1 );
    [ 1/2-1/2*E(4), 1/2+1/2*E(4) ]
    [ [ 1, 1 ], [ -1, 1 ] ]
    [ 1, 1 ]

Number field records are field records (see 6.17) representing a number field. Besides the obligatory record components, a number field record F contains the component

stabilizer

the list of prime residues modulo NofCyc( F .generators ) which fix all elements of F

and possibly

isIntegralBase

true if F .base is an integral vector space base of the field extension F / F .field, false else (used by 5.2 Ring); for the case that F .field is a cyclotomic field, 15.10 describes integral bases of the field extension;

isNormalBase

true if F .base is a normal vector space base of the field extension F /F .field,

     false else;
coeffslist

a list of integers used by 9.10 Coefficients; (see also 15.12);

coeffsmat

a matrix of cyclotomics used by 9.10 Coefficients; bound only if F .field is not a cyclotomic field (see also 15.12);

basechangemat

square matrix of dimension F .dimension, representing the basechange from the default base of F / F .field (see 15.12) to the base stored in F .base if these two are different; used by Coefficients.

Note: These fields and also the field base should not be changed by hand!

15.4 Cyclotomic Field Records

CyclotomicField( n )
CyclotomicField( gens )
CyclotomicField( subfield, n )
CyclotomicField( subfield, base )

CyclotomicField may be abbreviated CF; it returns cyclotomic fields, namely

CyclotomicField( n )

the field Q_{n} (over the rationals),

CyclotomicField( gens )

the smallest cyclotomic field containing the cyclotomics in the list gens (over the rationals),

CyclotomicField( subfield, n )

the field Q_{n} over the number field subfield,

CyclotomicField( subfield, base )

the cyclotomic extension field of the number field subfield which is as vector space generated by the elements of the list base of cyclotomics; that means, base must be or at least contain a vector space base of this extension, if base is a base it will be assigned to the base field of the cyclotomic field (see 15.12). subfield = 0 is equivalent

     to subfield = Rationals.
    gap> CF( 5 ); CF( [ EB(7), ER(3) ] ); CF( NF( [ ER(3) ] ), 24 );
    CF(5)
    CF(84)
    CF(24)/NF(12,[ 1, 11 ])
    gap> CF( CF(3), [ 1, E(4) ] );
    CF(12)/CF(3)

A cyclotomic field record is a field record (see 6.17), in particular a number field record (see 15.3) that represents a cyclotomic field. Besides the obligatory record fields, a cyclotomic field record F contains the fields

isCyclotomicField
     always true; used by 15.2 IsCyclotomicField,
zumbroichbase

a list containing ZumbroichBase( n, m ) (see 15.9) if F represents the field extension Q_{n}/Q_{m}, and containing Zumbroichbase( n, 1 ) if F is an extension of a number field that is not cyclotomic; used by 9.10 Coefficients, see 15.12

and possibly optional fields of number fields (see 15.3).

15.5 DefaultField and Field for Cyclotomics

For a set S of cyclotomics,

DefaultField( S ) = CF( S ) is the smallest cylotomic field containing S (see 6.3), the so–called conductor of S;

Field( S ) = NF( S ) is the smallest field containing S (see 6.2).

    gap> DefaultField( [ E(5) ] ); DefaultField( [ E(3), ER(6) ] );
    CF(5)
    CF(24)
    gap> Field( [ E(5) ] ); Field( [ E(3), ER(6) ] );
    CF(5)
    NF(24,[ 1, 19 ])

DefaultField and Field are used by functions that specify the field for which some cyclotomics are regarded as elements (see 6.3, 6.2), e.g., Trace with only one argument will compute the trace of this argument (which must be a cyclotomic) with respect to its default field.

15.6 DefaultRing and Ring for Cyclotomic Integers

For a set S of cyclotomic integers, DefaultRing( S ) is the ring of integers in CF( S ) (see 5.3), Ring( S ) is the ring of integers in NF( S ) (see 5.2).

    gap> Ring( [ E(5) ] );
    Ring( E(5) )
    gap> Ring( [ EB(7) ] );
    Ring( E(7)+E(7)^2+E(7)^4 )
    gap> DefaultRing( [ EB(7) ] );
    Ring( E(7) )

15.7 GeneratorsPrimeResidues

GeneratorsPrimeResidues( n )

returns a record with fields

primes

the set of prime divisors of the integer n,

exponents

the corresponding exponents in the factorization of n and

generators

generators of the group of prime residues: For each odd prime p there is one generator, corresponding to a primitive root of the subgroup (Z/p^{ν_{p}})^{∗} of (Z/nZ)^{∗}, where ν_{p} is the exponent of p in the factorization of n; for p = 2, we have one generator in the case that 8 does not divide n, and a list of two generators (corresponding to ⟨∗5, ∗(2^{ν_{2}} − 1)⟩ = (Z/2^{ν_{2}})^{∗}) else.

    gap> GeneratorsPrimeResidues( 9 );  # 2 is a primitive root
    rec(
      primes := [ 3 ],
      exponents := [ 2 ],
      generators := [ 2 ] )
    gap> GeneratorsPrimeResidues( 24 );  # 8 divides 24
    rec(
      primes := [ 2, 3 ],
      exponents := [ 3, 1 ],
      generators := [ [ 7, 13 ], 17 ] )
    gap> GeneratorsPrimeResidues( 1155 );
    rec(
      primes := [ 3, 5, 7, 11 ],
      exponents := [ 1, 1, 1, 1 ],
      generators := [ 386, 232, 661, 211 ] )

15.8 GaloisGroup for Number Fields

The Galois automorphisms of the cyclotomic field Q_{n} are given by linear extension of the maps ∗k : e_{n} ↦ e_{n}^{k} with 1 ≤ k < n and Gcd( n, k ) = 1 (see 13.11). Note that this action is not equal to exponentiation of cyclotomics, i.e., in general z^{∗k} is different from z^{k}:

gap> ( E(5) + E(5)^4 )^2; GaloisCyc( E(5) + E(5)^4, 2 );
-2*E(5)-E(5)^2-E(5)^3-2*E(5)^4
E(5)^2+E(5)^3

For Gcd( n, k ) ≠ 1, the map e_{n} ↦ e_{n}^{k} is not a field automorphism but only a linear map:

gap> GaloisCyc( E(5)+E(5)^4, 5 ); GaloisCyc( ( E(5)+E(5)^4 )^2, 5 );
2
-6

The Galois group Gal(Q_{n}, Q) of the field extension Q_{n}/Q is isomorphic to the group (Z/nZ)^{∗} of prime residues modulo n, via the isomorphism

\begin{matrix}(Z/nZ)^{∗} & → & Gal(Q_{n}, Q) \\ k & ↦ & (z ↦ z^{∗k}) \end{matrix} ,

thus the Galois group of the field extension Q_{n}/L with L ⊆ Q_{n} which is simply the factor group of Gal(Q_{n}, Q) modulo the stabilizer of L, and the Galois group of L/L′ which is the subgroup in this group that stabilizes L′, are easily described in terms of (Z/nZ)^{∗} (Generators of (Z/nZ)^{∗} can be computed using 15.7 GeneratorsPrimeResidues.).

The Galois group of a field extension can be computed using 6.7 GaloisGroup:

gap> f:= NF( [ EY(48) ] );
NF(48,[ 1, 47 ])
gap> g:= GaloisGroup( f );
Group( NFAutomorphism( NF(48,[ 1, 47 ]) , 17 ), NFAutomorphism( NF(48,
[ 1, 47 ]) , 11 ), NFAutomorphism( NF(48,[ 1, 47 ]) , 17 ) )
gap> Size( g ); IsCyclic( g ); IsAbelian( g );
8
false
true
gap> f.base[1]; g.1; f.base[1] ^g.1;
E(24)-E(24)^11
NFAutomorphism( NF(48,[ 1, 47 ]) , 17 )
E(24)^17-E(24)^19
gap> Operation( g, NormalBaseNumberField( f ), OnPoints );
Group( (1,6)(2,4)(3,8)(5,7), (1,4,8,5)(2,3,7,6), (1,6)(2,4)(3,8)
(5,7) )

The number field automorphism NFAutomorphism( F , k ) maps each element x of F to

GaloisCyc( x, k ), see 13.11.

15.9 ZumbroichBase

ZumbroichBase( n, m )

returns the set of exponents i where e_{n}^{i} belongs to the base B_{n,m} of the field extension Q_{n}/Q_{m}; for that, n and m must be positive integers where m divides n.

B_{n,m} is defined as follows:

Let P denote the set of prime divisors of n, n = ∏_{p∈P} p^{ν_{p}}, m = ∏_{p∈P} p^{μ_{p}} with μ_{p} ≤ ν_{p}, and {e_{n_{1}}^{j}}_{j∈J} ⊗ {e_{n_{2}}^{k}}_{k∈K} = {e_{n_{1}}^{j} · e_{n_{2}}^{k}}_{j∈J,k∈K}.

Then

B_{n,m} = ⨂_{p∈P} ⨂_{k=μ_{p}}^{ν_{p}−1}\{e_{p^{k+1}}^{j}\}_{j∈J_{k,p}} where J_{k,p} = \{ \begin{matrix}\{0\} & ; & k = 0, p = 2 \\ \{0, 1\} & ; & k > 0, p = 2 \\ \{1, …, p − 1\} & ; & k = 0, p ≠ 2 \\ \{−\frac{p−1}{2}, …, \frac{p−1}{2}\} & ; & k > 0, p ≠ 2 \end{matrix} .

B_{n,1} is equal to the base B(Q_{n}) of Q_{n} over the rationals given in [Zum89] (Note that the notation here is slightly different from that there.).

B_{n,m} consists of roots of unity, it is an integral base (that is, the integral elements in Q_{n} have integral coefficients, see 13.2), it is a normal base for squarefree n and closed under complex conjugation for odd n.

    gap> ZumbroichBase( 15, 1 ); ZumbroichBase( 12, 3 );
    [ 1, 2, 4, 7, 8, 11, 13, 14 ]
    [ 0, 3 ]
    gap> ZumbroichBase( 10, 2 ); ZumbroichBase( 32, 4 );
    [ 2, 4, 6, 8 ]
    [ 0, 1, 2, 3, 4, 5, 6, 7 ]

15.10 Integral Bases for Number Fields

LenstraBase( n, stabilizer, super )

returns a list [b_{1}, b_{2}, …, b_{m}] of lists, each b_{i} consisting of integers such that the elements ∑_{j∈b_{i}} E(n)^{j} form an integral base of the number field NF( n, stabilizer ), see 15.3.

super is a list representing a supergroup of the group described by the list stabilizer; the base is chosen such that the group of super acts on it, as far as this is possible.

Note: The b_{i} are in general not sets, since for stabilizer = super, b_{i}[1] is always an element of ZumbroichBase( N , 1 ); this is used by NF (see 15.3) and Coefficients (see 15.12).

stabilizer must not contain the stabilizer of a proper cyclotomic subfield of Q_{n}.

    gap> LenstraBase( 24, [ 1, 19 ], [ 1, 19 ] );  # a base of
    [ [ 1, 19 ], [ 8 ], [ 11, 17 ], [ 16 ] ]  # Q_{3}(\sqrt{6}),
    gap> LenstraBase( 24, [ 1, 19 ], [ 1, 5, 19, 23 ] ); # another one
    [ [ 1, 19 ], [ 5, 23 ], [ 8 ], [ 16 ] ]
    gap> LenstraBase( 15, [ 1, 4 ], PrimeResidues( 15 ) ); # normal base of
    [ [ 1, 4 ], [ 2, 8 ], [ 7, 13 ], [ 11, 14 ] ]  # Q_{3}(\sqrt{5})

15.11 NormalBaseNumberField

NormalBaseNumberField( F )
NormalBaseNumberField( F , x )

returns a list of cyclotomics which form a normal base of the number field F (see 15.3), i.e. a vector space base of the field F over its subfield F .field which is closed under the action of the Galois group F .galoisGroup of the field extension.

The normal base is computed as described in [Art68]: Let Φ denote the polynomial of a field extension L/L′, Φ′ its derivative and α one of its roots; then for all except finitely many elements z ∈ L′, the conjugates of \frac{Φ(z)}{(z−α)·Φ′(α)} form a normal base of L/L′.

When NormalBaseNumberField( F ) is called, z is chosen as integer, starting with 1, NormalBaseNumberField( F , x ) starts with z = x, increasing by one, until a normal base is found.

    gap> NormalBaseNumberField( CF( 5 ) );
    [ -E(5), -E(5)^2, -E(5)^3, -E(5)^4 ]
    gap> NormalBaseNumberField( CF( 8 ) );
    [ 1/4-2*E(8)-E(8)^2-1/2*E(8)^3, 1/4-1/2*E(8)+E(8)^2-2*E(8)^3,
      1/4+2*E(8)-E(8)^2+1/2*E(8)^3, 1/4+1/2*E(8)+E(8)^2+2*E(8)^3 ]

15.12 Coefficients for Number Fields

Coefficients( z )
Coefficients( F , z )

return the coefficient vector cfs of z with respect to a particular base B, i.e., we have z = cfs * B. If z is the only argument, B is the default base of the default field of z (see 15.5), otherwise F must be a number field containing z, and we have B = F .base.

The default base of a number field is defined as follows:

For the field extension Q_{n}/Q_{m} (i.e. both F and F .field are cyclotomic fields), B is the base B_{n,m} described in 15.9. This is an integral base which is closely related to the internal representation of cyclotomics, thus the coefficients are easy to compute, using only the

zumbroichbase fields of F and F .field.

For the field extension L/Q where L is not a cyclotomic field, B is the integral base described in 15.10 that consists of orbitsums on roots of unity. The computation of coefficients requires

the field F .coeffslist.

in future: replace Q by Q_{m}

In all other cases, B = NormalBaseNumberField( F ). Here, the coefficients of z with respect to B are computed using F .coeffslist and F .coeffsmat.

If F .base is not the default base of F , the coefficients with respect to the default base are multiplied with F .basechangemat. The only possibility where it is allowed to prescribe a base is when the field is constructed (see 15.3, 15.4).

    gap> F:= NF( [ ER(3), EB(7) ] ) / NF( [ ER(3) ] );
    NF(84,[ 1, 11, 23, 25, 37, 71 ])/NF(12,[ 1, 11 ])
    gap> Coefficients( F, ER(3) ); Coefficients( F, EB(7) );

[ -E(12)^7+E(12)^11, -E(12)^7+E(12)^11 ]
[ 11*E(12)^4+7*E(12)^7+11*E(12)^8-7*E(12)^11,
  -10*E(12)^4-7*E(12)^7-10*E(12)^8+7*E(12)^11 ]
gap> G:= CF( 8 ); H:= CF( 0, NormalBaseNumberField( G ) );
CF(8)
CF( 0,[ 1/4-2*E(8)-E(8)^2-1/2*E(8)^3, 1/4-1/2*E(8)+E(8)^2-2*E(8)^3,
  1/4+2*E(8)-E(8)^2+1/2*E(8)^3, 1/4+1/2*E(8)+E(8)^2+2*E(8)^3 ])
gap> Coefficients( G, ER(2) ); Coefficients( H, ER(2) );
[ 0, 1, 0, -1 ]
[ -1/3, 1/3, 1/3, -1/3 ]

15.13 Domain Functions for Number Fields

The following functions of FieldOps (see chapter 6) are overlaid in NumberFieldOps:

/, Coefficients, Conjugates, GaloisGroup, in, Intersection, Norm, Order, Print,
Random, Trace.

The following functions of NumberFieldOps are overlaid in CyclotomicFieldOps:

Coefficients, Conjugates, in, Norm, Print, Trace.

370 CHAPTER 15. SUBFIELDS OF CYCLOTOMIC FIELDS

Unknowns

Sometimes the result of an operation does not allow further computations with it. In many cases, then an error is signalled, and the computation is stopped.

This is not appropriate for some applications in character theory. For example, if a character shall be induced up (see 44.21) but the subgroup fusion is only a parametrized map (see chapter 45), there are positions where the value of the induced character are not known, and other values which are determined by the fusion map:

gap> m11:= CharTable( "M11" );; m12:= CharTable( "M12" );;
gap> fus:= InitFusion( m11, m12 );
[ 1, [ 2, 3 ], [ 4, 5 ], [ 6, 7 ], 8, [ 9, 10 ], [ 11, 12 ],
  [ 11, 12 ], [ 14, 15 ], [ 14, 15 ] ]
gap> Induced(m11,m12,Sublist(m11.irreducibles,[ 6 .. 9 ]),fus);
#I Induced: subgroup order not dividing sum in character 1 at class 4
#I Induced: subgroup order not dividing sum in character 1 at class 5
#I Induced: subgroup order not dividing sum in character 1 at class 14
#I Induced: subgroup order not dividing sum in character 1 at class 15
#I Induced: subgroup order not dividing sum in character 2 at class 4
#I Induced: subgroup order not dividing sum in character 2 at class 5
#I Induced: subgroup order not dividing sum in character 2 at class 14
#I Induced: subgroup order not dividing sum in character 2 at class 15
#I Induced: subgroup order not dividing sum in character 3 at class 2
#I Induced: subgroup order not dividing sum in character 3 at class 3
#I Induced: subgroup order not dividing sum in character 3 at class 4
#I Induced: subgroup order not dividing sum in character 3 at class 5
#I Induced: subgroup order not dividing sum in character 3 at class 9
#I Induced: subgroup order not dividing sum in character 3 at class 10
#I Induced: subgroup order not dividing sum in character 4 at class 2
#I Induced: subgroup order not dividing sum in character 4 at class 3
#I Induced: subgroup order not dividing sum in character 4 at class 6
#I Induced: subgroup order not dividing sum in character 4 at class 7
#I Induced: subgroup order not dividing sum in character 4 at class 11
#I Induced: subgroup order not dividing sum in character 4 at class 12

    #I Induced: subgroup order not dividing sum in character 4 at class 14
    #I Induced: subgroup order not dividing sum in character 4 at class 15
    [ [ 192, 0, 0, Unknown(9), Unknown(12), 0, 0, 2, 0, 0, 0, 0, 0,
          Unknown(15), Unknown(18) ],
      [ 192, 0, 0, Unknown(27), Unknown(30), 0, 0, 2, 0, 0, 0, 0, 0,
          Unknown(33), Unknown(36) ],
      [ 528, Unknown(45), Unknown(48), Unknown(51), Unknown(54), 0, 0,
          -2, Unknown(57), Unknown(60), 0, 0, 0, 0, 0 ],
      [ 540, Unknown(75), Unknown(78), 0, 0, Unknown(81), Unknown(84), 0,
          0, 0, Unknown(87), Unknown(90), 0, Unknown(93), Unknown(96) ] ]

For this and other situations, in GAP there is the data type unknown. Objects of this type, further on called unknowns, may stand for any cyclotomic (see 13).

Unknowns are parametrized by positive integers. When a GAP session is started, no unknowns do exist.

The only ways to create unknowns are to call 16.1 Unknown or a function that calls it, or to do arithmetical operations with unknowns (see 16.4).

Two properties should be noted:

Lists of cyclotomics and unknowns are no vectors, so cannot be added or multiplied like vectors; as a consequence, unknowns never occur in matrices.

GAP objects which are printed to files will contain fixed unknowns, i.e., function calls Unknown( n ) instead of Unknown(), so be careful to read files printed in different sessions, since there may be the same unknown at different places.

The rest of this chapter contains informations about the unknown constructor (see 16.1), the characteristic function (see 16.2), and comparison of and arithmetical operations for unknowns (see 16.3, 16.4); more is not yet known about unknowns.

16.1 Unknown

Unknown()
Unknown( n )

Unknown() returns a new unknown value, i.e. the first one that is larger than all unknowns which exist in the actual GAP session.

Unknown( n ) returns the n-th unknown; if it did not exist already, it is created.

    gap> Unknown(); Unknown(2000); Unknown();

Unknown(97) # There were created already 96 unknowns.

    Unknown(2000)
    Unknown(2001)

16.2 IsUnknown

IsUnknown( obj )

returns true if obj is an object of type unknown, and false otherwise. Will signal an error if obj is an unbound variable.

    gap> IsUnknown( Unknown ); IsUnknown( Unknown() );

false
true
gap> IsUnknown( Unknown(2) );
true

16.3 Comparisons of Unknowns

To compare unknowns with other objects, the operators <, <=, =, >=, > and <> can be used. The result will be true if the first operand is smaller, smaller or equal, equal, larger or equal, larger, or inequal, respectively, and false otherwise.

We have Unknown( n ) >= Unknown( m ) if and only if n >= m holds; unknowns are larger than cyclotomics and finite field elements, unknowns are smaller than all objects which are not cyclotomics, finite field elements or unknowns.

gap> Unknown() >= Unknown();
false
gap> Unknown(2) < Unknown(3);
true
gap> Unknown() > 3;
true
gap> Unknown() > Z(8);
false
gap> Unknown() > E(3);
true
gap>  Unknown() > [];
false

16.4 Operations for Unknowns

The operators +, -, * and / are used for addition, subtraction, multiplication and division of unknowns and cyclotomics. The result will be a new unknown except in one of the following cases:

Multiplication with zero yields zero, and multiplication with one or addition of zero yields the old unknown.

gap> Unknown() + 1; Unknown(2) + 0; last * 3; last * 1; last * 0;
Unknown(2010)
Unknown(2)
Unknown(2011)
Unknown(2011)
0

Note that division by an unknown causes an error, since an unknown might stand for zero.

374 CHAPTER 16. UNKNOWNS

Finite Fields

Finite fields comprise an important algebraic domain. The elements in a field form an additive group and the nonzero elements form a multiplicative group. For every prime power q there exists a unique field of size q up to isomorphism. GAP supports finite fields of size at most 2^{16}.

The first section in this chapter describes how you can enter elements of finite fields and how GAP prints them (see 17.1).

The next sections describe the operations applicable to finite field elements (see 17.2 and 17.3).

The next section describes the function that tests whether an object is a finite field element (see 17.4).

The next sections describe the functions that give basic information about finite field elements (see 17.5, 17.6, and 17.7).

The next sections describe the functions that compute various other representations of finite field elements (see 17.8 and 17.9).

The next section describes the function that constructs a finite field (see 17.10).

Finite fields are domains, thus all set theoretic functions are applicable to them (see chapter 4 and 17.12).

Finite fields are of course fields, thus all field functions are applicable to them and to their elements (see chapter 6 and 17.13).

All functions are in LIBNAME/"finfield.g".

17.1 Finite Field Elements

Z( p^d )

The function Z returns the designated generator of the multiplicative group of the finite field with p^d elements. p must be a prime and p^d must be less than or equal to 2^{16} = 65536.

The root returned by Z is a generator of the multiplicative group of the finite field with p^{d} elements, which is cyclic. The order of the element is of course p^{d} − 1. The p^{d} − 1 different powers of the root are exactly the nonzero elements of the finite field.

Thus all nonzero elements of the finite field with p^d elements can be entered as Z(p^d)^i. Note that this is also the form that GAP uses to output those elements.

The additive neutral element is 0*Z(p). It is different from the integer 0 in subtle ways. First IsInt( 0*Z(p) ) (see 10.5) is false and IsFFE( 0*Z(p) ) (see 17.4) is true, whereas it is just the other way around for the integer 0.

The multiplicative neutral element is Z(p)^0. It is different from the integer 1 in subtle ways. First IsInt( Z(p)^0 ) (see 10.5) is false and IsFFE( Z(p)^0 ) (see 17.4) is true, whereas it is just the other way around for the integer 1. Also 1+1 is 2, whereas, e.g., Z(2)^0

+ Z(2)^0 is 0*Z(2).

The various roots returned by Z for finite fields of the same characteristic are compatible in the following sense. If the field GF (p^{n}) is a subfield of the field GF (p^{m}), i.e., n divides m, then Z(p^{n}) = Z(p^{m})^{(p^{m}−1)/(p^{n}−1)}. Note that this is the simplest relation that may hold between a generator of GF (p^{n}) and GF (p^{m}), since Z(p^{n}) is an element of order p^{m} − 1 and Z(p^{m}) is an element of order p^{n} − 1. This is achieved by choosing Z(p) as the smallest primitive root modulo p and Z(p^{n}) as a root of the n-th Conway polynomial of characteristic p. Those polynomials where defined by J.H. Conway and computed by R.A. Parker.

    gap> z := Z(16);
    Z(2^4)
    gap> z*z;
    Z(2^4)^2

17.2 Comparisons of Finite Field Elements

z1 = z2 z1 <> z2

The equality operator = evaluates to true if the two elements in a finite field z1 and z2 are equal and to false otherwise. The inequality operator <> evaluates to true if the two elements in a finite finite field z1 and z2 are not equal and to false otherwise.

Note that the integer 0 is not equal to the zero element in any finite field. There comparisons z = 0 will always evaluate to false. Use z = 0*z instead, or even better z = F .zero, where F is the field record for a finite field of the same characteristic.

    gap> Z(2^4)^10 = Z(2^4)^25;

true # Z(2^4) has order 15

    gap> Z(2^4)^10 = Z(2^2)^2;

true # shows the embedding of GF(4) into GF(16)

    gap> Z(2^4)^10 = Z(3);
    false

z1 < z2 z1 <= z2 z1 > z2 z1 >= z2

The operators <, <=, >, and => evaluate to true if the element in a finite field z1 is less than, less than or equal to, greater than, and greater than or equal to the element in a finite field z2 .

Elements in finite fields are ordered as follows. If the two elements lie in fields of different characteristics the one that lies in the field with the smaller characteristic is smaller. If the two elements lie in different fields of the same characteristic the one that lies in the smaller field is smaller. If the two elements lie in the same field and one is the zero and the other is not, the zero element is smaller. If the two elements lie in the same field and both are nonzero, and are represented as Z(p^{d})^{i_{1}} and Z(p^{d})^{i_{2}} respectively, then the one with the smaller i is smaller.

You can compare elements in a finite field with objects of other types. Integers, rationals, and cyclotomics are smaller than elements in finite fields, all other objects are larger. Note especially that the integer 0 is smaller than the zero in every finite field.

    gap> Z(2) < Z(3);
    true
    gap> Z(2) < Z(4);
    true
    gap> 0*Z(2) < Z(2);
    true
    gap> Z(4) < Z(4)^2;
    true
    gap> 0 < 0*Z(2);
    true
    gap> Z(4) < [ Z(4) ];
    true

17.3 Operations for Finite Field Elements

z1 + z2 z1 - z2 z1 * z2 z1 / z2

The operators +, -, * and / evaluate to the sum, difference, product, and quotient of the two finite field elements z1 and z2 , which must lie in fields of the same characteristic. For the quotient / z2 must of course be nonzero. The result must of course lie in a finite field of size less than or equal to 2^{16}, otherwise an error is signalled.

Either operand may also be an integer i. If i is zero it is taken as the zero in the finite field, i.e., F .zero, where F is a field record for the finite field in which the other operand lies. If i is positive, it is taken as i-fold sum F .one+F .one+..+F .one. If i is negative it is taken as the additive inverse of -i.

    gap> Z(8) + Z(8)^4;
    Z(2^3)^2
    gap> Z(8) - 1;
    Z(2^3)^3
    gap> Z(8) * Z(8)^6;
    Z(2)^0
    gap> Z(8) / Z(8)^6;
    Z(2^3)^2
    gap> -Z(9);

Z(3^2)^5

z ^i

The powering operator ^returns the i-th power of the element in a finite field z. i must be an integer. If the exponent i is zero, z^i is defined as the one in the finite field, even if z is zero; if i is positive, z^i is defined as the i-fold product z*z*..*z; finally, if i is negative, z^i is defined as (1/z)^-i. In this case z must of course be nonzero.

gap> Z(4)^2;
Z(2^2)^2
gap> Z(4)^3;

Z(2)^0 # is in fact 1

gap> (0*Z(4))^0;
Z(2)^0

17.4 IsFFE

IsFFE( obj )

IsFFE returns true if obj , which may be an object of an arbitrary type, is an element in a finite field and false otherwise. Will signal an error if obj is an unbound variable.

Note that integers, even though they can be multiplied with elements in finite fields, are not considered themselves elements in finite fields. Therefore IsFFE will return false for integer arguments.

gap> IsFFE( Z(2^4)^7 );
true
gap> IsFFE( 5 );
false

17.5 CharFFE

CharFFE( z ) or CharFFE( vec ) or CharFFE( mat )

CharFFE returns the characteristic of the finite field F containing the element z, respectively all elements of the vector vec over a finite field (see 30), or matrix mat over a finite field (see 32).

gap> CharFFE( Z(16)^7 );
2
gap> CharFFE( Z(16)^5 );
2
gap> CharFFE( [ Z(3), Z(27)^11, Z(9)^3 ] );
3
gap> CharFFE( [ [ Z(5), Z(125)^3 ], [ Z(625)^13, Z(5) ] ] );
Error, CharFFE: <z> must be a finite field element, vector, or matrix

# The smallest finite field which contains all four of these elements # is too large for GAP

17.6 DegreeFFE

DegreeFFE( z ) or DegreeFFE( vec ) or DegreeFFE( mat )

DegreeFFE returns the degree of the smallest finite field F containing the element z, respectively all elements of the vector vec over a finite field (see 30), or matrix mat over a finite field (see 32). For vectors and matrices, an error is signalled if the smallest finite field containing all elements of the vector or matrix has size larger than 2^{16}.

gap> DegreeFFE( Z(16)^7 );
4
gap> DegreeFFE( Z(16)^5 );
2
gap> DegreeFFE( [ Z(3), Z(27)^11, Z(9)^3 ] );
6
gap> DegreeFFE( [ [ Z(5), Z(125)^3 ], [ Z(625)^13, Z(5) ] ] );
Error, DegreeFFE: <z> must be a finite field element, vector, or matrix

# The smallest finite field which contains all four of these elements # is too large for GAP

17.7 OrderFFE

OrderFFE( z )

OrderFFE returns the order of the element z in a finite field. The order is the smallest positive integer i such that z^i is 1. The order of the zero in a finite field is defined to be 0.

gap> OrderFFE( Z(16)^7 );
15
gap> OrderFFE( Z(16)^5 );
3
gap> OrderFFE( Z(27)^11 );
26
gap> OrderFFE( Z(625)^13 );
48
gap> OrderFFE( Z(211)^0 );
1

17.8 IntFFE

IntFFE( z )

IntFFE returns the integer corresponding to the element z, which must lie in a finite prime field. That is IntFFE returns the smallest nonnegative integer i such that i * z^0 = z.

The correspondence between elements from a finite prime field of characteristic p and the integers between 0 and p-1 is defined by choosing Z(p) the smallest primitive root mod p (see 11.6).

gap> IntFFE( Z(13) );
2
gap> PrimitiveRootMod( 13 );

    2
    gap> IntFFE( Z(409) );
    21
    gap> IntFFE( Z(409)^116 );
    311
    gap> 21^116 mod 409;
    311

17.9 LogFFE

LogFFE( z )
LogFFE( z, r )

In the first form LogFFE returns the discrete logarithm of the element z in a finite field with respect to the root FieldFFE(z).root. An error is signalled if z is zero.

In the second form LogFFE returns the discrete logarithm of the element z in a finite field with respect to the root r. An error is signalled if z is zero, or if z is not a power of r.

The discrete logarithm of an element z with respect to a root r is the smallest nonnegative integer i such that r^{i} = z.

    gap> LogFFE( Z(409)^116 );
    116
    gap> LogFFE( Z(409)^116, Z(409)^2 );
    58

17.10 GaloisField

GaloisField( p^d )
GF( p^d )
GaloisField( p|S, d|pol|bas )

GF( p|S, d|pol|bas )

GaloisField returns a field record (see 6.17) for a finite field. It takes two arguments. The form GaloisField(p,d), where p,d are integers, can also be given as GaloisField(p^d). GF is an abbreviation for GaloisField.

The first argument specifies the subfield S over which the new field F is to be taken. It can be a prime or a finite field record. If it is a prime p, the subfield is the prime field of this characteristic. If it is a field record S, the subfield is the field described by this record.

The second argument specifies the extension. It can be an integer, an irreducible polynomial, or a base. If it is an integer d, the new field is constructed as the polynomial extension with the Conway polynomial of degree d over the subfield S. If it is an irreducible polynomial pol, in which case the elements of the list pol must all lie in the subfield S, the new field is constructed as polynomial extension of the subfield S with this polynomial. If it is a base bas, in which case the elements of the list bas must be linear independently over the subfield S, the new field is constructed as a linear vector space over the subfield S.

Note that the subfield over which a field was constructed determines over which field the Galois group, conjugates, norm, trace, minimal polynom, and characteristic polynom are computed (see 6.7, 6.8, 6.9, 6.10, 6.12, 6.11, and 17.13).

    gap> GF( 2^4 );
    GF(2^4)
    gap> GF( GF(2^4), 2 );
    GF(2^8)/GF(2^4)

17.11 FrobeniusAutomorphism

FrobeniusAutomorphism( F )

FrobeniusAutomorphism returns the Frobenius automorphism of the finite field F as a field homomorphism (see 6.13). The Frobenius automorphism f of a finite field F of characteristic p is the function that takes each element z of F to its p-th power. Each automorphism of F is a power of the Frobenius automorphism. Thus the Frobenius automorphism is a generator for the Galois group of F (and an appropriate power of it is a generator of the Galois group of F over a subfield S) (see 6.7).

    gap> f := GF(16);
    GF(2^4)
    gap> x := FrobeniusAutomorphism( f );
    FrobeniusAutomorphism( GF(2^4) )
    gap> Z(16) ^x;
    Z(2^4)^2

The image of an element z under the i-th power of the Frobenius automorphism f of a finite field F of characteristic p is simply computed by computing the p^i-th power of z. The product of the i-th power and the j -th power of f is the k-th power of f , where k is i*j mod (Size(F )-1). The zeroth power of f is printed as IdentityMapping( F ).

17.12 Set Functions for Finite Fields

Finite fields are of course domains. Thus all set theoretic functions are applicable to finite fields (see chapter 4). This section gives further comments on the definitions and implementations of those functions for finite fields. All set theoretic functions not mentioned here are not treated specially for finite fields.

Elements

The elements of a finite field are computed using the fact that the finite field is a vector space over its prime field.

in

The membership test is of course very simple, we just have to test whether the element is a finite field element with IsFFE, whether it has the correct characteristic with CharFFE, and whether its degree divides the degree of the finite field with DegreeFFE (see 17.4, 17.5, and 17.6).

Random

A random element of GF (p^{n}) is computed by computing a random integer i from [0..p^{n} − 1] and returning 0 ∗ Z(p) if i = 0 and Z(p^{n})^{i−1} otherwise.

Intersection

The intersection of GF (p^{n}) and GF (p^{m}) is the finite field GF (p^{Gcd(n,m)}), and is returned as finite field record.

17.13 Field Functions for Finite Fields

Finite fields are, as the name already implies, fields. Thus all field functions are applicable to finite fields and their elements (see chapter 6). This section gives further comments on the definitions and implementations of those functions for finite fields. All domain functions not mentioned here are not treated specially for finite fields.

Field and DefaultField

Both Field and DefaultField return the smallest finite field containing the arguments as an extension of the prime field.

GaloisGroup

The Galois group of a finite field F of size p^{m} over a subfield S of size q = p^{n} is a cyclic group of size m/n. It is generated by the Frobenius automorphism that takes every element of F to its q-th power. This automorphism of F leaves exactly the subfield S fixed.

Conjugates

According to the above theorem about the Galois group, each element of F has m/n conjugates, z, z^{q}, z^{q^{2}}, ..., z^{q^{m/n−1}}.

Norm

The norm is the product of the conjugates, i.e., z^{p^{m}−1/p^{n}−1}. Because we have Z(p^{n}) = Z(p^{m})^{p^{m}−1/p^{n}−1}, it follows that Norm(GF (p^{m})/GF (p^{n}), Z(p^{m})^{i}) = Z(p^{n})^{i}.

Polynomials

Let R be a commutative ring-with-one. A (univariate) Laurent polynomial over R is a sequence (..., c_{−1}, c_{0}, c_{1}, ...) of elements of R such that only finitely many are non-zero. For a ring element r of R and polynomials f = (..., f_{−1}, f_{0}, f_{1}, ...) and g = (..., g_{−1}, g_{0}, g_{1}, ...) we define f + g = (..., f_{−1} + g_{−1}, f_{0} + g_{0}, f_{1} + g_{1}, ...) , r · f = (..., rf_{−1}, rf_{0}, rf_{1}, ...), and f ∗ g = (..., s_{−1}, s_{0}, s_{1}, ...), where s_{k} = ... + f_{i}g_{k−i} + .... Note that s_{k} is well-defined as only finitely many f_{i} and g_{i} are non-zero. We call the largest integers d(f), such that f_{d(f)} is non-zero, the degree of f, f_{i} the i.th coefficient of f, and f_{d(f)} the leading coefficient of f. If the smallest integer v(f), such that f_{v(f)} is non-zero, is negative, we say that f has a pole of order v at 0, otherwise we say that f has a root of order v at 0. We call R the base (or coefficient) ring of f. If f = (..., 0, 0, 0, ...) we set d(f) = −1 and v(f) = 0.

The set of all Laurent polynomials L(R) over a ring R together with above definitions of + and ∗ is again a ring, the Laurent polynomial ring over R, and R is called the base ring of L(R). The subset of all polynomials f with non-negative v(f) forms a subring P (R) of L(R), the polynomial ring over R. If R is indeed a field then both rings L(R) and P (R) are Euclidean. Note that L(R) and P (R) have different Euclidean degree functions. If f is an element of P (R) then the Euclidean degree of f is simply the degree of f. If f is viewed as an element of L(R) then the Euclidean degree is the difference between d(f) and v(f). The units of P (R) are just the units of R, while the units of L(R) are the polynomials f such that v(f) = d(f) and f_{d(f)} is a unit in R.

GAP uses the above definition of polynomials. This definition has some advantages and some drawbacks. First of all, the polynomial (..., x_{0} = 0, x_{1} = 1, x_{2} = 0, ...) is commonly denoted by x and is called an indeterminate over R, (..., c_{−1}, c_{0}, c_{1}, ...) is written as ... + c_{−1}x^{−1}+c_{0}+c_{1}x+c_{2}x^{2}+..., and P (R) as R[x] (note that the way GAP outputs a polynomial resembles this definition). But if we introduce a second indeterminate y it is not obvious whether the product xy lies in (R[x])[y], the polynomial ring in y over the polynomial ring in x, in (R[y])[x], in R[x, y], the polynomial ring in two indeterminates, or in R[y, x] (which should be equal to R[x, y]). Using the first definition we would define y as indeterminate over R[x] and we know then that xy lies in (R[x])[y].

    gap> x := Indeterminate(Rationals);; x.name := "x";;
    gap> Rx := LaurentPolynomialRing(Rationals);;
    gap> y := Indeterminate(Rx);; y.name := "y";;

gap> y^2 + x;
y^2 + (x)
gap> last^2;
y^4 + (2*x)*y^2 + (x^2)
gap> last + x;
y^4 + (2*x)*y^2 + (x^2 + x)
gap> (x^2 + x + 1) * y^2 + y + 1;
(x^2 + x + 1)*y^2 + y + (x^0)
gap> x * y;
(x)*y
gap> y * x;
(x)*y
gap> 2 * x;
2*x
gap> x * 2;
2*x

Note that GAP does not embed the base ring of a polynomial into the polynomial ring. The trivial polynomial and the zero of the base ring are always different.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> Rx := LaurentPolynomialRing(Rationals);;
gap> y := Indeterminate(Rx);; y.name := "y";;
gap> 0 = 0*x;
false

gap> nx := 0*x; # a polynomial over the rationals

0*x^0

gap> ny := 0*y; # a polynomial over a polynomial ring

0*y^0

gap> nx = ny; # different base rings

false

The result 0*x ≠ 0*y is probably not what you expect or want. In order to compute with two indeterminates over R you must embed x into R[x][y].

gap> x  := Indeterminate(Rationals);; x.name := "x";;
gap> Rx := LaurentPolynomialRing(Rationals);;
gap> y  := Indeterminate(Rx);; y.name := "y";;
gap> x  := x * y^0;
x*y^0
gap> 0*x = 0*y;
true

The other point which might be startling is that we require the supply of a base ring for a polynomial. But this guarantees that Factor gives a predictable result.

gap> f5 := GF(5);; f5.name := "f5";;
gap> f25 := GF(25);; f25.name := "f25";;
gap> Polynomial( f5, [3,2,1]*Z(5)^0 );
Z(5)^0*(X(f5)^2 + 2*X(f5) + 3)
gap> Factors(last);
[ Z(5)^0*(X(f5)^2 + 2*X(f5) + 3) ]

    gap> Polynomial( f25, [3,2,1]*Z(5)^0 );
    X(f25)^2 + Z(5)*X(f25) + Z(5)^3
    gap> Factors(last);
    [ X(f25) + Z(5^2)^7, X(f25) + Z(5^2)^11 ]

The first sections describe how polynomials are constructed (see 18.2, 18.3, and 18.4).

The next sections describe the operations applicable to polynomials (see 18.5 and 18.6).

The next sections describe the functions for polynomials (see 18.7, 18.10 and 18.9).

The next sections describe the functions for constructing the Laurent polynomial ring L(R) and the polynomial ring P (R) (see 18.12 and 18.14).

The next sections describe the ring functions applicable to Laurent polynomial rings. (see 18.16 and 18.17).

18.1 Multivariate Polynomials

As explained above, each ring R has exactly one indeterminate associated with R. In order to construct a polynomial ring with two indeterminates over R you must first construct the polynomial ring P (R) and then the polynomial ring over P (R).

    gap> x  := Indeterminate(Integers);; x.name := "x";;
    gap> Rx := PolynomialRing(Integers);;
    gap> y  := Indeterminate(Rx);; y.name := "y";;
    gap> x  := y^0 * x;
    x*y^0
    gap> f := x^2*y^2 + 3*x*y + x + 4*y;
    (x^2)*y^2 + (3*x + 4)*y + (x)
    gap> Value( f, 4 );
    16*x^2 + 13*x + 16
    gap> Value( last, -2 );
    54
    gap> (-2)^2 * 4^2 + 3*(-2)*4 + (-2) + 4*4;
    54

We plan to add support for (proper) multivariate polynomials in one of the next releases of GAP.

18.2 Indeterminate

Indeterminate( R )
X( R )

Indeterminate returns the polynomial (..., x_{0} = 0, x_{1} = 1, x_{2} = 0, ...) over R, which must be a commutative ring-with-one or a field.

Note that you can assign a name to the indeterminate, in which case all polynomials over R are printed using this name. Keep in mind that for each ring there is exactly one indeterminate.

    gap> x := Indeterminate( Integers );; x.name := "x";;
    gap> f := x^10 + 3*x - x^-1;

    x^10 + 3*x - x^(-1)
    gap> y := Indeterminate( Integers );;  # this is x
    gap> y.name := "y";;

gap> f; # so f is also printed differently from now on

    y^10 + 3*y - y^(-1)

18.3 Polynomial

Polynomial( R, l )
Polynomial( R, l, v )

l must be a list of coefficients of the polynomial f to be constructed, namely (..., f_{v} = l[1], f_{v+1} = l[2], ...) over R, which must be a commutative ring-with-one or a field. The default for v is 0. Polynomial returns this polynomial f.

For interactive calculation it might by easier to construct the indeterminate over R and construct the polynomial using ^, + and *.

    gap> x := Indeterminate( Integers );;
    gap> x.name := "x";;
    gap> f := Polynomial( Integers, [1,2,0,0,4] );
    4*x^4 + 2*x + 1
    gap> g := 4*x^4 + 2*x + 1;
    4*x^4 + 2*x + 1

18.4 IsPolynomial

IsPolynomial( obj )

IsPolynomial returns true if obj , which can be an object of arbitrary type, is a polynomial and false otherwise. The function will signal an error if obj is an unbound variable.

    gap> IsPolynomial( 1 );
    false
    gap> IsPolynomial( Indeterminate(Integers) );
    true

18.5 Comparisons of Polynomials

f = g

f <> g

The equality operator = evaluates to true if the polynomials f and g are equal, and to false otherwise. The inequality operator <> evaluates to true if the polynomials f and g are not equal, and to false otherwise.

Note that polynomials are equal if and only if their coefficients and their base rings are equal. Polynomials can also be compared with objects of other types. Of course they are never equal.

    gap> f := Polynomial( GF(5^3), [1,2,3]*Z(5)^0 );
    Z(5)^3*X(GF(5^3))^2 + Z(5)*X(GF(5^3)) + Z(5)^0
    gap> x := Indeterminate(GF(25));;

gap> g := 3*x^2 + 2*x + 1;
Z(5)^3*X(GF(5^2))^2 + Z(5)*X(GF(5^2)) + Z(5)^0
gap> f = g;
false
gap> x^0 = Z(25)^0;
false

f < g

f <= g

f > g

f >= g

The operators <, <=, >, and >= evaluate to true if the polynomial f is less than, less than or equal to, greater than, or greater than or equal to the polynomial g, and to false otherwise.

A polynomial f is less than g if v(f ) is less than v(g), or if v(f ) and v(g) are equal and d(f ) is less than d(g). If v(f ) is equal to v(g) and d(f ) is equal to d(g) the coefficient lists of f and g are compared.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> (1 + x^2 + x^3)*x^3 < (2 + x^2 + x^3);
false
gap> (1 + x^2 + x^4) < (2 + x^2 + x^3);
false
gap> (1 + x^2 + x^3) < (2 + x^2 + x^3);
true

18.6 Operations for Polynomials

The following operations are always available for polynomials. The operands must have a common base ring, no implicit conversions are performed.

f + g

The operator + evaluates to the sum of the polynomials f and g, which must be polynomials over a common base ring.

gap> f := Polynomial( GF(2), [Z(2), Z(2)] );
Z(2)^0*(X(GF(2)) + 1)
gap> f + f;
0*X(GF(2))^0
gap> g := Polynomial( GF(4), [Z(2), Z(2)] );
X(GF(2^2)) + Z(2)^0
gap> f + g;
Error, polynomials must have the same ring

f + scl scl + f

The operator + evaluates to the sum of the polynomial f and the scalar scl, which must lie in the base ring of f .

gap> x := Indeterminate( Integers );; x.name := "x";;
gap> h := Polynomial( Integers, [1,2,3,4] );

4*x^3 + 3*x^2 + 2*x + 1
gap> h + 1;
4*x^3 + 3*x^2 + 2*x + 2
gap> 1/2 + h;
Error, <l> must lie in the base ring of <r>

f - g

The operator - evaluates to the difference of the polynomials f and g, which must be polynomials over a common base ring.

gap> x := Indeterminate( Integers );; x.name := "x";;
gap> h := Polynomial( Integers, [1,2,3,4] );
4*x^3 + 3*x^2 + 2*x + 1
gap> h - 2*h;
-4*x^3 - 3*x^2 - 2*x - 1

f - scl scl - f

The operator - evaluates to the difference of the polynomial f and the scalar scl, which must lie in the base ring of f .

gap> x := Indeterminate( Integers );; x.name := "x";;
gap> h := Polynomial( Integers, [1,2,3,4] );
4*x^3 + 3*x^2 + 2*x + 1
gap> h - 1;
4*x^3 + 3*x^2 + 2*x
gap> 1 - h;
-4*x^3 - 3*x^2 - 2*x

f * g

The operator * evaluates to the product of the two polynomials f and g, which must be polynomial over a common base ring.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> h := 4*x^3 + 3*x^2 + 2*x + 1;
4*x^3 + 3*x^2 + 2*x + 1
gap> h * h;
16*x^6 + 24*x^5 + 25*x^4 + 20*x^3 + 10*x^2 + 4*x + 1

f * scl scl * f

The operator * evaluates to the product of the polynomial f and the scalar scl, which must lie in the base ring of f .

gap> f := Polynomial( GF(2), [Z(2), Z(2)] );
Z(2)^0*(X(GF(2)) + 1)
gap> f - Z(2);
X(GF(2))
gap> Z(4) - f;
Error, <l> must lie in the base ring of <r>

f ^n The operator ^evaluates the the n-th power of the polynomial f . If n is negative ^will try to invert f in the Laurent polynomial ring ring.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> k := x - 1 + x^-1;
x - 1 + x^(-1)
gap> k ^3;
x^3 - 3*x^2 + 6*x - 7 + 6*x^(-1) - 3*x^(-2) + x^(-3)
gap> k^-1;
Error, cannot invert <l> in the laurent polynomial ring

f / scl

The operator / evaluates to the product of the polynomial f and the inverse of the scalar scl, which must be invertable in its default ring.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> h := 4*x^3 + 3*x^2 + 2*x + 1;
4*x^3 + 3*x^2 + 2*x + 1
gap> h / 3;
(4/3)*x^3 + x^2 + (2/3)*x + (1/3)

scl / f

The operator / evaluates to the product of the scalar scl and the inverse of the polynomial f , which must be invertable in its Laurent ring.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> 30 / x;
30*x^(-1)
gap> 3 / (1+x);
Error, cannot invert <l> in the laurent polynomial ring

f / g

The operator / evaluates to the quotient of the two polynomials f and g, if such quotient exists in the Laurent polynomial ring. Otherwise / signals an error.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> f := (1+x+x^2) * (3-x-2*x^2);
-2*x^4 - 3*x^3 + 2*x + 3
gap> f / (1+x+x^2);
-2*x^2 - x + 3
gap> f / (1+x);
Error, cannot divide <l> by <r>

18.7 Degree

Degree( f )

Degree returns the degree d_{f} of f (see 18).

Note that this is only equal to the Euclidean degree in the polynomial ring P (R). It is not equal in the Laurent polynomial ring L(R).

gap> x := Indeterminate(Rationals);; x.name := "x";;

gap> Degree( x^10 + x^2 + 1 );
10
gap> EuclideanDegree( x^10 + x^2 + 1 );

10 # the default ring is the polynomial ring

gap> Degree( x^-10 + x^-11 );
-10
gap> EuclideanDegree( x^-10 + x^-11 );

1 # the default ring is the Laurent polynomial ring

18.8 LeadingCoefficient

LeadingCoefficient( f )

LeadingCoefficient returns the last non-zero coefficient of f (see 18).

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> LeadingCoefficient( 3*x^2 + 2*x + 1);
3

18.9 Value

Value( f , w )

Let f be a Laurent polynomial (..., f_{−1}, f_{0}, f_{1}, ...). Then Value returns the finite sum ... + f_{−1}w^{−1} + f_{0}w^{0} + f_{1}w + ....

Note that x need not be contained in the base ring of f .

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> k := -x + 1;
-x + 1
gap> Value( k, 2 );
-1
gap> Value( k, [[1,1],[0,1]] );
[ [ 0, -1 ], [ 0, 0 ] ]

18.10 Derivative

Derivative( f )

Let f be a Laurent polynomial (..., f_{−1}, f_{0}, f_{1}, ...). Then Derivative returns the polynomial g = (..., g_{i−1} = i ∗ f_{i}, ...).

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> Derivative( x^10 + x^-11 );
10*x^9 - 11*x^(-12)
gap> y := Indeterminate(GF(5));; y.name := "y";;
gap> Derivative( y^10 + y^-11 );
Z(5)^2*y^(-12)

18.11 InterpolatedPolynomial

InterpolatedPolynomial( R, x, y )

InterpolatedPolynomial returns the unique polynomial of degree less than n which has value y[i] at x[i] for all i = 1, ..., n, where x and y must be lists of elements of the ring or field R, if such a polynomial exists. Note that the elements in x must be distinct.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> p := InterpolatedPolynomial( Rationals, [1,2,3,4], [3,2,4,1] );
(-4/3)*x^3 + (19/2)*x^2 + (-121/6)*x + 15
gap> List( [1,2,3,4], x -> Value(p,x) );
[ 3, 2, 4, 1 ]

18.12 PolynomialRing

PolynomialRing( R )

PolynomialRing returns the ring of all polynomials over a field R or ring-with-one R.

gap> f2 := GF(2);;
gap> R := PolynomialRing( f2 );
PolynomialRing( GF(2) )
gap> Z(2) in R;
false
gap> Polynomial( f2, [Z(2),Z(2)] ) in R;
true
gap> Polynomial( GF(4), [Z(2),Z(2)] ) in R;
false
gap> R := PolynomialRing( GF(2) );
PolynomialRing( GF(2) )

18.13 IsPolynomialRing

IsPolynomialRing( domain )

IsPolynomialRing returns true if the object domain is a ring record, representing a polynomial ring (see 18.12), and false otherwise.

gap> IsPolynomialRing( Integers );
false
gap> IsPolynomialRing( PolynomialRing( Integers ) );
true
gap> IsPolynomialRing( LaurentPolynomialRing( Integers ) );
false

18.14 LaurentPolynomialRing

LaurentPolynomialRing( R )

LaurentPolynomialRing returns the ring of all Laurent polynomials over a field R or ringwith-one R.

gap> f2 := GF(2);;
gap> R := LaurentPolynomialRing( f2 );
LaurentPolynomialRing( GF(2) )
gap> Z(2) in R;
false
gap> Polynomial( f2, [Z(2),Z(2)] ) in R;
true
gap> Polynomial( GF(4), [Z(2),Z(2)] ) in R;
false
gap> Indeterminate(f2)^-1 in R;
true

18.15 IsLaurentPolynomialRing

IsLaurentPolynomialRing( domain )

IsLaurentPolynomialRing returns true if the object domain is a ring record, representing a Laurent polynomial ring (see 18.14), and false otherwise.

gap> IsPolynomialRing( Integers );
false
gap> IsLaurentPolynomialRing( PolynomialRing( Integers ) );
false
gap> IsLaurentPolynomialRing( LaurentPolynomialRing( Integers ) );
true

18.16 Ring Functions for Polynomial Rings

As was already noted in the introduction to this chapter polynomial rings are rings, so all ring functions (see chapter 5) are applicable to polynomial rings. This section comments on the implementation of those functions.

Let R be a commutative ring-with-one or a field and let P be the polynomial ring over R.

EuclideanDegree( P, f )

P is an Euclidean ring if and only if R is field. In this case the Euclidean degree of f is simply the degree of f . If R is not a field then the function signals an error.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> EuclideanDegree( x^10 + x^2 + 1 );
10
gap> EuclideanDegree( x^0 );
0

EuclideanRemainder( P, f , g )

P is an Euclidean ring if and only if R is field. In this case it is possible to divide f by g with remainder.

gap> x := Indeterminate(Rationals);; x.name := "x";;

gap> EuclideanRemainder( (x+1)*(x+2)+5, x+1 );
5*x^0

EuclideanQuotient( P, f , g )

P is an Euclidean ring if and only if R is field. In this case it is possible to divide f by g with remainder.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> EuclideanQuotient( (x+1)*(x+2)+5, x+1 );
x + 2

QuotientRemainder( P, f , g )

P is an Euclidean ring if and only if R is field. In this case it is possible to divide f by g with remainder.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> QuotientRemainder( (x+1)*(x+2)+5, x+1 );
[ x + 2, 5*x^0 ]

Gcd( P, f , g )

P is an Euclidean ring if and only if R is field. In this case you can compute the greatest common divisor of f and g using Gcd.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> g := x^2 + 2*x + 1;;
gap> h := x^2 - 1;;
gap> Gcd( g, h );
x + 1
gap> GcdRepresentation( g, h );
[ 1/2*x^0, -1/2*x^0 ]
gap> g * (1/2) * x^0 - h * (1/2) * x^0;
x + 1

Factors( P, f )

This method is only implemented for a polynomial ring P over a finite field R. In this case f is factored using a Cantor-Zassenhaus algorithm. As f is an element of P if and only if the base ring of f is R you must embed the polynomial into the polynomial ring P if it is written as polynomial over a subring.

gap> f5 := GF(5);; f5.name := "f5";;
gap> x  := Indeterminate(f5);; x.name := "x";;
gap> g := x^20 + x^8 + 1;
Z(5)^0*(x^20 + x^8 + 1)
gap> Factors(g);
[ Z(5)^0*(x^8 + 4*x^4 + 2), Z(5)^0*(x^12 + x^8 + 4*x^4 + 3) ]
gap> f25 := GF(25);; Indeterminate(f25).name := "y";;
gap> l := Factors( EmbeddedPolynomial( PolynomialRing(f25), g ) );

[ y^4 + Z(5^2)^13, y^4 + Z(5^2)^17, y^6 + Z(5)^3*y^2 + Z(5^2)^3,
  y^6 + Z(5)^3*y^2 + Z(5^2)^15 ]
gap> l[1] * l[2];
y^8 + Z(5)^2*y^4 + Z(5)
gap> l[3] * l[4];
y^12 + y^8 + Z(5)^2*y^4 + Z(5)^3

StandardAssociate( P, f )

For a ring R the standard associate a of f is a multiple of f such that the leading coefficient of a is the standard associate in R. For a field R the standard associate a of f is a multiple of f such that the leading coefficient of a is 1.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> StandardAssociate( -2 * x^3 - x );
2*x^3 + x

18.17 Ring Functions for Laurent Polynomial Rings

As was already noted in the introduction to this chapter Laurent polynomial rings are rings, so all ring functions (see chapter 5) are applicable to polynomial rings. This section comments on the implementation of those functions.

Let R be a commutative ring-with-one or a field and let P be the polynomial ring over R.

EuclideanDegree( P, f )

P is an Euclidean ring if and only if R is field. In this case the Euclidean degree of f is the difference of d(f) and v(f) (see 18). If R is not a field then the function signals an error.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> LR := LaurentPolynomialRing(Rationals);;
gap> EuclideanDegree( LR, x^10 + x^2 );
8
gap> EuclideanDegree( LR, x^7 );
0
gap> EuclideanDegree( x^7 );
7
gap> EuclideanDegree( LR, x^2 + x^-2 );
4
gap> EuclideanDegree( x^2 + x^-2 );
4

Gcd( P, f , g )

P is an Euclidean ring if and only if R is field. In this case you can compute the greatest common divisor of f and g using Gcd.

gap> x := Indeterminate(Rationals);; x.name := "x";;
gap> LR := LaurentPolynomialRing(Rationals);;
gap> g := x^3 + 2*x^2 + x;;

gap> h := x^3 - x;;
gap> Gcd( g, h );
x^2 + x
gap> Gcd( LR, g, h );

x + 1 # x is a unit in LR

gap> GcdRepresentation( LR, g, h );
[ (1/2)*x^(-1), (-1/2)*x^(-1) ]

Factors( P, f )

This method is only implemented for a Laurent polynomial ring P over a finite field R. In this case f is factored using a Cantor-Zassenhaus algorithm. As f is an element of P if and only if the base ring of f is R you must embed the polynomial into the polynomial ring P if it is written as polynomial over a subring.

gap> f5 := GF(5);; f5.name := "f5";;
gap> x  := Indeterminate(f5);; x.name := "x";;
gap> g := x^10 + x^-2 + x^-10;
Z(5)^0*(x^10 + x^(-2) + x^(-10))
gap> Factors(g);
[ Z(5)^0*(x^(-2) + 4*x^(-6) + 2*x^(-10)),
  Z(5)^0*(x^12 + x^8 + 4*x^4 + 3) ]
gap> f25 := GF(25);; Indeterminate(f25).name := "y";;
gap> gg := EmbeddedPolynomial( LaurentPolynomialRing(f25), g );
y^10 + y^(-2) + y^(-10)
gap> l := Factors( gg );
[ y^(-6) + Z(5^2)^13*y^(-10), y^4 + Z(5^2)^17,
  y^6 + Z(5)^3*y^2 + Z(5^2)^3, y^6 + Z(5)^3*y^2 + Z(5^2)^15 ]
gap> l[1] * l[2];
y^(-2) + Z(5)^2*y^(-6) + Z(5)*y^(-10)
gap> l[3]*[4];
[ Z(5)^2*y^6 + Z(5)*y^2 + Z(5^2)^15 ]

StandardAssociate( P, f )

For a ring R the standard associate a of f is a multiple of f such that the leading coefficient of a is the standard associate in R and v(a) is zero. For a field R the standard associate a of f is a multiple of f such that the leading coefficient of a is 1 and v(a) is zero.

gap> x := Indeterminate(Integers);; x.name := "x";;
gap> LR := LaurentPolynomialRing(Integers);;
gap> StandardAssociate( LR, -2 * x^3 - x );
2*x^2 + 1

396 CHAPTER 18. POLYNOMIALS

Permutations

GAP is a system especially designed for the computations in groups. Permutation groups are a very important class of groups and GAP offers a data type permutation to describe the elements of permutation groups.

Permutations in GAP operate on positive integers. Whenever group elements operate on a domain we call the elements of this domain points. Thus in this chapter we often call positive integers points, if we want to emphasize that a permutation operates on them. An integer i is said to be moved by a permutation p if the image i^{p} of i under p is not i. The largest integer moved by any permutation may not be larger than 2^{28} − 1.

Note that permutations do not belong to a specific group. That means that you can work with permutations without defining a permutation group that contains them. This is just like it is with integers, with which you can compute without caring about the domain Integers that contains them. It also means that you can multiply any two permutations.

Permutations are entered and displayed in cycle notation.

    gap> (1,2,3);
    (1,2,3)
    gap> (1,2,3) * (2,3,4);
    (1,3)(2,4)

The first sections in this chapter describe the operations that are available for permutations (see 19.1 and 19.2). The next section describes the function that tests whether an object is a permutation (see 19.3). The next sections describe the functions that find the largest and smallest point moved by a permutation (see 19.4 and 19.5). The next section describes the function that computes the sign of a permutation (see 19.6). The next section describes the function that computes the smallest permutation that generates the same cyclic subgroup as a given permutation (see 19.7). The final sections describe the functions that convert between lists and permutations (see 19.8, 19.9, 19.10, and 19.11).

Permutations are elements of groups operating on positive integers in a natural way, thus see chapter 7 and chapter 2.10 for more functions.

The external functions are in the file LIBNAME/"permutat.g".

19.1 Comparisons of Permutations

p1 = p2 p1 <> p2

The equality operator = evaluates to true if the two permutations p1 and p2 are equal, and to false otherwise. The inequality operator <> evaluates to true if the two permutations p1 and p2 are not equal, and to false otherwise. You can also compare permutations with objects of other types, of course they are never equal.

Two permutations are considered equal if and only if they move the same points and if the images of the moved points are the same under the operation of both permutations.

    gap> (1,2,3) = (2,3,1);
    true
    gap> (1,2,3) * (2,3,4) = (1,3)(2,4);
    true

p1 <p2 p1 <= p2 p1 > p2 p1 >= p2

The operators <, <=, >, and >= evaluate to true if the permutation p1 is less than, less than or equal to, greater than, or greater than or equal to the permutation p2 , and to false otherwise.

Let p_{1} and p_{2} be two permutations that are not equal. Then there exists at least one point i such that i^{p_{1}} <> i^{p_{2}}. Let k be the smallest such point. Then p_{1} is considered smaller than p_{2} if and only if k^{p_{1}} < k^{p_{2}}. Note that this implies that the identity permutation is the smallest permutation.

You can also compare permutations with objects of other types. Integers, rationals, cyclotomics, unknowns, and finite field elements are smaller than permutations. Everything else is larger.

    gap> (1,2,3) < (1,3,2);

true # 1^{(1,2,3)} = 2 < 3 = 1^{(1,3,2)}

    gap> (1,3,2,4) < (1,3,4,2);

false # 2^{(1,3,2,4)} = 4 > 1 = 2^{(1,3,4,2)}

19.2 Operations for Permutations

p1 * p2

The operator * evaluates to the product of the two permutations p1 and p2 .

p1 / p2

The operator / evaluates to the quotient p1 ∗ p2^{−1} of the two permutations p1 and p2 .

LeftQuotient( p1 , p2 )

LeftQuotient returns the left quotient p1^{−1} ∗ p2 of the two permutations p1 and p2 . (This can also be written p1 mod p2 .)

p ^i The operator ^evaluates to the i-th power of the permutation p. p1 ^p2 The operator ^evaluates to the conjugate p2^{−1} ∗ p1 ∗ p2 of the permutation p1 by the permutation p2 .

Comm( p1 , p2 )

Comm returns the commutator p1^{−1} ∗ p2^{−1} ∗ p1 ∗ p2 of the two permutations p1 and p2 . i ^p The operator ^evaluates to the image i^{p} of the positive integer i under the permutation p. i / p The operator / evaluates to the preimage i^{p^{−1}} of the integer i under the permutation p. list * p p * list The operator * evaluates to the list of products of the permutations in list with the permutation p. That means that the value is a new list new such that new[i] = list[i] * p respectively new[i] = p * list[i]. list / p The operator / evaluates to the list of quotients of the permutations in list with the permutation p. That means that the value is a new list new such that new[i] = list[i] / p. For the precedence of the operators see 2.10.

19.3 IsPerm

IsPerm( obj )

IsPerm returns true if obj , which may be an object of arbitrary type, is a permutation and false otherwise. It will signal an error if obj is an unbound variable.

    gap> IsPerm( (1,2) );
    true
    gap> IsPerm( 1 );
    false

19.4 LargestMovedPointPerm

LargestMovedPointPerm( perm )

LargestMoverPointPerm returns the largest point moved by the permutation perm, i.e., the largest positive integer i such that i^perm <> i. It will signal an error if perm is trivial (see also 19.5).

    gap> LargestMovedPointPerm( (2,3,1) );
    3
    gap> LargestMovedPointPerm( (1,2)(1000,1001) );

19.5 SmallestMovedPointPerm

SmallestMovedPointPerm( perm )

SmallestMovedPointPerm returns the smallest point moved by the permutation perm, i.e., the smallest positive integer i such that i^perm <> i. It will signal an error if perm is trivial (see also 19.4).

    gap> SmallestMovedPointPerm( (4,7,5) );
    4

19.6 SignPerm

SignPerm( perm )

SignPerm returns the sign of the permutation perm.

The sign s of a permutation p is defined by s = ∏_{i<j} (i^{p} − j^{p})/ ∏_{i<j} (i − j), where n is the largest point moved by p and i, j range over 1...n.

One can easily show that sign is equivalent to the determinant of the permutation matrix of perm. Thus it is obvious that the function sign is a homomorphism.

    gap> SignPerm( (1,2,3)(5,6) );
    -1

19.7 SmallestGeneratorPerm

SmallestGeneratorPerm( perm )

SmallestGeneratorPerm returns the smallest permutation that generates the same cyclic group as the permutation perm.

    gap> SmallestGeneratorPerm( (1,4,3,2) );
    (1,2,3,4)

Note that SmallestGeneratorPerm is very efficient, even when perm has huge order.

19.8 ListPerm

ListPerm( perm )

ListPerm returns a list list that contains the images of the positive integers under the permutation perm. That means that list[i] = i^perm, where i lies between 1 and the largest point moved by perm (see 19.4).

    gap> ListPerm( (1,2,3,4) );
    [ 2, 3, 4, 1 ]
    gap> ListPerm( () );
    [  ]

PermList (see 19.9) performs the inverse operation.

19.9 PermList

PermList( list )

PermList returns the permutation perm that moves points as describes by the list list. That means that i^perm = list[i] if i lies between 1 and the length of list, and i^perm = i if i is larger than the length of the list list. It will signal an error if list does not define a permutation, i.e., if list is not a list of integers without holes, or if list contains an integer twice, or if list contains an integer not in the range [1..Length(list)].

    gap> PermList( [6,2,4,1,5,3] );
    (1,6,3,4)
    gap> PermList( [] );
    ()

ListPerm (see 19.8) performs the inverse operation.

19.10 RestrictedPerm

RestrictedPerm( perm, list )

RestrictedPerm returns the new permutation new that operates on the points in the list list in the same way as the permutation perm, and that fixes those points that are not in list. list must be a list of positive integers such that for each i in list the image i^perm is also in list, i.e., it must be the union of cycles of perm.

    gap> RestrictedPerm( (1,2,3)(4,5), [4,5] );
    (4,5)

19.11 MappingPermListList

MappingPermListList( list1 , list2 )

MappingPermListList returns a permutation perm such that list1 [i] ^perm = list2 [i]. perm fixes all points larger then the maximum of the entries in list1 and list2 . If there are several such permutations, it is not specified which MappingPermListList returns. list1 and list2 must be lists of positive integers of the same length, and neither may contain an element twice.

    gap> MappingPermListList( [3,4], [6,9] );
    (3,6,4,9,8,7,5)
    gap> MappingPermListList( [], [] );
    ()

402 CHAPTER 19. PERMUTATIONS

Permutation Groups

A permutation group is a group of permutations on a set Ω of positive integers (see chapters 7 and 19).

Our standard example in this chapter will be the symmetric group of degree 4, which is defined by the following GAP statements.

    gap> s4 := Group( (1,2), (1,2,3,4) );
    Group( (1,2), (1,2,3,4) )

This introduction is followed by a section that describes the function that tests whether an object is a permutation group or not (see section 20.1). The next sections describe the functions that are related to the set of points moved by a permutation group (see 20.2, 20.3, 20.4, and 20.5). The following section describes the concept of stabilizer chains, which are used by most functions for permutation groups (see 20.6). The following sections describe the functions that compute or change a stabilizer chain (see 20.7, 20.8, 20.9, 20.10). The next sections describe the functions that extract information from stabilizer chains (see 20.11, 20.14, 20.12, and 20.13). The next two sections describe the functions that find elements or subgroups of a permutaiton group with a property (see 20.15 and 20.16).

Because each permutation group is a domain all set theoretic functions can be applied to it (see chapter 4 and 20.19). Also because each permutation group is after all a group all group functions can be applied to it (see chapter 7 and 20.20). Finally each permutation group operates naturally on the positive integers, so all operations functions can be applied (see chapter 8 and 20.21). The last section in this chapter describes the representation of permutation groups (see 20.23).

The external functions are in the file LIBNAME/"permgrp.g".

20.1 IsPermGroup

IsPermGroup( obj )

IsPermGroup returns true if the object obj , which may be an object of an arbitrary type, is a permutation group, and false otherwise. It will signal an error if obj is an unbound variable.

    gap> s4 := Group( (1,2), (1,2,3,4) );; s4.name := "s4";;

gap> IsPermGroup( s4 );
true
gap> f := FactorGroup( s4, Subgroup( s4, [(1,2)(3,4),(1,3)(2,4)] ) );
(s4 / Subgroup( s4, [ (1,2)(3,4), (1,3)(2,4) ] ))
gap> IsPermGroup( f );

false # see section 7.31

gap> IsPermGroup( [ 1, 2 ] );
false

20.2 PermGroupOps.MovedPoints

PermGroupOps.MovedPoints( G )

PermGroupOps.MovedPoints returns the set of moved points of the permutation group G, i.e., points which are moved by at least one element of G (also see 20.5).

gap> s4 := Group( (1,3,5,7), (1,3) );;
gap> PermGroupOps.MovedPoints( s4 );
[ 1, 3, 5, 7 ]
gap> PermGroupOps.MovedPoints( Group( () ) );
[  ]

20.3 PermGroupOps.SmallestMovedPoint

PermGroupOps.SmallestMovedPoint( G )

PermGroupOps.SmallestMovedPoint returns the smallest positive integer which is moved by the permutation group G (see also 20.4). This function signals an error if G is trivial.

gap> s3b := Group( (2,3), (2,3,4) );;
gap> PermGroupOps.SmallestMovedPoint( s3b );
2

20.4 PermGroupOps.LargestMovedPoint

PermGroupOps.LargestMovedPoint( G )

PermGroupOps.LargestMovedPoint returns the largest positive integer which is moved by the permutation group G (see also 20.3). This function signals an error if G is trivial.

gap> s4 := Group( (1,2,3,4), (1,2) );;
gap> PermGroupOps.LargestMovedPoint( s4 );
4

20.5 PermGroupOps.NrMovedPoints

PermGroupOps.NrMovedPoints( G )

PermGroupOps.NrMovedPoints returns the number of moved points of the permutation group G, i.e., points which are moved by at least one element of G (also see 20.2).

gap> s4 := Group( (1,3,5,7), (1,3) );;
gap> PermGroupOps.NrMovedPoints( s4 );

    4
    gap> PermGroupOps.NrMovedPoints( Group( () ) );
    0

20.6 Stabilizer Chains

Most of the algorithms for permutation groups need a stabilizer chain of the group. The concept of stabilizer chains was introduced by Charles Sims in [Sim70].

If [b_{1}, …, b_{n}] is a list of points, G^{(1)} = G and G^{(i+1)} = Stab_{G^{(i)}}(b_{i}) such that G^{(n+1)} = {()}. The list [b_{1}, …, b_{n}] is called a base of G, the points b_{i} are called basepoints. A set S of generators for G satisfying the condition < S ∩ G^{(i)} > = G^{(i)} for each 1 ≤ i ≤ n, is called a strong generating set (SGS) of G. More precisely we ought to say that a set S that satisfies the conditions above is a SGS of G relative to B. The chain of subgroups of G itself is called the stabilizer chain of G relative to B.

Since [b_{1}, …, b_{n}], where n is the degree of G and b_{i} are the moved points of G, certainly is a base for G there exists a base for each permutation group. The number of points in a base is called the length of the base. A base B is called reduced if no stabilizer in the chain relative to B is trivial, i.e., there exists no i such that G^{(i)} = G^{(i+1)}. Note that different reduced bases for one group G may have different length. For example, the Chevalley Group G_{2}(4) possesses reduced bases of length 5 and 7.

Let R^{(i)} be a right transversal of G^{(i+1)} in G^{(i)}, i.e., a set of right coset representatives of the cosets of G^{(i+1)} in G^{(i)}. Then each element g of G has a unique representation of the following form g = r_{n} … r_{1} with r_{i} ∈ R^{(i)}. Thus with the knowledge of the transversals R^{(i)} we know each element of G, in principle. This is one reason why stabilizer chains are one of the most useful tools for permutation groups. Furthermore basic group theory tells us that we can identify the cosets of G^{(i+1)} in G^{(i)} with the points in O^{(i)} := b_{i}^{G^{(i)}}. So we could represent a transversal as a list T such that T [p] is a representative of the coset corresponding to the point p ∈ O^{(i)}, i.e., an element of G^{(i)} that takes b_{i} to p.

For permutation groups of small degree this might be possible, but for permutation groups of large degree it is still not good enough. Our goal then is to store as few different permutations as possible such that we can still reconstruct each representative in R^{(i)}, and from them the elements in G. A factorized inverse transversal T is a list where T [p] is a generator of G^{(i)} such that p^{T [p]} is a point that lies earlier in O^{(i)} than p (note that we consider O^{(i)} as a list not as a set). If we assume inductively that we know an element r ∈ G^{(i)} that takes b_{i} to p^{T [p]}, then rT [p]^{−1} is an element in G^{(i)} that takes b_{i} to p.

A stabilizer chain (see 20.7, 20.23) is stored recursively in GAP. The group record of a permutation group G with a stabilizer chain has the following additional components.

orbit

List of orbitpoints of orbit[1] (which is the basepoint) under the action of the generators.

transversal

Factorized inverse transversal as defined above.

stabilizer

Record for the stabilizer of the point orbit[1] in the group generated by generators. The components of this record are again generators, orbit, transversal and stabilizer. The last stabilizer in the stabilizer chain only contains the component generators, which is an empty list.

Note that the values of these components are changed by functions that change, extend, or reduce a base (see 20.7, 20.8, and 20.9).

Note that the records that represent the stabilizers are not group records (see 7.111). Thus you cannot take such a stabilizer and apply group functions to it. The last stabilizer in the stabilizer chain is a record whose component generators is empty.

Below you find an example for a stabilizer chain for the symmetric group of degree 4.

    rec(
        identity    := (),
        generators  := [ (1,2), (1,2,3,4) ],
        orbit       := [ 1, 2, 4, 3 ],
        transversal := [ (), (1,2), (1,2,3,4), (1,2,3,4) ],
        stabilizer := rec(
            identity    := (),
            generators  := [ (3,4), (2,4) ],
            orbit       := [ 2, 4, 3 ],
            transversal := [ , (), (3,4), (2,4) ],
            stabilizer := rec(
                identity    := (),
                generators  := [ (3,4) ],
                orbit       := [ 3, 4 ],
                transversal := [ ,, (), (3,4) ],
                stabilizer := rec(
                    identity   := (),
                    generators := [  ]
                )
            )
        )
    )

20.7 MakeStabChain

MakeStabChain( G )
MakeStabChain( G, lst )

MakeStabChain computes a reduced stabilizer chain for the permutation group G.

If no stabilizer chain for G is already known and no argument lst is given, it computes a reduced stabilizer chain for the lexicographically smallest reduced base of G.

If no stabilizer chain for G is already known and an argument lst is given, it computes a reduced stabilizer chain with a base that starts with the points in lst. Note that points in lst that would lead to trivial stabilizers will be skipped (see 20.8).

The stabilizer chain is computed using the Schreier-Sims-Algorithm, which is described in [Leo80]. The time used is in practice proportional to the third power of the degree of the group.

If a stabilizer chain for G is already known and no argument lst is given, it reduces the known stabilizer chain.

If a stabilizer chain for G is already known and an argument lst is given, it changes the stabilizer chain such that the result is a reduced stabilizer chain with a base that starts with the points in lst (see 20.8). Note that points in lst that would lead to trivial stabilizers will be skipped.

The algorithm used in this case is called basechange, which is described in [But82]. The worst cases for the basechange algorithm are groups of large degree which have a long base.

gap> s4 := Group( (1,2), (1,2,3,4) );
Group( (1,2), (1,2,3,4) )

gap> MakeStabChain( s4 ); # compute a stabilizer chain

gap> Base( s4 );
[ 1, 2, 3 ]
gap> MakeStabChain( s4, [4,3,2,1] );  # perform a basechange
gap> Base( s4 );
[ 4, 3, 2 ]

20.8 ExtendStabChain

ExtendStabChain( G, lst )

ExtendStabChain inserts trivial stabilizers into the known stabilizer chain of the permutation group G such that lst becomes the base of G. The stabilizer chain which belongs to the base lst must reduce to the old stabilizer chain (see 20.9).

This function is useful if two different (sub-)groups have to have exactly the same base.

gap> s4 := Group( (1,2), (1,2,3,4) );;
gap> MakeStabChain( s4, [3,2,1] );  Base( s4 );
[ 3, 2, 1 ]
gap> h := Subgroup( Parent(s4), [(1,2,3,4), (2,4)] );
Subgroup( Group( (1,2), (1,2,3,4) ), [ (1,2,3,4), (2,4) ] )
gap> Base( h );
[ 1, 2 ]
gap> MakeStabChain( h, Base( s4 ) );  Base( h );
[ 3, 2 ]
gap> ExtendStabChain( h, Base( s4 ) );  Base( h );
[ 3, 2, 1 ]

20.9 ReduceStabChain

ReduceStabChain( G )

ReduceStabChain removes trivial stabilizers from a known stabilizer chain of the permutation group G. The result is a reduced stabilizer chain (also see 20.8).

gap> s4 := Group( (1,2), (1,2,3,4) );;
gap> Base( s4 );
[ 1, 2, 3 ]
gap> ExtendStabChain( s4, [ 1, 2, 3, 4 ] );  Base( s4 );

[ 1, 2, 3, 4 ]
gap> PermGroupOps.Indices( s4 );
[ 4, 3, 2, 1 ]
gap> ReduceStabChain( s4 );  Base( s4 );
[ 1, 2, 3 ]

20.10 MakeStabChainStrongGenerators

MakeStabChainStrongGenerators( G, base, stronggens )

MakeStabChainStrongGenerators computes a reduced stabilizer chain for the permutation group G with the base base and the strong generating set stronggens. stronggens must be a strong generating set for G relative to the base base; note that this is not tested. Since the generators for G are not changed the strong generating set of G got by PermGroupOps.StrongGenerators is not exactly stronggens afterwards. This function is mostly used to reconstruct a stabilizer chain for a group G and works considerably faster than MakeStabChain (see 20.7).

gap> G := Group( (1,2), (1,2,3), (4,5) );;
gap> Base( G );
[ 1, 2, 4 ]
gap> ExtendStabChain( G, [1,2,3,4] );
gap> PermGroupOps.Indices( G );  base := Base( G );

[ 3, 2, 1, 2 ] # note that the stabilizer chain is not reduced

[ 1, 2, 3, 4 ]
gap> stronggens := PermGroupOps.StrongGenerators( G );
[ (4,5), (2,3), (1,2), (1,2,3) ]
gap> H := Group( (1,2), (1,3), (4,5) );
Group( (1,2), (1,3), (4,5) )  # of course G = H
gap> MakeStabChainStrongGenerators( H, base, stronggens );
gap> PermGroupOps.Indices( H );  Base( H );

[ 3, 2, 2 ] # note that the stabilizer chain is reduced

[ 1, 2, 4 ]
gap> PermGroupOps.StrongGenerators( H );
[ (4,5), (2,3), (1,2), (1,3) ]

# note that this is different from stronggens

20.11 Base for Permutation Groups

Base( G )

Base returns a base for the permutation group G. If a stabilizer chain for G is already known, Base returns the base for this stabilizer chain. Otherwise a stabilizer chain for the lexicographically smallest reduced base is computed and its base is returned (see 20.6).

gap> s4 := Group( (1,2,3,4), (1,2) );;
gap> Base( s4 );
[ 1, 2, 3 ]

20.12 PermGroupOps.Indices

PermGroupOps.Indices( G )

PermGroupOps.Indices returns a list l of indices of the permutation group G with respect to a stabilizer chain of G, i.e., l[i] is the index of G^{(i+1)} in G^{(i)}. Thus the size of G is the product of all indices in l. If a stabilizer chain for G is already known, PermGroupOps.Indices returns the indices corresponding to this stabilizer chain. Otherwise a stabilizer chain with the lexicographically smallest reduced base is computed and the indices corresponding to this chain are returned (see 20.6).

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> PermGroupOps.Indices( s4 );

[ 4, 3, 2 ] # note that for s4 the indices are # actually independent of the base

20.13 PermGroupOps.StrongGenerators

PermGroupOps.StrongGenerators( G )

PermGroupOps.StrongGenerators returns a list of strong generators for the permutation group G. If a stabilizer chain for G is already known, PermGroupOps.StrongGenerators returns a strong generating set corresponding to this stabilizer chain. Otherwise a stabilizer chain with the lexicographically smallest reduced base is computed and a strong generating set corresponding to this chain is returned (see 20.6).

    gap> s4 := Group( (1,2,3,4), (1,2) );;
    gap> Base( s4 );
    [ 1, 2, 3 ]
    gap> PermGroupOps.StrongGenerators( s4 );
    [ (3,4), (2,3,4), (1,2), (1,2,3,4) ]

20.14 ListStabChain

ListStabChain( G )

ListStabChain returns a list of stabilizer records of the stabilizer chain of the permutation group G, i.e., the result is a list l such that l[i] is the i-th stabilizer G^{(i)}. The records in that list are identical to the records of the stabilizer chain. Thus changes made in a record l[i] are simultaneously done in the stabilizer chain (see 39.3).

20.15 PermGroupOps.ElementProperty

PermGroupOps.ElementProperty( G, prop )
PermGroupOps.ElementProperty( G, prop, K )

PermGroupOps.ElementProperty returns an element g in the permutation group G such that prop(g) is true. prop must be a function of one argument that returns either true or false when applied to an element of G. If G has no such element, false is returned.

    gap> V4 := Group((1,2),(3,4));;
    gap> PermGroupOps.ElementProperty( V4, g -> (1,2)^g = (3,4) );
    false

PermGroupOps.ElementProperty first computes a stabilizer chain for G, if necessary. Then it performs a backtrack search through G for an element satisfying prop, i.e., enumerates all elements of G as described in section 20.6, and applies prop to each until one element g is found for which prop(g) is true. This algorithm is described in detail in [But82].

gap> S8 := Group( (1,2), (1,2,3,4,5,6,7,8) );;  S8.name := "S8";;
gap> Size( S8 );
40320
gap> V := Subgroup( S8, [(1,2),(1,2,3),(6,7),(6,7,8)] );;
gap> Size( V );
36
gap> U := V ^(1,2,3,4)(5,6,7,8);;
gap> PermGroupOps.ElementProperty( S8, g -> U ^g = V );

(1,4,2)(5,6) # another permutation conjugating U to V

This search will of course take quite a while if G is large, especially if no element of G satisfies prop, and therefore all elements of G must be tried.

To speed up the computation you may pass a subgroup K of G as optional third argument. This subgroup must preserve prop in the sense that either all elements of a left coset g*K satisfy prop or no element of g*K does.

In our example above such a subgroup is the normalizer N_{G}(V) because h ∈ gN_{G}(V) takes U to V if and only if g does. Of course every subgroup of N_{G}(V) has this property too. Below we use the subgroup V itself. In this example this speeds up the computation by a factor of 4.

gap> K := Subgroup( S8, V.generators );;
gap> PermGroupOps.ElementProperty( S8, g -> U ^g = V, K );
(1,4,2)(5,6)

In the following example, we use the same subgroup, but with a larger generating system. This speeds up the computation by another factor of 3. Something like this may happen frequently. The reason is too complicated to be explained here.

gap> K2 := Subgroup( S8, Union( V.generators, [(2,3),(7,8)] ) );;
gap> K2 = K;
true
gap> PermGroupOps.ElementProperty( S8, g -> U ^g = V, K2 );
(1,4,2)(5,6)

Passing the full normalizer speeds up the computation in this example by another factor of 2. Beware though that in other examples the computation of the normalizer alone may take longer than calling PermGroupOps.ElementProperty with only the subgroup itself as argument.

gap> N := Normalizer( S8, V );
Subgroup( S8, [ (1,2), (1,2,3), (6,7), (6,7,8), (2,3), (7,8),
  (1,6)(2,7)(3,8), (4,5) ] )
gap> Size( N );
144
gap> PermGroupOps.ElementProperty( S8, g -> U ^g = V, N );
(1,4)(5,6)

20.16 PermGroupOps.SubgroupProperty

PermGroupOps.SubgroupProperty( G, prop )
PermGroupOps.SubgroupProperty( G, prop, K )

PermGroupOps.SubgroupProperty returns the subgroup U of the permutation group G of all elements in G that satisfy prop, i.e., the subgroup of all elements g in G such that prop(g) is true. prop must be a function of one argument that returns either true or false when applied to an element of G. Of course the elements that satisfy prop must form a subgroup of G. PermGroupOps.SubgroupProperty builds a stabilizer chain for U .

    gap> S8 := Group( (1,2), (1,2,3,4,5,6,7,8) );;  S8.name := "S8";;
    gap> Size(S8);
    40320
    gap> V := Subgroup( S8, [(1,2),(1,2,3),(6,7),(6,7,8)] );;
    gap> Size(V);
    36
    gap> PermGroupOps.SubgroupProperty( S8, g -> V ^g = V );
    Subgroup( S8, [ (7,8), (6,7), (4,5), (2,3)(4,5)(6,8,7), (1,2),
      (1,6,3,8)(2,7) ] )

# the normalizer of V in S8

PermGroupOps.SubgroupProperty first computes a stabilizer chain for G, if necessary. Then it performs a backtrack search through G for the elements satisfying prop, i.e., enumerates all elements of G as described in section 20.6, and applies prop to each, adding elements for which prop(g) is true to the subgroup U . Once U has become non-trivial, it is used to eliminate whole cosets of stabilizers in the stabilizer chain of G if they cannot contain elements with the property prop that are not already in U . This algorithm is described in detail in [But82].

This search will of course take quite a while if G is large. To speed up the computation you may pass a subgroup K of U as optional third argument.

Passing the subgroup V itself, speeds up the computation in this example by a factor of 2.

    gap> K := Subgroup( S8, V.generators );;
    gap> PermGroupOps.SubgroupProperty( S8, g -> V ^g = V, K );
    Subgroup( S8, [ (1,2), (1,2,3), (6,7), (6,7,8), (2,3), (7,8), (4,5),
      (1,6,3,8)(2,7) ] )

20.17 CentralCompositionSeriesPPermGroup

CentralCompositionSeriesPPermGroup( G )

This function calculates a central composition series for the p-group G. The method used is known as Holt’s algorithm. If G is not a p-group, an error is signalled.

    gap> D := Group( (1,2,3,4), (1,3) );; D.name := "d8";;
    gap> CentralCompositionSeriesPPermGroup( D );
    [ d8, Subgroup( d8, [ (2,4), (1,3) ] ),
      Subgroup( d8, [ (1,3)(2,4) ] ), Subgroup( d8, [  ] ) ]

20.18 PermGroupOps.PgGroup

PermGroupOps.PgGroup( G )

This function converts a permutation group G of prime power order p^{d} into an ag group P such that the presentation corresponds to a p-step central series of G. This central composition series is constructed by calling CentralCompositionSeriesPPermGroup (see 20.17). An isomorphism from the ag group to the permutation group is bound to P.bijection.

There is no dispatcher to this function, it must be called as PermGroupOps.PgGroup.

20.19 Set Functions for Permutation Groups

All set theoretic functions described in chapter 4 are also applicable to permutation groups. This section describes which functions are implemented specially for permutation groups. Functions not mentioned here are handled by the default methods described in the respective sections.

Random( G )

To compute a random element in a permutation group G GAP computes a stabilizer chain for G, takes on each level a random representative and returns the product of those. All elements of G are chosen with equal probability by this method.

Size( G )

Size calls MakeStabChain (see 20.7), if necessary, and returns the product of the indices of the stabilizer chain (see 20.6).

Elements( G )

Elements calls MakeStabChain (see 20.7), if necessary, and enumerates the elements of G as described in 20.6. It returns the set of those elements.

Intersection( G1 , G2 )

Intersection first computes stabilizer chains for G1 and G2 for a common base. If either group already has a stabilizer chain a basechange is performed (see 20.7). Intersection enumerates the elements of G1 and G2 using a backtrack algorithm, eliminating whole cosets of stabilizers in the stabilizer chains if possible (see 20.16). It builds a stabilizer chain for the intersection.

20.20 Group Functions for Permutation Groups

All group functions for groups described in chapter 7.9 are also applicable to permutation groups. This section describes which functions are implemented specially for permutation groups. Functions not mentioned here are handled by the default methods described in the respective sections.

G ^p

ConjugateSubgroup( G, p )

Returns the conjugate permutation group of G with the permutation p. p must be an element of the parent group of G. If a stabilizer chain for G is already known, it is also conjugated.

Centralizer( G, U )
Centralizer( G, g )
Normalizer( G, U )

These functions first compute a stabilizer chain for G. If a stabilizer chain is already known a basechange may be performed to obtain a base that is better suited for the problem. These functions then enumerate the elements of G with a backtrack algorithm, eliminating whole cosets of stabilizers in the stabilizer chain if possible (see 20.16). They build a stabilizer chain for the resulting subgroup.

SylowSubgroup( G, p )

If G is not transitive, its p-Sylow subgroup is computed by starting with P:=G, and for each transitive constituent homomorphism hom iterating

P := PreImage( SylowSubgroup( Image( hom, P ), p ) ).

If G is transitive but not primitive, its p-Sylow subgroup is computed as

SylowSubgroup( PreImage( SylowSubgroup(Image(hom,G),p) ), p ).

If G is primitive, SylowSubgroup takes random elements in G, until it finds a p-element g, whose centralizer in G contains the whole p-Sylow subgroup. Such an element must exist, because a p-group has a nontrivial centre. Then the p-Sylow subgroup of the centralizer is computed and returned. Note that the centralizer must be a proper subgroup of G, because it operates imprimitively on the cycles of g.

Coset( U , g )

Returns the coset U *g. The representative chosen is the lexicographically smallest element of that coset. It is computed with an algorithm that is very similar to the backtrack algorithm.

    gap> s4 := Group( (1,2,3,4), (1,2) );;  s4.name := "s4";;
    gap> u := Subgroup( s4, [(1,2,3)] );;
    gap> Coset( u, (1,3,2) );
    (Subgroup( s4, [ (1,2,3) ] )*())
    gap> Coset( u, (3,2) );
    (Subgroup( s4, [ (1,2,3) ] )*(2,3))

Cosets( G, U )

Returns the cosets of U in G. Cosets first computes stabilizer chains for G and U with a common base. If either subgroup already has a stabilizer chain, a basechange is performed (see 20.7). A transversal is computed recursively using the fact that if S is a transversal of U^{(2)} = Stab_{U}(b_{1}) in G^{(2)} = Stab_{G}(b_{1}), and R^{(1)} is a transversal of G^{(2)} in G, then a transversal of U in G is a subset of S ∗ R^{(1)}.

    gap> Cosets( s4, u );
    [ (Subgroup( s4, [ (1,2,3) ] )*()),
      (Subgroup( s4, [ (1,2,3) ] )*(3,4)),
      (Subgroup( s4, [ (1,2,3) ] )*(2,3)),
      (Subgroup( s4, [ (1,2,3) ] )*(2,3,4)),
      (Subgroup( s4, [ (1,2,3) ] )*(2,4,3)),
      (Subgroup( s4, [ (1,2,3) ] )*(2,4)),
      (Subgroup( s4, [ (1,2,3) ] )*(1,2,3,4)),
      (Subgroup( s4, [ (1,2,3) ] )*(1,2,4)) ]

PermutationCharacter( P )

Computes the character of the natural permutation representation of P, i.e. it does the same as PermutationCharacter( P, Stab_{P} (1) ) but works much faster.

    gap> G := SymmetricPermGroup(5);;
    gap> PermutationCharacter(G);
    [ 5, 3, 1, 2, 0, 1, 0 ]

ElementaryAbelianSeries( G )

This function builds an elementary abelian series of G by iterated construction of normal closures. If a partial elementary abelian series reaches up to a subgroup U of G which does not yet contain the generator s of G then the series is extended up to the normal closure N of U and s. If the factor N /U is not elementary abelian, i.e., if some commutator of s with one of its conjugates under G does not lie in U , intermediate subgroups are calculated recursively by extending U with that commutator. If G is solvable this process must come to an end since commutators of arbitrary depth cannot exist in solvable groups.

Hence this method gives an elementary abelian series if G is solvable and gives an infinite recursion if it is not. For permutation groups, however, there is a bound on the derived length that depends only on the degree d of the group. According to Dixon this is (5 log_{3}(d))/2. So if the commutators get deeper than this bound the algorithm stops and sets G.isSolvable to false, signalling an error. Otherwise G.isSolvable is set to true and the elementary abelian series is returned as a list of subgroups of G.

    gap> S := Group( (1,2,3,4), (1,2) );; S.name := "s4";;
    gap> ElementaryAbelianSeries( S );
    [ Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3), (1,2,4), (1,2,3,4) ] ),
      Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3), (1,2,4) ] ),
      Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3) ] ), Subgroup( s4, [  ] ) ]
    gap> A := Group( (1,2,3), (3,4,5) );;
    gap> ElementaryAbelianSeries( A );
    Error, <G> must be solvable

IsSolvable( G )

Solvability of a permutation group G is tested by trying to construct an elementary abelian series as described above. After this has been done the flag G.isSolvable is set correctly, so its value is returned.

    gap> S := Group( (1,2,3,4), (1,2) );;
    gap> IsSolvable( S );
    true
    gap> A := Group( (1,2,3), (3,4,5) );;
    gap> IsSolvable( A );
    false
CompositionSeries( G )

A composition series for the solvable group G is calculated either from a given subnormal series, which must be bound to G.subnormalSeries, in which case G.bssgs must hold the corresponding base-strong subnormal generating system, or from an elementary abelian series (as computed by ElementaryAbelianSeries( G ) above) by inserting intermediate subgroups (i.e. powers of the polycyclic generators or composition series along bases of the vector spaces in the elementary abelian series). In either case, after execution of this function, G.bssgs holds a base-strong pag system corresponding to the composition series calculated.

    gap> S := Group( (1,2,3,4), (1,2) );; S.name := "s4";;
    gap> CompositionSeries( S );
    [ Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3), (1,2,4), (1,2,3,4) ] ),
      Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3), (1,2,4) ] ),
      Subgroup( s4, [ (1,2)(3,4), (1,4)(2,3) ] ),
      Subgroup( s4, [ (1,2)(3,4) ] ), Subgroup( s4, [  ] ) ]

If G is not solvable then a composition series cs is computed with an algorithm by A. Seress and R. Beals. In this case the factor group of each element cs[i] in the composition series modulo the next one cs[i+1] are represented as primitive permutation groups. One should call cs[i].operations.FactorGroup( cs[i], cs[i+1] ) directly to avoid the check in FactorGroup that cs[i+1] is normal in cs[i]. The natural homomorphism of cs[i] onto this factor group will be given as a GroupHomomorphismByImages (see 7.106).

    gap> pyl29 := Group( (1,2,3)(4,5,6)(7,8,9), (2,6,4,9,3,8,7,5),
    >                     (4,7)(5,8)(6,9), (1,10)(4,7)(5,6)(8,9) );;
    gap> pyl29.name := "pyl29";;
    gap> cs := CompositionSeries( pyl29 );
    [ Subgroup( pyl29, [ (1,9,5)(2,7,6)(3,8,4), (2,7,3,4)(5,8,9,6),
          ( 1, 2,10)( 4, 9, 5)( 6, 8, 7), (2,6,4,9,3,8,7,5),
          (4,7)(5,8)(6,9) ] ),
      Subgroup( pyl29, [ (1,9,5)(2,7,6)(3,8,4), (2,7,3,4)(5,8,9,6),
          ( 1, 2,10)( 4, 9, 5)( 6, 8, 7), (2,6,4,9,3,8,7,5) ] ),
      Subgroup( pyl29, [ (1,9,5)(2,7,6)(3,8,4), (2,7,3,4)(5,8,9,6),
          ( 1, 2,10)( 4, 9, 5)( 6, 8, 7) ] ), Subgroup( pyl29, [  ] ) ]
    gap> List( [1..3], i->cs[i].operations.FactorGroup(cs[i],cs[i+1]) );
    [ Group( (1,2) ), Group( (1,2) ),
      Group( (1,9,5)(2,7,6)(3,8,4), (2,7,3,4)(5,8,9,6), ( 1, 2,10)
        ( 4, 9, 5)( 6, 8, 7) ) ]

    gap> List( last, Size );
    [ 2, 2, 360 ]

ExponentsPermSolvablePermGroup( G, perm [, start ] )
ExponentsPermSolvablePermGroup returns a list e, such that perm = G.bssgs[1]^e[1]
* G.bssgs[2]^e[2] * ...  * G.bssgs[n]^e[n], where G.bssgs must be a prime-step

base-strong subnormal generating system as calculated by ElementaryAbelianSeries (see 7.37 and above). If the optional third argument start is given, the list entries exps[1], ..., exps[start-1] are left unbound and the element perm is decomposed as product of the remaining pag generators G.bssgs[start], ..., G.bssgs[n].

    gap> S := Group( (1,2,3,4), (1,2) );; S.name := "s4";;
    gap> ElementaryAbelianSeries( S );;
    gap> S.bssgs;
    [ (1,2), (1,3,2), (1,4)(2,3), (1,2)(3,4) ]
    gap> ExponentsPermSolvablePermGroup( S, (1,2,3) );
    [ 0, 2, 0, 0 ]

AgGroup( G )

This function converts a solvable permutation group into an ag group. It calculates an elementary abelian series and a prime-step bssgs for G (see ElementaryAbelianSeries above) and then finds the relators belonging to this prime-step bssgs using the function ExponentsPermSolvablePermGroup (see above). An isomorphism from the ag group to the permutation group is bound to AgGroup( G ).bijection.

    gap> G := WreathProduct( SymmetricGroup( 4 ), CyclicGroup( 3 ) );;
    gap> A := AgGroup( G );
    Group( g1, g2, g3, g4, g5, g6, g7, g8, g9, g10, g11, g12, g13 )
    gap> (A.1*A.3)^A.bijection;
    ( 1, 6,10, 2, 5, 9)( 3, 7,11)( 4, 8,12)

20.21 Operations of Permutation Groups

All functions that deal with operations of groups are applicable to permutation groups (see 8). This section describes which functions are implemented specially for permutation groups. Functions not mentioned here are handled by the default methods described in the respective sections.

IsSemiRegular( G, D, opr )

IsSemiRegular returns true if G operates semiregularly on the domain D and false otherwise.

If D is a list of integers and opr is OnPoints, IsSemiRegular uses the lemma that says that such an operation is semiregular if all orbits of G on D have the same length, and if for an arbitrary point p of D and for each generator g of G there is a permutation z_{g} (not necessarily in G) such that p^{z_{g}} = p^{g} and which commutes with all elements of G, and if there is a permutation z (again not necessarily in G) that permutes the orbits of G on D setwise and commutes with all elements of G. This can be tested in time proportional to on^{2} + dn, where o is the size of a single orbit, n is the number of generators of G, and d is the size of D.

RepresentativeOperation( G, d, e, opr )

RepresentativeOperation returns a permutation perm in G that maps d to e in respect to the given operation opr if such a permutation exists, and false otherwise.

If the operation is OnPoints, OnPairs, OnTuples, or OnSets and d and e are positive integers or lists of integers, a basechange is performed and the representative is computed from the factorized inverse transversal (see 20.6 and 20.7).

If the operation is OnPoints, OnPairs, OnTuples or OnSets and d and e are permutations or lists of permutations, a backtrack search is performed (see 20.15).

Stabilizer( G, D, opr )

Stabilizer returns the stabilizer of D in G using the operation opr on the D. If D is a positive integer (respectively a list of positive integers) and the operation opr is OnPoints (respectively OnPairs or OnTuples) a basechange of G is performed (see 20.7). If D is a set of positive integers and the operation opr is OnSets a backtrack algorithm for set-stabilizers of permutation groups is performed.

Blocks( G, D [, seed ] [, operation ] )

Returns a partition of D being a minimal block system of G in respect to the operation opreration on the objects of D. If the argument seed is given the objects of seed are contained in the same block. If D is a list of positive integers an Atkinson algorithm is performed.

Theoretically the algorithm lies in O(n^{3}m) but in practice it is mostly in O(n^{2}m) with m the number of generators and n the cardinality of D.

20.22 Homomorphisms for Permutation Groups

This section describes the various homomorphisms that are treated specially for permutation groups.

GroupHomomorphisByImages( P, H , gens, imgs )

The group homomorphism of a permutation group P into another group H is handled especially by GroupHomomorphisByImages. Below we describe how the various mapping functions are implemented for such a group homomorphism ghom. The mapping functions not mentioned below are implemented by the default functions described in 7.106.

To work with ghom, a stabilizer chain for the source of ghom is computed and stored as ghom.orbit, ghom.transversal, ghom.stabilizer. For every stabilizer stab in the stabilizer chain there is a list parallel to stab.generators, which is called stab.genimages, and contains images of the generators. The stabilizer chain is computed with a random Schreier Sims algorithm, using the size of the source to know when to stop.

IsMapping( ghom )

To test if ghom is a (single valued) mapping, all Schreier generatores are computed. Each Schreier generator is then reduced along the stabilizer chain. Because the chain is complete, each one must reduce to the identity. Parallel the images of the strong generators are multiplied. If they also reduce to the identity (in the range), ghom is a function, otherwise the remainders form a normal generating set for the subgroup of images of the identity of the source.

Image( ghom, elm )

The image of an element elm can be computed by reducing the element along the stabilizer chain, and at each step multiplying the corresponding images of the strong generators.

CompositionMapping( hom, ghom )

The composition of an arbitrary group homomorphism hom and ghom the stabilizer chain of ghom is copied. On each level the images of the generators in stab.genimages are replaced by their images under hom.

OperationHomomorphism( P, Operation( P, list ) )

The operation of a permutation group P on a list list of integers is handled especially by OperationHomomorphism. (Note that list must be a union of orbits of P for Operation to work.) We call the resulting homomorphism a transitive constituent homomorphism. Below we describe how the various mapping functions are implemented for a transitive constituent homomorphism tchom. The mapping functions not mentioned below are implemented by the default functions described in 8.19.

Image( tchom, elm )

The image of an element is computed by restricting elm to list (see 19.10) and conjugating the restricted permutation with tchom.conperm, which maps it to a permutation that operates on [1..Length(list)] instead of list.

Image( tchom, H )

The image of a subgroup H is computed as follows. First a stabilizer chain for H is computed. This stabilizer chain is such that the base starts with points in list. Then the images of the strong generators of sub form a strong generating set of the image.

PreImages( tchom, H )

The preimage of a subgroup H is computed as follows. First a stabilizer chain for the source of tchom is computed. This stabilizer chain is such that the base starts with the point in list. Then the kernel of tchom is a stabilizer in this stabilizer chain. The preimages of the strong generators for H together with the strong generators for the kernel form a strong generating set of the preimage subgroup.

OperationHomomorphism( P, Operation( P, blocks, OnSets ) )

The operation of a permutation group P on a block system blocks (see 8.20) is handled especially by OperationHomomorphism. We call the resulting homomorphism a blocks homomorphism. Below we describe how the various mapping functions are implemented for a blocks homomorphism bhom. The mapping functions not mentioned below are implemented by the default functions described in 8.19.

Image( bhom, elm )

To compute the image of an element elm under bhom, the record for bhom contains a list bhom.reps, which contains for each point in the union of the blocks the position of this block in blocks. Then the image of an element can simply be computed by applying the element to a representative of each block and using bhom.reps to find in which block the image lies.

Image( bhom, H )
PreImage( bhom, elm )
PreImage( bhom, H )
Kernel( bhom )

The image of a subgroup, the preimage of an element, and the preimage of a subgroup are computed by rather complicated algorithms. For a description of these algorithms see [But85].

20.23 Permutation Group Records

All groups are represented by a record that contains information about the group. A permutation group record contains the following components in addition to those described in section 7.111.

isPermGroup

always true.

isFinite

always true as permutation groups are always of finite order.

A stabilizer chain (see 20.6) is stored recursively in GAP. The group record of a permutation group G with a stabilizer chain has the following additional components.

orbit

List of orbitpoints of orbit[1] (which is the basepoint) under the action of the generators.

transversal

Factorized inverse transversal as defined above.

stabilizer

Record for the stabilizer of the point orbit[1] in the group generated by generators. The components of this record are again generators, orbit, transversal and stabilizer. The last stabilizer in the stabilizer chain only contains the component generators, which is an empty list.

Note that the values of these components are changed by functions that change, extend, or reduce a base (see 20.7, 20.8, and 20.9).

Note that the records that represent the stabilizers are not themselves group records (see 7.111). Thus you cannot take such a stabilizer and apply group functions to it. The last stabilizer in the stabilizer chain is a record whose component generators is empty.

Words in Abstract Generators

Words in abstract generators are a type of group elements in GAP. In the following we will abbreviate their full name to words.

A word is just a sequence of letters, where each letter is an abstract generator or its inverse. Words are multiplied by concatenating them and removing adjacent pairs of a generator and its inverse. Abstract generators are created by the function AbstractGenerator (see 21.1).

Note that words do not belong to a certain group. Any two words can be multiplied. In effect we compute with words in a free group of potentially infinite rank (potentially infinite because we can always create new abstract generators with AbstractGenerator).

Words are entered as expressions in abstract generators and are displayed as product of abstract generators (and powers thereof). The trivial word can be entered and is displayed

as IdWord.
    gap> a := AbstractGenerator( "a" );
    a
    gap> b := AbstractGenerator( "b" );
    b
    gap> w := (a^2*b)^5*b^-1;
    a^2*b*a^2*b*a^2*b*a^2*b*a^2
    gap> a^0;
    IdWord

The first sections in this chapter describe the functions that create abstract generators (see 21.1 and 21.2). The next sections define the operations for words (see 21.3 and 21.4). The next section describes the function that tests whether an object is a word (see 21.5). The next sections describe the functions that compute the number of letters of a word (see 21.6 and 21.7). The next sections describe the functions that extract or find a subword (see 21.8 and 21.9). The final sections describe the functions that modify words (see 21.10, 21.11, and 21.12).

Note that words in abstract generators are different from words in finite polycyclic groups (see 23).

21.1 AbstractGenerator

AbstractGenerator( string )

AbstractGenerator returns a new abstract generator. This abstract generator is printed using the string string passed as argument to AbstractGenerator.

gap> a := AbstractGenerator( "a" );
a
gap> a^5;
a^5

Note that the string is only used to print the abstract generator and to order abstract generators (see 21.3). It is possible for two different abstract generators to use the same string and still be different.

gap> b := AbstractGenerator( "a" );
a
gap> a = b;
false

Also when you define abstract generators interactively it is a good idea to use the identifier of the variable as the name of the abstract generator, because then what GAP will output for a word is equal to what you can input to obtain this word. The following is an example of what you should probably not do.

gap> c := AbstractGenerator( "d" );
d
gap> d := AbstractGenerator( "c" );
c
gap> (c*d)^3;
d*c*d*c*d*c
gap> d*c*d*c*d*c;
c*d*c*d*c*d

21.2 AbstractGenerators

AbstractGenerators( string, n )

AbstractGenerators returns a list of n new abstract generators. These new generators are printed using string1, string2, ..., stringn.

gap> AbstractGenerators( "a", 3 );
[ a1, a2, a3 ]

AbstractGenerators could be defined as follows (see 21.1).

AbstractGenerators := function ( string, n )
    local   gens, i;
    gens := [];
    for i  in [1..n]  do
        Add( gens,
             AbstractGenerator(
                 ConcatenationString( string, String(i) ) ) );

        od;
        return gens;
    end;

21.3 Comparisons of Words

w1 = w2 w1 <> w2

The equality operator = evaluates to true if the two words w1 and w2 are equal and to false otherwise. The inequality operator <> evaluates to true if the two words w1 and w2 are not equal and to false otherwise.

You can compare words with objects of other types, but they are never equal of course.

    gap> a := AbstractGenerator( "a" );;
    gap> b := AbstractGenerator( "b" );;
    gap> a = b;
    false
    gap> (a^2*b)^5*b^-1 = a^2*b*a^2*b*a^2*b*a^2*b*a^2;
    true

w1 < w2 w1 <= w2 w1 > w2 w1 >= w2

The operators <, <=, >, and => evaluate to true if the word w1 is less than, less than or equal to, greater than, and greater than or equal to the word w2 .

Words are ordered as follows. One word w1 is considered smaller than another word w2 it it is shorted, or, if they have the same length, if it is first in the lexicographical ordering implied by the ordering of the abstract generators. The ordering of abstract generators is as follows. The abstract generators are ordered with respect to the strings that were passed to AbstractGenerator when creating these abstract generators. Each abstract generator g is also smaller than its inverse, but this inverse is smaller than any abstract generator that is larger than g.

Words can also be compared with objects of other types. Integers, rationals, cyclotomics, finite field elements, and permutations are smaller than words, everything else is larger.

    gap> IdWord<a;  a<a^-1;  a^-1<b;  b<b^-1;  b^-1<a^2; a^2<a*b;
    true
    true
    true
    true
    true
    true

21.4 Operations for Words

w1 * w2 The operator * evaluates to the product of the two words w1 and w2 . Note that words do not belong to a specific group, thus any two words can be multiplied. Multiplication of words is done by concatenating the words and removing adjacent pairs of an abstract generator and its inverse.

w1 / w2

The operator / evaluates to the quotient w1 ∗ w2^{−1} of the two words w1 and w2 . Inversion of a word is done by reversing the order of its letters and replacing each abstract generator with its inverse.

w1 ^w2

The operator ^evaluates to the conjugate w2^{−1} ∗ w1 ∗ w2 of the word w1 under the word w2 .

w1 ^i

The powering operator ^returns the i-th power of the word w1 , where i must be an integer. If i is zero, the value is IdWord.

list * w1 w1 * list

In this form the operator * returns a new list where each entry is the product of w1 and the corresponding entry of list. Of course multiplication must be defined between w1 and each entry of list.

list / w1

In this form the operator / returns a new list where each entry is the quotient of w1 and the corresponding entry of list. Of course division must be defined between w1 and each entry of list.

Comm( w1 , w2 )

Comm returns the commutator w1^{−1} ∗ w2^{−1} ∗ w1 ∗ w2 of two words w1 and w2 .

LeftQuotient( w1 , w2 )

LeftQuotient returns the left quotient w1^{−1} ∗ w2 of two words w1 and w2 .

21.5 IsWord

IsWord( obj )

IsWord returns true if the object obj , which may be an object of arbitrary type, is a word and false otherwise. Signals an error if obj is an unbound variable.

    gap> a := AbstractGenerator("a");;
    gap> b := AbstractGenerator("b");;

gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> IsWord( w );
true
gap> a := (1,2,3);;
gap> IsWord( a^2 );
false

21.6 LengthWord

LengthWord( w )

LengthWord returns the length of the word w, i.e., the number of letters in the word.

gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> LengthWord( w );
14
gap> LengthWord( a^13 );
13
gap> LengthWord( IdWord );
0

21.7 ExponentSumWord

ExponentSumWord( w, gen )

ExponentSumWord returns the number of times the generator gen appears in the word w minus the number of times its inverse appears in w. If gen and its inverse do no occur in w, 0 is returned. gen may also be the inverse of a generator of course.

gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> ExponentSumWord( w, a );
10
gap> ExponentSumWord( w, b );
4
gap> ExponentSumWord( (a*b*a^-1)^3, a );
0
gap> ExponentSumWord( (a*b*a^-1)^3, b^-1 );
-3

21.8 Subword

Subword( w, from, to )

Subword returns the subword of the word w that begins at position from and ends at position to. from and to must be positive integers. Indexing is done with origin 1.

gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> Subword( w, 5, 8 );
a*b*a^2

21.9 PositionWord

PositionWord( w, sub, from )

PositionWord returns the position of the first occurence of the word sub in the word w starting at position from. If there is no such occurence, false is returned. from must be a positive integer. Indexing is done with origin 1.

In other words, PositionWord(w,sub,from) returns the smallest integer i larger than or equal to from such that Subword( w, i, i+LengthWord(sub)-1 ) = sub (see 21.8).

gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> PositionWord( w, a^2*b, 2 );
4
gap> PositionWord( w, a*b^2, 2 );
false

21.10 SubstitutedWord

SubstitutedWord( w, from, to, by )

SubstitutedWord returns a new word where the subword of the word w that begins at position from and ends at position to is replaced by the 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 word Subword(w,1,from-1) * by

* Subword(w,to+1,LengthWord(w) (see 21.8).
gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> SubstitutedWord(w,5,8,b^-1);
a^2*b*a^3*b*a^2

21.11 EliminatedWord

EliminatedWord( word, gen, by )

EliminatedWord returns a new word where each occurence of the generator gen is replaced by the word by.

gap> a := AbstractGenerator("a");;

gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> EliminatedWord( w, b, b^2 );
a^2*b^2*a^2*b^2*a^2*b^2*a^2*b^2*a^2

21.12 MappedWord

MappedWord( w, gens, imgs )

MappedWord returns the new group element that is obtained by replacing each occurence of a generator gen in the list of generators gens by the corresponding group element img in the list of group elements imgs. The lists gens and imgs must of course have the same length.

gap> a := AbstractGenerator("a");;
gap> b := AbstractGenerator("b");;
gap> w := (a^2*b)^5*b^-1;
a^2*b*a^2*b*a^2*b*a^2*b*a^2
gap> MappedWord( w, [a,b], [(1,2,3),(1,2)] );
(1,3,2)

If the images in imgs are all words, and some of them are equal to the corresponding generators in gens, then those may be omitted.

gap> MappedWord( w, [a], [a^2] );
a^4*b*a^4*b*a^4*b*a^4*b*a^4

Note that the special case that the list gens and imgs have only length 1 is handled more efficiently by EliminatedWord (see 21.11).

428 CHAPTER 21. WORDS IN ABSTRACT GENERATORS

Finitely Presented Groups

A finitely presented group is a group generated by a set of abstract generators subject to a set of relations that these generators satisfy. Each group can be represented as finitely presented group.

A finitely presented group is constructed as follows. First create an appropriate free group (see 22.1). Then create the finitely presented group as a factor of this free group by the relators.

    gap> f2 := FreeGroup( "a", "b" );
    Group( a, b )
    gap> a5 := f2 / [ f2.1^2, f2.2^3, (f2.1*f2.2)^5 ];
    Group( a, b )
    gap> Size( a5 );
    60
    gap> a := a5.1;;  b := a5.2;;
    gap> Index( a5, Subgroup( a5, [ a*b ] ) );
    12

Note that, even though the generators print with the names given to FreeGroup, no variables of that name are defined. That means that the generators must be entered as freegroup.number and fp-group.number.

Note that the generators of the free group are different from the generators of the finitely presented group (even though they print with the same name). That means that words in the generators of the free group are not elements of the finitely presented group.

Note that the relations are entered as relators, i.e., as words in the generators of the free group. To enter an equation use the quotient operator, i.e., for the relation a^{b} = ab you have to enter a^b/(a*b).

You must not change the relators of a finitely presented group at all.

The elements of a finitely presented group are words. There is one fundamental problem with this. Different words can correspond to the same element in a finitely presented group. For example in the group a5 defined above, a and a^3 are actually the same element. However, a is not equal to a^3 (in the sense that a = a^3 is false). This leads to the following

anomaly: a^3 in a5 is true, but a^3 in Elements(a5) is false. Some set and group

functions will not work correctly because of this problem. You should therefore only use the functions mentioned in 22.2 and 22.3.

The first section in this chapter describes the function FreeGroup that creates a free group (see 22.1). The next sections describe which set theoretic and group functions are implemented specially for finitely presented groups and how they work (see 22.2 and 22.3). The next section describes the basic function CosetTableFpGroup that is used by most other functions for finitely presented groups (see 22.4). The next section describes how you can compute a permutation group that is a homomorphic image of a finitely presented group (see 22.5). The final section describes the function that finds all subgroups of a finitely presented group of small index (see 22.6).

22.1 FreeGroup

FreeGroup( n )
FreeGroup( n, string )
FreeGroup( name1 , name2 ..  )

FreeGroup returns the free group on n generators. The generators are displayed as string.1, string.2, ..., string.n. If string is missing it defaults to "f". If string is the name of the variable that you use to refer to the group returned by FreeGroup you can also enter the generators as string.i.

    gap> f2 := FreeGroup( 2, "a5" );;
    gap> a5 := f2 / [ f2.1^2, f.2^3, (f2.1*f2.2)^5 ];
    Group( a5.1, a5.2 )
    gap> Size( a5 );
    60
    gap> f2 := FreeGroup( "a", "b" );;
    gap> d8 := f2 / [ f2.1^4, f2.2^2, f2.1^f2.2 / f2.1 ];
    Group( a, b );
    gap> a := d8.1;;  b := d8.2;;
    gap> Index( d8, Subgroup( d8, [ a ] ) );
    2

22.2 Set Functions for Finitely Presented Groups

Finitely presented groups are domains, thus in principle all set theoretic functions are applicable to them (see chapter 4). However because words that are not equal may denote the same element of a finitely presented group many of them will not work correctly. This sections describes which set theoretic functions are implemented specially for finitely presented groups and how they work. You should not use the set theoretic functions that are not mentioned in this section.

The general information that enables GAP to work with a finitely presented group G is a coset table (see 22.4). Basically a coset table is the permutation representation of the finitely presented group on the cosets of a subgroup (which need not be faithful if the subgroup has a nontrivial core). Most of the functions below use the regular representation of G, i.e., the coset table of G over the trivial subgroup. Such a coset table is computed by a method called coset enumeration.

Size( G )

The size is simply the degree of the regular representation of G.

w in G

A word w lies in a parent group G if all its letters are among the generators of G.

w in H

To test whether a word w lies in a subgroup H of a finitely presented group G, GAP computes the coset table of G over H . Then it tests whether the permutation one gets by replacing each generator of G in w with the corresponding permutation is trivial.

Elements( G )

The elements of a finitely presented group are computed by computing the regular representation of G. Then for each point p GAP adds the smallest word w that, when viewed as a permutation, takes 1 to p to the set of elements. Note that this implies that each word in the set returned is the smallest word that denotes an element of G.

Elements( H )

The elements of a subgroup H of a finitely presented group G are computed by computing the elements of G and returning those that lie in H .

Intersection( H1 , H2 )

The intersection of two subgroups H1 and H2 of a finitely presented group G is computed as follows. First GAP computes the coset tables of G over H1 and H2 . Then it computes the tensor product of those two permutation representations. The coset table of the intersection is the transitive constituent of 1 in this tensored permutation representation. Finally GAP computes a set of Schreier generators for the intersection by performing another coset enumeration using the already complete coset table. The intersection is returned as the subgroup generated by those Schreier generators.

22.3 Group Functions for Finitely Presented Groups

Finitely presented groups are after all groups, thus in principle all group functions are applicable to them (see chapter 7). However because words that are not equal may denote the same element of a finitely presented group many of them will not work correctly. This sections describes which group functions are implemented specially for finitely presented groups and how they work. You should not use the group functions that are not mentioned in this section.

The general information that enables GAP to work with a finitely presented group G is a coset table (see 22.4). Basically a coset table is the permutation representation of the finitely presented group on the cosets of a subgroup (which need not be faithful if the subgroup has a nontrivial core). Most of the functions below use the regular representation of G, i.e., the coset table of G over the trivial subgroup. Such a coset table is computed by a method called coset enumeration.

Order( G, g )

The order of an element g is computed by translating the element into the regular permutation representation and computing the order of this permutation (which is the length of the cycle of 1).

Index( G, H )

The index of a subgroup H in a finitely presented group G is simply the degree of the permutation representation of the group G on the cosets of H .

Normalizer( G, H )

The normalizer of a subgroup H of a finitely presented group G is the union of those cosets of H in G that are fixed by all the generators of H when viewed as permutations in the permutation representation of G on the cosets of H . The normalizer is returned as the subgroup generated by the generators of H and representatives of such cosets.

CommutatorFactorGroup( G )

The commutator factor group of a finitely presented group G is returned as a new finitely presented group. The relations of this group are the relations of G plus the commutator of all the pairs of generators of G.

AbelianInvariants( G )

The abelian invariants of a abelian finitely presented group (e.g., a commutator factor group of an arbitrary finitely presented group) are computed by building the relation matrix of G and transforming this matrix to diagonal form with ElementaryDivisorsMat (see 32.17).

AbelianInvariantsSubgroupFpGroup( G, H )

This function is equivalent to AbelianInvariantsSubgroupFpGroupRrs below, but note

that there is an alternative function, AbelianInvariantsSubgroupFpGroupMtc.

AbelianInvariantsSubgroupFpGroupRrs( G, H )

AbelianInvariantsSubgroupFpGroupRrs returns the invariants of the commutator factor group H/H’ of a subgroup H of a finitely presented group G. They are computed by first applying an abelianized Reduced Reidemeister-Schreier procedure (see 22.9) to construct a relation matrix of H/H’ and then transforming this matrix to diagonal form with

ElementaryDivisorsMat (see 32.17).

AbelianInvariantsSubgroupFpGroupMtc( G, H )

AbelianInvariantsSubgroupFpGroupMtc returns the invariants of the commutator factor group H/H’ of a subgroup H of a finitely presented group G. They are computed by applying an abelianized Modified Todd-Coxeter procedure (see 22.9) to construct a relation matrix of H/H’ and then transforming this matrix to diagonal form with ElementaryDivisorsMat (see 32.17).

AbelianInvariantsNormalClosureFpGroup( G, H )
This function is equivalent to AbelianInvariantsNormalClosureFpGroupRrs below.

AbelianInvariantsNormalClosureFpGroupRrs( G, H )
AbelianInvariantsNormalClosureFpGroupRrs returns the invariants of the commutator

factor group N/N’ of the normal closure N a subgroup H of a finitely presented group G. They are computed by first applying an abelianized Reduced Reidemeister-Schreier procedure (see 22.9) to construct a relation matrix of N/N’ and then transforming this matrix to diagonal form with ElementaryDivisorsMat (see 32.17).

gap> # Define the Coxeter group E1.
gap> f5 := FreeGroup( "x1", "x2", "x3", "x4", "x5" );;
gap> E1 := f5 / [ f5.1^2, f5.2^2, f5.3^2, f5.4^2, f5.5^2,
>  ( f5.1 * f5.3 )^2, ( f5.2 * f5.4 )^2, ( f5.1 * f5.2 )^3,
>  ( f5.2 * f5.3 )^3, ( f5.3 * f5.4 )^3, ( f5.4 * f5.1 )^3,
>  ( f5.1 * f5.5 )^3, ( f5.2 * f5.5 )^2, ( f5.3 * f5.5 )^3,
>  ( f5.4 * f5.5 )^2,
>  ( f5.1 * f5.2 * f5.3 * f5.4 * f5.3 * f5.2 )^2 ];;
gap> x1:=E1.1;;  x2:=E1.2;;  x3:=E1.3;;  x4:=E1.4;;  x5:=E1.5;;
gap> # Get normal subgroup generators for B1.
gap> H := Subgroup( E1, [ x5 * x2^-1, x5 * x4^-1 ] );;
gap> # Compute the abelian invariants of B1/B1'.
gap> A := AbelianInvariantsNormalClosureFpGroup( E1, H );
[ 2, 2, 2, 2, 2, 2, 2, 2 ]
gap> # Compute a presentation for B1.
gap> P := PresentationNormalClosure( E1, H );
<< presentation with 18 gens and 46 rels of total length 132 >>
gap> SimplifyPresentation( P );
#I  there are 8 generators and 30 relators of total length 148
gap> B1 := FpGroupPresentation( P );
Group( _x1, _x2, _x3, _x4, _x6, _x7, _x8, _x11 )
gap> # Compute normal subgroup generators for B1'.
gap> gens := B1.generators;;
gap> numgens := Length( gens );;
gap> comms := [ ];;
gap> for i in [ 1 .. numgens - 1 ] do
>        for j in [i+1 .. numgens ] do
>            Add( comms, Comm( gens[i], gens[j] ) );
>        od;
>    od;
gap> # Compute the abelian invariants of B1'/B1".

    gap> K := Subgroup( B1, comms );;
    gap> A := AbelianInvariantsNormalClosureFpGroup( B1, K );
    [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0 ]

The prededing calculation for B_{1} and a similar one for B_{0} have been used to prove that B′_{1}/B′′_{1} ≅ Z_{2}^{9} × Z^{3} and B′_{0}/B′′_{0} ≅ Z_{2}^{91} × Z^{27} as stated in Proposition 5 in [FJNT].

The following functions are not implemented specially for finitely presented groups, but they work nevertheless. However, you probably should not use them for larger finitely presented groups.

Core( G, U )
SylowSubgroup( G, p )
FittingSubgroup( G )

22.4 CosetTableFpGroup

CosetTableFpGroup( G, H )

CosetTableFpGroup returns the coset table of the finitely presented group G on the cosets of the subgroup H .

Basically a coset table is the permutation representation of the finitely presented group on the cosets of a subgroup (which need not be faithful if the subgroup has a nontrivial core). Most of the set theoretic and group functions use the regular representation of G, i.e., the coset table of G over the trivial subgroup.

The coset table is returned as a list of lists. For each generator of G and its inverse the table contains a generator list. A generator list is simply a list of integers. If l is the generator list for the generator g and l[i] = j then generator g takes the coset i to the coset j by multiplication from the right. Thus the permutation representation of G on the cosets of H is obtained by applying PermList to each generator list (see 19.9). The coset table is standardized, i.e., the cosets are sorted with respect to the smallest word that lies in each coset.

    gap> f2 := FreeGroup( "a", "b" );
    Group( a, b )
    gap> a5 := f2 / [ f2.1^2, f2.2^3, (f2.1*f2.2)^5 ];
    Group( a, b )
    gap> CosetTableFpGroup( a5,
    >            Subgroup( a5, [ a5.1, a5.2*a5.1*a5.2*a5.1*a5.2^-1 ] ) );
    [ [ 1, 3, 2, 5, 4 ],

[ 1, 3, 2, 5, 4 ], # inverse of above, a5.1 is an involution

      [ 2, 4, 3, 1, 5 ],

[ 4, 1, 3, 2, 5 ] ] # inverse of above

    gap> List( last, PermList );
    [ (2,3)(4,5), (2,3)(4,5), (1,2,4), (1,4,2) ]

The coset table is computed by a method called coset enumeration. A Felsch strategy is used to decide how to define new cosets.

The variable CosetTableFpGroupDefaultLimit determines for how many cosets the table has initially room. CosetTableFpGroup will automatically extend this table if need arises, but this is an expensive operation. Thus you should set CosetTableFpGroupDefaultLimit to the number of cosets that you expect will be needed at most. However you should not set it too high, otherwise too much space will be used by the coset table.

The variable CosetTableFpGroupDefaultMaxLimit determines the maximal size of the coset table. If a coset enumeration reaches this limit it signals an error and enters the breakloop. You can either continue or quit the computation from there. Setting the limit to 0 allows arbitrary large coset tables.

22.5 OperationCosetsFpGroup

OperationCosetsFpGroup( G, H )

OperationCosetsFpGroup returns the permutation representation of the finitely presented group G on the cosets of the subgroup H as a permutation group. Note that this permutation representation is faithful if and only if H has a trivial core in G.

gap> f2 := FreeGroup( "a", "b" );
Group( a, b )
gap> a5 := f2 / [ f2.1^2, f2.2^3, (f2.1*f2.2)^5 ];
Group( a, b )
gap> OperationCosetsFpGroup( a5,
>            Subgroup( a5, [ a5.1, a5.2*a5.1*a5.2*a5.1*a5.2^-1 ] ) );
Group( (2,3)(4,5), (1,2,4) )
gap> Size( last );
60
OperationCosetsFpGroup simply calls CosetTableFpGroup, applies PermList to each row

of the table, and returns the group generated by those permutations (see 22.4, 19.9).

22.6 LowIndexSubgroupsFpGroup

LowIndexSubgroupsFpGroup( G, H , index )

LowIndexSubgroupsFpGroup returns a list of representatives of the conjugacy classes of subgroups of the finitely presented group G that contain the subgroup H of H and that have index less than or equal to index.

gap> f2 := FreeGroup( "a", "b" );
Group( a, b )
gap> a5 := f2 / [ f2.1^2, f2.2^3, (f2.1*f2.2)^5 ];
Group( a, b )
gap> s := LowIndexSubgroupsFpGroup( a5, TrivialSubgroup( a5 ), 12 );
[ a5,
  Subgroup( a5, [ a, b*a*b^-1 ] ),
  Subgroup( a5, [ a, b*a*b*a^-1*b^-1 ] ),
  Subgroup( a5, [ a, b*a*b*a*b^-1*a^-1*b^-1 ] ),
  Subgroup( a5, [ b*a^-1 ] ) ]
gap> List( s, h -> Index( a5, h ) );

[ 1, 6, 5, 10, 12 ] # the indices of the subgroups

gap> List( s, h -> Index( a5, Normalizer( a5, h ) ) );

[ 1, 6, 5, 10, 6 ] # the lengths of the conjugacy classes LowIndexSubgroupsFpGroup systematically constructs all permutation representations of G. How large you can choose index depends on the presentation of G, but you should be careful. Usually the time required grows exponentially with index.

22.7 Presentation Records

In GAP, finitely presented groups are distinguished from group presentations which are GAP objects of their own and which are stored in presentation records. The reason is that very often presentations have to be changed (e.g. simplified) by Tietze transformations, but since in these new generators and relators are introduced, all words in the generators of a finitely presented group would also have to be changed if such a Tietze transformation were applied to the presentation of a finitely presented group. Therefore, in GAP the presentation defining a finitely presented group is never changed; changes are only allowed for group presentations which are not considered to define a particular group. GAP offers a bundle of commands to perform Tietze transformations on finite group presentations (see 22.10, 22.11). In order to speed up the respective routines, the relators in such a presentation record are not represented by ordinary GAP words, but by lists of positive or negative generator numbers. The term ”Tietze record”will sometimes be used as an alias for ”presentation record”. It occurs, in particular, in certain error messages. The following two commands can be used to create a presentation record from a finitely presented group or, vice versa, to create a finitely presented group from a presentation.

PresentationFpGroup( G )
PresentationFpGroup( G, printlevel )

PresentationFpGroup returns a presentation record containing a copy of the presentation of the given finitely presented group G on the same set of generators. The optional printlevel parameter can be used to restrict or to extend the amount of output provided by Tietze transformation commands when being applied to the created presentation record. The default value 1 is designed for interactive use and implies explicit messages to be displayed by most of these commands. A printlevel value of 0 will suppress these messages, whereas a printlevel value of 2 will enforce some additional output.

FpGroupPresentation( P )

FpGroupPresentation returns a finitely presented group defined by the presentation in the given presentation record P. If some presentation record P, say, contains a large presentation, then it would be nasty to wait for the end of an unintentionally started printout of all of its components (or, more precisely, of its component P.tietze which contains the essential lists). Therefore, whenever you use the standard print facilities to display a presentation record, GAP will provide just one line of text containing the number of generators, the number of relators, and the total length of all relators. Of course, you may use the RecFields and PrintRec commands to display all components of P. In addition, you may use the following commands to extract and print different amounts of information from a presentation record.

TzPrintStatus( P )

TzPrintStatus prints the current state of a presentation record P, i.e., the number of generators, the number of relators, and the total length of all relators.

If you are working interactively, you can get the same information by just typing P;

TzPrintGenerators( P )
TzPrintGenerators( P, list )

TzPrintGenerators prints the current list of generators of a presentation record P, providing for each generator its name, the total number of its occurrences in the relators, and, if that generator is known to be an involution, an appropriate message.

If a list list has been specified as second argument, then it is expected to be a list of the position numbers of the generators to be printed. list need not be sorted and may contain duplicate elements. The generators are printed in the order in which and as often as their numbers occur in list. Position numbers out of range (with respect to the list of generators) will be ignored.

TzPrintRelators( P )
TzPrintRelators( P, list )

TzPrintRelators prints the current list of relators of a presentation record P.

If a list list has been specified as second argument, then it is expected to be a list of the position numbers of the relators to be printed. list need not be sorted and may contain duplicate elements. The relators are printed in the order in which and as often as their numbers occur in list. Position numbers out of range (with respect to the list of relators) will be ignored.

TzPrintPresentation( P )

TzPrintPresentation prints the current lists of generators and relators and the current state of a presentation record P. In fact, the command

    TzPrintPresentation( P )

is an abbreviation of the command sequence

    Print( "generators:\n" ); TzPrintGenerators( P );
    Print( "relators:\n" ); TzPrintRelators( P );
    TzPrintStatus( P );

TzPrint( P )
TzPrint( P, list )

TzPrint provides a kind of fast print out for a presentation record P.

Remember that in order to speed up the Tietze transformation routines, each relator in a presentation record P is internally represented by a list of positive or negative generator numbers, i.e., each factor of the proper GAP word is represented by the position number of the corresponding generator with respect to the current list of generators, or by the respective negative number, if the factor is the inverse of a generator which is not known to be an involution. In contrast to the commands TzPrintRelators and TzPrintPresentation described above, TzPrint does not convert these lists back to the corresponding GAP words.

TzPrint prints the current list of generators, and then for each relator its length and its internal representation as a list of positive or negative generator numbers.

If a list list has been specified as second argument, then it is expected to be a list of the position numbers of the relators to be printed. list need not be sorted and may contain duplicate elements. The relators are printed in the order in which and as often as their numbers occur in list. Position numbers out of range (with respect to the list of relators) will be ignored.

There are three more print commands for presentation records which are convenient in the context of the interactive Tietze transformation commands:

TzPrintLengths( P )

See 22.11.

TzPrintPairs( P )
TzPrintPairs( P, n )

See 22.11.

TzPrintOptions( P )

See 22.11.

Save( file, P, name )

Save writes the presentation record P to the file file in such a way that the file can be read by GAP. This allows you, in particular, to restart a computation with the current presentation not only in the present, but also in a later GAP session. Then, when you read the file, a copy of P will be assigned to the variable specified by the argument name in the current call of the Save command.

Example.

    gap> f2 := FreeGroup( "a", "b" );;
    gap> g := f2 / [ f2.1^2, f2.2^7, Comm(f2.1,f2.1^f2.2),
    >                Comm(f2.1,f2.1^(f2.2^2))*(f2.1^f2.2)^-1 ];
    Group( a, b )
    gap> a := g.1;; b := g.2;;
    gap> p := PresentationFpGroup( g );
    << presentation with 2 gens and 4 rels of total length 30 >>
    gap> TzPrintGenerators( p );
    #I  1.  a   11 occurrences   involution
    #I  2.  b   19 occurrences
    gap> TzPrintRelators( p );
    #I  1. a^2
    #I  2. b^7
    #I  3. a*b^-1*a*b*a*b^-1*a*b

    #I  4. a*b^-2*a*b^2*a*b^-2*a*b*a*b
    gap> TzPrint( p );
    #I  generators: [ a, b ]
    #I  relators:
    #I  1.  2  [ 1, 1 ]
    #I  2.  7  [ 2, 2, 2, 2, 2, 2, 2 ]
    #I  3.  8  [ 1, -2, 1, 2, 1, -2, 1, 2 ]
    #I  4.  13  [ 1, -2, -2, 1, 2, 2, 1, -2, -2, 1, 2, 1, 2 ]
    gap> TzPrintStatus( p );
    #I  there are 2 generators and 4 relators of total length 30
    gap> Save( "checkpoint", p, "p0" );
    gap> Read( "checkpoint" );
    #I  presentation record p0 read from file
    gap> p0;
    << presentation with 2 gens and 4 rels of total length 30 >>

22.8 Changing Presentations

The commands described in this section can be used to change the presentation in a presentation record. Note that, in general, they will change the isomorphism type of the group defined by the presentation. Hence, though they sometimes are called as subroutines by Tietze transformation commands like TzSubstitute (see 22.11), they do not perform Tietze transformations themselves.

AddGenerator( P )
AddGenerator( P, generator )

AddGenerator adds a new generator to the list of generators.

If you don’t specify a second argument, then AddGenerator will define a new abstract generator xi and save it in a new component P.i of the given presentation record where i is the least positive integer which has not yet been used as a generator number. Though this new generator will be printed as xi, you will have to use the external variable P.i if you want to access it.

If you specify a second argument, then generator must be an abstract generator which does not yet occur in the presentation. AddGenerator will add it to the presentation and save it in a new component P.i in the same way as described for xi above.

AddRelator( P, word )

AddRelator adds the word word to the list of relators. word must be a word in the generators of the given presentation.

RemoveRelator( P, n )

RemoveRelator removes the nth relator and then resorts the list of relators in the given presentation record P.

22.9 Subgroup Presentations

PresentationSubgroupRrs( G, H )
PresentationSubgroupRrs( G, H , string )

PresentationSubgroupRrs returns a presentation record (see 22.7) containing a presentation for the subgroup H of the finitely presented group G. It uses the Reduced Reidemeister-Schreier method to construct this presentation.

The generators in the resulting presentation will be named by string1, string2, ..., the default string is " x".

The Reduced Reidemeister-Schreier algorithm is a modification of the Reidemeister-Schreier algorithm of George Havas [Hav74]. It was proposed by Joachim Neubüser and first implemented in 1986 by Andrea Lucchini and Volkmar Felsch in the SPAS system [Leh89b]. Like George Havas’ Reidemeister-Schreier algorithm, it needs only the presentation of G and a coset table of H in G to construct a presentation of H .

Whenever you call the PresentationSubgroupRrs command, it checks first whether a coset table of H in G has already been computed and saved in the subgroup record of H by a preceding call of some appropriate command like CosetTableFpGroup (see 22.4), Index (see 7.48), or LowIndexSubgroupsFpGroup (see 22.6). Only if the coset table is not yet available,

is it now constructed by PresentationSubgroupRrs which calls CosetTableFpGroup for this

purpose. In this case, of course, a set of generators of H is required, but they will not be used any more in the subsequent steps.

Next, a set of generators of H is determined by reconstructing the coset table and introducing in that process as many Schreier generators of H in G as are needed to do a Felsch strategy coset enumeration without any coincidences. (In general, though containing redundant generators, this set will be much smaller than the set of all Schreier generators. That’s why we call the method the Reduced Reidemeister-Schreier.)

After having constructed this set of primary subgroup generators , say, the coset table is extended to an augmented coset table which describes the action of the group generators on coset representatives, i.e., on elements instead of cosets. For this purpose, suitable words in the (primary) subgroup generators have to be associated to the coset table entries. In order to keep the lengths of these words short, additional secondary subgroup generators are introduced as abbreviations of subwords. Their number may be large.

Finally, a Reidemeister rewriting process is used to get defining relators for H from the relators of G. As the resulting presentation of H is a presentation on primary and secondary generators, in general you will have to simplify it by appropriate Tietze transformations (see 22.11) or by the DecodeTree command (see 22.12) before you can use it. Therefore it is returned in the form of a presentation record.

Compared with the Modified Todd-Coxeter method described below, the Reduced Reidemeister-Schreier method (as well as Havas’ original Reidemeister-Schreier program) has the advantage that it does not require generators of H to be given if a coset table of H in G is known. This provides a possibility to compute a presentation of the normal closure of a given subgroup (see the PresentationNormalClosureRrs command below).

A few examples are given in section 22.11.

PresentationSubgroupMtc( G, H )
PresentationSubgroupMtc( G, H , string )
PresentationSubgroupMtc( G, H , printlevel )
PresentationSubgroupMtc( G, H , string, printlevel )

PresentationSubgroupMtc returns a presentation record (see 22.7) containing a presentation for the subgroup H of the finitely presented group G. It uses a Modified Todd-Coxeter method to construct this presentation.

The generators in the resulting presentation will be named by string1, string2, ..., the default string is " x".

The optional printlevel parameter can be used to restrict or to extend the amount of output provided by the PresentationSubgroupMtc command. In particular, by specifying the printlevel parameter to be 0, you can suppress the output of the DecodeTree command which is called by the PresentationSubgroupMtc command (see below). The default value of printlevel is 1.

The so called Modified Todd-Coxeter method was proposed, in slightly different forms, by Nathan S. Mendelsohn and William O. J. Moser in 1966. Moser’s method was proved by Michael J. Beetham and Colin M. Campbell (see [BC76]). Another proof for a special version was given by D. H. McLain (see [McL77]). It was generalized to cover a broad spectrum of different versions (see the survey [Neu82]). Moser’s method was implemented by Harvey A. Campbell (see [Cam71]. Later, a Modified Todd-Coxeter program was implemented in St. Andrews by David G. Arrell, Sanjiv Manrai, and Michael F. Worboys (see [AMW82]) and further developed by David G. Arrel and Edmund F. Robertson (see [AR84]) and by Volkmar Felsch in the SPAS system [Leh89b].

The Modified Todd-Coxeter method performs an enumeration of coset representatives. It proceeds like an ordinary coset enumeration (see CosetTableFpGroup 22.4), but as the product of a coset representative by a group generator or its inverse need not be a coset representative itself, the Modified Todd-Coxeter has to store a kind of correction element for each coset table entry. Hence it builds up a so called augmented coset table of H in G consisting of the ordinary coset table and a second table in parallel which contains the associated subgroup elements.

Theoretically, these subgroup elements could be expressed as words in the given generators of H , but in general these words tend to become unmanageable because of their enormous lengths. Therefore, a highly redundant list of subgroup generators is built up starting from the given (”primary”) generators of H and adding additional (”secondary”) generators which are defined as abbreviations of suitable words of length two in the preceding generators such that each of the subgroup elements in the augmented coset table can be expressed as a word of length at most one in the resulting (primary and secondary) subgroup generators.

Then a rewriting process (which is essentially a kind of Reidemeister rewriting process) is used to get relators for H from the defining relators of G.

The resulting presentation involves all the primary, but not all the secondary generators of H . In fact, it contains only those secondary generators which explicitly occur in the augmented coset table. If we extended this presentation by those secondary generators which are not yet contained in it as additional generators, and by the definitions of all secondary generators as additional relators, we would get a presentation of H , but, in general, we would end up with a large number of generators and relators.

On the other hand, if we avoid this extension, the current presentation will not necessarily define H although we have used the same rewriting process which in the case of the SubgroupPresentationRrs command computes a defining set of relators for H from an augmented coset table and defining relators of G. The different behaviour here is caused by the fact that coincidences may have occurred in the Modified Todd-Coxeter coset enumeration. To overcome this problem without extending the presentation by all secondary generators, the SubgroupPresentationMtc command applies the so called tree decoding algorithm which provides a more economical approach. The reader is strongly recommended to carefully read section 22.12 where this algorithm is described in more detail. Here we will only mention that this procedure adds many fewer additional generators and relators in a process which in fact eliminates all secondary generators from the presentation and hence finally provides a presentation of H on the primary, i.e., the originally given, generators of H . This is a remarkable advantage of the SubgroupPresentationMtc command compared to the SubgroupPresentationRrs command. But note that, for some particular subgroup H , the Reduced Reidemeister-Schreier method might quite well produce a more concise presentation. The resulting presentation is returned in the form of a presentation record. Though the tree decoding routine already involves a lot of Tietze transformations, we recommend that you try to further simplify the presentation by appropriate Tietze transformations (see 22.11). An example is given in section 22.12.

PresentationSubgroup( G, H )
PresentationSubgroup( G, H , string )

PresentationSubgroup returns a presentation record (see 22.7) containing a presentation for the subgroup H of the finitely presented group G. The current GAP implementation offers a choice between two different methods for constructing subgroup presentations, namely the Reduced Reidemeister-Schreier and the Modified Todd-Coxeter procedure. You can specify either of them by calling the commands

PresentationSubgroupRrs or PresentationSubgroupMtc, respectively. Further methods

may be added in a later GAP version. If, in some concrete application, you don’t care for the method to be selected, you may use the PresentationSubgroup command as a kind of default command. In the present installation, it will call the Reduced Reidemeister-Schreier method, i.e., it is identical with the PresentationSubgroupRrs command. A few examples are given in section 22.11.

PresentationNormalClosureRrs( G, H )
PresentationNormalClosureRrs( G, H , string )

PresentationNormalClosureRrs returns a presentation record (see 22.7) containing a presentation for the normal closure of the subgroup H of the finitely presented group G. It uses the Reduced Reidemeister-Schreier method to construct this presentation. This provides a possibility to compute a presentation for a normal subgroup for which only ”normal subgroup generators”, but not necessarily a full set of generators are known. The generators in the resulting presentation will be named by string1, string2, ..., the default string is " x".

PresentationNormalClosureRrs first establishes an intermediate group record for the factor group of G by the normal closure N , say, of H in G. Then it performs a coset enumeration of the trivial subgroup in that factor group. The resulting coset table can be considered as coset table of N in G, hence a presentation for N can be constructed using the Reduced Reidemeister-Schreier algorithm as described for the PresentationSubgroupRrs command.

PresentationNormalClosure( G, H )
PresentationNormalClosure( G, H , string )

PresentationNormalClosure returns a presentation record (see 22.7) containing a presentation for the normal closure of the subgroup H of the finitely presented group G. This provides a possibility to compute a presentation for a normal subgroup for which only ”normal subgroup generators”, but not necessarily a full set of generators are known.

If, in a later release, GAP offers different methods for the construction of normal closure presentations, then PresentationNormalClosure will call one of these procedures as a kind of default method. At present, however, the Reduced Reidemeister-Schreier algorithm is the only one implemented so far. Therefore, at present the PresentationNormalClosure command is identical with the PresentationNormalClosureRrs command described above.

22.10 SimplifiedFpGroup

SimplifiedFpGroup( G )

SimplifiedFpGroup applies Tietze transformations to a copy of the presentation of the given finitely presented group G in order to reduce it with respect to the number of generators, the number of relators, and the relator lengths.

SimplifiedFpGroup returns the resulting finitely presented group (which is isomorphic to G).

    gap> f6 := FreeGroup( 6, "g" );;
    gap> g := f6 / [ f6.1^2, f6.2^2, f6.4*f6.6^-1, f6.5^2, f6.6^2,
    >         f6.1*f6.2^-1*f6.3, f6.1*f6.5*f6.3^-1, f6.2*f6.4^-1*f6.3,
    >         f6.3*f6.4*f6.5^-1, f6.1*f6.6*f6.3^-2, f6.3^4 ];;
    gap> h := SimplifiedFpGroup( g );
    Group( g.1, g.3 )
    gap> h.relators;
    [ g.1^2, g.1*g.3^-1*g.1*g.3^-1, g.3^4 ]

In fact, the command

    h := SimplifiedFpGroup( g );

is an abbreviation of the command sequence

    p := PresentationFpGroup( g, 0 );;
    SimplifyPresentation( p );
    h := FpGroupPresentation( p );

which applies a rather simple-minded strategy of Tietze transformations to the intermediate presentation record P (see 22.7). If for some concrete group the resulting presentation is unsatisfying, then you should try a more sophisticated, interactive use of the available Tietze transformation commands (see 22.11).

22.11 Tietze Transformations

The GAP commands being described in this section can be used to modify a group presentation in a presentation record by Tietze transformations.

In general, the aim of such modifications will be to simplify the given presentation, i.e., to reduce the number of generators and the number of relators without increasing too much the sum of all relator lengths which we will call the total length of the presentation. Depending on the concrete presentation under investigation one may end up with a nice, short presentation or with a very huge one.

Unfortunately there is no algorithm which could be applied to find the shortest presentation which can be obtained by Tietze transformations from a given one. Therefore, what GAP offers are some lower-level Tietze transformation commands and, in addition, some higherlevel commands which apply the lower-level ones in a kind of default strategy which of course cannot be the optimal choice for all presentations.

The design of these commands follows closely the concept of the ANU Tietze transformation program designed by George Havas [Hav69] which has been available from Canberra since 1977 in a stand-alone version implemented by Peter Kenne and James Richardson and later on revised by Edmund F. Robertson (see [HKRR84], [Rob88]).

In this section, we first describe the higher-level commands SimplifyPresentation, TzGo, and TzGoGo (the first two of these commands are identical).

Then we describe the lower-level commands TzEliminate, TzSearch, TzSearchEqual, and TzFindCyclicJoins. They are the bricks of which the preceding higher-level commands have been composed. You may use them to try alternative strategies, but if you are satisfied by the performance of TzGo and TzGoGo, then you don’t need them.

Some of the Tietze transformation commands listed so far may eliminate generators and hence change the given presentation to a presentation on a subset of the given set of generators, but they all do not introduce new generators. However, sometimes you will need to substitute certain words as new generators in order to improve your presentation. There-

fore GAP offers the two commands TzSubstitute and TzSubstituteCyclicJoins which

introduce new generators. These commands will be described next.

Subsequently we describe some print commands, namely TzPrintLengths, TzPrintPairs, and TzPrintOptions, which are useful if you run the Tietze transformations interactively.

At the end of this section we list the Tietze options and give their default values. These are parameters which essentially influence the performance of the commands mentioned above. However, they are not specified as arguments of function calls. Instead, they are associated to the presentation records: Each presentation record keeps its own set of Tietze option values in the form of ordinary record components.

SimplifyPresentation( P )
TzGo( P )

SimplifyPresentation performs Tietze transformations on a presentation P. It is perhaps the most convenient of the interactive Tietze transformation commands. It offers a kind of default strategy which, in general, saves you from explicitly calling the lower-level commands it involves.

Roughly speaking, SimplifyPresentation consists of a loop over a procedure which involves two phases: In the search phase it calls TzSearch and TzSearchEqual described below which try to reduce the relator lengths by substituting common subwords of relators, in the elimination phase it calls the command TzEliminate described below (or, more precisely, a subroutine of TzEliminate in order to save some administrative overhead) which tries to eliminate generators that can be expressed as words in the remaining generators.

If SimplifyPresentation succeeds in reducing the number of generators, the number of relators, or the total length of all relators, then it displays the new status before returning (provided that you did not set the print level to zero). However, it does not provide any output if all these three values have remained unchanged, even if the TzSearchEqual command involved has changed the presentation such that another call of SimplifyPresentation might provide further progress. Hence, in such a case it makes sense to repeat the call of the command for several times (or to call instead the TzGoGo command which we will describe next).

As an example we compute a presentation of a subgroup of index 408 in P SL(2, 17).

    gap> f2 := FreeGroup( "a", "b" );;
    gap> g := f2 / [ f2.1^9, f2.2^2, (f2.1*f2.2)^4, (f2.1^2*f2.2)^3 ];;
    gap> a := g.1;;  b := g.2;;
    gap> h := Subgroup( g, [ (a*b)^2, (a^-1*b)^2 ] );;
    gap> Index( g, h );
    408
    gap> p := PresentationSubgroup( g, h );
    << presentation with 8 gens and 36 rels of total length 111 >>
    gap> p.printLevel := 2;;
    gap> SimplifyPresentation( p );
    #I  there are 4 generators and 8 relators of total length 21
    #I  there are 4 generators and 7 relators of total length 18
    #I  eliminating _x4 = _x3^-1*_x2^-1
    #I  eliminating _x1 = _x3^-1*_x2
    #I  there are 2 generators and 5 relators of total length 18
    #I  there are 2 generators and 4 relators of total length 13
    #I  there are 2 generators and 3 relators of total length 9
    gap> TzPrintRelators( p );
    #I  1. _x3^2
    #I  2. _x2^3
    #I  3. _x3*_x2*_x3*_x2

Note that the number of loops over the two phases as well as the number of subword searches or generator eliminations in each phase are determined by a set of option parameters which may heavily influence the resulting presentation and the computing time (see Tietze options below).

TzGo is just another name for the SimplifyPresentation command. It has been introduced for the convenience of those GAP users who are used to that name from the go option of the ANU Tietze transformation stand-alone program or from the go command in SPAS.

TzGoGo( P )

TzGoGo performs Tietze transformations on a presentation P. It repeatedly calls the TzGo command until neither the number of generators nor the number of relators nor the total length of all relators have changed during five consecutive calls of TzGo.

This may remarkably save you time and effort if you handle small presentations, however it may lead to annoyingly long and fruitless waiting times in case of large presentations.

TzEliminate( P )
TzEliminate( P, gen )
TzEliminate( P, n )

TzEliminate tries to eliminate a generator from a presentation P via Tietze transformations.

Any relator which contains some generator just once can be used to substitute that generator by a word in the remaining generators. If such generators and relators exist, then TzEliminate chooses a generator for which the product of its number of occurrences and the length of the substituting word is minimal, and then it eliminates this generator from the presentation, provided that the resulting total length of the relators does not exceed the associated Tietze option parameter P.spaceLimit. The default value of P.spaceLimit is infinity, but you may alter it appropriately (see Tietze options below).

If you specify a generator gen as second argument, then TzEliminate only tries to eliminate that generator.

If you specify an integer n as second argument, then TzEliminate tries to eliminate up to n generators. Note that the calls TzEliminate( P ) and TzEliminate( P, 1 ) are equivalent.

TzSearch( P )

TzSearch performs Tietze transformations on a presentation P. It tries to reduce the relator lengths by substituting common subwords of relators by shorter words.

The idea is to find pairs of relators r_{1} and r_{2} of length l_{1} and l_{2}, respectively, such that l_{1} ≤ l_{2} and r_{1} and r_{2} coincide (possibly after inverting or conjugating one of them) in some maximal subword w, say, of length greater than l_{1}/2, and then to substitute each copy of w in r_{2} by the inverse complement of w in r_{1}.

Two of the Tietze option parameters which are listed at the end of this section may strongly influence the performance and the results of the TzSearch command. These are the parameters P.saveLimit and P.searchSimultaneous. The first of them has the following effect.

When TzSearch has finished its main loop over all relators, then, in general, there are relators which have changed and hence should be handled again in another run through the whole procedure. However, experience shows that it really does not pay to continue this way until no more relators change. Therefore, TzSearch starts a new loop only if the loop just finished has reduced the total length of the relators by at least P.saveLimit per cent.

The default value of P.saveLimit is 10.

To understand the effect of the parameter P.searchSimultaneous, we have to look in more detail at how TzSearch proceeds.

First, it sorts the list of relators by increasing lengths. Then it performs a loop over this list. In each step of this loop, the current relator is treated as short relator r_{1}, and a subroutine is called which loops over the succeeding relators, treating them as long relators r_{2} and performing the respective comparisons and substitutions.

As this subroutine performs a very expensive process, it has been implemented as a C routine in the GAP kernel. For the given relator r_{1} of length l_{1}, say, it first determines the minimal match length l which is l_{1}/2 + 1, if l_{1} is even, or (l_{1} + 1)/2, otherwise. Then it builds up a hash list for all subwords of length l occurring in the conjugates of r_{1} or r_{1}^{−1}, and finally it loops over all long relators r_{2} and compares the hash values of their subwords of length l against this list. A comparison of subwords which is much more expensive is only done if a hash match has been found.

To improve the efficiency of this process we allow the subroutine to handle several short relators simultaneously provided that they have the same minimal match length. If, for example, it handles n short relators simultaneously, then you save n − 1 loops over the long relators r_{2}, but you pay for it by additional fruitless subword comparisons. In general, you will not get the best performance by always choosing the maximal possible number of short relators to be handled simultaneously. In fact, the optimal choice of the number will depend on the concrete presentation under investigation. You can use the parameter P.searchSimultaneous to prescribe an upper bound for the number of short relators to be handled simultaneously.

The default value of P.searchSimultaneous is 20.

TzSearchEqual( P )

TzSearchEqual performs Tietze transformations on a presentation P. It tries to alter relators by substituting common subwords of relators by subwords of equal length.

The idea is to find pairs of relators r_{1} and r_{2} of length l_{1} and l_{2}, respectively, such that l_{1} is even, l_{1} ≤ l_{2}, and r_{1} and r_{2} coincide (possibly after inverting or conjugating one of them) in some maximal subword w, say, of length at least l_{1}/2. Let l be the length of w. Then, if l > l_{1}/2, the pair is handled as in TzSearch. Otherwise, if l = l_{1}/2, then TzSearchEqual substitutes each copy of w in r_{2} by the inverse complement of w in r_{1}.

The Tietze option parameter P.searchSimultaneous is used by TzSearchEqual in the same way as described for TzSearch.

However, TzSearchEqual does not use the parameter P.saveLimit: The loop over the relators is executed exactly once.

TzFindCyclicJoins( P )

TzFindCyclicJoins performs Tietze transformations on a presentation P. It searches for pairs of generators which generate the same cyclic subgroup and eliminates one of the two generators of each such pair it finds.

More precisely: TzFindCyclicJoins searches for pairs of generators a and b such that (possibly after inverting or conjugating some relators) the set of relators contains the commutator [a, b], a power a^{n}, and a product of the form a^{s}b^{t} with s prime to n. For each such pair, TzFindCyclicJoins uses the Euclidian algorithm to express a as a power of b, and then it eliminates a.

TzSubstitute( P )
TzSubstitute( P, n )
TzSubstitute( P, n, eliminate )

TzSubstitute performs Tietze transformations on a presentation P. Basically, it substitutes a squarefree word of length 2 as a new generator and then eliminates a generator from the extended generator list. We will describe this process in more detail.

The parameters n and eliminate are optional. If you specify arguments for them, then n is expected to be a positive integer, and eliminate is expected to be 0, 1, or 2. The default values are n = 1 and eliminate = 0.

TzSubstitute first determines the n most frequently occurring squarefree relator subwords of length 2 and sorts them by decreasing numbers of occurrences. Let ab be the nth word in that list, and let i be the smallest positive integer which has not yet been used as a generator number. Then TzSubstitute defines a new generator P.i (see AddGenerator for details), adds it to the presentation together with a new relator P.i^{−1}ab, and replaces all occurrences of ab in the given relators by P.i.

Finally, it eliminates some generator from the extended presentation. The choice of that generator depends on the actual value of the eliminate parameter:

If eliminate is zero, then the generator to be eliminated is chosen as by the TzEliminate command. This means that in this case it may well happen that it is the generator P.i just introduced which is now deleted again so that you do not get any remarkable progress in transforming your presentation. On the other hand, this procedure guaranties that the total length of the relators will not be increased by a call of TzSubstitute with eliminate = 0.

Otherwise, if eliminate is 1 or 2, then TzSubstitute eliminates the respective factor of the substituted word ab, i.e., a for eliminate = 1 or b for eliminate = 2. In this case, it may well happen that the total length of the relators increases, but sometimes such an intermediate extension is the only way to finally reduce a given presentation.

In order to decide which arguments might be appropriate for the next call of TzSubstitute, often it is helpful to print out a list of the most frequently occurring squarefree relator subwords of length 2. You may use the TzPrintPairs command described below to do this.

As an example we handle a subgroup of index 266 in the Janko group J_{1}.

    gap> f2 := FreeGroup( "a", "b" );;
    gap> J1 := f2 / [ f2.1^2, f2.2^3, (f2.1*f2.2)^7,
    >    Comm(f2.1,f2.2)^10, Comm(f2.1,f2.2^-1*(f2.1*f2.2)^2)^6 ];;
    gap> a := J1.1;;  b := J1.2;;
    gap> H := Subgroup ( J1, [ a, b^(a*b*(a*b^-1)^2) ] );;
    gap> P := PresentationSubgroup( J1, H );
    << presentation with 23 gens and 82 rels of total length 530 >>
    gap> TzGoGo( P );
    #I  there are 3 generators and 47 relators of total length 1368
    #I  there are 2 generators and 46 relators of total length 3773
    #I  there are 2 generators and 46 relators of total length 2570
    gap> TzGoGo( P );
    #I  there are 2 generators and 46 relators of total length 2568
    gap> TzGoGo( P );
    gap> # We do not get any more progress without substituting a new

gap> # generator
gap> TzSubstitute( P );
#I  substituting new generator _x28 defined by _x6*_x23^-1
#I  eliminating _x28 = _x6*_x23^-1
gap> # GAP cannot substitute a new generator without extending the
gap> # total length, so we have to explicitly ask for it
gap> TzPrintPairs( P );
#I  1.  504  occurrences of  _x6 * _x23^-1
#I  2.  504  occurrences of  _x6^-1 * _x23
#I  3.  448  occurrences of  _x6 * _x23
#I  4.  448  occurrences of  _x6^-1 * _x23^-1
gap> TzSubstitute( P, 2, 1 );
#I  substituting new generator _x29 defined by _x6^-1*_x23
#I  eliminating _x6 = _x23*_x29^-1
#I  there are 2 generators and 46 relators of total length 2867
gap> TzGoGo( P );
#I  there are 2 generators and 45 relators of total length 2417
#I  there are 2 generators and 45 relators of total length 2122
gap> TzSubstitute( P, 1, 2 );
#I  substituting new generator _x30 defined by _x23*_x29^-1
#I  eliminating _x29 = _x30^-1*_x23
#I  there are 2 generators and 45 relators of total length 2192
gap> TzGoGo( P );
#I  there are 2 generators and 42 relators of total length 1637
#I  there are 2 generators and 40 relators of total length 1286
#I  there are 2 generators and 36 relators of total length 807
#I  there are 2 generators and 32 relators of total length 625
#I  there are 2 generators and 22 relators of total length 369
#I  there are 2 generators and 18 relators of total length 213
#I  there are 2 generators and 13 relators of total length 141
#I  there are 2 generators and 12 relators of total length 121
#I  there are 2 generators and 10 relators of total length 101
gap> TzPrintPairs( P );
#I  1.  19  occurrences of  _x23 * _x30^-1
#I  2.  19  occurrences of  _x23^-1 * _x30
#I  3.  14  occurrences of  _x23 * _x30
#I  4.  14  occurrences of  _x23^-1 * _x30^-1
gap> # If we save a copy of the current presentation, then later we
gap> # will be able to restart the computation from the current state
gap> P1 := Copy( P );;
gap> # Just for demonstration, let's make an inconvenient choice
gap> TzSubstitute( P, 3, 1 );
#I  substituting new generator _x31 defined by _x23*_x30
#I  eliminating _x23 = _x31*_x30^-1
#I  there are 2 generators and 10 relators of total length 122
gap> TzGoGo( P );
#I  there are 2 generators and 9 relators of total length 105
gap> # The presentation is worse than the one we have saved, so let's

gap> # restart from that one again
gap> P := Copy( P1 );
<< presentation with 2 gens and 10 rels of total length 101 >>
gap> TzSubstitute( P, 2, 1);
#I  substituting new generator _x31 defined by _x23^-1*_x30
#I  eliminating _x23 = _x30*_x31^-1
#I  there are 2 generators and 10 relators of total length 107
gap> TzGoGo( P );
#I  there are 2 generators and 9 relators of total length 84
#I  there are 2 generators and 8 relators of total length 75
gap> TzSubstitute( P, 2, 1);
#I  substituting new generator _x32 defined by _x30^-1*_x31
#I  eliminating _x30 = _x31*_x32^-1
#I  there are 2 generators and 8 relators of total length 71
gap> TzGoGo( P );
#I  there are 2 generators and 7 relators of total length 56
#I  there are 2 generators and 5 relators of total length 36
gap> TzPrintRelators( P );
#I  1. _x32^5
#I  2. _x31^5
#I  3. _x31^-1*_x32^-1*_x31^-1*_x32^-1*_x31^-1*_x32^-1
#I  4. _x31*_x32*_x31^-1*_x32*_x31^-1*_x32*_x31*_x32^-2
#I  5. _x31^-1*_x32^2*_x31*_x32^-1*_x31^2*_x32^-1*_x31*_x32^2

As shown in the preceding example, you can use the Copy command to save a copy of a presentation record and to restart from it again if you want to try an alternative strategy. However, this copy will be lost as soon as you finish your current GAP session. If you use the Save command (see 22.7) instead, then you get a permanent copy on a file which you can read in again in a later session.

TzSubstituteCyclicJoins( P )

TzSubstituteCyclicJoins performs Tietze transformations on a presentation P. It tries to find pairs of generators a and b, say, for which among the relators (possibly after inverting or conjugating some of them) there are the commutator [a, b] and powers a^{m} and b^{n} with mutually prime exponents m and n. For each such pair, it substitutes the product ab as a new generator, and then it eliminates the generators a and b.

TzPrintLengths( P )

TzPrintLengths prints the list of the lengths of all relators of the given presentation P.

TzPrintPairs( P )
TzPrintPairs( P, n )

TzPrintPairs determines in the given presentation P the n most frequently occurring squarefree relator subwords of length 2 and prints them together with their numbers of occurrences. The default value of n is 10. A value n = 0 is interpreted as infinity.

This list is a useful piece of information in the context of using the TzSubstitute command described above.

TzPrintOptions( P )

Several of the Tietze transformation commands described above are controlled by certain parameters, the Tietze options, which often have a tremendous influence on their performance and results. However, in each application of the commands, an appropriate choice of these option parameters will depend on the concrete presentation under investigation. Therefore we have implemented the Tietze options in such a way that they are associated to the presentation records: Each presentation record keeps its own set of Tietze option parameters in the form of ordinary record components. In particular, you may alter the value of any of these Tietze options by just assigning a new value to the respective record component. TzPrintOptions prints the Tietze option components of the specified presentation P.

The Tietze options have the following meaning.

eliminationsLimit

Whenever the elimination phase of the TzGo command is entered for a presentation P, then it will eliminate at most P.eliminationsLimit generators (except for further ones which have turned out to be trivial). Hence you may use the eliminationsLimit parameter as a break criterion for the TzGo command. Note, however, that it is ignored by the TzEliminate command. The default value of eliminationsLimit is 100.

expandLimit

Whenever the routine for eliminating more than 1 generators is called for a presentation P by the TzEliminate command or the elimination phase of the TzGo command, then it saves the given total length of the relators, and subsequently it checks the current total length against its value before each elimination. If the total length has increased to more than P.expandLimit per cent of its original value, then the routine returns instead of eliminating another generator. Hence you may use the expandLimit parameter as a break criterion for the TzGo command. The default value of expandLimit is 150.

generatorsLimit

Whenever the elimination phase of the TzGo command is entered for a presentation P with n generators, then it will eliminate at most n−P.generatorsLimit generators (except for generators which turn out to be trivial). Hence you may use the generatorsLimit parameter as a break criterion for the TzGo command. The default

value of generatorsLimit is 0.
lengthLimit

The Tietze transformation commands will never eliminate a generator of a presentation P, if they cannot exclude the possibility that the resulting total length of the relators exceeds the value of P.lengthLimit. The default value of lengthLimit is

infinity.
loopLimit

Whenever the TzGo command is called for a presentation P, then it will loop over at most P.loopLimit of its basic steps. Hence you may use the loopLimit parameter as a break criterion for the TzGo command. The default value of loopLimit is infinity.

printLevel

Whenever Tietze transformation commands are called for a presentation P with P.printLevel = 0, they will not provide any output except for error messages. If P.printLevel = 1, they will display some reasonable amount of output which allows you to watch the progress of the computation and to decide about your next commands. In the case P.printLevel = 2, you will get a much more generous amount of output. Finally, if P.printLevel = 3, various messages on internal details will be added. The default value of printLevel is 1.

saveLimit

Whenever the TzSearch command has finished its main loop over all relators of a presentation P, then it checks whether during this loop the total length of the relators has been reduced by at least P.saveLimit per cent. If this is the case, then TzSearch repeats its procedure instead of returning. Hence you may use the saveLimit parameter as a break criterion for the TzSearch command and, in particular, for the search phase of the TzGo command. The default value of saveLimit is 10.

searchSimultaneous

Whenever the TzSearch or the TzSearchEqual command is called for a presentation P, then it is allowed to handle up to P.searchSimultaneously short relators simultaneously (see for the description of the TzSearch command for more details). The choice of this parameter may heavily influence the performance as well as the result of the TzSearch and the TzSearchEqual commands and hence also of the search phase of the TzGo command. The default value of searchSimultaneous is 20.

As soon as a presentation record has been defined, you may alter any of its Tietze option parameters at any time by just assigning a new value to the respective component.

To demonstrate the effect of the eliminationsLimit parameter, we will give an example in which we handle a subgroup of index 240 in a group of order 40320 given by a presentation due to B. H. Neumann. First we construct a presentation of the subgroup, and then we apply to it the TzGoGo command for different values of the eliminationsLimit parameter (including the default value 100). In fact, we also alter the printLevel parameter, but this is only done in order to suppress most of the output. In all cases the resulting presentations cannot be improved any more by applying the TzGoGo command again, i.e., they are the best results which we can get without substituting new generators.

gap> f3 := FreeGroup( "a", "b", "c" );;
gap> g := f3 / [ f3.1^3, f3.2^3, f3.3^3, (f3.1*f3.2)^5,
>       (f3.1^-1*f3.2)^5, (f3.1*f3.3)^4, (f3.1*f3.3^-1)^4,
>       f3.1*f3.2^-1*f3.1*f3.2*f3.3^-1*f3.1*f3.3*f3.1*f3.3^-1,
>       (f3.2*f3.3)^3, (f3.2^-1*f3.3)^4 ];;
gap> a := f3.1;;  b := f3.2;;  c := f3.3;;
gap> h := Subgroup( g, [ a, c ] );;
gap> p := PresentationSubgroup( g, h );
<< presentation with 224 gens and 593 rels of total length 2769 >>
gap> for i in [ 28, 29, 30, 94, 100 ] do
>       pi := Copy( p );

>       pi.eliminationsLimit := i;
>       Print( "#I  eliminationsLimit set to ", i, "\n" );
>       pi.printLevel := 0;
>       TzGoGo( pi );
>       TzPrintStatus( pi );
>    od;
#I  eliminationsLimit set to 28
#I  there are 2 generators and 95 relators of total length 10817
#I  eliminationsLimit set to 29
#I  there are 2 generators and 5 relators of total length 35
#I  eliminationsLimit set to 30
#I  there are 3 generators and 98 relators of total length 2928
#I  eliminationsLimit set to 94
#I  there are 4 generators and 78 relators of total length 1667
#I  eliminationsLimit set to 100
#I  there are 3 generators and 90 relators of total length 3289

Similarly, we demonstrate the influence of the saveLimit parameter by just continuing the preceding example for some different values of the saveLimit parameter (including its default value 10), but without changing the eliminationsLimit parameter which keeps its default value 100.

gap> for i in [ 9, 10, 11, 12, 15 ] do
>       pi := Copy( p );
>       pi.saveLimit := i;
>       Print( "#I  saveLimit set to ", i, "\n" );
>       pi.printLevel := 0;
>       TzGoGo( pi );
>       TzPrintStatus( pi );
>    od;
#I  saveLimit set to 9
#I  there are 3 generators and 97 relators of total length 5545
#I  saveLimit set to 10
#I  there are 3 generators and 90 relators of total length 3289
#I  saveLimit set to 11
#I  there are 3 generators and 103 relators of total length 3936
#I  saveLimit set to 12
#I  there are 2 generators and 4 relators of total length 21
#I  saveLimit set to 15
#I  there are 3 generators and 143 relators of total length 18326

22.12 DecodeTree

DecodeTree( P )

DecodeTree eliminates the secondary generators from a presentation P constructed by the Modified Todd-Coxeter (see PresentationSubgroupMtc) or the Reduced Reidemeister-

Schreier procedure (see PresentationSubgroupRrs, PresentationNormalClosureRrs). It

is called automatically by the PresentationSubgroupMtc command where it reduces P to a presentation on the given subgroup generators.

In order to explain the effect of this command we need to insert a few remarks on the subgroup presentation commands described in section 22.9. All these commands have the common property that in the process of constructing a presentation for a given subgroup H of a finitely presented group G they first build up a highly redundant list of generators of H which consists of an (in general small) list of ”primary”generators, followed by an (in general large) list of ”secondary” generators, and then construct a presentation P_{0}, say, on a sublist of these generators by rewriting the defining relators of G. This sublist contains all primary, but, at least in general, by far not all secondary generators.

The role of the primary generators depends on the concrete choice of the subgroup presentation command. If the Modified Todd-Coxeter method is used, they are just the given generators of H , whereas in the case of the Reduced Reidemeister-Schreier algorithm they are constructed by the program.

Each of the secondary generators is defined by a word of length two in the preceding generators and their inverses. By historical reasons, the list of these definitions is called the subgroup generators tree though in fact it is not a tree but rather a kind of bush.

Now we have to distinguish two cases. If P_{0} has been constructed by the Reduced Reidemeister-Schreier routines, it is a presentation of H . However, if the Modified Todd-Coxeter routines have been used instead, then the relators in P_{0} are valid relators of H , but they do not necessarily define H . We handle these cases in turn, starting with the latter one.

Also in the case of the Modified Todd-Coxeter method, we could easily extend P_{0} to a presentation of H by adding to it all the secondary generators which are not yet contained in it and all the definitions from the generators tree as additional generators and relators. Then we could recursively eliminate all secondary generators by Tietze transformations using the new relators. However, this procedure turns out to be too inefficient to be of interest.

Instead, we use the so called tree decoding procedure which has been developed in St. Andrews by David G. Arrell, Sanjiv Manrai, Edmund F. Robertson, and Michael F. Worboys (see [AMW82], [AR84]). It proceeds as follows.

Starting from P = P_{0}, it runs through a number of steps in each of which it eliminates the current ”last” generator (with respect to the list of all primary and secondary generators). If the last generator g, say, is a primary generator, then the procedure finishes. Otherwise it checks whether there is a relator in the current presentation which can be used to substitute g by a Tietze transformation. If so, this is done. Otherwise, and only then, the tree definition of g is added to P as a new relator, and the generators involved are added as new generators if they have not yet been contained in P. Subsequently, g is eliminated.

Note that the extension of P by one or two new generators is not a Tietze transformation. In general, it will change the isomorphism type of the group defined by P. However, it is a remarkable property of this procedure, that at the end, i.e., as soon as all secondary generators have been eliminated, it provides a presentation P = P_{1}, say, which defines a group isomorphic to H . In fact, it is this presentation which is returned by the DecodeTree command and hence by the PresentationSubgroupMtc command.

If, in the other case, the presentation P_{0} has been constructed by the Reduced Reidemeister-Schreier algorithm, then P_{0} itself is a presentation of H , and the corresponding subgroup

presentation command (PresentationSubgroupRrs or PresentationNormalClosureRrs)

just returns P_{0}.

As mentioned in section 22.9, we recommend further simplifying this presentation before using it. The standard way to do this is to start from P_{0} and to apply suitable Tietze transformations, e.g., by calling the TzGo or TzGoGo commands. This is probably the most efficient approach, but you will end up with a presentation on some unpredictable set of generators. As an alternative, GAP offers you the DecodeTree command which you can use to eliminate all secondary generators (provided that there are no space or time problems). For this purpose, the subgroup presentation commands do not only return the resulting presentation, but also the tree (together with some associated lists) as a kind of side result in a component P.tree of the resulting presentation record P.

Note, however, that the tree decoding routines will not work correctly any more on a presentation from which generators have already been eliminated by Tietze transformations. Therefore, to prevent you from getting wrong results by calling the DecodeTree command in such a situation, GAP will automatically remove the subgroup generators tree from a presentation record as soon as one of the generators is substituted by a Tietze transformation.

Nevertheless, a certain misuse of the command is still possible, and we want to explicitly warn you from this. The reason is that the Tietze option parameters described in section 22.11 apply to the DecodeTree command as well. Hence, in case of inadequate values of these parameters, it may happen that the DecodeTree routine stops before all the secondary generators have vanished. In this case GAP will display an appropriate warning. Then you should change the respective parameters and continue the process by calling the DecodeTree command again. Otherwise, if you would apply Tietze transformations, it might happen because of the convention described above that the tree is removed and that you end up with a wrong presentation.

After a successful run of the DecodeTree command it is convenient to further simplify the resulting presentation by suitable Tietze transformations.

As an example of an explicit call of the DecodeTree command we compute two presentations of a subgroup of order 384 in a group of order 6912. In both cases we use the Reduced Reidemeister-Schreier algorithm, but in the first run we just apply the Tietze transformations offered by the TzGoGo command with its default parameters, whereas in the second run we call the DecodeTree command before.

    gap> f2 := FreeGroup( "a", "b" );;
    gap> g := f2 / [ f2.1*f2.2^2*f2.1^-1*f2.2^-1*f2.1^3*f2.2^-1,
    >                f2.2*f2.1^2*f2.2^-1*f2.1^-1*f2.2^3*f2.1^-1 ];;
    gap> a := g.1;;  b := g.2;;
    gap> h := Subgroup( g, [ Comm(a^-1,b^-1), Comm(a^-1,b), Comm(a,b) ] );;
    gap> #
    gap> # We use the Reduced Reidemeister Schreier method and default
    gap> # Tietze transformations to get a presentation for H.
    gap> P := PresentationSubgroupRrs( g, h );
    << presentation with 18 gens and 35 rels of total length 169 >>
    gap> TzGoGo( p );
    #I  there are 3 generators and 20 relators of total length 488
    #I  there are 3 generators and 20 relators of total length 466
    gap> # We end up with 20 relators of total length 466.
    gap> #
    gap> # Now we repeat the procedure, but we call the tree decoding

gap> # algorithm before doing the Tietze transformations.
gap> p := PresentationSubgroupRrs( g, h );
<< presentation with 18 gens and 35 rels of total length 169 >>
gap> DecodeTree( p );
#I  there are 9 generators and 26 relators of total length 185
#I  there are 6 generators and 23 relators of total length 213
#I  there are 3 generators and 20 relators of total length 252
#I  there are 3 generators and 20 relators of total length 244
gap> TzGoGo( p );
#I  there are 3 generators and 19 relators of total length 168
#I  there are 3 generators and 17 relators of total length 138
#I  there are 3 generators and 15 relators of total length 114
#I  there are 3 generators and 13 relators of total length 96
#I  there are 3 generators and 12 relators of total length 84
gap> # This time we end up with a shorter presentation.

As an example of an implicit call of the command via the PresentationSubgroupMtc command we handle a subgroup of index 240 in a group of order 40320 given by a presentation due to B. H. Neumann.

gap> f3 := FreeGroup( "a", "b", "c" );;
gap> g := f3 / [ f3.1^3, f3.2^3, f3.3^3, (f3.1*f3.2)^5,
>       (f3.1^-1*f3.2)^5, (f3.1*f3.3)^4, (f3.1*f3.3^-1)^4,
>       f3.1*f3.2^-1*f3.1*f3.2*f3.3^-1*f3.1*f3.3*f3.1*f3.3^-1,
>       (f3.2*f3.3)^3, (f3.2^-1*f3.3)^4 ];;
gap> a := g.1;;  b := g.2;;  c := g.3;;
gap> h := Subgroup( g, [ a, c ] );;
gap> InfoFpGroup1 := Print;;
gap> p := PresentationSubgroupMtc( g, h );;
#I  index is 240
#I  MTC defined 2 primary and 4472 secondary subgroup generators
#I  there are 246 generators and 617 relators of total length 2893
#I  calling DecodeTree
#I  there are 114 generators and 380 relators of total length 1829
#I  there are 72 generators and 293 relators of total length 1849
#I  there are 47 generators and 252 relators of total length 2062
#I  there are 36 generators and 214 relators of total length 2341
#I  there are 25 generators and 188 relators of total length 2957
#I  there are 20 generators and 170 relators of total length 3313
#I  there are 20 generators and 158 relators of total length 4155
#I  there are 21 generators and 155 relators of total length 6088
#I  there are 24 generators and 155 relators of total length 9014
#I  there are 27 generators and 155 relators of total length 13812
#I  there are 31 generators and 155 relators of total length 20654
#I  there are 36 generators and 155 relators of total length 32444
#I  there are 39 generators and 155 relators of total length 51121
#I  there are 42 generators and 155 relators of total length 74713
#I  there are 40 generators and 153 relators of total length 85251
#I  there are 12 generators and 140 relators of total length 29401

#I  there are 7 generators and 131 relators of total length 18067
#I  there are 4 generators and 122 relators of total length 20325
#I  there are 2 generators and 115 relators of total length 21588
#I  there are 2 generators and 114 relators of total length 16370
gap> TzGoGo( p );
#I  there are 2 generators and 112 relators of total length 13126
#I  there are 2 generators and 99 relators of total length 7572
#I  there are 2 generators and 62 relators of total length 3384
#I  there are 2 generators and 24 relators of total length 1506
#I  there are 2 generators and 20 relators of total length 1000
#I  there are 2 generators and 13 relators of total length 494
#I  there are 2 generators and 10 relators of total length 314
#I  there are 2 generators and 9 relators of total length 252
#I  there are 2 generators and 8 relators of total length 192
#I  there are 2 generators and 7 relators of total length 92
#I  there are 2 generators and 6 relators of total length 52
gap> TzPrintGenerators( p );
#I  1.  _x1   26 occurrences
#I  2.  _x2   26 occurrences

458 CHAPTER 22. FINITELY PRESENTED GROUPS

Words in Finite Polycyclic Groups

Ag words are the GAP datatype for elements of finite polycyclic groups. Unlike permutations, which are all considered to be elements of one large symmetric group, each ag word belongs to a specified group. Only ag words of the same finite polycyclic group can be multiplied.

The following sections describe ag words and their parent groups (see 23.1), how ag words are compared (see 23.2), functions for ag words and some low level functions for ag words (starting at 23.3 and 23.9).

For operations and functions defined for group elements in general see 7.2, 7.3.

23.1 More about Ag Words

Let G be a group and G = G_{0} > G_{1} > ... > G_{n} = 1 be a subnormal series of G ≠ 1 with finite cyclic factors, i.e., G_{i} < G_{i−1} for all i = 1, ..., n and G_{i−1} = ⟨G_{i}, g_{i}⟩. Then G will be called an ag group with AG generating sequence or, for short, AG-system (g_{1}, ..., g_{n}). Let o_{i} be the order of G_{i−1}/G_{i}. If all o_{1}, ..., o_{n} are primes the system (g_{1}, ..., g_{n}) is called a PAG-system. With respect to a given AG-system the group G has a so called power-commutator presentation

\begin{aligned}g_{i}^{o_{i}} & = & w_{ii}(g_{i+1}, ..., g_{n}) & for 1 ≤ i ≤ n, \\ [g_{i}, g_{j}] & = & w_{ij}(g_{j+1}, ..., g_{n}) & for 1 ≤ j < i ≤ n \end{aligned}

and a so called power-conjugate presentation

\begin{aligned}g_{i}^{o_{i}} & = & w_{ii}(g_{i+1}, ..., g_{n}) & for 1 ≤ i ≤ n, \\ g_{i}^{g_{j}} & = & w′_{ij}(g_{j+1}, ..., g_{n}) & for 1 ≤ j < i ≤ n. \end{aligned}

For both kinds of presentations we shall use the term AG-presentation. Each element g of G can be expressed uniquely in the form

g = g_{1}^{ν_{1}} ∗ ... ∗ g_{n}^{ν_{n}} for 0 ≤ ν_{i} < o_{i}.

We call the composition series G_{0} > G_{1} > ... > G_{n} the AG-series of G and define ν_{i}(g) := ν_{i}. If ν_{i} = 0 for i = 1, ..., k − 1 and ν_{k} ≠ 0, we call ν_{k} the leading exponent and k the depth of g and denote them by ν_{k} =: λ(g) and k =: δ(g). We call o_{k} the relative order of g.

Each element g of G is called ag word and we say that G is the parent group of g. A parent group is constructed in GAP using AgGroup (see 24.26) or AgGroupFpGroup (see 24.28).

Our standard example in the following sections is the symmetric group of degree 4, defined by the following sequence of GAP statements. You should enter them before running any example. For details on AbstractGenerators see 21.1.

    gap> a  := AbstractGenerator( "a" );; # (1,2)
    gap> b  := AbstractGenerator( "b" );; # (1,2,3)
    gap> c  := AbstractGenerator( "c" );; # (1,3)(2,4)
    gap> d  := AbstractGenerator( "d" );; # (1,2)(3,4)
    gap> s4 := AgGroupFpGroup( rec(
    >        generators := [ a, b, c, d ],
    >        relators   := [ a^2, b^3, c^2, d^2, Comm( b, a ) / b,
    >                        Comm( c, a ) / d, Comm( d, a ),
    >                        Comm( c, b ) / ( c*d ), Comm( d, b ) / c,
    >                        Comm( d, c ) ] ) );
    Group( a, b, c, d )
    gap> s4.name := "s4";;
    gap> a := s4.generators[1];; b := s4.generators[2];;
    gap> c := s4.generators[3];; d := s4.generators[4];;

23.2 Ag Word Comparisons

g < h

g <= h
g >= h

g > h

The operators <, >, <= and >= return true if g is strictly less, strictly greater, not greater, not less, respectively, than h. Otherwise they return false.

If g and h have a common parent group they are compared with respect to the AG-series of this group. If two ag words have different depths, the one with the higher depth is less than the other one. If two ag words have the same depth but different leading exponents, the one with the smaller leading exponent is less than the other one. Otherwise the leading generator is removed in both ag words and the remaining ag words are compared.

If g and h do not have a common parent group, then the composition lengths of the parent groups are compared.

You can compare ag words with objects of other types. Field elements, unkowns, permutations and abstract words are smaller than ag words. Objects of other types, i.e., functions, lists and records are larger.

    gap> 123/47 < a;
    true

gap> (1,2,3,4) < a;
true
gap> [1,2,3,4] < a;
false
gap> true < a;
false
gap> rec() < a;
false
gap> c < a;
true
gap> a*b < a*b^2;
true

23.3 CentralWeight

CentralWeight( g )

CentralWeight returns the central weight of an ag word g, with respect to the central series used in the combinatorial collector, as integer.

This presumes that g belongs to a parent group for which the combinatorial collector is used. See 24.34 for details.

If g is the identity, 0 is returned.

Note that CentralWeight allows records that mimic ag words as arguments.

gap> d8 := AgGroup( Subgroup( s4, [ a, c, d ] ) );
Group( g1, g2, g3 )
gap> ChangeCollector( d8, "combinatorial" );
gap> List( d8.generators, CentralWeight );
[ 1, 1, 2 ]

23.4 CompositionLength

CompositionLength( g )

Let G be the parent group of the ag word g. Then CompositionLength returns the length of the AG-series of G as integer.

Note that CompositionLength allows records that mimic ag words as arguments.

gap> CompositionLength( c );
5

23.5 Depth

Depth( g )

Depth returns the depth of an ag word g with respect to the AG-series of its parent group as integer.

Let G be the parent group of g and G = G_{0} > ... > G_{n} = {1} the AG-series of G. Let δ be the maximal positive integer such that g is an element of G_{δ−1}. Then δ is the depth of g.

Note that Depth allows record that mimic ag words as arguments.

    gap> Depth( a );
    1
    gap> Depth( d );
    4
    gap> Depth( a^0 );
    5

23.6 IsAgWord

IsAgWord( obj )

IsAgWord returns true if obj , which can be an arbitrary object, is an ag word and false otherwise.

    gap> IsAgWord( 5 );
    false
    gap> IsAgWord( a );
    true

23.7 LeadingExponent

LeadingExponent( g )

LeadingExponent returns the leading exponent of an ag word g as integer.

Let G be the parent group of g and (g_{1}, ..., g_{n}) the AG-system of G and let o_{i} be the relative order of g_{i}. Then the element g can be expressed uniquely in the form g_{1}^{ν_{1}} ∗ ... ∗ g_{n}^{ν_{n}} for integers ν_{i} such that 0 ≤ ν_{i} < o_{i}. The leading exponent of g is the first nonzero ν_{i}.

If g is the identity 0 is returned.

Although ExponentAgWord( g, Depth( g ) ) returns the leading exponent of g, too, this function is faster and is able to handle the identity.

Note that LeadingExponent allows records that mimic ag words as arguments.

    gap> LeadingExponent( a * b^2 * c^2 * d );
    1
    gap> LeadingExponent( b^2 * c^2 * d );
    2

23.8 RelativeOrder

RelativeOrder( g )

RelativeOrder returns the relative order of an ag word g as integer.

Let G be the parent group of g and G = G_{0} > ... > G_{n} = {1} the AG-series of G. Let δ be the maximal positive integer such that g is an element of G_{δ−1}. The relative order of g is the index of G_{δ+1} in G_{δ}, that is the order of the factor group G_{δ}/G_{δ+1}.

If g is the identity 1 is returned.

Note that RelativeOrder allows records that mimic agwords as arguments.

gap> RelativeOrder( a );
2
gap> RelativeOrder( b );
3
gap> RelativeOrder( b^2 * c * d );
3

23.9 CanonicalAgWord

CanonicalAgWord( U , g )

Let U be an ag group with parent group G, let g be an element of G. Let (u_{1}, ..., u_{m}) be an induced generating system of U and (g_{1}, ..., g_{n}) be a canonical generating system of G. Then CanonicalAgWord returns a word x = g ∗ u = g_{i_{1}}^{e_{1}} ∗ ... ∗ g_{i_{k}}^{e_{k}} such that u ∈ U and no i_{j} is equal to the depth of any generator u_{l}.

gap> v4 := MergedCgs( s4, [ a*b^2, c*d ] );
Subgroup( s4, [ a*b^2, c*d ] )
gap> CanonicalAgWord( v4, a*c );
b^2*d
gap> CanonicalAgWord( v4, a*b*c*d );
b
gap> (a*b*c*d) * (a*b^2);
b*c*d
gap> last * (c*d);
b

23.10 DifferenceAgWord

DifferenceAgWord( u, v )

DifferenceAgWord returns an ag word s representing the difference of the exponent vectors of u and v. Let G be the parent group of u and v. Let (g_{1}, ..., g_{n}) be the AG-system of G and o_{i} be the relative order or g_{i}. Then u can be expressed uniquely as g_{1}^{u_{1}} ∗ ... ∗ g_{n}^{u_{n}} for integers u_{i} between 0 and o_{i} −1 and v can be expressed uniquely as g_{1}^{v_{1}} ∗...∗g_{n}^{v_{n}} for integers v_{i} between 0 and o_{i} − 1. The function DifferenceAgWord returns an ag word s = g_{1}^{s_{1}} ∗ ... ∗ g_{n}^{s_{n}} with integer s_{i} such that 0 ≤ s_{i} < o_{i} and s_{i} ≡ u_{i} − v_{i} mod o_{i}.

gap> DifferenceAgWord( a * b, a );
b
gap> DifferenceAgWord( a, b );
a*b^2
gap> z27 := CyclicGroup( AgWords, 27 );
Group( c27_1, c27_2, c27_3 )
gap> x := z27.1 * z27.2;
c27_1*c27_2
gap> x * x;
c27_1^2*c27_2^2
gap> DifferenceAgWord( x, x );
IdAgWord

23.11 ReducedAgWord

ReducedAgWord( b, x )

Let b and x be ag words of the same depth, then ReducedAgWord returns an ag word a such that a is an element of the coset Ub, where U is the cyclic group generated by x, and a has a higher depth than b and x.

Note that the relative order of b and x must be a prime.

Let p be the relative order of b and x. Let β and ξ be the leading exponent of b and x respectively. Then there exits an integer i such that ξ ∗ i = β modulo p. We can set a = x^{−i}b.

Typically this function is used when b and x occur in a generating set of a subgroup W. Then b can be replaced by a in the generating set of W , but a and x have different depth.

    gap> ReducedAgWord( a*b^2*c, a );
    b^2*c
    gap> ReducedAgWord( last, b );
    c

23.12 SiftedAgWord

SiftedAgWord( U , g )

SiftedAgWord tries to sift an ag word g, which must be an element of the parent group of an ag group U , through an induced generating system of U . SiftedAgWord returns the remainder of this shifting process.

The identity is returned if and only if g is an element of U .

Let u_{1}, ..., u_{m} be an induced generating system of U . If there exists an u_{i} such that u_{i} and g have the same depth, then g is reduced with u_{i} using ReducedAgWord (see 23.11). The process is repeated until no u_{i} can be found or the g is reduced to the identity.

SiftedAgWord allows factor group arguments. See 24.53 for details.

Note that SiftedAgGroup adds a record component U .shiftInfo to the ag group record of U . This entry is used by subsequent calls with the same ag group in order to speed up computation. If you ever change the component U .igs by hand, not using Normalize, you must unbind U .shiftInfo, otherwise all following results of SiftedAgWord will be corrupted.

    gap> s3 := Subgroup( s4, [ a, b ] );
    Subgroup( s4, [ a, b ] )
    gap> SiftedAgWord( s3, a * b^2 * c );
    c

23.13 SumAgWord

SumAgWord( u, v )

SumAgWord returns an ag word s representing the sum of the exponent vectors of u and v.

Let G be the parent group of u and v. Let (g_{1}, ..., g_{n}) be the AG-system of G and o_{i} be the relative order or g_{i}. Then u can be expressed uniquely as g_{1}^{u_{1}} ∗ ... ∗ g_{n}^{u_{n}} for integers u_{i} between 0 and o_{i} −1 and v can be expressed uniquely as g_{1}^{v_{1}} ∗...∗g_{n}^{v_{n}} for integers v_{i} between 0 and o_{i} − 1. Then SumAgWord returns an ag word s = g_{1}^{s_{1}} ∗ ... ∗ g_{n}^{s_{n}} with integer s_{i} such that 0 ≤ s_{i} < o_{i} and s_{i} ≡ u_{i} + v_{i} mod o_{i}.

gap> SumAgWord( b, a );
a*b
gap> SumAgWord( a*b, a );
b
gap> RelativeOrderAgWord( a );
2
gap> z27 := CyclicGroup( AgWords, 27 );
Group( c27_1, c27_2, c27_3 )
gap> x := z27.1 * z27.2;
c27_1*c27_2
gap> y := x ^2;
c27_1^2*c27_2^2
gap> x * y;
c27_2*c27_3
gap> SumAgWord( x, y );
IdAgWord

23.14 ExponentAgWord

ExponentAgWord( g, k )

ExponentAgWord returns the exponent of the k.th generator in an ag word g as integer, where k refers to the numbering of generators of the parent group of g.

Let G be the parent group of g and (g_{1}, ..., g_{n}) the AG-system of G and let o_{i} be the relative order of g_{i}. Then the element g can be expressed uniquely in the form g_{1}^{ν_{1}} ∗ ... ∗ g_{n}^{ν_{n}} for integers ν_{i} between 0 and o_{i} − 1. The exponent of the k.th generator is ν_{k}.

See also 23.15 and 24.68.

gap> ExponentAgWord( a * b^2 * c^2 * d, 2 );
2
gap> ExponentAgWord( a * b^2 * c^2 * d, 4 );
1
gap> ExponentAgWord( a * b^2 * c^2 * d, 3 );
0
gap> a * b^2 * c^2 * d;
a*b^2*d

23.15 ExponentsAgWord

ExponentsAgWord( g )
ExponentsAgWord( g, s, e )
ExponentsAgWord( g, s, e, root )

In its first form ExponentsAgWord returns the exponent vector of an ag word g, with respect to the AG-system of the supergroup of g, as list of integers. In the second form ExponentsAgWord returns the sublist of the exponent vector of g starting at position s and ending at position e as list of integers. In the third form the vector is returned as list of finite field elements over the same finite field as root.

Let G be the parent group of g and (g_{1}, ..., g_{n}) the AG-system of G and let o_{i} be the relative order of g_{i}. Then the element g can be expressed uniquely in the form g_{1}^{ν_{1}} ∗ ... ∗ g_{n}^{ν_{n}} for integers ν_{i} between 0 and o_{i} − 1. The exponent vector of g is the list [ν_{1}, ..., ν_{n}].

Note that you must use Exponents if you want to get the exponent list of g with respect not to the parent group of g but to a given subgroup, which contains g. See 24.68 for details.

    gap> ExponentsAgWord( a * b^2 * c^2 * d );
    [ 1, 2, 0, 1 ]
    gap> a * b^2 * c^2 * d;
    a*b^2*d

Finite Polycyclic Groups

Ag groups (see 23) are a subcategory of finitely generated groups (see 7).

The following sections describe how subgroups of ag groups are represented (see 24.1), additional operators and record components of ag groups (see 24.3 and 24.4) and functions which work only with ag groups (see 24.25 and 24.57). Some additional information about generating systems of subgroups and factor groups are given in 24.44 and 24.53.

24.80 describes how to compute the groups of one coboundaries and one cocycles for given ag groups. 24.83 gives informations how to obtain complements and conjugacy classes of complements for given ag groups.

24.1 More about Ag Groups

Let G be a finite polycyclic group with PAG-system (g_{1}, ..., g_{n}) as described in 23. Let U be a subgroup of G. A generating system (u_{1}, ..., u_{r}) of U is called the canonical generating system, CGS for short, of U with respect to (g_{1}, ..., g_{n}) if and only if

(i) (u_{1}, ..., u_{r}) is a PAG-system for U,
(ii) δ(u_{i}) > δ(u_{j}) for i > j,
(iii) λ(u_{i}) = 1 for i = 1, ..., r,
(iv) ν_{δ(u_{i})}(u_{j}) = 0 for i ≠ j.

If a generating system (u_{1}, ..., u_{r}) fulfills only conditions (i) and (ii) this system is called an induced generating system, IGS for short, of U. With respect to the PAG-system of G a CGS but not an IGS of U is unique.

If a power-commutator or power-conjugate presentation of G is known, a finite polycyclic group with collector can be initialized in GAP using AgGroupFpGroup (see 24.28). AgGroup (see 24.26) converts other types of finite solvable groups, for instance solvable permutation groups, into an ag group. The collector can be changed by ChangeCollector (see 24.34). The elements of these group are called ag words.

A canonical generating system of a subgroup U of G is returned by Cgs (see 24.46) if a generating set of ag words for U is known. See 24.44 for details.

We call G a parent, that is a ag group with collector and U a subgroup, that is a group which is obtained as subgroup of a parent group. An ag group is either a parent group with PAG system or a subgroup of such a parent group.

Although parent groups need only an AG-system, only AgGroupFpGroup (see 24.28) and RefinedAgSeries (see 24.33) work correctly with a parent group represented by an AGsystem which is not a PAG-system, because subgroups are identified by canonical generating systems with respect to the PAG-system of the parent group. Inconsistent power-conjugate or power-commutator presentations are not allowed (see 24.29). Some functions support factor group arguments. See 24.53 and 24.56 for details.

Our standard example in the following sections is the symmetric group of degree 4, defined by the following sequence of GAP statements. You should enter them before running any example. For details on AbstractGenerators see 21.1.

    gap> a  := AbstractGenerator( "a" );; # (1,2)
    gap> b  := AbstractGenerator( "b" );; # (1,2,3)
    gap> c  := AbstractGenerator( "c" );; # (1,3)(2,4)
    gap> d  := AbstractGenerator( "d" );; # (1,2)(3,4)
    gap> s4 := AgGroupFpGroup( rec(
    >        generators := [ a, b, c, d ],
    >        relators   := [ a^2, b^3, c^2, d^2, Comm( b, a ) / b,
    >                        Comm( c, a ) / d, Comm( d, a ),
    >                        Comm( c, b ) / ( c*d ), Comm( d, b ) / c,
    >                        Comm( d, c ) ] ) );;
    gap> s4.name := "s4";;
    gap> a := s4.generators[1];; b := s4.generators[2];;
    gap> c := s4.generators[3];; d := s4.generators[4];;

24.2 Construction of Ag Groups

The most fundamental way to construct a new finite polycyclic group is AgGroupFpGroup (see 24.28) together with RefinedAgSeries (see 24.33), if a presentation for an AG-system of a finite polycyclic group is known.

But usually new finite polycyclic groups are constructed from already existing finite polycyclic groups. The direct product of known ag groups can be formed by DirectProduct (see 7.92); also, if for instance a permutation representation P of a finite polycyclic group G is known, WreathProduct (see 7.97) returns the P -wreath product of G with a second ag group. If a homomorphism of a finite polycyclic group G into the automorphism group of another finite polycyclic group H is known, SemidirectProduct returns the semi direct product of G with H.

Fundamental finite polycyclic groups, such as elementary abelian, arbitrary finite abelian groups, and cyclic groups, are constructed by the appropriate functions (see 35.1).

24.3 Ag Group Operations

In addition to the operators described in 7.110 the following operator can be used for ag groups.

G mod H

mod returns a record representing an factor group argument, which can be used as argument for some functions (see 24.68). See 24.53 and 24.56 for details.

24.4 Ag Group Records

In addition to the record components described in 7.111 the following components may be present in the group record of an ag group G.

isAgGroup

is always true.

isConsistent

is true if G has a consistent presentation (see 24.29).

compositionSeries

contains a composition series of G (see 7.36).

cgs

contains a canonical generating system for G. If G is a parent group, it is always present. See 24.44 for details.

igs

contains an induced generating system for G. See 24.44 for details.

elementaryAbelianFactors

see 7.37.

sylowSystem

contains a Sylow system (see 24.63).

24.5 Set Functions for Ag Groups

As already mentioned in the introduction of the chapter, ag groups are domains. Thus all set theoretic functions, for example Intersection and Size, can be applied to ag groups. This and the following sections give further comments on the definition and implementations of those functions for ag groups. All set theoretic functions not mentioned here not treated special for ag groups.

Elements( G )

The elements of a group G are constructed using a canonical generating system. See 24.6.

g in G

Membership is tested using SiftedAgWord (see 23.12), if g lies in the parent group of G. Otherwise false is returned.

IsSubset( G, H )

If G and H are groups then IsSubset tests if the generators of H are elements of G.

Otherwise DomainOps.IsSubset is used.

Intersection( G, H )

The intersection of ag groups G and H is computed using Glasby’s algorithm. See 24.7.

Size( G )

The size of G is computed using a canonical generating system of G. See 24.8.

24.6 Elements for Ag Groups

AgGroupOps.Elements( G )

Let G be an ag group with canonical generating system (g_{1}, ..., g_{n}) where the relative order of g_{i} is o_{i}. Then {g_{1}^{e_{1}}...g_{n}^{e_{n}}; 0 ≤ e_{i} < o_{i}} is the set of elements of G.

24.7 Intersection for Ag Groups

AgGroupOps.Intersection( U , V )

If either V or U is not an ag group then GroupOps.Intersection is used in order to compute the intersection of U and V . If U and V have different parent groups then the empty list is returned.

Let U and V be two ag group with common parent group G. If one subgroup if known to be normal in G the NormalIntersection (see 7.25) is used in order to compute the intersection.

If the size of U or V is smaller than GS SIZE then the intersection is computed using

GroupOps.Intersection. By default GS SIZE is 20.

If an elementary abelian ag series of G is known, Glasby’s generalized covering algorithm is used (see [GS90]). Otherwise a warning is given and GroupOps.Intersection is used, but this may be too slow.

    gap> d8_1 := Subgroup( s4, [ a, c, d ] );
    Subgroup( s4, [ a, c, d ] )
    gap> d8_2 := Subgroup( s4, [ a*b, c, d ] );
    Subgroup( s4, [ a*b, c, d ] )
    gap> Intersection( d8_1, d8_2 );
    Subgroup( s4, [ c, d ] )
    gap> Intersection( d8_1^b, d8_2^b );
    Subgroup( s4, [ c*d, d ] )

24.8 Size for Ag Groups

AgGroupOps.Size( G )

Let G be an ag group with induced generating system (g_{1}, ..., g_{n}) where the relative order of g_{i} is o_{i}. Then the size of G is o_{1} ∗ ... ∗ o_{n}.

AgGroupOps.Size allows a factor argument (see 24.56) for G. It uses Index (see 7.48) in such a case.

24.9 Group Functions for Ag Groups

As ag groups are groups, all group functions, for example IsAbelian and Normalizer, can be applied to ag groups. This and the following sections give further comments on the definition and implementations of those functions for ag groups. All group functions not mentioned here are not treated in a special way.

Group( U )

See 24.11.

CompositionSeries( G )

Let (g_{1}, ..., g_{n}) be an induced generating system of G with respect to the parent group of G. Then for i ∈ {1, ..., n} the i.th composition subgroup S_{i} of the AG-system is generated by (g_{i}, ..., g_{n}). The n + 1.th composition subgroup S_{n+1} is the trivial subgroup of G. The AG-series of G is the series {S_{1}, ..., S_{n+1}}.

Centralizer( U )

The centralizer of an ag group U in its parent group is computed using linear methods while stepping down an elementary abelian series of its parent group.

Centralizer( U , H )

This function call computes the centralizer of H in U using linear methods. H and U must have a common parent.

Centralizer( U , g )

The centralizer of a single element g in an ag group U may be computed whenever g lies in the parent group of U . In that case the same algorithm as for the centralizer of subgroups is used.

ConjugateSubgroup( U , g )

If g is an element of U then U is returned. Otherwise the remainder of the shifting of g through U is used to conjugate an induced generating system of U . In that case

the information bound to U .isNilpotent, U .isAbelian, U .isElementaryAbelian and

U .isCyclic, if known, is copied to the conjugate subgroup.

Core( S, U )

AgGroupOps.Core computes successively the core of U stepping up a composition series of S. See [Thi87].

CommutatorSubgroup( G, H )

See 24.12 for details.

ElementaryAbelianSeries( G )

AgGroupOps.ElementaryAbelianSeries returns a series of normal subgroups of G with elementary abelian factors.

    gap> ElementaryAbelianSeries( s4 );
    [ s4, Subgroup( s4, [ b, c, d ] ), Subgroup( s4, [ c, d ] ),
      Subgroup( s4, [  ] ) ]
    gap> d8 := Subgroup( s4, [ a*b^2, c, d ] );
    Subgroup( s4, [ a*b^2, c, d ] )
    gap> ElementaryAbelianSeries( d8 );
    [ Subgroup( s4, [ a*b^2, c, d ] ), Subgroup( s4, [ c, d ] ),
      Subgroup( s4, [  ] ) ]

If G is no parent group then AgGroupOps.ElementaryAbelianSeries will compute a elementary abelian series for the parent group and intersect this series with G. If G is a parent group then IsElementaryAbelianAgSeries (see 24.30) is used in order to check if such a series exists. Otherwise an elementary abelian is computed refining the derived series (see [LNS84, Gla87]).

ElementaryAbelianSeries( L )

L must be a list of ag groups S_{1} = H, ..., S_{m} = {1} with a common parent group such that S_{i} is a subgroup of S_{i−1} and S_{i} is normal in G for all i ∈ {2, ..., m}. Then the function returns a series of normal subgroups of G with elementary abelian factors refining the series L.

NormalIntersection( V , W )

If V is an element of the AG-series of G, then AgGroupOps.NormalIntersection uses the depth of V in order to compute the intersection. Otherwise it uses the Zassenhaus sumintersection algorithm (see [GS90]).

Normalizer( G, U )

See 24.13.

SylowSubgroup( G, p )

AgGroupOps.SylowSubgroup uses HallSubgroup (see 24.59) in order to compute the sylow subgroup of G.

DerivedSeries( G )
AgGroupOps.DerivedSeries uses DerivedSubgroup (see 7.22) in order to compute the de-

rived series of G. It checks if G is normal in its parent group H. If it is normal all the derived subgroups are also normal in H. G is always the first element of this list and the trivial group always the last one since G is soluble.

LowerCentralSeries( G )

24.9. GROUP FUNCTIONS FOR AG GROUPS 473

AgGroupOps.LowerCentralSeries uses CommutatorSubgroup (see 7.19) in order to com-

pute the lower central series of G. It checks if G is normal in its parent group H. If it is normal all subgroups of the lower central series are also normal in H.

AbelianInvariants( G )

See 24.14.

Random( U )

Let (u_{1}, ..., u_{r}) be a induced generating system of U . Let e_{1}, ..., e_{r} be the relative order of u_{1}, ..., u_{r}. Then for r random integers ν_{i} between 0 and e_{i} − 1 the word u_{1}^{ν_{1}} ∗ ... ∗ u_{r}^{ν_{r}} is returned.

IsCyclic( G )

See 24.15.

IsFinite( G )

As G is a finite solvable group AgGroupOps.IsFinite returns true.

IsNilpotent( U )

AgGroupOps.IsNilpotent uses Glasby’s nilpotency test for ag groups (see [Gla87]).

IsNormal( G, U )

See 24.16.

IsPerfect( G )

As G is a finite solvable group it is perfect if and only if G is trivial.

IsSubgroup( G, U )

See 24.17.

ConjugacyClasses( H )

The conjugacy classes of elements are computed using linear methods. The algorithm depends on the ag series of the parent group of H being a refinement of an elementary abelian series. Thus if this is not true or if H is not a member of the elementary abelian series, an isomorphic group, in which the computation can be done, is created. The algorithm that is used steps down an elementary abelian series of the parent group of H , basically using affine operation to construct the conjugacy classes of H step by step from its factorgroups.

Orbit( U , pt, op )

AgGroupOps.Orbit returns the orbit of pt under U using the operation op. The function calls AgOrbitStabilizer in order to compute the orbit, so please refer to 24.73 for details.

Stabilizer( U , pt, op )

See 24.18.

AsGroup( D )

See 24.10.

FpGroup( U )

See 24.24.

RightCoset( U , g )

See 24.23.

AbelianGroup( D, L )

Let L be the list [o_{1}, ..., o_{n}] of nonnegative integers o_{i} > 1. Then AgWordsOps.AbelianGroup returns the direct product of the cyclic groups of order o_{i} using the domain description D. The generators of these cyclic groups are named beginning with “a”, “b”, “c”, ... followed by a number if o_{i} is a composite integer.

CyclicGroup( D, n )

See 24.19.

ElementaryAbelianGroup( D, n )

See 24.20.

DirectProduct( L )

See 24.21.

WreathProduct( G, H , α )

See 24.22.

24.10 AsGroup for Ag Groups

AgGroupOps.AsGroup( G )

AgGroupOps.AsGroup returns a copy H of G. It does not change the parent status. If G is a subgroup so is H.

AgWordsOps.AsGroup( L )

Let L be a list of ag words. Then AgWordsOps.AsGroup uses MergedCgs (see 24.51) in order to compute a canonical generating system for the subgroup generated by L in the parent group of the elements of L.

24.11 Group for Ag Groups

AgGroupOps.Group( G )

AgGroupOps.Group returns an isomorphic group H such that H is a parent group and H.bijection is bond to an isomorphism between H and G.

AgWordsOps.Group( D, gens, id )

Constructs the group G generated by gens with identity id. If these generators do not generate a parent group, a new parent group H is construct. In that case new generators are used and H.bijection is bound to isomorphism between H and G.

24.12 CommutatorSubgroup for Ag Groups

AgGroupOps.CommutatorSubgroup( G, H )

Let g_{1}, ..., g_{n} be an canonical generating system for G and h_{1}, ..., h_{m} be an canonical generating system for H . The normal closure of the subgroup S generated by Comm(g_{i}, h_{j}) for 1 ≤ i ≤ n and 1 ≤ j ≤ m under G and H is the commutator subgroup of G and H .

But if G or H is known to be normal in the common parent of G amd H then the subgroup S is returned because if G normalizes H or vice versa then S is already the commutator subgroup (see [Gla87]).

If G = H the commutator subgroup is generated by Comm(g_{i}, g_{j}) for 1 ≤ i < j ≤ n

(see [LNS84]). Note that AgGroupOps.CommutatorSubgroup checks G.derivedSubgroup

in that case.

24.13 Normalizer for Ag Groups

AgGroupOps.Normalizer( S, U )

Note that the AG-series of G should be the refinement of an elementary abelian series, see 24.30. Otherwise the calculation of the normalizer is done using an orbit algorithm, which is generally too slow or space extensive. You can construct a new polycyclic presentation for G such that AG-series is a refinement of an elementary abelian series with

ElementaryAbelianSeries (see 7.37) and IsomorphismAgGroup.

For details on the implementation see [GS90, CNW90].

24.14 AbelianInvariants for Ag Groups

AgGroupOps.AbelianInvariants( G )

G must be a finite abelian group of order p_{1}^{e_{1}}...p_{n}^{e_{n}} where p_{i} are primes.

AgGroupOps.AbelianInvariants constructs for every prime p_{i} the series G, G^{p_{i}}, G^{p_{i}^{2}}, ... and computes the abelian invariants using the indices of these groups.

AgGroupOps.AbelianInvariants computes the groups G_{i}^{p} using the fact that the map x ↦ x^{p_{i}} is a homomorphism of G, so that the p_{i}.th powers of an induced generating system of G are a homomorphic image of an igs (see [Cel92]).

24.15 IsCyclic for Ag Groups

AgGroupOps.IsCyclic( G )

AgGroupOps.IsCyclic returns false if G is no abelian group. Otherwise G is finite of order p_{1}^{e_{1}}...p_{n}^{e_{n}} where the p_{i} are distinct primes then G is cyclic if and only if each G^{p_{i}} has index p_{i} in G. AgGroupOps.IsCyclic computes the groups G_{i}^{p} using the fact that the map x ↦ x^{p_{i}} is a homomorphism of G, so that the p_{i}.th powers of an induced generating system of G are a homomorphic image of an igs (see [Cel92]).

24.16 IsNormal for Ag Groups

AgGroupOps.IsNormal( G, U )

Let G be a parent group. Then AgGroupOps.IsNormal checks if the conjugate of each generator of U under each induced generator of G which has a depth not contained in U is an element of U . Otherwise AgGroupOps.IsNormal checks if the conjugate of each generator of U under each generator of G is an element of U .

24.17 IsSubgroup for Ag Groups

AgGroupOps.IsSubgroup( G, U )

If G is a parent group of U , then AgGroupOps.IsSubgroup returns true. If the CGS of U is longer than that of G, U cannot be a subgroup of G. Otherwise AgGroupOps.IsSubgroup shifts each generator of U through G (see 23.12) in order to check if U is a subgroup of G.

24.18 Stabilizer for Ag Groups

AgGroupOps.Stabilizer( U , pt, op )

Let U be an ag group acting on a set Ω by op. Let pt be an element of Ω. Then AgGroupOps.Stabilizer returns the stabilizer of pt in U . op must be a function taking two arguments such that op(p, u) is the image of a point p ∈ Ω under the action of an element u of U . If conjugation should be used op must be OnPoints. The stabilizer is computed by stepping up the composition series of U . The whole orbit pt^{U} is not stored during the computation (see [LNS84]). Of course this saving of space is bought at the cost of time. If you need a faster function, which may use more memory, you can use AgOrbitStabilizer (see 24.73) instead.

24.19 CyclicGroup for Ag Groups

AgWordsOps.CyclicGroup( D, n )
AgWordsOps.CyclicGroup( D, n, str )

Let n be a nonnegative integer. AgWordsOps.CyclicGroup returns the cyclic group of order n. Let n be a composite number with r prime factors. If no str is given, the names of the r generators are cn 1, ..., cn r. Otherwise, the names of the r generators are str1, ..., strr, where str must be a string of letters, digits and the special symbol “ ”.

If the order n is a prime, the name of the generator is either cn or str.

    gap> CyclicGroup( AgWords, 31 );
    Group( c31 )
    gap> AgWordsOps.CyclicGroup( AgWords, 5 * 5, "e" );
    Group( e1, e2 )

24.20 ElementaryAbelianGroup for Ag Groups

AgWordsOps.ElementaryAbelianGroup( D, n )
AgWordsOps.ElementaryAbelianGroup( D, n, str )

AgWordsOps.ElementaryAbelianGroup returns the elementary abelian group of order n, which must be a prime power.

Let n be a prime power p^{r}. If no str is given the names of the r generators are mn 1, ..., mn r. Otherwise the names of the r generators are str1, ..., strr, where str must be a string of letters, digits and the special symbol “ ”.

If the order n is a prime, the name of the generator is either mn or str.

    gap> ElementaryAbelianGroup( AgWords, 31 );
    Group( m31 )
    gap> ElementaryAbelianGroup( AgWords, 31^2 );
    Group( m961_1, m961_2 )
    gap> AgWordsOps.ElementaryAbelianGroup( AgWords, 31^2, "e" );
    Group( e1, e2 )

24.21 DirectProduct for Ag Groups

AgGroupOps.DirectProduct( L )

L must be list of groups or pairs of group and name as described below. If not all groups are ag groups GroupOps.DirectProduct (see 7.92) is used in order to construct the direct product.

Let L be a list of ag groups L = [U_{1}, ..., U_{n}]. AgGroupOps.DirectProduct returns the direct product of all U_{i} as new ag group with collector.

The names of the group generators are the same as the names of the generators of the given groups U_{i}, unless you give a pair [ U_{i}, S ] instead a group U_{i}. In that case the generators corresponding to U_{i} are named Sj for integers j starting with 1 up to the number of induced generators for U_{i}.

Note that GAP distinguishs the generators, even if they have the same name. So if you form the direct product of a group with itself, each generator name will appear twice in the direct product, but each generator is still unique. See also 21.1 for details about problems of this kind.

AgGroupOps.DirectProduct computes for each U_{i} its natural power-commutator presentation for an induced generating system of U_{i}.

Note that the arguments need no common parent group.

    gap> c3 := 1;

gap> z3 := CyclicGroup( AgWords, 3 );
Group( c3 )
gap> z3.generators[ 1 ];

c3 # this is the name of an agword

gap> c3;

1 # not the name of a variable

gap> g := DirectProduct( z3, z3 );
Group( c3, c3 ) # same name
gap> g.generators[ 1 ] = g.generators[ 2 ];

false # but different agword

gap> g := AgGroupOps.DirectProduct( [ [z3, "a"], [z3, "b"] ] );
Group( a, b )

24.22 WreathProduct for Ag Groups

AgGroupOps.WreathProduct( G, H , α )

If H and G are not both ag group GroupOps.WreathProduct (see 7.97) is used. Let H and G be two ag group with possible different parent group and let α be a homomorphism H into a permutation group of degree d. Let (g_{1}, ..., g_{r}) be an IGS of G, (h_{1}, ..., h_{n}) an IGS of H . The wreath product has a PAGsystem (b_{1}, ..., b_{n}, a_{11}, ..., a_{1r}, a_{d1}, ..., a_{dr}) such that b_{1}, ..., b_{n} generate a subgroup isomorph to H and a_{i1}, ..., a_{ir} generate a subgroup isomorph to G for each i in {1, ..., r}. The names of b_{1}, ..., b_{n} are h1, ..., hn, the names of a_{i1}, ..., a_{ir} are ni 1, ..., ni r. AgGroupOps.WreathProduct uses the natural power-commutator presentations of H and G for induced generating system of H and G (see [Thi87]).

gap> s3 := Subgroup( s4, [ a, b ] );
Subgroup( s4, [ a, b ] )
gap> c2 := Subgroup( s4, [ a ] );
Subgroup( s4, [ a ] )
gap> r := RightCosets( s3, c2 );;
gap> S3 := Operation( s3, r, OnRight );
Group( (2,3), (1,2,3) )
gap> f := GroupHomomorphismByImages(s3,S3,[a,b],[(2,3),(1,2,3)]);
GroupHomomorphismByImages( Subgroup( s4, [ a, b ] ), Group( (2,3),
(1,2,3) ), [ a, b ], [ (2,3), (1,2,3) ] )
gap> WreathProduct( c2, s3, f );
Group( h1, h2, n1_1, n2_1, n3_1 )

24.23 RightCoset for Ag Groups

AgGroupOps.Coset( G )

A coset C = G∗x is represented as record with the following components.

representative

contains the representative x.

group

contains the group G.

isDomain
     is true.
isRightCoset
     is true.
isFinite
     is true.
operations

contains the operations record RightCosetAgGroupOps.

RightCosetAgGroupOps.<( C1 , C2 )

If C1 and C2 do not have a common group or if one argument is no coset then the functions uses DomainOps.< in order to compare C1 and C2 . Note that this will compute the set of elements of C1 and C2 .

If C1 and C2 have a common group then AgGroupCosetOps.< will use SiftedAgWord (see 23.12) and ConjugateSubgroup (see 7.20) in order to compare C1 and C2 . It does not compute the set of elements of C1 and C2 .

24.24 FpGroup for Ag Groups

AgGroupOps.FpGroup( U )
AgGroupOps.FpGroup( U , str )

AgGroupOps.FpGroup returns a finite presentation of an ag group U .

If no str is given, the abstract group generators have the same names as the generators of the ag group U . Otherwise they have names of the form stri for integers i from 1 to the number of induced generators.

AgGroupOps.FpGroup computes the natural power-commutator presentation of an induced generating system of the finite polycyclic group U .

24.25 Ag Group Functions

The following functions either construct new parent ag group (see 24.26 and 24.28), test properties of parent ag groups (see 24.29 and 24.30) or change the collector (see 24.34) but they do not compute subgroups. These functions are either described in general in chapter 7 or in 24.57 for specialized functions.

24.26 AgGroup

AgGroup( D )

AgGroup converts a finite polycyclic group D into an ag group G. G.bijection is bound to isomorphism between G and D.

    gap> S4p := Group( (1,2,3,4), (1,2) );
    Group( (1,2,3,4), (1,2) )
    gap> S4p.name := "S4_PERM";;
    gap> S4a := AgGroup( S4p );

    Group( g1, g2, g3, g4 )
    gap> S4a.name := "S4_AG";;
    gap> L := CompositionSeries( S4a );
    [ S4_AG, Subgroup( S4_AG, [ g2, g3, g4 ] ),
      Subgroup( S4_AG, [ g3, g4 ] ), Subgroup( S4_AG, [ g4 ] ),
      Subgroup( S4_AG, [  ] ) ]
    gap> List( L, x -> Image( S4a.bijection, x ) );
    [ Subgroup( S4_PERM, [ (1,2), (1,3,2), (1,4)(2,3), (1,2)(3,4) ] ),
      Subgroup( S4_PERM, [ (1,3,2), (1,4)(2,3), (1,2)(3,4) ] ),
      Subgroup( S4_PERM, [ (1,4)(2,3), (1,2)(3,4) ] ),
      Subgroup( S4_PERM, [ (1,2)(3,4) ] ), Subgroup( S4_PERM, [  ] ) ]

Note that the function will not work for finitely presented groups, see 24.28 for details.

24.27 IsAgGroup

IsAgGroup( obj )

IsAgGroup returns true if obj , which can be an arbitrary object, is an ag group and false otherwise.

    gap> IsAgGroup( s4 );
    true
    gap> IsAgGroup( a );
    false

24.28 AgGroupFpGroup

AgGroupFpGroup( F )

AgGroupFpGroup returns an ag group isomorphic to a finitely presented finite polycyclic group F .

A finitely presented finite polycyclic group F must a be record with components generators and relators, such that generators is a list of abstract generators and relators a list of words in these generators describing a power-commutator or power-conjugate presentation.

Let g_{1}, ..., g_{n} be the generators of F . Then the words of relators must be the power relators g_{k}^{e_{k}} ∗ w_{kk}^{−1} and commutator relator Comm(g_{i}, g_{j}) ∗ w_{ij}^{−1} or conjugate relators g_{i}^{g_{j}} ∗ w_{ij}^{−1} for all 1 ≤ k ≤ n and 1 ≤ j < i ≤ n, such that w_{kk} are words in g_{k+1}, ..., g_{n} and w_{ij} are words in g_{j+1}, ..., g_{n}. It is possible to omit some of the commutator or conjugate relators. Pairs of generators without commutator or conjugate relator are assumed to commute.

The relative order e_{i} of g_{i} need not to be primes, but as all functions for ag groups need a PAG-system, not only an AG-system, you must refine the AG-series using RefinedAgSeries (see 24.33) in case some e_{i} are composite numbers.

Note that it is not checked if the AG-presentation is consistent. You can use IsConsistent (see 24.29) to test the consistency of a presentation. Inconsistent presentations may cause other ag group functions to return incorrect results.

Initially a collector from the left following the algorithm described in [LGS90] is used in order to collect elements of the ag group. This could be changed using ChangeCollector (see 24.34).

Note that AgGroup will not work with finitely presented groups, you must use the function AgGroupFpGroup instead. As no checks are done you can construct an ag group with inconsistent presentation using AgGroupFpGroup.

24.29 IsConsistent

IsConsistent( G )
IsConsistent( G, all )

IsConsistent returns true if the finite polycyclic presentation of a parent group G is consistent and false otherwise.

If all is true then G.inconsistencies contains a list for pairs [w_{1}, w_{2}] such that the words w_{1} and w_{2} are equal in G but have different normal forms.

Note that IsConsistent check and sets G.isConsistent.
gap> InfoAgGroup2 := Print;;
gap> x := AbstractGenerator( "x" );;
gap> y := AbstractGenerator( "y" );;
gap> z := AbstractGenerator( "z" );;
gap> G := AgGroupFpGroup( rec(
>       generators := [ x, y, z ],
>       relators   := [ x^2 / y, y^2 / z, z^2,
>                       Comm( y, x ) / ( y * z ),
>                       Comm( z, x ) / ( y * z )] ) );
Group( x, y, z )
gap> IsConsistent( G );
#I  IsConsistent: y * ( y * x ) <> ( y * y ) * x
false
gap> IsConsistent( G, true );
#I  IsConsistent: y * ( y * x ) <> ( y * y ) * x
#I  IsConsistent: z * ( z * x ) <> ( z * z ) * x
#I  IsConsistent: y * ( x * x ) <> ( y * x ) * x
#I  IsConsistent: z * ( x * x ) <> ( z * x ) * x
#I  IsConsistent: x * ( x * x ) <> ( x * x ) * x
false
gap> G.inconsistencies;
[ [ x, x*y ], [ x*z, x ], [ z, y ], [ y*z, y ], [ x*y, x ] ]

24.30 IsElementaryAbelianAgSeries

IsElementaryAbelianAgSeries( G )

Let G be a parent group. IsElementaryAbelianAgSeries returns true if and only if the AG-series of G is the refinement of an elementary abelian series of G.

The function sets G.elementaryAbelianSeries G in case of a true result. This component is described in 7.37.

gap> IsElementaryAbelianAgSeries( s4 );
true

gap> ElementaryAbelianSeries( s4 );
[ s4, Subgroup( s4, [ b, c, d ] ), Subgroup( s4, [ c, d ] ),
  Subgroup( s4, [  ] ) ]
gap> CompositionSeries( s4 );
[ s4, Subgroup( s4, [ b, c, d ] ), Subgroup( s4, [ c, d ] ),
  Subgroup( s4, [ d ] ), Subgroup( s4, [  ] ) ]

24.31 MatGroupAgGroup

MatGroupAgGroup( U , M )

Let U and M be two ag groups with a common parent group and let M be a elementary abelian group normalized by U . Then MatGroupAgGroup returns the matrix representation of U acting on M .

See also 24.74.

gap> v4 := AgSubgroup( s4, [ c, d ], true );
Subgroup( s4, [ c, d ] )
gap> a4 := AgSubgroup( s4, [ b, c, d ], true );
Subgroup( s4, [ b, c, d ] )
gap> MatGroupAgGroup( s4, v4 );
Group( [ [ Z(2)^0, Z(2)^0 ], [ 0*Z(2), Z(2)^0 ] ],
[ [ 0*Z(2), Z(2)^0 ], [ Z(2)^0, Z(2)^0 ] ] )

24.32 PermGroupAgGroup

PermGroupAgGroup( G, U )

Let U be a subgroup of a ag group G. Then PermGroupAgGroup returns the permutation representation of G acting on the cosets of U .

gap> v4 := AgSubgroup( s4, [ s4.1, s4.4 ], true );
Subgroup( s4, [ a, d ] )
gap> PermGroupAgGroup( s4, v4 );
Group( (3,5)(4,6), (1,3,5)(2,4,6), (1,2)(3,4), (3,4)(5,6) )

24.33 RefinedAgSeries

RefinedAgSeries( G )

RefinedAgSeries returns a new parent group isomorphic to a parent group G with a PAGseries, if the ag group G has only an AG-series.

Note that in the case that G has a PAG-series, G is returned without any further action.

The names of the new generators are constructed as follows. Let (g_{1}, ..., g_{n}) be the AGsystem of the ag group G and n(g_{i}) the name of g_{i}. If the relative order of g_{i} is a prime, then n(g_{i}) is the name of a new generator. If the relative order is a composite number with r prime factors, then there exist r new generators with names n(g_{i}) 1, ..., n(g_{i}) r.

gap> c12 := AbstractGenerator( "c12" );;
gap> F := rec( generators := [ c12 ],
>              relators   := [ c12 ^( 2 * 2 * 3 ) ] );

    rec(
      generators := [ c12 ],
      relators := [ c12^12 ] )
    gap> G := AgGroupFpGroup( F );
    #W  AgGroupFpGroup: composite index, use 'RefinedAgSeries'
    Group( c12 )
    gap> RefinedAgSeries( G );
    Group( c121, c122, c123 )

24.34 ChangeCollector

ChangeCollector( G, name )
ChangeCollector( G, name, n )

ChangeCollector changes the collector of a parent group G and all its subgroups. name is the name of the new collector. The following collectors are supported.

“single” initializes a collector from the left following the algorithm described in [LGS90].

“triple” initializes a collector from the left collecting with triples g_{i}ˆ g_{j}^{r} for j < i and r = 1, ..., n (see [Bis89]).

“quadruple” initializes a collector from the left collecting with quadruples g_{i}^{s}ˆ g_{j}^{r} for j < i, r = 1, ..., n and s = 1, ..., n. Note that r and s have the same upper bound (see [Bis89]).

“combinatorial” initializes a combinatorial collector from the left for a p-group G. In that case the commutator or conjugate relations of the G must be of the form g_{i}^{g_{j}} = w_{ij} or Comm(g_{i}, g_{j}) = w_{ij} for 1 ≤ j < i ≤ n, such that w_{ij} are words in g_{i+1}, ..., g_{n} fulfilling the central weight condition (see [HN80, VL84]). If these conditions are not fulfilled, the collector could not be initialized, a warning will be printed and collection will be done with the old collector.

For collectors which collect with tuples a maximal bound of those tuples is n, set to 5 by default.

24.35 The Prime Quotient Algorithm

The following sections describe the np-quotient functions. PQuotient allows to compute quotient of prime power order of finitely presented groups. For further references see [HN80] and [VL84].

There is a C standalone version of the p-quotient algorithm, the ANU p-Quotient Program, which can be called from GAP. For further information see chapter 49.

24.36 PQuotient

PQuotient( G, p, cl )
PrimeQuotient( G, p, cl )

PQuotient computes quotients of prime power order of finitely presented groups. G must be a group given by generators and relations. PQuotient expects G to be a record with the record fields generators and relators. The record field generators must be a list of abstract generators created by the function AbstractGenerator (see 21.1). The record field relators must be a list of words in the generators which are the relators of the group. p must be a prime. cl has to be an integer, which specifies that the quotient of prime power order computed by PQuotient is the largest p-quotient of G of class at most cl. PQuotient returns a record Q, the PQp record, which has, among others, the following record fields describing the p-quotient Q.

generators

A list of abstract generators which generate Q.

pcp

The internal power-commutator presentation for Q.

dimensions

A list, where dimensions[i] is the dimension of the i-th factor in the lower exponentp central series calculated by the p-quotient algorithm.

prime

The integer p, which is a prime.

definedby

A list which contains the definition of the k-th generator in the k-th place. There are three different types of entries, namely lists, positive and negative integers.

[ j, i ]

the generator is defined to be the commutator of the j-th and the i-th element in

generators.
i

the generator is defined as the p-th power of the i-th element in generators.

-i

the generator is defined as an image of the i-th generator in the finite presentation for G, consequently it must be a generator of weight 1.

epimorphism

A list containing an image in Q of each generator of G. The image is either an integer i if it is the i-th element of generators of Q or an abstract word w if it is the abstract word w in the generators of Q.

An example of the computation of the largest quotient of class 4 of the group given by the finite presentation {x, y | x^{25}/(x · y)^{5}, [x, y]^{5}, (x^{y})^{25}}.

# Define the group

gap> x := AbstractGenerator("x");;
gap> y := AbstractGenerator("y");;
gap> G := rec( generators := [x,y],
>              relators := [ x^25/(x*y)^5, Comm(x,y)^5, (x^y)^25] );
rec(
 generators := [ x, y ],
 relators :=
  [ x^25*y^-1*x^-1*y^-1*x^-1*y^-1*x^-1*y^-1*x^-1*y^-1*x^-1,
     x^-1*y^-1*x*y*x^-1*y^-1*x*y*x^-1*y^-1*x*y*x^-1*y^-1*x*y*x^-1*y^-\
1*x*y, y^-1*x^25*y ] )

# Call pQuotient

gap> P := PQuotient( G, 5, 4 );

    #I  PQuotient: class 1 : 2
    #I  PQuotient: Runtime : 0
    #I  PQuotient: class 2 : 2
    #I  PQuotient: Runtime : 27
    #I  PQuotient: class 3 : 2
    #I  PQuotient: Runtime : 1437
    #I  PQuotient: class 4 : 3
    #I  PQuotient: Runtime : 1515
    PQp( rec(
       generators  := [ g1, g2, a3, a4, a6, a7, a11, a12, a14 ],
       definedby   := [ -1, -2, [ 2, 1 ], 1, [ 3, 1 ], [ 3, 2 ],
      [ 5, 1 ], [ 5, 2 ], [ 6, 2 ] ],
       prime       := 5,
       dimensions  := [ 2, 2, 2, 3 ],
       epimorphism := [ 1, 2 ],
       powerRelators := [ g1^5/(a4), g2^5/(a4^4), a3^5, a4^5, a6^5, a7^
    5, a11^5, a12^5, a14^5 ],
       commutatorRelators := [ Comm(g2,g1)/(a3), Comm(a3,g1)/(a6),
    Comm(a3,g2)/(a7), Comm(a6,g1)/(a11),Comm(a6,g2)/(a12),
    Comm(a7,g1)/(a12),Comm(a7,g2)/(a14) ],
       definingCommutators := [ [ 2, 1 ], [ 3, 1 ], [ 3, 2 ], [ 5, 1 ],
      [ 5, 2 ], [ 6, 1 ], [ 6, 2 ] ] ) )

The p-quotient algorithm returns a PQp record for the exponent-5 class 4 quotient. Note that instead of printing the PQp record P an equivalent representation is printed which can be read in to GAP. See 24.38 for details. The quotient defined by P has nine generators, g1, g2, a3, a4, a6, a7,a11, a12, a14, stored in the list P.generators. From powerRelators we can read off that g1^5 =: a4 and g2^5 = a4^4 and all other generators have trivial 5-th powers. From the list commutatorRelators we can read off the non-trivial commutator relations Comm(g2,g1)

=: a3, Comm(a3,g1) =: a6, Comm(a3,g2)  =: a7, Comm(a6,g1) =: a11,Comm(a6,g2) =:
a12, Comm(a7,g1) =  a12 and Comm(a7,g2) =: a14. In this list =: denotes that the gen-

erator on the right hand side is defined as the left hand side. This information is given by the list definedby. The list dimensions shows that P is a class-4 quotient of order 5^{2} · 5^{2} · 5^{2} · 5^{3} = 5^{9}. The epimorphism of G onto the quotient P is given by the map x ↦ g1 and y ↦ g2.

24.37 Save

Save( file, Q, N )

Save saves the PQp record Q to the file file in such a way that the file can be read by GAP. The name of the record in the file will be N . This differs from printing Q to a file in that the required abstract generators are also created in file.

    gap> x := AbstractGenerator("x");;
    gap> y := AbstractGenerator("y");;
    gap> G := rec( generators := [x,y],
    >              relators := [ x^25/(x*y)^5, Comm(x,y)^5, (x^y)^25] );;
    gap> P := PQuotient( G, 5, 4 );;

#I  PQuotient: class 1 : 2
#I  PQuotient: Runtime : 0
#I  PQuotient: class 2 : 2
#I  PQuotient: Runtime : 27
#I  PQuotient: class 3 : 2
#I  PQuotient: Runtime : 78
#I  PQuotient: class 4 : 3
#I  PQuotient: Runtime : 156
gap> Save( "Quo54", P, "Q" );
gap> Exec( "cat Quo54" );
g1 := AbstractGenerator("g1");
g2 := AbstractGenerator("g2");
a3 := AbstractGenerator("a3");
a4 := AbstractGenerator("a4");
a6 := AbstractGenerator("a6");
a7 := AbstractGenerator("a7");
a11 := AbstractGenerator("a11");
a12 := AbstractGenerator("a12");
a14 := AbstractGenerator("a14");
Q := PQp( rec(
   generators  := [ g1, g2, a3, a4, a6, a7, a11, a12, a14 ],
   definedby   := [ -1, -2, [ 2, 1 ], 1, [ 3, 1 ], [ 3, 2 ],
  [ 5, 1 ], [ 5, 2 ], [ 6, 2 ] ],
   prime       := 5,
   dimensions  := [ 2, 2, 2, 3 ],
   epimorphism := [ 1, 2 ],
   powerRelators := [ g1^5/(a4), g2^5/(a4^4), a3^5, a4^5, a6^5, a7^
5, a11^5, a12^5, a14^5 ],
   commutatorRelators := [ Comm(g2,g1)/(a3), Comm(a3,g1)/(a6), Comm(a3\
,g2)/(a7), Comm(a6,g1)/(a11), Comm(a6,g2)/(a12), Comm(a7,g1)/(a12), Co\
mm(a7,g2)/(a14) ],
   definingCommutators := [ [ 2, 1 ], [ 3, 1 ], [ 3, 2 ], [ 5, 1 ],
  [ 5, 2 ], [ 6, 1 ], [ 6, 2 ] ] ) );

24.38 PQp

PQp( r )

PQp takes as argument a record r containing all information necessary to restore a PQp record Q. A PQp record Q is printed as function call to PQp with an argument describing Q. This is necessary because the internal power-commutator representation cannot be printed. Therefore all information about Q is encoded in a record r and Q is printed as PQp( <r>

).

24.39 InitPQp

InitPQp( n, p )

InitPQp creates a PQp record for an elementary abelian group of rank n and of order p^{n} for a prime p.

24.40 FirstClassPQp

FirstClassPQp( G, p )

FirstClassPQp returns a PQp record for the exponent-p class 1 quotient of G.

24.41 NextClassPQp

NextClassPQp( G, P )

Let P be the PQp record for the exponent-p class c quotient of G. NextClassPQp returns a PQp record for the class c + 1 quotient of G, if such a quotient exists, and P otherwise. In latter case there exists a maximal p-quotient of G which has class c and this is indicated by a comment if InfoPQ1 is set the Print.

24.42 Weight

Weight( P, w )

Let P be a PQp record and w a word in the generators of P. The function Weight returns the weight of w with respect to the lower exponent-p central series defined by P.

24.43 Factorization for PQp

Factorization( P, w )

Let P be a PQp record and w a word in the generators of P. The function Factorization returns a word in the weight 1 generators of P expressing w.

24.44 Generating Systems of Ag Groups

For an ag group G there exists three different types of generating systems. The generating system in G.generators is a list of ag words generating the group G with the only condition that none of the ag words is the identity of G. If an induced generating system for G is known it is bound to G.igs, while an canonical generating system is bound to G.cgs. But as every canonical generating system is also an induced one, G.cgs and G.igs may contain the same system.

The functions Cgs, Igs, Normalize, Normalized and IsNormalized change or manipulate

these systems. The following overview shows when to use this functions. For details see 24.46, 24.47, 24.49, 24.50 and 24.48.

Igs returns an induced generating system for G. If neither G.igs nor G.cgs are present, it uses MergedIgs (see 24.52) in order to construct an induced generating system from G.generators. In that case the induced generating system is bound to G.igs. If G.cgs but not G.igs is present, this is returned, as every canonical generating system is also an induced one. If G.igs is present this is returned.

Cgs returns a canonical generating system for G. If neither G.igs nor G.cgs are present, it uses MergedCgs (see 24.51) in order to construct a canonical generating system from G.generators. In that case the canonical generating system is bound to G.cgs. If G.igs but not G.cgs is present, this is normalized and bound to G.cgs, but G.igs is left unchanged. If G.cgs is present this is returned.

Normalize computes a canonical generating system, binds it to G.cgs and unbinds an induced generating bound to G.igs. Normalized normalizes a copy without changing the original ag group. This function should be preferred.

IsNormalized checks if an induced generating system is a canonical one and, if being canonical, binds it to G.cgs and unbinds G.igs. If G.igs is unbound IsNormalized computes a canonical generating system, binds it to G.cgs and returns true.

Most functions need an induced or canonical generating system, all function descriptions state clearly what is used, if this is relevant, see 24.68 for example.

24.45 AgSubgroup

AgSubgroup( U , gens, flag )

Let U be an ag group with ag group G, gens be an induct or canonical generating system for a subgroup S of U and flag a boolean. Then AgSubgroup returns the record of an ag group representing this finite polycyclic group S as subgroup of G.

If flag is true, gens must be a canonical generating with respect to G. If flag is false gens must be a an induced generating with respect to G.

gens will be bound to S.generators. If flag is true, it is also bound to S.cgs, if it is false, gens is also bound to S.igs. Note that AgSubgroup does not copy gens.

Note that it is not check whether gens are an induced or canonical system. If gens fails to be one, all following computations with this group are most probably wrong.

    gap> v4 := AgSubgroup( s4, [ c, d ], true );
    Subgroup( s4, [ c, d ] )

24.46 Cgs

Cgs( U )

Cgs returns a canonical generating system of U with respect to the parent group of U as list of ag words (see 24.1).

If U .cgs is bound, this is returned without any further action. If U .igs is bound, a copy of this component is normalized, bound to U .cgs and returned. If neither U .igs nor U .cgs are bound, a canonical generating system for U is computed using MergedCgs (see 24.51) and bound to U .cgs.

24.47 Igs

Igs( U )

Igs returns an induced generating system of U with respect to the parent group of U as list of ag words (see 24.1).

If U .igs is bound, this is returned without any further action. If U .cgs but not U .igs is bound, this is returned. If neither U .igs nor U .cgs are bound, an induced generating system for U is computed using MergedIgs (see 24.52) and bound to U .igs.

24.48 IsNormalized

IsNormalized( U )

IsNormalized returns true if no induced generating system but an canonical generating system for U is known. If U .cgs but not U .igs is bound, true is returned. If neither U .cgs nor U .igs are bound, a canonical generating system is computed, bound to U .cgs and true is retuned. If U .igs is present, it is check, if U .igs is a canonical generating. If so, the canonical generating system is bound to U .cgs and U .igs is unbound.

24.49 Normalize

Normalize( U )

Normalize converts an induced generating system of an ag group U into a canonical one. If U .cgs and not U .igs is bound, U is returned without any further action. If U contains both components U .cgs and U .igs, U .igs is unbound. If only U .igs but not U .cgs is bound the generators in U .igs are converted into a canonical generating and bound to U .cgs, while U .igs is unbound. If neither U .igs nor U .cgs are bound a canonical generating system is computed using Cgs (see 24.46).

24.50 Normalized

Normalized( U )

Normalized returns a normalized copy of an ag group U . For details see 24.49. Note that this function does not alter the record of U and always returns a copy of U , even if U is already normalized.

24.51 MergedCgs

MergedCgs( U , objs )

Let U be an ag group with parent group G and objs be a list of elements and subgroups of U . Then MergedCgs returns the subgroup S of G generated by the elements and subgroups in the list objs. The subgroup S contains a canonical generating system bound to S.cgs. As objs contains only elements and subgroups of U , the subgroup S is not only a subgroup of G but also of U. Its parent group is nevertheless G and MergedCgs computes a canonical generating system of S with respect to G. If subgroups of S are known at least the largest should be an element of objs, because MergedCgs is much faster in such cases. Note that this function may return a wrong subgroup, if the elements of objs do not belong to U . See also 24.44 for differences between canonical and induced generating systems.

    gap> d8 := MergedCgs( s4, [ a*c, c ] );
    Subgroup( s4, [ a, c, d ] )
    gap> MergedCgs( s4, [ a*b*c*d, d8 ] );
    s4
    gap> v4 := MergedCgs( d8, [ c*d, c ] );
    Subgroup( s4, [ c, d ] )

24.52 MergedIgs

MergedIgs( U , S, gens, normalize )

Let U and S be ag groups with a common parent group G such that S is a subgroup of U . Let gens be a list of elements of U . Then MergedIgs returns the subgroup K of G generated by S and gens.

As gens contains only elements of U , the subgroup K is not only a subgroup of G but also of U. Its parent group is nevertheless G and MergedIgs computes a induced generating system of S with respect to G.

If normalize is true, a canonical generating system for K is computed and bound to K.cgs. If normalize is false only an induced generating system is computed and bound to K.igs or K.cgs. If no subgroup S is known, rec() can be given instead.

Note that U must be an ag group which contains S and gens.

24.53 Factor Groups of Ag Groups

It is possible to deal with factor groups of ag groups in three different ways. If an ag group G and a normal subgroup N of G is given, you can construct a new polycyclic presentation for F = G/N using FactorGroup. You can apply all functions for ag groups to that new parent group F and even switch between G and F using the homomorphisms returned by NaturalHomomorphism. See 7.31 for more information on that kind of factor groups.

But if you are only interested in an easy way to test a property or an easy way to calculate a subgroup of a factor group, the first approach might be too slow, as it involves the construction of a new polycyclic presentation for the factor group together with the creation of a new collector for that factor group. In that case you can use CollectorlessFactorGroup in order to construct a new ag group without initializing a new collector but using records faking ag words instead. But now multiplication is still done in G and the words must be canonicalized with respect to N, so that multiplication in this group is rather slow. However if you for instance want to check if a chief factor, which is not part of the AG-series, is central this may be faster then constructing a new collector. But generally FactorGroup should be used.

The third possibility works only for Exponents (see 24.68) and SiftedAgWord (see 23.12). If you want to compute the action of G on a factor M/N then you can pass M/N as factor group argument using M mod N or FactorArg (see 24.56).

24.54 FactorGroup for AgGroups

AgGroupOps.FactorGroup( U , N )

Let N and U be ag groups with a common parent group, such that N is a normal subgroup of U . Let H be the factor group U /N . FactorGroup returns the finite polycyclic group H as new parent group.

If the ag group U is not a parent group or if N is not an element of the AG-series of U (see 24.65), then FactorGroup constructs a new polycyclic presentation and collector for the factor group using both FpGroup (see 24.24) and AgGroupFpGroup (see 24.28). Otherwise FactorGroup copies the old collector of U and cuts of the tails which lie in N .

Note that N must be a normal subgroup of U . You should keep in mind, that although the new generators and the old ones may have the same names, they cannot be multiplitied as they are elements of different groups. The only way to transfer information back and forth is to use the homomorphisms returned by NaturalHomomorphism (see 7.31).

    gap> c2 := Subgroup( s4, [ d ] );
    Subgroup( s4, [ d ] )
    gap> d8 := Subgroup( s4, [ a, c, d ] );
    Subgroup( s4, [ a, c, d ] )
    gap> v4 := FactorGroup( d8, c2 );
    Group( g1, g2 )
    gap> v4.2 ^v4.1;
    g2
    gap> d8 := Subgroup( s4, [ a, c, d ] );
    Subgroup( s4, [ a, c, d ] )
    gap> d8.2^d8.1;
    c*d

24.55 CollectorlessFactorGroup

CollectorlessFactorgroup( G, N )

CollectorlessFactorgroup constructs the factorgroup F = G/N without initializing a new collector. The elements of F are records faking ag words.

Each element f of F contains the following components.

representative

a canonical representative d in G for f.

isFactorGroupElement contains true.
info

a record containing information about the factor group.

operations
     the operations record FactorGroupAgWordsOps.

24.56 FactorArg

FactorArg( U , N )

Let N be a normal subgroup of an ag group U . Then FactorArg returns a record with the following components with can be used as argument for Exponents.

isFactorArg
     is true.
factorNum

contains U .

factorDen

contains N .

identity

contains the identity of U .

generators

contains a list of those induced generators u_{i} of U of depth d_{i} such that no induced generator in N has depth d_{i}.

operations

contains the operations record FactorArgOps.

Note that FactorArg is bound to AgGroupOps.mod.
gap> d8 := Subgroup( s4, [ a, c, d ] );
Subgroup( s4, [ a, c, d ] )
gap> c2 := Subgroup( s4, [ d ] );
Subgroup( s4, [ d ] )
gap> M := d8 mod c2;;
gap> d8.1 * d8.2 * d8.3;
a*c*d
gap> Exponents( M, last );
[ 1, 1 ]
gap> d8 := AgSubgroup( s4, [ a*c, c, d ], false );
Subgroup( s4, [ a*c, c, d ] )
gap> M := d8 mod c2;;
gap> Exponents( M, a*c*d );
[ 1, 0 ]

24.57 Subgroups and Properties of Ag Groups

The subgroup functions compute subgroups or series of subgroups from given ag groups, e.g. PRump (see 24.60) or ElementaryAbelianSeries (see 7.37). They return group records described in 7.111 and 24.4 for the computed subgroups. All the following functions only work for ag groups. Subgroup functions which work for various types of groups are described in 7.14. Properties and property tests which work for various types of groups are described in 7.43.

24.58 CompositionSubgroup

CompositionSubgroup( G, i )

CompositionSubgroup returns the i.th subgroup of the AG-series of an ag group G. Let (g_{1}, ..., g_{n}) be an induced generating system of G with respect to the parent group of G. Then the i.th composition subgroup S of the AG-series is generated by (g_{i}, ..., g_{n}).

gap> CompositionSubgroup( s4, 2 );
Subgroup( s4, [ b, c, d ] )
gap> CompositionSubgroup( s4, 4 );
Subgroup( s4, [ d ] )
gap> CompositionSubgroup( s4, 5 );
Subgroup( s4, [  ] )

24.59 HallSubgroup

HallSubgroup( G, n )
HallSubgroup( G, L )

Let G be an ag group. Then HallSubgroup returns a π-Hall-subgroup of G for the set π of all prime divisors of the integer n or the join π of all prime divisors of the integers of L.

The Hall-subgroup is constructed using Glasby’s algorithm (see [Gla87]), which descends along an elementary abelian series for G and constructs complements in the coprime case (see 24.86). If no such series is known for G the function uses ElementaryAbelianSeries (see 7.37) in order to construct such a series for G.

gap> HallSubgroup( s4, 2 );
Subgroup( s4, [ a, c, d ] )
gap> HallSubgroup( s4, [ 3 ] );
Subgroup( s4, [ b ] )
gap> z5 := CyclicGroup( AgWords, 5 );
Group( c5 )
gap> DirectProduct( s4, z5 );
Group( a, b, c, d, c5 )
gap> HallSubgroup( last, [ 5, 3 ] );
Subgroup( Group( a, b, c, d, c5 ), [ b, c5 ] )

24.60 PRump

PRump( G, p )

PRump returns the p-rump of an ag group G for a prime p.

The p-rump of a group G is the normal closure under G of the subgroup generated by the commutators and p.th powers of the generators of G.

gap> PRump( s4, 2 );
Subgroup( s4, [ b, c, d ] )
gap> PRump( s4, 3 );
s4

24.61 RefinedSubnormalSeries

RefinedSubnormalSeries( L )

Let L be a list of ag groups G_{1}, ..., G_{n}, such that G_{i+1} is a normal subgroup of G_{i}. Then the function computes a composition series H_{1} = G_{1}, ..., H_{m} = G_{n} which refines the given subnormal series L and has cyclic factors of prime order.

See also 7.41.

gap> v4 := Subgroup( s4, [ c, d ] );
Subgroup( s4, [ c, d ] )
gap> T := TrivialSubgroup( s4 );
Subgroup( s4, [  ] )
gap> RefinedSubnormalSeries( [ s4, v4, T ] );
[ s4, Subgroup( s4, [ b, c, d ] ), Subgroup( s4, [ c, d ] ),
  Subgroup( s4, [ d ] ), Subgroup( s4, [  ] ) ]

24.62 SylowComplements

SylowComplements( U )

SylowComplements returns a Sylow complement system of U . This system S is represented as a record with at least the components S.primes and S.sylowComplements, additionally there may be a component S.sylowSubgroups (see 24.63).

primes

A list of all prime divisors of the group order of U .

sylowComplements

contains a list of Sylow complements for all primes in S.primes, so that if the i.th element of S.primes is p, then the i.th element of sylowComplements is a Sylow-p-complement of U .

sylowSubgroups

contains a list of Sylow subgroups for all primes in S.primes, such that if the i.th element of S.primes is p, then the i.th element of S.sylowSubgroups is a Sylow-p-subgroup of U .

SylowComplements uses HallSubgroup (see 24.59) in order to compute the various Sylow complements of U , if no Sylow system is known for U . If a Sylow system {S_{1}, ..., S_{n}} is known, SylowComplements computes the various Hall subgroups H_{i} using the fact that H_{i} is the product of all S_{j} except S_{i}.

SylowComplements sets and checks U.sylowSystem.
    gap> SylowComplements( s4 );
    rec(
      primes := [ 2, 3 ],
      sylowComplements :=
       [ Subgroup( s4, [ b ] ), Subgroup( s4, [ a, c, d ] ) ] )

24.63 SylowSystem

SylowSystem( U )

SylowSystem returns a Sylow system {S_{1}, ..., S_{n}} of an ag group U . The system S is represented as a record with at least the components S.primes and S.sylowSubgroups, additionally there may be a component S.sylowComplements, see 24.62 for information about this addtional component.

primes

A list of all prime divisors of the group order of U .

sylowComplements

contains a list of Sylow complements for all primes in S.primes, so that if the i.th element of S.primes is p, then the i.th element of sylowComplements is a Sylow-p-complement of U .

sylowSubgroups

contains a list of Sylow subgroups for all primes in S.primes, such that if the i.th element of S.primes is p, then the i.th element of S.sylowSubgroups is a Sylow-p-subgroup of U .

A Sylow system of a group U is a system of Sylow subgroups S_{i} for each prime divisor of the group order of U such that S_{i} ∗ S_{j} = S_{j} ∗ S_{i} is fulfilled for each pair i, j.

SylowSystem uses SylowComplements (see 24.63) in order to compute the various Sylow complements H_{i} of U . Then the Sylow system is constructed using the fact that the intersection S_{i} of all Sylow complements H_{j} except H_{i} is a Sylow subgroup and that all these subgroups S_{i} form a Sylow system of U . See [Gla87].

SylowSystem sets and checks S.sylowSystem.
    gap> z5 := CyclicGroup( AgWords, 5 );
    Group( c5 )
    gap> D := DirectProduct( z5, s4 );
    Group( c5, a, b, c, d )
    gap> D.name := "z5Xs4";;
    gap> SylowSystem( D );
    rec(
      primes := [ 2, 3, 5 ],
      sylowComplements :=
       [ Subgroup( z5Xs4, [ c5, b ] ), Subgroup( z5Xs4, [ c5, a, c, d ] ),
          Subgroup( z5Xs4, [ a, b, c, d ] ) ],
      sylowSubgroups :=
       [ Subgroup( z5Xs4, [ a, c, d ] ), Subgroup( z5Xs4, [ b^2 ] ),
          Subgroup( z5Xs4, [ c5^4 ] ) ] )

24.64 MinimalGeneratingSet

MinimalGeneratingSet( G )

Let G be an ag group. Then MinimalGeneratingSet returns a subset L of G of minimal cardinality with the property that L generates G.

    gap> l := MinimalGeneratingSet(s4);
    [ a*d, b ]
    gap> s4 = Subgroup( s4, l );
    true

24.65 IsElementAgSeries

IsElementAgSeries( U )

IsElementAgSeries returns true if the ag group U is part of the AG-series of the parent group G of U and false otherwise.

24.66 IsPNilpotent

IsPNilpotent( U , p )

IsPNilpotent returns true, if the ag group U is p-nilpotent for the prime p, and false otherwise.

IsPNilpotent uses Glasby’s p-nilpotency test (see [Gla87]).

    gap> IsPNilpotent( s4, 2 );

    false
    gap> s3 := Subgroup( s4, [ a, b ] );
    Subgroup( s4, [ a, b ] )
    gap> IsPNilpotent( s3, 2 );
    true
    gap> IsPNilpotent( s3, 3 );
    false

24.67 NumberConjugacyClasses

NumberConjugacyClasses( H )

This functions computes the number of conjugacy classes of elements of a group H .

The function uses an algorithm that steps down an elementary abelian series of the parent group of H and computes the number of conjugacy classes using the same method as AgGroupOps.ConjugacyClasses does, up to the last factor group. In the last step the Cauchy-Frobenius-Burnside lemma is used.

This algorithm is especially designed to supply at least the number of conjugacy classes of H , whenever ConjugacyClasses fails because of storage reasons. So one would rather use this function if the last normal subgroup of the elementary abelian series is too big to be

dealt with ConjugacyClasses.

NumberConjugacyClasses( U , H )

This version of the call to NumberConjugacyClasses computes the number of conjugacy classes of H under the operation of U . Thus for the operation to be well defined, U must be a subgroup of the normalizer of H in their common parent group.

    gap> a4 := DerivedSubgroup(s4);;
    gap> NumberConjugacyClasses( s4 );
    5
    gap> NumberConjugacyClasses( a4, s4 );
    6
    gap> NumberConjugacyClasses( a4 );
    4
    gap> NumberConjugacyClasses( s4, a4 );
    3

24.68 Exponents

Exponents( U , u )
Exponents( U , u, F )

Exponents returns the exponent vector of an ag word u with respect to an induced generating system of U as list of integers if no field F is given. Otherwise the product of the exponent vector and F .one is returned. Note that u must be an element of U .

Let (u_{1}, ..., u_{r}) be an induced generating system of U . Then u can be uniquely written as u_{1}^{ν_{1}} ∗ ... ∗ u_{r}^{ν_{r}} for integer ν_{i}. The exponent vector of u is [ν_{1}, ..., ν_{r}].

Exponents allows factor group arguments. See 24.53 for details.

Note that Exponents adds a record component U .shiftInfo. This entry is used by subsequent calls with the same ag group in order to speed up computation. If you ever change the component U .igs by hand, not using Normalize, you must unbind the component U .shiftInfo, otherwise all following results of Exponents will be corrupted. In case U is a parent group you can use ExponentsAgWord (see 23.15), which is slightly faster but requires a parent group U . Note that you you may get a weird error message if u is no element of U . So it is strictly required that u is an element of U .

Note that Exponents uses ExponentsAgWord but not ExponentAgWord, so for records that

mimic agwords Exponents may be used in ExponentAgWord.

    gap> v4 := AgSubgroup( s4, [ c, d ], true );
    Subgroup( s4, [ c, d ] )
    gap> Exponents( v4, c * d );
    [ 1, 1 ]
    gap> Exponents( s4 mod v4, a * b^2 * c * d );
    [ 1, 2 ]

24.69 FactorsAgGroup

FactorsAgGroup( U )

FactorsAgGroup returns the factorization of the group order of an ag group U as list of positive integers. Note that it is faster to use FactorsAgGroup than to use Factors and Size.

    gap> v4 := Subgroup( s4, [ c, d ] );;
    gap> FactorsAgGroup( s4 );
    [ 2, 2, 2, 3 ]
    gap> Factors( Size( s4 ) );
    [ 2, 2, 2, 3 ]

24.70 MaximalElement

MaximalElement( U )

MaximalElement returns the ag word in U with maximal exponent vector. Let G be the parent group of U with canonical generating system (g_{1}, ..., g_{n}) and let (u_{1}, ..., u_{m}) be the canonical generating system of U , d_{i} is the depth of u_{i} with respect to G. Then an ag word u = g_{1}^{ν_{1}} ∗ ... ∗ g_{n}^{ν_{n}} ∈ U is returned such that ∑_{i=1}^{m} ν_{d_{i}} is maximal.

24.71 Orbitalgorithms of Ag Groups

The functions Orbit (see 8.14) and Stabilizer (see 8.22 and 24.18) compute the orbit and stabilizer of an ag group acting on a domain. AgOrbitStabilizer (see 24.73) computes the orbit and stabilizer in case that a compatible homomorphism into a group H exists, such that the action of H on the domain is more efficient than the operation of the ag group; for example, if an ag group acts linearly on a vectorspace, the operation can by described using matrices. The functions AffineOperation (see 24.72) and LinearOperation (see 24.74) compute matrix groups describing the affine or linear action of an ag group on a vectorspace.

24.72 AffineOperation

AffineOperation( U , V , φ, τ )

Let U be an ag group with an induced generating system u_{1}, ..., u_{m} and let V be a vectorspace with base (o_{1}, ..., o_{n}). Further U should act affinely on V . So if v is an element of V and u is an element of U , then v^{u} = v_{u} +x_{u}, such that the function which maps v to v_{u} is linear and x_{u} is an element of V . These actions are given by the functions φ and τ as follows. φ(v, u) must return the representation of v_{u} with respect to the base (o_{1}, ..., o_{n}) as sequence of finite field elements. τ(u) must return the representation of x_{u} in the base (o_{1}, ..., o_{n}) as sequence of finite field elements. If these conditions are fulfilled, AffineOperation returns a matrix group M describing this action.

Note that M.images contains a list of matrices m_{i}, such that m_{i} describes the action of u_{i} and m_{i} is of the form

( \begin{matrix}L_{u_{i}} & 0 \\ x_{u_{i}} & 1 \end{matrix} ) ,

where L_{u} is the matrix which describes the linear operation v ∈ V ↦ v_{u}.

    gap> v4 := AgSubgroup( s4, [ c, d ], true );
    Subgroup( s4, [ c, d ] )
    gap> v4.field := GF( 2 );
    GF(2)
    gap> phi := function( v, g )
    >      return Exponents( v4, v^g, v4.field );
    >    end;
    function ( v, g ) ... end
    gap> tau := g -> Exponents( v4, v4.identity, v4.field );
    function ( g ) ... end
    gap> V := rec( base := [ c, d ], isDomain := true );
    rec(
      base := [ c, d ],
      isDomain := true )
    gap> AffineOperation( s4, V, phi, tau );
    Group( [ [ Z(2)^0, Z(2)^0, 0*Z(2) ], [ 0*Z(2), Z(2)^0, 0*Z(2) ],
      [ 0*Z(2), 0*Z(2), Z(2)^0 ] ],
    [ [ 0*Z(2), Z(2)^0, 0*Z(2) ], [ Z(2)^0, Z(2)^0, 0*Z(2) ],
      [ 0*Z(2), 0*Z(2), Z(2)^0 ] ] )

24.73 AgOrbitStabilizer

AgOrbitStabilizer( U , gens, ω )
AgOrbitStabilizer( U , gens, ω, f )

Let U be an ag group acting on a set Ω. Let ω be an element of Ω. Then AgOrbitStabilizer returns the point-stabilizer of ω in the group U and the orbit of ω under this group. The stabilizer and orbit are returned as record R with components R.stabilizer and R.orbit. R.stabilizer is the point-stabilizer of ω. R.orbit is the list of the elements of ω^{U} .

Let (u_{1}, ..., u_{n}) be an induced generating system of U and gens be a list h_{1}, ..., h_{n} of generators of a group H, such that the map u_{i} ↦ h_{i} extends to an homomorphism α from U to H, which is compatible with the action of G and H on Ω, such that g ∈ Stab_{U}(ω) if and only if g^{α} ∈ Stab_{H}(ω). If f is missing OnRight is assumed, a typical application of this function being the linear action of U on an vectorspace. If f is OnPoints then ^is used as operation of H on Ω. Otherwise f must be a function, which takes two arguments, the first one must be a point p of Ω and the second an element h of H and which returns p^{h}.

    gap> AgOrbitStabilizer( s4, [a,b,c,d], d, OnPoints );
    rec(
      stabilizer := Subgroup( s4, [ a, c, d ] ),
      orbit := [ d, c*d, c ] )

24.74 LinearOperation

LinearOperation( U , V , φ )

Let U be an ag group with an induced generating system u_{1}, ..., u_{m} and V a vectorspace with base (o_{1}, ..., o_{n}). U must act linearly on V . Let v be an element of V , u be an element of U . The action of U on V should be given as follows. If v^{u} = a_{1} ∗ o_{1} + ... + a_{n} ∗ o_{n}, then the function φ(v, u) must return (a_{1}, ..., a_{n}) as list of finite field elements. If these condition are fulfilled, LinearOperation returns a matrix group M describing this action.

Note that M.images is bound to a list of matrices m_{i}, such that m_{i} describes the action of u_{i}.

    gap> v4 := AgSubgroup( s4, [ c, d ], true );
    Subgroup( s4, [ c, d ] )
    gap> v4.field := GF( 2 );
    GF(2)
    gap> V := rec( base := [ c, d ], isDomain := true );
    rec(
      base := [ c, d ],
      isDomain := true )
    gap> phi := function( v, g )
    >      return Exponents( v4, v^g, v4.field );
    >    end;
    function ( v, g ) ... end
    gap> LinearOperation( s4, V, phi );
    Group( [ [ Z(2)^0, Z(2)^0 ], [ 0*Z(2), Z(2)^0 ] ],
    [ [ 0*Z(2), Z(2)^0 ], [ Z(2)^0, Z(2)^0 ] ] )

24.75 Intersections of Ag Groups

There are two kind of intersection algorithms. Whenever the product of two subgroups is a subgroup, a generalized Zassenhaus algorithm can be used in order to compute the intersection and sum (see [GS90]). In case one subgroup is a normalized by the other, an element of the sum can easyly be decomposed. The functions IntersectionSumAgGroup (see

24.77), NormalIntersection (see 7.25 ), SumFactorizationFunctionAgGroup (see 24.79)

and SumAgGroup (see 24.78) should be used in such cases.

These functions are faster than the general function Intersection (see 4.12 and 24.7), which can compute the intersection of two subgroups even if their product is no subgroup.

24.76 ExtendedIntersectionSumAgGroup

ExtendedIntersectionSumAgGroup( W , V )

Let V and W be ag groups with a common parent group, such that V ∗ W = W ∗ V . Then V ∗ W is a subgroup and ExtendedIntersectionSumAgGroup returns the intersection and the sum of V and W . The information about these groups is returned as a record with the components intersection, sum and the additional information leftSide and rightSide.

intersection

is bound to the intersection W ∩ V .

sum

is bound to the sum V ∗ W .

leftSide

is lists of ag words, see below.

rightSide

is lists of agwords, see below.

The function uses the Zassenhaus sum-intersection algorithm. Let V be generated by v_{1}, ..., v_{a}, W be generated by w_{1}, ..., w_{b}. Then the matrix

( \begin{matrix}v_{1} & 1 \\ ⋮ & ⋮ \\ v_{a} & 1 \\ w_{1} & w_{1} \\ ⋮ & ⋮ \\ w_{b} & w_{b} \end{matrix} )

is echelonized by using the sifting algorithm to produce the following matrix

( \begin{matrix}l_{1} & k_{1} \\ ⋮ & ⋮ \\ l_{c} & k_{c} \\ 1 & k_{c+1} \\ ⋮ & ⋮ \\ 1 & k_{a+b} \end{matrix} ) .

Then l_{1}, ..., l_{c} is a generating sequence for the sum, while the sequence k_{c+1}, ..., k_{a+b} is is a generating sequence for the intersection. leftSide is bound to a list, such that the i.th list element is l_{j}, if there exists a j, such that l_{j} has depth i, and IdAgWord otherwise. rightSide is bound to a list, such that the i.th list element is k_{j}, if there exists a j less than c + 1, such that k_{j} has depth i, and IdAgWord otherwise. See also 24.79.

Note that this functions returns an incorrect result if V ∗ W ≠ W ∗ V.

gap> v4_1 := AgSubgroup( s4, [ a*b, c ], true );
Subgroup( s4, [ a*b, c ] )
gap> v4_2 := AgSubgroup( s4, [ c, d ], true );
Subgroup( s4, [ c, d ] )
gap> ExtendedIntersectionSumAgGroup( v4_1, v4_2 );
rec(
  leftSide := [ a*b, IdAgWord, c, d ],
  rightSide := [ a*b, IdAgWord, c, IdAgWord ],
  sum := Subgroup( s4, [ a*b, c, d ] ),
  intersection := Subgroup( s4, [ c ] ) )

24.77 IntersectionSumAgGroup

IntersectionSumAgGroup( V , W )

Let V and W be ag groups with a common parent group, such that V ∗ W = W ∗ V . Then V ∗ W is a subgroup and IntersectionSumAgGroup returns the intersection and the sum of V and W as record R with components R.intersection and R.sum.

The function uses the Zassenhaus sum-intersection algorithm. See also 7.25 and 24.78. For more information about the Zassenhaus algorithm see 24.76 and 24.79.

Note that this functions returns an incorrect result if V ∗ W ≠ W ∗ V.

gap> d8_1 := AgSubgroup( s4, [ a, c, d ], true );
Subgroup( s4, [ a, c, d ] )
gap> d8_2 := AgSubgroup( s4, [ a*b, c, d ], true );
Subgroup( s4, [ a*b, c, d ] )
gap> IntersectionSumAgGroup( d8_1, d8_2 );
rec(
  sum := Group( a, b, c, d ),
  intersection := Subgroup( s4, [ c, d ] ) )

24.78 SumAgGroup

SumAgGroup( V , W )

Let V and W be ag groups with a common parent group, such that V ∗ W = W ∗ V . Then V ∗ W is a subgroup and SumAgGroup returns V ∗ W .

The function uses the Zassenhaus sum-intersection algorithm (see [GS90]).

Note that this functions returns an incorrect result if V ∗ W ≠ W ∗ V .

gap> d8_1 := Subgroup( s4, [ a, c, d ] );
Subgroup( s4, [ a, c, d ] )
gap> d8_2 := Subgroup( s4, [ a*b, c, d ] );
Subgroup( s4, [ a*b, c, d ] )
gap> SumAgGroup( d8_1, d8_2 );
Group( a, b, c, d )

24.79 SumFactorizationFunctionAgGroup

SumFactorizationFunctionAgGroup( U , N )

Let U and N be ag group with a common parent group such that U normalizes N . Then the function returns a record R with the following components.

intersection

is bound to the intersection U ∩ N .

sum

is bound to the sum U ∗ N .

factorization

is bound to function, which takes an element g of U ∗ N and returns the factorization of g in an element u of U and n of N, such that g = u∗n. This factorization is returned as record r with components r.u and r.n, where r.u is bound to the ag word u, r.n to the ag word n.

Note that N must be a normal subgroup of U ∗ N , it is not sufficient that U ∗ N = N ∗ U .

gap> v4 := AgSubgroup( s4, [ a*b, c ], true );
Subgroup( s4, [ a*b, c ] )
gap> a4 := AgSubgroup( s4, [ b, c, d ], true );
Subgroup( s4, [ b, c, d ] )
gap> sd := SumFactorizationFunctionAgGroup;
function ( U, N ) ... end
gap> sd := SumFactorizationFunctionAgGroup( v4, a4 );
rec(
  sum := Group( a*b, b, c, d ),
  intersection := Subgroup( s4, [ c ] ),
  factorization := function ( un ) ... end )
gap> sd.factorization( a*b*c*d );
rec(
  u := a*b*c,
  n := d )
gap> sd.factorization( a*b^2*c*d );
rec(
  u := a*b*c,
  n := b*c )

24.80 One Cohomology Group

Let G be a finite group, M a normal p-elementary abelian subgroup of G. Then the group of one coboundaries B^{1}(G/M, M) is defined as

B^{1}(G/M, M) = {γ : G/M → M ; ∃m ∈ M∀g ∈ G : γ(gM) = (m^{−1})^{g} · m}

and is a Z_{p}-vectorspace. The group of cocycles Z^{1}(G/M, M) is defined as

Z^{1}(G/M, M) = {γ : G/M → M ; ∀g_{1}, g_{2} ∈ G : γ(g_{1}M · g_{2}M) = γ(g_{1}M)^{g_{2}} · γ(g_{2}M)} and is also a Z_{p}-vectorspace.

Let α be the isomorphism of M into a row vectorspace W and (g_{1}, ..., g_{l}) representatives for a generating set of G/M. Then there exists a monomorphism β of Z^{1}(G/M, M) in the l-fold direct sum of W, such that β(γ) = (α(γ(g_{1}M)), ..., α(γ(g_{l}M))) for every γ ∈ Z^{1}(G/M, M).

OneCoboundaries (see 24.81) and OneCocycles (see 24.82) compute the group of one coboundaries and one cocyles given a ag group G and a elementary abelian normal subgroup M. If Info1Coh1, Info1Coh2 and Info1Coh3 are set to Print information about the computation is given.

24.81 OneCoboundaries

OneCoboundaries( G, M )

Let M be a normal p-elementary abelian subgroup of G. Then OneCoboundaries computes the vectorspace V = β(B^{1}(G/M , M )), which is isomorphic to the group of one coboundaries B^{1}(G, M) as described in 24.80. The functions returns a record C with the following components.

oneCoboundaries

contains the vectorspace V.

generators

contains representatives (g_{1}, ..., g_{l}) for the canonical generating system of G/M

cocycleToList

contains a functions which takes an element v of V as argument and returns a list [n_{1}, ..., n_{l}], where n_{i} is an element of M , such that n_{i} = (β^{−1}(v))(g_{i}M ).

listToCocycles

is the inverse of cocycleToList.

OneCoboundaries( G, α, M )

In that form OneCoboundaries computes the one coboundaries in the semidirect product of G and M where G acts on M using α (see 7.94).

    gap> s4xc2 := DirectProduct( s4, CyclicGroup( AgWords, 2 ) );
    Group( a, b, c, d, c2 )
    gap> m := CompositionSubgroup( s4xc2, 3 );
    Subgroup( Group( a, b, c, d, c2 ), [ c, d, c2 ] )
    gap> oc := OneCoboundaries( s4xc2, m );
    rec(
      oneCoboundaries :=
       RowSpace( [ [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
          [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ] ], GF(2) ),
      generators := [ a, b ],
      cocycleToList := function ( c ) ... end,
      listToCocycle := function ( L ) ... end )
    gap> v := Base( oc.oneCoboundaries );
    [ [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
      [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ] ]
    gap> oc.cocycleToList( v[1] );

    [ d, d ]
    gap> oc.cocycleToList( v[2] );
    [ IdAgWord, c ]
    gap> oc.cocycleToList( v[1]+v[2] );
    [ d, c*d ]

24.82 OneCocycles

OneCocycles( G, M )

Let M be a normal p-elementary abelian subgroup of G. Then OneCocycles computes the vectorspace V = β(Z^{1}(G/M , M )), which is isomorphic to the group of one cocyles Z^{1}(G, M) as described in 24.80. The function returns a record C with the following components.

oneCoboundaries

contains the vectorspace isomorphic to B^{1}(G, M).

oneCocycles

contains the vectorspace V.

generators

contains representatives (g_{1}, ..., g_{l}) for the canonical generating system of G/M

isSplitExtension

If G splits over M , C.isSplitExtension is true, otherwise it is false. In case of a split extension three more components C.complement, C.cocycleToComplement

     and C.complementToCycles are returned.
complement

contains a subgroup of G which is a complement of M .

cocycleToList

contains a functions which takes an element v of V as argument and returns a list [n_{1}, ..., n_{l}], where n_{i} is an element of M , such that n_{i} = (β^{−1}(v))(g_{i}M ).

listToCocycles

is the inverse of cocycleToList.

cocycleToComplement

contains a function which takes an element of V as argument and returns a complement of M .

complementToCocycle

is its inverse. This is possible, because in a split extension there is a one to one correspondence between the elements of V and the complements of M .

OneCocycles( G, α, M )

In that form OneCocycles computes the one cocycles in the semidirect product of G and M where G acts on M using α (see 7.94). In that case C only contains C.oneCoboundaries,

C.oneCocycles, C.generators, C.cocycleToList and C.listToCocycle.
    gap> s4xc2 := DirectProduct( s4, CyclicGroup( AgWords, 2 ) );;
    gap> s4xc2.name := "s4xc2";;
    gap> m := CompositionSubgroup( s4xc2, 3 );
    Subgroup( s4xc2, [ c, d, c2 ] )

gap> oc := OneCocycles( s4xc2, m );;
gap> oc.oneCocycles;
RowSpace( [ [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
  [ 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2) ],
  [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ] ], GF(2) )
gap> v := Base( oc.oneCocycles );
[ [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
  [ 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2) ],
  [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ] ]
gap> oc.cocycleToList( v[1] );
[ d, d ]
gap> oc.cocycleToList( v[2] );
[ c2, IdAgWord ]
gap> oc.cocycleToList( v[2] );
[ c2, IdAgWord ]
gap> oc.cocycleToList( v[3] );
[ IdAgWord, c ]
gap> Igs( oc.complement );
[ a, b ]
gap> Igs( oc.cocycleToComplement( v[1]+v[2]+v[3] ) );
[ a*d*c2, b*c*d ]
gap> z4 := CyclicGroup( AgWords, 4 );
Group( c4_1, c4_2 )
gap> m := CompositionSubgroup( z4, 2 );
Subgroup( Group( c4_1, c4_2 ), [ c4_2 ] )
gap> OneCocycles( z4, m );
rec(
  oneCoboundaries := RowSpace( [  ], GF(2), [ 0*Z(2) ] ),
  oneCocycles := RowSpace( 1, GF(2) ),
  generators := [ c4_1 ],
  isSplitExtension := false )

24.83 Complements

Complement (see 24.84) tries to find one complement to a given normal subgroup, while Complementclasses (see 24.85) finds all complements and returns representatives for the conjugacy classes of complements in a given ag group. If InfoAgCo1 and InfoAgCo2 are set to Print information about the computation is given.

24.84 Complement

Complement( U , N )

Let N and U be ag group such that N is a normal subgroup of U . Complement returns a complement of N in U if the U splits over N . Otherwise false is returned. Complement descends along an elementary abelian series of U containing N . See [CNW90] for details.

gap> v4 := Subgroup( s4, [ c, d ] );

Subgroup( s4, [ c, d ] )
gap> Complement( s4, v4 );
#I  FactorGroup: uses composition factor group
#I  FactorGroup: uses composition factor group
Subgroup( s4, [ a, b ] )
gap> z4 := CyclicGroup( AgWords, 4 );
Group( c4_1, c4_2 )
gap> z2 := Subgroup( z4, [ z4.2 ] );
Subgroup( Group( c4_1, c4_2 ), [ c4_2 ] )
gap> Complement( z4, z2 );
#I  FactorGroup: uses composition factor group
#I  FactorGroup: uses composition factor group
false
gap> m9 := ElementaryAbelianGroup( AgWords, 9 );
Group( m9_1, m9_2 )
gap> m3 := Subgroup( m9, [ m9.2 ] );
Subgroup( Group( m9_1, m9_2 ), [ m9_2 ] )
gap> Complement( m9, m3 );
#I  FactorGroup: uses composition factor group
#I  FactorGroup: uses composition factor group
Subgroup( Group( m9_1, m9_2 ), [ m9_1 ] )

24.85 Complementclasses

Complementclasses( U , N )

Let U and N be ag groups such that N is a normal subgroup of U . Complementclasses returns a list of representatives for the conjugacy classes of complements of N in U . Note that the empty list is returned if U does not split over N . Complementclasses descends along an elementary abelian series of U containing N . See [CNW90] for details.

gap> v4 := Subgroup( s4, [ c, d ] );
Subgroup( s4, [ c, d ] )
gap> Complementclasses( s4, v4 );
#I  FactorGroup: uses composition factor group
[ Subgroup( s4, [ a, b ] ) ]
gap> z4 := CyclicGroup( AgWords, 4 );
Group( c4_1, c4_2 )
gap> z2 := Subgroup( z4, [ z4.2 ] );
Subgroup( Group( c4_1, c4_2 ), [ c4_2 ] )
gap> Complementclasses( z4, z2 );
#I  FactorGroup: uses composition factor group
#I  FactorGroup: uses composition factor group
[  ]
gap> m9 := ElementaryAbelianGroup( AgWords, 9 );
Group( m9_1, m9_2 )
gap> m3 := Subgroup( m9, [ m9.2 ] );
Subgroup( Group( m9_1, m9_2 ), [ m9_2 ] )

gap> Complementclasses( m9, m3 );
#I  FactorGroup: uses composition factor group
#I  FactorGroup: uses composition factor group
[ Subgroup( Group( m9_1, m9_2 ), [ m9_1 ] ),
  Subgroup( Group( m9_1, m9_2 ), [ m9_1*m9_2 ] ),
  Subgroup( Group( m9_1, m9_2 ), [ m9_1*m9_2^2 ] ) ]

24.86 CoprimeComplement

CoprimeComplement( U , N )

CoprimeComplement returns a complement of a normal p-elementary abelian Hall subgroup N of U .

Note that, as N is a normal Hall-subgroup of U , the theorem of Schur guarantees the existence of a complement.

gap> s4xc25 := DirectProduct( s4, CyclicGroup( AgWords, 25 ) );
Group( a, b, c, d, c25_1, c25_2 )
gap> s4xc25.name := "s4xc25";;
gap> a4xc25 := Subgroup( s4xc25,
>                  Sublist( s4xc25.generators, [2..5] ) );
Subgroup( s4xc25, [ b, c, d, c25_1 ] )
gap> N := Subgroup( s4xc25, [ s4xc25.3, s4xc25.4 ] );
Subgroup( s4xc25, [ c, d ] )
gap> CoprimeComplement( a4xc25, N );
Subgroup( s4xc25, [ b, c25_1, c25_2 ] )

24.87 ComplementConjugatingAgWord

ComplementConjugatingAgWord( N , U , V )
ComplementConjugatingAgWord( N , U , V , K )

Let N , U , V and K be ag groups with a common parent group G, such that N is pelementary abelian and normal in G, U ∗ N = V ∗ N , U ∩ N = V ∩ N = {1}, K is a normal subgroup of U N contained in U ∩ V and U is conjugate to V under an element n of N . Then this function returns an element n of N such that U^{n} = V as ag word. If K is not given, the trivial subgroup is assumed.

In a typical application N is a normal p-elementary abelian subgroup and U , V and K are subgroups such that U /K is a q-group with q ≠ p.

Note that this function does not check any of the above conditions. So the result may either be false or an ag word with does not conjugate U into V , if U and V are not conjugate.

gap> c3a := Subgroup( s4, [ b ] );
Subgroup( s4, [ b ] )
gap> c3b := Subgroup( s4, [ b*c ] );
Subgroup( s4, [ b*c ] )
gap> v4 := Subgroup( s4, [ c, d ] );
Subgroup( s4, [ c, d ] )
gap> ComplementConjugatingAgWord( v4, c3a, c3b );

    #I  ComplementConjugatingAgWord: 'GS' as 2<>3 and 3<21
    d
    gap> c3a ^d;
    Subgroup( s4, [ b*c ] )

24.88 HallConjugatingWordAgGroup

HallConjugatingAgWord( S, H , K )

Let H , K and S be ag group with a common parent group such that H and K are Hallsubgroups of S, then HallConjugatingAgWord returns an element g of S as ag word, such that H^{g} = K .

    gap> d8 := HallSubgroup( s4, 2 );
    Subgroup( s4, [ a, c, d ] )
    gap> d8 ^b;
    Subgroup( s4, [ a*b^2, c*d, d ] )
    gap> HallConjugatingAgWord( s4, d8, d8 ^b );
    #I  ComplementConjugatingAgWord: 'GS' as 3<>2 and 2<21
    b
    gap> HallConjugatingAgWord( s4, d8 ^b, d8 );
    #I  ComplementConjugatingAgWord: 'GS' as 3<>2 and 2<21
    b^2

24.89 Example, normal closure

We will now show you how to write a GAP function, which computes the normal closure of an ag group. Such a function already exists in the library (see 7.24), but this should be an example on how to put functions together. You should at least be familiar with the basic definitions and functions for ag groups, so please refer to 23, 24 and 24.1 for the definitions of finite polycyclic groups and its subgroups, see 24.44 for information about calculating induced or canonical generating system for subgroups.

Let U and S be subgroups of a group G. Then the normal closure N of U under S is the smallest subgroup in G, which contains U and is invariant under conjugation with elements of S. It is clear that N is invariant under conjugating with generators of S if and only if it is invariant under conjugating with all elements of S.

So in order to compute the normal closure of U, we can start with N := U, conjugate N with a generator s of S and set N to the subgroup generated by N and N^{s}. Then we take the next generator of S. The whole process is repeated until N is stable. A GAP function doing this looks like

    NormalClosure := function( S, U )

local G, # the common supergroup of S and U N, # closure computed so far M, # next closure under generators of S s; # one generator of S

        G := Parent( S, U );

    M := U;
    repeat
        N := M;
        for s  in Igs( S )  do
            M := MergedCgs( G, [ M ^s, M ] );
        od;
    until M = N;
    return N;
end;

Let S = G be the wreath product of the symmetric group on four points with itself using the natural permutation representation. Let U be a randomly chosen subgroup of order 12. The above functions needs, say, 100 time units to compute the normal closure of U under S, which is a subgroup N of index 2 in G.

gap> prms := [ (1,2), (1,2,3), (1,3)(2,4), (1,2)(3,4) ];
[ (1,2), (1,2,3), (1,3)(2,4), (1,2)(3,4) ]
gap> f := GroupHomomorphismByImages( s4, Group( prms, () ),
>             s4.generators, prms );;
gap> G := WreathProduct( s4, s4, f );
Group( h1, h2, h3, h4, n1_1, n1_2, n1_3, n1_4, n2_1, n2_2, n2_3,
n2_4, n3_1, n3_2, n3_3, n3_4, n4_1, n4_2, n4_3, n4_4 )
gap> G.name := "G";;
gap> u := Random( G );
h4*n1_1*n1_2^2*n2_2*n2_3*n2_4*n3_1*n3_2^2*n4_2
gap> U := MergedCgs( G, [ u ] );
Subgroup( G, [ h4*n1_2^2*n1_4*n2_1*n2_2^2*n3_2^2*n4_1*n4_2^2,
  n1_1*n1_2*n1_4*n2_1*n2_3*n3_1*n3_2*n4_1, n1_3*n2_4 ] )
gap> Size( U );
8

Now we can ask to speed up things. The first observation is that computing a canonical generating system is usablely a more time consuming task than computing a conjugate subgroup. So we form a canonical generating system after we have computed all conjugate subgroups, although now an additional repeat-until loop could be necessary.

NormalClosure := function( S, U )
    local   G,  N,  M,  s,  gens;
    G := Parent( S, U );
    M := U;
    repeat
        N := M;
        gens := [ M ];
        for s  in Igs( S )  do
            Add( gens, M ^s );
        od;
        M := MergedCgs( G, gens );
    until M = N;
    return N;

    end;

If we now test this new normal closure function with the above groups, we see that the running time has decreased to 48 time units. The canonical generating system algorithm is faster if it knows a large subgroup of the group which should be generated but it does not gain speed if it knows several of them. A canonical generating system for the conjugated subgroup M^s is computed, although we only need generators for this subgroup. So we can rewrite our algorithm.

    NormalClosure := function( S, U )

local G, # the common supergroup of S and U N, # closure computed so far M, # next closure under generators of S gensS, # generators of S gens; # generators of next step

        G := Parent( S, U );
        M := U;
        gens := Igs( S );
        repeat
            N := M;
            gens := Concatenation( [ M ], Concatenation( List( S, s ->
                        List( Igs( M ), m -> m ^s ) ) ) );
            M := MergedCgs( G, gens );
        until M = N;
        return N;
    end;

Now a canonical generating system is generated only once per repeat-until loop. This reduces the running time to 33 time units. Let m ∈ M and s ∈ S. Then ⟨M, m^{s}⟩ = ⟨M, m^{−1}m^{s}⟩. So we can substitute m^s by Comm( m, s ). If m is invariant under s the new generator would be 1 instead of m. With this modification the running times drops to 23 time units.

As next step we can try to compute induced generating systems instead of canonical ones. In that case we cannot compare aggroups by =, but as N is a subgroup M it is sufficient to compare the composition lengths.

    NormalClosure := function( S, U )

local G, # the common supergroup of S and U N, # closure computed so far M, # next closure under generators of S gensS, # generators of S gens; # generators of next step

        G := Parent( S, U );
        M := U;
        gens := Igs( S );

        repeat
            N := M;
            gens := Concatenation( List( S, s -> List( Igs( M ),
                        m -> Comm( m, s ) ) ) );
            M := MergedIgs( G, N, gens, false );
        until Length( Igs( M ) ) = Length( Igs( M ) );
        Normalize( N );
        return N;
    end;

But if we try the example above the running time has increased to 31. As the normal closure has index 2 in G the agwords involved in a canonical generating system are of length one or two. But agwords of induced generating system may have much large length. So we have avoided some collections but made the collection process itself much more complicated. Nevertheless in examples with subgroups of greater index the last function is slightly faster.

512 CHAPTER 24. FINITE POLYCYCLIC GROUPS

Lists

Lists are the most important way to collect objects and treat them together. A list is a collection of elements. A list also implies a partial mapping from the integers to the elements. I.e., there is a first element of a list, a second, a third, and so on.

List constants are written by writing down the elements in order between square brackets [, ], and separating them with commas ,. An empty list, i.e., a list with no elements, is written as [].

    gap> [ 1, 2, 3 ];

[ 1, 2, 3 ] # a list with three elements

    gap> [ [], [ 1 ], [ 1, 2 ] ];

[ [ ], [ 1 ], [ 1, 2 ] ] # a list may contain other lists

Usually a list has no holes, i.e., contain an element at every position. However, it is absolutely legal to have lists with holes. They are created by leaving the entry between the commas empty. Lists with holes are sometimes convenient when the list represents a mapping from a finite, but not consecutive, subset of the positive integers. We say that a list that has no holes is dense.

    gap> l := [ , 4, 9,, 25,, 49,,,, 121 ];;
    gap> l[3];
    9
    gap> l[4];
    Error, List Element: <list>[4] must have a value

It is most common that a list contains only elements of one type. This is not a must though. It is absolutely possible to have lists whose elements are of different types. We say that a list whose elements are all of the same type is homogeneous.

    gap> l := [ 1, E(2), Z(3), (1,2,3), [1,2,3], "What a mess" ];;
    gap> l[1];  l[3];  l[5][2];
    1
    Z(3)
    2

The first sections describe the functions that test if an object is a list and convert an object to a list (see 25.1 and 25.2).

The next section describes how one can access elements of a list (see 25.3 and 25.4).

The next sections describe how one can change lists (see 25.5, 25.6, 25.7, 25.8, 25.10).

The next sections describe the operations applicable to lists (see 25.11 and 25.12).

The next sections describe how one can find elements in a list (see 25.13, 25.14, 25.15, 25.16).

The next sections describe the functions that construct new lists, e.g., sublists (see 25.17, 25.18, 25.19, 25.20, 25.21).

The next sections describe the functions deal with the subset of elements of a list that have a certain property (see 25.22, 25.23, 25.24, 25.25, 25.26, 25.27).

The next sections describe the functions that sort lists (see 25.28, 25.29, 25.30, 25.31).

The next sections describe the functions to compute the product, sum, maximum, and minimum of the elements in a list (see 25.32, 25.33, 25.34, 25.35, 25.36).

The final section describes the function that takes a random element from a list (see 25.37).

Lists are also used to represent sets, subsets, vectors, and ranges (see 26, 27, 30, and 29).

25.1 IsList

IsList( obj )

IsList returns true if the argument obj , which can be an arbitrary object, is a list and false otherwise. Will signal an error if obj is an unbound variable.

    gap> IsList( [ 1, 3, 5, 7 ] );
    true
    gap> IsList( 1 );
    false

25.2 List

List( obj )

List( list, func )

In its first form List returns the argument obj , which must be a list, a permutation, a string or a word, converted into a list. If obj is a list, it is simply returned. If obj is a permutation, List returns a list where the i-th element is the image of i under the permutation obj . If obj is a word, List returns a list where the i-th element is the i-th generator of the word, as a word of length 1.

    gap> List( [1,2,3] );
    [ 1, 2, 3 ]
    gap> List( (1,2)(3,4,5) );
    [ 2, 1, 4, 5, 3 ]

In its second form List returns a new list, where each element is the result of applying the function func, which must take exactly one argument and handle the elements of list, to the corresponding element of the list list. The list list must not contain holes.

    gap> List( [1,2,3], x->x^2 );
    [ 1, 4, 9 ]
    gap> List( [1..10], IsPrime );

    [ false, true, true, false, true, false, true, false, false, false ]

Note that this function is called map in Lisp and many other similar programming languages. This name violates the GAP rule that verbs are used for functions that change their arguments. According to this rule map would change list, replacing every element with the result of the application func to this argument.

25.3 List Elements

list[ pos ]

The above construct evaluates to the pos-th element of the list list. pos must be a positive integer. List indexing is done with origin 1, i.e., the first element of the list is the element at position 1.

    gap> l := [ 2, 3, 5, 7, 11, 13 ];;
    gap> l[1];
    2
    gap> l[2];
    3
    gap> l[6];
    13

If list does not evaluate to a list, or pos does not evaluate to a positive integer, or list[pos] is unbound an error is signalled. As usual you can leave the break loop (see 3.2) with quit;. On the other hand you can return a result to be used in place of the list element by return expr;.

list{ poss }

The above construct evaluates to a new list new whose first element is list[poss[1]], whose second element is list[poss[2]], and so on. poss must be a dense list of positive integers, it need, however, not be sorted and may contain duplicate elements. If for any i, list[ poss[i] ] is unbound, an error is signalled.

    gap> l := [ 2, 3, 5, 7, 11, 13, 17, 19 ];;
    gap> l{[4..6]};
    [ 7, 11, 13 ]
    gap> l{[1,7,1,8]};
    [ 2, 17, 2, 19 ]

The result is a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the left operand (see 25.8).

It is possible to nest such sublist extractions, as can be seen in the following example.

    gap> m := [ [1,2,3], [4,5,6], [7,8,9], [10,11,12] ];;
    gap> m{[1,2,3]}{[3,2]};
    [ [ 3, 2 ], [ 6, 5 ], [ 9, 8 ] ]
    gap> l := m{[1,2,3]};; l{[3,2]};
    [ [ 7, 8, 9 ], [ 4, 5, 6 ] ]

Note the difference between the two examples. The latter extracts elements 1, 2, and 3 from m and then extracts the elements 3 and 2 from this list. The former extracts elements 1, 2, and 3 from m and then extracts the elements 3 and 2 from each of those element lists.

To be precise. With each selector [pos] or {poss} we associate a level that is defined as the number of selectors of the form {poss} to its left in the same expression. For example

        l[pos1]{poss2}{poss3}[pos4]{poss5}[pos6]
    level   0      0      1     1      1     2

Then a selector list[pos] of level level is computed as ListElement(list,pos,level), where ListElement is defined as follows

    ListElement := function ( list, pos, level )
        if level = 0  then
            return list[pos];
        else
            return List( list, elm -> ListElement(elm,pos,level-1) );
        fi;
    end;

and a selector list{poss} of level level is computed as ListElements(list,poss,level), where ListElements is defined as follows

    ListElements := function ( list, poss, level )
        if level = 0  then
            return list{poss};
        else
            return List( list, elm -> ListElements(elm,poss,level-1) );
        fi;
    end;

25.4 Length

Length( list )

Length returns the length of the list list. The length is defined as 0 for the empty list, and as the largest positive integer index such that list[index] has an assigned value for nonempty lists. Note that the length of a list may change if new elements are added to it or assigned to previously unassigned positions.

    gap> Length( [] );
    0
    gap> Length( [ 2, 3, 5, 7, 11, 13, 17, 19 ] );
    8
    gap> Length( [ 1, 2,,, 5 ] );
    5

For lists that contain no holes Length, Number (see 25.22), and Size (see 4.10) return the same value. For lists with holes Length returns the largest index of a bound entry, Number returns the number of bound entries, and Size signals an error.

25.5 List Assignment

list[ pos ] := object; The list assignment assigns the object object, which can be of any type, to the list entry at the position pos, which must be a positive integer, in the list list. That means that accessing the pos-th element of the list list will return object after this assignment.

    gap> l := [ 1, 2, 3 ];;

gap> l[1] := 3;; l; # assign a new object

    [ 3, 2, 3 ]
    gap> l[2] := [ 4, 5, 6 ];;  l;  # object may be of any type
    [ 3, [ 4, 5, 6 ], 3 ]

gap> l[ l[1] ] := 10;; l; # index may be an expression

    [ 3, [ 4, 5, 6 ], 10 ]

If the index pos is larger than the length of the list list (see 25.4), the list is automatically enlarged to make room for the new element. Note that it is possible to generate lists with holes that way.

    gap> l[4] := "another entry";;  l; # list is enlarged
    [ 3, [ 4, 5, 6 ], 10, "another entry" ]
    gap> l[ 10 ] := 1;;  l;  # now list has a hole
    [ 3, [ 4, 5, 6 ], 10, "another entry",,,,,, 1 ]

The function Add (see 25.6) should be used if you want to add an element to the end of the list.

Note that assigning to a list changes the list. The ability to change an object is only available for lists and records (see 25.8).

If list does not evaluate to a list, pos does not evaluate to a positive integer or object is a call to a function which does not return a value, for example Print (see 3.14), an error is signalled As usual you can leave the break loop (see 3.2) with quit;. On the other hand you can continue the assignment by returning a list, an index or an object using return expr;.

list{ poss } := objects;

The list assignment assigns the object objects[1], which can be of any type, to the list list at the position poss[1], the object objects[2] to list[poss[2]], and so on. poss must be a dense list of positive integers, it need, however, not be sorted and may contain duplicate elements. objects must be a dense list and must have the same length as poss.

    gap> l := [ 2, 3, 5, 7, 11, 13, 17, 19 ];;
    gap> l{[1..4]} := [10..13];;  l;
    [ 10, 11, 12, 13, 11, 13, 17, 19 ]
    gap> l{[1,7,1,10]} := [ 1, 2, 3, 4 ];; l;
    [ 3, 11, 12, 13, 11, 13, 2, 19,, 4 ]

It is possible to nest such sublist assignments, as can be seen in the following example.

    gap> m := [ [1,2,3], [4,5,6], [7,8,9], [10,11,12] ];;
    gap> m{[1,2,3]}{[3,2]} := [ [11,12], [13,14], [15,16] ];;  m;
    [ [ 1, 12, 11 ], [ 4, 14, 13 ], [ 7, 16, 15 ], [ 10, 11, 12 ] ]

The exact behaviour is defined in the same way as for list extractions (see 25.3). Namely with each selector [pos] or {poss} we associate a level that is defined as the number of selectors of the form {poss} to its left in the same expression. For example

        l[pos1]{poss2}{poss3}[pos4]{poss5}[pos6]
    level   0      0      1     1      1     2

Then a list assignment list[pos] := vals; of level level is computed as ListAssignment( list, pos, vals, level ), where ListAssignment is defined as follows

    ListAssignment := function ( list, pos, vals, level )
        local  i;
        if level = 0  then
            list[pos] := vals;
        else
            for i  in [1..Length(list)]  do
                ListAssignment( list[i], pos, vals[i], level-1 );
            od;
        fi;
    end;

and a list assignment list{poss} := vals of level level is computed as ListAssignments( list, poss, vals, level ), where ListAssignments is defined as follows

    ListAssignments := function ( list, poss, vals, level )
        local  i;
        if level = 0  then
            list{poss} := vals;
        else
            for i  in [1..Length(list)]  do
                ListAssignments( list[i], poss, vals[i], level-1 );
            od;
        fi;
    end;

25.6 Add

Add( list, elm )

Add adds the element elm to the end of the list list, i.e., it is equivalent to the assignment list[ Length(list) + 1 ] := elm. The list is automatically enlarged to make room for the new element. Add returns nothing, it is called only for its side effect.

Note that adding to a list changes the list. The ability to change an object is only available for lists and records (see 25.8).

To add more than one element to a list use Append (see 25.7).

    gap> l := [ 2, 3, 5 ];;  Add( l, 7 );  l;
    [ 2, 3, 5, 7 ]

25.7 Append

Append( list1 , list2 )

Append adds (see 25.6) the elements of the list list2 to the end of the list list1 . list2 may contain holes, in which case the corresponding entries in list1 will be left unbound. Append returns nothing, it is called only for its side effect.

    gap> l := [ 2, 3, 5 ];; Append( l, [ 7, 11, 13 ] );  l;
    [ 2, 3, 5, 7, 11, 13 ]
    gap> Append( l, [ 17,, 23 ] ); l;
    [ 2, 3, 5, 7, 11, 13, 17,, 23 ]

Note that appending to a list changes the list. The ability to change an object is only available for lists and records (see 25.8).

Note that Append changes the first argument, while Concatenation (see 25.17) creates a new list and leaves its arguments unchanged. As usual the name of the function that work destructively is a verb, but the name of the function that creates a new object is a substantive.

25.8 Identical Lists

With the list assignment (see 25.5, 25.6, 25.7) it is possible to change a list. The ability to change an object is only available for lists and records. This section describes the semantic consequences of this fact.

You may think that in the following example the second assignment changes the integer, and that therefore the above sentence, which claimed that only lists and records can be changed is wrong

    i := 3;
    i :=  i + 1;

But in this example not the integer 3 is changed by adding one to it. Instead the variable i is changed by assigning the value of i+1, which happens to be 4, to i. The same thing happens in the following example

    l := [ 1,  2 ];
    l := [ 1, 2,  3 ];

The second assignment does not change the first list, instead it assigns a new list to the variable l. On the other hand, in the following example the list is changed by the second assignment.

    l := [ 1,  2 ];
    l[3] := 3;

To understand the difference first think of a variable as a name for an object. The important point is that a list can have several names at the same time. An assignment var := list; means in this interpretation that var is a name for the object list. At the end of the following example l2 still has the value [ 1, 2 ] as this list has not been changed and nothing else has been assigned to it.

    l1 := [ 1, 2 ];
    l2 := l1;
    l1 := [ 1, 2, 3 ];

But after the following example the list for which l2 is a name has been changed and thus the value of l2 is now [ 1, 2, 3 ].

    l1 := [ 1, 2 ];
    l2 := l1;
    l1[3] := 3;

We shall say that two lists are identical if changing one of them by a list assignment also changes the other one. This is slightly incorrect, because if two lists are identical, there are actually only two names for one list. However, the correct usage would be very awkward and would only add to the confusion. Note that two identical lists must be equal, because there is only one list with two different names. Thus identity is an equivalence relation that is a refinement of equality.

Let us now consider under which circumstances two lists are identical.

If you enter a list literal than the list denoted by this literal is a new list that is not identical to any other list. Thus in the following example l1 and l2 are not identical, though they are equal of course.

    l1 := [ 1, 2 ];
    l2 := [ 1, 2 ];

Also in the following example, no lists in the list l are identical.

    l := [];
    for i  in [1..10]  do l[i] := [ 1, 2 ];  od;

If you assign a list to a variable no new list is created. Thus the list value of the variable on the left hand side and the list on the right hand side of the assignment are identical. So in the following example l1 and l2 are identical lists.

    l1 := [ 1, 2 ];
    l2 := l1;

If you pass a list as argument, the old list and the argument of the function are identical. Also if you return a list from a function, the old list and the value of the function call are identical. So in the following example l1 and l2 are identical list

    l1 := [ 1, 2 ];
    f := function ( l )  return l;  end;
    l2 := f( l1 );

The functions Copy and ShallowCopy (see 39.11 and 39.12) accept a list and return a new list that is equal to the old list but that is not identical to the old list. The difference between Copy and ShallowCopy is that in the case of ShallowCopy the corresponding elements of the new and the old lists will be identical, whereas in the case of Copy they will only be equal. So in the following example l1 and l2 are not identical lists.

    l1 := [ 1, 2 ];
    l2 := Copy( l1 );

If you change a list it keeps its identity. Thus if two lists are identical and you change one of them, you also change the other, and they are still identical afterwards. On the other hand, two lists that are not identical will never become identical if you change one of them. So in the following example both l1 and l2 are changed, and are still identical.

    l1 := [ 1, 2 ];
    l2 := l1;
    l1[1] := 2;

25.9 IsIdentical

IsIdentical( l, r )

IsIdentical returns true if the objects l and r are identical. Unchangeable objects are considered identical if the are equal. Changeable objects, i.e., lists and records, are identical if changing one of them by an assignment also changes the other one, as described in 25.8.

    gap> IsIdentical( 1, 1 );
    true
    gap> IsIdentical( 1, () );
    false
    gap> l := [ 'h', 'a', 'l', 'l', 'o' ];;
    gap> l = "hallo";
    true
    gap> IsIdentical( l, "hallo" );
    false

25.10 Enlarging Lists

The previous section (see 25.5) told you (among other things), that it is possible to assign beyond the logical end of a list, automatically enlarging the list. This section tells you how this is done.

It would be extremly wasteful to make all lists large enough so that there is room for all assignments, because some lists may have more than 100000 elements, while most lists have less than 10 elements.

On the other hand suppose every assignment beyond the end of a list would be done by allocating new space for the list and copying all entries to the new space. Then creating a list of 1000 elements by assigning them in order, would take half a million copy operations and also create a lot of garbage that the garbage collector would have to reclaim.

So the following strategy is used. If a list is created it is created with exactly the correct size. If a list is enlarged, because of an assignment beyond the end of the list, it is enlarged by at least length/8 + 4 entries. Therefore the next assignments beyond the end of the list do not need to enlarge the list. For example creating a list of 1000 elements by assigning them in order, would now take only 32 enlargements.

The result of this is of course that the physical length, which is also called the size, of a list may be different from the logical length, which is usually called simply the length of the list. Aside from the implications for the performance you need not be aware of the physical length. In fact all you can ever observe, for example by calling Length is the logical length.

Suppose that Length would have to take the physical length and then test how many entries at the end of a list are unassigned, to compute the logical length of the list. That would take too much time. In order to make Length, and other functions that need to know the logical length, more efficient, the length of a list is stored along with the list.

A note aside. In the previous version 2.4 of GAP a list was indeed enlarged every time an assignment beyond the end of the list was performed. To deal with the above inefficiency the following hacks where used. Instead of creating lists in order they were usually created in reverse order. In situations where this was not possible a dummy assignment to the last position was performed, for example

    l := [];
    l[1000] := "dummy";
    l[1] := first_value();
    for i  from 2  to 1000  do l[i] := next_value(l[i-1]);  od;

25.11 Comparisons of Lists

list1 = list2 list1 <> list2

The equality operator = evaluates to true if the two lists list1 and list2 are equal and false otherwise. The inequality operator <> evaluates to true if the two lists are not equal and false otherwise. Two lists list1 and list2 are equal if and only if for every index i, either both entries list1 [i] and list2 [i] are unbound, or both are bound and are equal, i.e., list1 [i] = list2 [i] is true.

    gap> [ 1, 2, 3 ] = [ 1, 2, 3 ];
    true
    gap> [ , 2, 3 ] = [ 1, 2, ];
    false
    gap> [ 1, 2, 3 ] = [ 3, 2, 1 ];
    false

list1 < list2 , list1 <= list2 list1 > list2 , list1 >= list2

The operators <, <=, > and >= evaluate to true if the list list1 is less than, less than or equal to, greater than, or greater than or equal to the list list2 and to false otherwise. Lists are ordered lexicographically, with unbound entries comparing very small. That means the following. Let i be the smallest positive integer i, such that neither both entries list1 [i] and list2 [i] are unbound, nor both are bound and equal. Then list1 is less than list2 if either list1 [i] is unbound (and list2 [i] is not) or both are bound and list1 [i] < list2 [i]

is true.
    gap> [ 1, 2, 3, 4 ] < [ 1, 2, 4, 8 ];
    true  # list1 [3] < list2 [3]
    gap> [ 1, 2, 3 ] < [ 1, 2, 3, 4 ];

true # list1 [4] is unbound and therefore very small

    gap> [ 1, , 3, 4 ] < [ 1, 2, 3 ];

true # list1 [2] is unbound and therefore very small

You can also compare objects of other types, for example integers or permutations with lists. Of course those objects are never equal to a list. Records (see 39) are greater than lists, objects of every other type are smaller than lists.

    gap> 123 < [ 1, 2, 3 ];
    true
    gap> [ 1, 2, 3 ] < rec( a := 123 );
    true

25.12 Operations for Lists

list * obj obj * list

The operator * evaluates to the product of list list by an object obj . The product is a new list that at each position contains the product of the corresponding element of list by obj . list may contain holes, in which case the result will contain holes at the same positions.

The elements of list and obj must be objects of the following types; integers (see 10), rationals (see 12), cyclotomics (see 13), elements of a finite field (see 17), permutations (see 19), matrices (see 32), words in abstract generators (see 21), or words in solvable groups (see 23).

    gap> [ 1, 2, 3 ] * 2;
    [ 2, 4, 6 ]
    gap> 2 * [ 2, 3,, 5,, 7 ];
    [ 4, 6,, 10,, 14 ]
    gap> [ (), (2,3), (1,2), (1,2,3), (1,3,2), (1,3) ] * (1,4);
    [ (1,4), (1,4)(2,3), (1,2,4), (1,2,3,4), (1,3,2,4), (1,3,4) ]

Many more operators are available for vectors and matrices, which are also represented by lists (see 30.1, 32.1).

25.13 In

elm in list The in operator evaluates to true if the object elm is an element of the list list and to false otherwise. elm is an element of list if there is a positive integer index such that list[index]=elm is true. elm may be an object of an arbitrary type and list may be a list containing elements of any type. It is much faster to test for membership for sets, because for sets, which are always sorted (see 26), in can use a binary search, instead of the linear search used for ordinary lists. So if you have a list for which you want to perform a large number of membership tests you may consider converting it to a set with the function Set (see 26.2).

    gap> 1 in [ 2, 2, 1, 3 ];
    true
    gap> 1 in [ 4, -1, 0, 3 ];
    false
    gap> s := Set([2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32]);;
    gap> 17 in s;

false # uses binary search and only 4 comparisons

    gap> 1 in [ "This", "is", "a", "list", "of", "strings" ];
    false
    gap> [1,2] in [ [0,6], [0,4], [1,3], [1,5], [1,2], [3,4] ];
    true

Position (see 25.14) and PositionSorted (see 25.15) allow you to find the position of an element in a list.

25.14 Position

Position( list, elm )

Position returns the position of the element elm, which may be an object of any type, in the list list. If the element is not in the list the result is false. If the element appears several times, the first position is returned. It is much faster to search for an element in a set, because for sets, which are always sorted (see 26), Position can use a binary search, instead of the linear search used for ordinary lists. So if you have a list for which you want to perform a large number of searches you may consider converting it to a set with the function Set (see 26.2).

gap> Position( [ 2, 2, 1, 3 ],  1 );
3
gap> Position( [ 2, 1, 1, 3 ], 1 );
2
gap> Position( [ 4, -1, 0, 3 ],  1 );
false
gap> s := Set([2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32]);;
gap> Position( s,  17 );

false # uses binary search and only 4 comparisons

gap> Position( [ "This", "is", "a", "list", "of", "strings" ],  1 );
false
gap> Position( [ [0,6], [0,4], [1,3], [1,5], [1,2], [3,4] ],  [1,2] );
5

The in operator (see 25.13) can be used if you are only interested to know whether the element is in the list or not. PositionSorted (see 25.15) can be used if the list is sorted. PositionProperty (see 25.16) allows you to find the position of an element that satisfies a certain property in a list.

25.15 PositionSorted

PositionSorted( list, elm )
PositionSorted( list, elm, func )

In the first form PositionSorted returns the position of the element elm, which may be an object of any type, with respect to the sorted list list. In the second form PositionSorted returns the position of the element elm, which may be an object of any type with respect to the list list, which must be sorted with respect to func. func must be a function of two arguments that returns true if the first argument is less than the second argument and false otherwise. PositionSorted returns pos such that list[pos-1] <elm and elm <= list[pos]. That means, if elm appears once in list, its position is returned. If elm appears several times in list, the position of the first occurrence is returned. If elm is not an element of list, the index where elm must be inserted to keep the list sorted is returned.

gap> PositionSorted( [1,4,5,5,6,7], 0 );
1
gap> PositionSorted( [1,4,5,5,6,7], 2 );
2
gap> PositionSorted( [1,4,5,5,6,7], 4 );
2
gap> PositionSorted( [1,4,5,5,6,7], 5 );
3
gap> PositionSorted( [1,4,5,5,6,7], 8 );
7

Position (see 25.14) is another function that returns the position of an element in a list. Position accepts unsorted lists, uses linear instead of binary search and returns false if elm is not in list.

25.16 PositionProperty

PositionProperty( list, func )

PositionProperty returns the position of the first element in the list list for which the unary function func returns true. list must not contain holes. If func returns false for all elements of list false is returned. func must return true or false for every element of list, otherwise an error is signalled.

    gap> PositionProperty( [10^7..10^8], IsPrime );
    20
    gap> PositionProperty( [10^5..10^6],
    >                      n -> not IsPrime(n) and IsPrimePowerInt(n) );
    490

First (see 25.27) allows you to extract the first element of a list that satisfies a certain property.

25.17 Concatenation

Concatenation( list1 , list2 ..  )
Concatenation( list )

In the first form Concatenation returns the concatenation of the lists list1 , list2 , etc. The concatenation is the list that begins with the elements of list1 , followed by the elements of list2 and so on. Each list may also contain holes, in which case the concatenation also contains holes at the corresponding positions.

    gap> Concatenation( [ 1, 2, 3 ], [ 4, 5 ] );
    [ 1, 2, 3, 4, 5 ]
    gap> Concatenation( [2,3,,5,,7], [11,,13,,,,17,,19] );
    [ 2, 3,, 5,, 7, 11,, 13,,,, 17,, 19 ]

In the second form list must be a list of lists list1 , list2 , etc, and Concatenation returns the concatenation of those lists.

    gap> Concatenation( [ [1,2,3], [2,3,4], [3,4,5] ] );
    [ 1, 2, 3, 2, 3, 4, 3, 4, 5 ]

The result is a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the argument lists (see 25.8).

Note that Concatenation creates a new list and leaves it arguments unchanged, while Append (see 25.7) changes its first argument. As usual the name of the function that works destructively is a verb, but the name of the function that creates a new object is a substantive.

Set(Concatenation(set1 ,set2 ..)) (see 26.2) is a way to compute the union of sets, however, Union (see 4.13) is more efficient.

25.18 Flat

Flat( list )

Flat returns the list of all elements that are contained in the list list or its sublists. That is, Flat first makes a new empty list new. Then it loops over the elements elm of list. If elm is not a list it is added to new, otherwise Flat appends Flat( elm ) to new.

    gap> Flat( [ 1, [ 2, 3 ], [ [ 1, 2 ], 3 ] ] );
    [ 1, 2, 3, 1, 2, 3 ]
    gap> Flat( [ ] );
    [  ]

25.19 Reversed

Reversed( list )

Reversed returns a new list that contains the elements of the list list, which must not contain holes, in reverse order. The argument list is unchanged.

    gap> Reversed( [ 1, 4, 5, 5, 6, 7 ] );
    [ 7, 6, 5, 5, 4, 1 ]

The result is a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the argument list (see 25.8).

25.20 Sublist

Sublist( list, inds )

Sublist returns a new list in which the i-th element is the element list[ inds[ i ] ], of the list list. inds must be a list of positive integers without holes, it need, however, not be sorted and may contains duplicate elements. If list[ inds[ i ] ] is unbound for an i, an error is signalled.

    gap> Sublist( [ 2, 3, 5, 7, 11, 13, 17, 19 ], [4..6] );
    [ 7, 11, 13 ]
    gap> Sublist( [ 2, 3, 5, 7, 11, 13, 17, 19 ], [1,7,1,8] );
    [ 2, 17, 2, 19 ]

The result is a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the argument list (see 25.8).

Filtered (see 25.24) allows you to extract elements from a list according to a predicate.

Sublist has been made obsolete by the introduction of the construct list{ inds } (see 25.3).

25.21 Cartesian

Cartesian( list1 , list2 ..  )
Cartesian( list )

In the first form Cartesian returns the cartesian product of the lists list1 , list2 , etc.

In the second form list must be a list of lists list1 , list2 , etc., and Cartesian returns the cartesian product of those lists.

The cartesian product is a list cart of lists tup, such that the first element of tup is an element of list1 , the second element of tup is an element of list2 , and so on. The total number of elements in cart is the product of the lengths of the argument lists. In particular cart is empty if and only if at least one of the argument lists is empty. Also cart contains duplicates if and only if no argument list is empty and at least one contains duplicates.

The last index runs fastest. That means that the first element tup1 of cart contains the first element from list1 , from list2 and so on. The second element tup2 of cart contains the first element from list1 , the first from list2 , an so on, but the last element of tup2 is the second element of the last argument list. This implies that cart is a set if and only if all argument lists are sets.

    gap> Cartesian( [1,2], [3,4], [5,6] );
    [ [ 1, 3, 5 ], [ 1, 3, 6 ], [ 1, 4, 5 ], [ 1, 4, 6 ], [ 2, 3, 5 ],
      [ 2, 3, 6 ], [ 2, 4, 5 ], [ 2, 4, 6 ] ]
    gap> Cartesian( [1,2,2], [1,1,2] );
    [ [ 1, 1 ], [ 1, 1 ], [ 1, 2 ], [ 2, 1 ], [ 2, 1 ], [ 2, 2 ],
      [ 2, 1 ], [ 2, 1 ], [ 2, 2 ] ]

The function Tuples (see 40.9) computes the k-fold cartesian product of a list.

25.22 Number

Number( list )
Number( list, func )

In the first form Number returns the number of bound entries in the list list.

For lists that contain no holes Number, Length (see 25.4), and Size (see 4.10) return the same value. For lists with holes Number returns the number of bound entries, Length returns the largest index of a bound entry, and Size signals an error.

Number returns the number of elements of the list list for which the unary function func returns true. If an element for which func returns true appears several times in list it will also be counted several times. func must return either true or false for every element of list, otherwise an error is signalled.

    gap> Number( [ 2, 3, 5, 7 ] );
    4
    gap> Number( [, 2, 3,, 5,, 7,,,, 11 ] );
    5
    gap> Number( [1..20], IsPrime );
    8
    gap> Number( [ 1, 3, 4, -4, 4, 7, 10, 6 ], IsPrimePowerInt );
    4
    gap> Number( [ 1, 3, 4, -4, 4, 7, 10, 6 ],
    >            n -> IsPrimePowerInt(n) and n mod 2 <> 0 );
    2

Filtered (see 25.24) allows you to extract the elements of a list that have a certain property.

25.23 Collected

Collected( list )

Collected returns a new list new that contains for each different element elm of list a list of two elements, the first element is elm itself, and the second element is the number of times elm appears in list. The order of those pairs in new corresponds to the ordering of the elements elm, so that the result is sorted.

    gap> Factors( Factorial( 10 ) );
    [ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 5, 5, 7 ]
    gap> Collected( last );
    [ [ 2, 8 ], [ 3, 4 ], [ 5, 2 ], [ 7, 1 ] ]
    gap> Collected( last );
    [ [ [ 2, 8 ], 1 ], [ [ 3, 4 ], 1 ], [ [ 5, 2 ], 1 ], [ [ 7, 1 ], 1 ] ]

25.24 Filtered

Filtered( list, func )

Filtered returns a new list that contains those elements of the list list for which the unary function func returns true. The order of the elements in the result is the same as the order of the corresponding elements of list. If an element, for which func returns true appears several times in list it will also appear the same number of times in the result. list may contain holes, they are ignored by Filtered. func must return either true or false for every element of list, otherwise an error is signalled.

    gap> Filtered( [1..20], IsPrime );
    [ 2, 3, 5, 7, 11, 13, 17, 19 ]
    gap> Filtered( [ 1, 3, 4, -4, 4, 7, 10, 6 ], IsPrimePowerInt );
    [ 3, 4, 4, 7 ]
    gap> Filtered( [ 1, 3, 4, -4, 4, 7, 10, 6 ],
    >              n -> IsPrimePowerInt(n) and n mod 2 <> 0 );
    [ 3, 7 ]

The result is a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of the argument list (see 25.8).

Sublist (see 25.20) allows you to extract elements of a list according to indices given in another list.

25.25 ForAll

ForAll( list, func )

ForAll returns true if the unary function func returns true for all elements of the list list and false otherwise. list may contain holes. func must return either true or false for every element of list, otherwise an error is signalled.

    gap> ForAll( [1..20], IsPrime );
    false
    gap> ForAll( [2,3,4,5,8,9], IsPrimePowerInt );
    true
    gap> ForAll( [2..14], n -> IsPrimePowerInt(n) or n mod 2 = 0 );
    true

ForAny (see 25.26) allows you to test if any element of a list satisfies a certain property.

25.26 ForAny

ForAny( list, func )

ForAny returns true if the unary function func returns true for at least one element of the list list and false otherwise. list may contain holes. func must return either true or false for every element of list, otherwise ForAny signals an error.

    gap> ForAny( [1..20], IsPrime );
    true
    gap> ForAny( [2,3,4,5,8,9], IsPrimePowerInt );
    true
    gap> ForAny( [2..14],
    >    n -> IsPrimePowerInt(n) and n mod 5 = 0 and not IsPrime(n) );
    false

ForAll (see 25.25) allows you to test if all elements of a list satisfies a certain propertie.

25.27 First

First( list, func )

First returns the first element of the list list for which the unary function func returns true. list may contain holes. func must return either true or false for every element of list, otherwise an error is signalled. If func returns false for every element of list an error is signalled.

    gap> First( [10^7..10^8], IsPrime );
    10000019
    gap> First( [10^5..10^6],
    >           n -> not IsPrime(n) and IsPrimePowerInt(n) );
    100489

PositionProperty (see 25.16) allows you to find the position of the first element in a list that satisfies a certain property.

25.28 Sort

Sort( list )

Sort( list, func )

Sort sorts the list list in increasing order, using shellsort. In the first form Sort uses the operator < to compare the elements. In the second form Sort uses the function func to compare elements. This function must be a function taking two arguments that returns true if the first is strictly smaller than the second and false otherwise.

Sort does not return anything, since it changes the argument list. Use ShallowCopy (see 39.12) if you want to keep list. Use Reversed (see 25.19) if you want to get a new list sorted in decreasing order.

It is possible to sort lists that contain multiple elements which compare equal. It is not guaranteed that those elements keep their relative order, i.e., Sort is not stable.

    gap> list := [ 5, 4, 6, 1, 7, 5 ];;  Sort( list );  list;

[ 1, 4, 5, 5, 6, 7 ]
gap> list := [ [0,6], [1,2], [1,3], [1,5], [0,4], [3,4] ];;
gap> Sort( list, function(v,w) return v*v < w*w; end ); list;
[ [ 1, 2 ], [ 1, 3 ], [ 0, 4 ], [ 3, 4 ], [ 1, 5 ], [ 0, 6 ] ]

# sorted according to the Euclidian distance from [0,0]

gap> list := [ [0,6], [1,3], [3,4], [1,5], [1,2], [0,4], ];;
gap> Sort( list, function(v,w) return v[1] < w[1]; end ); list;
[ [ 0, 6 ], [ 0, 4 ], [ 1, 3 ], [ 1, 5 ], [ 1, 2 ], [ 3, 4 ] ]

# note the random order of the elements with equal first component

SortParallel (see 25.29) allows you to sort a list and apply the exchanges that are necessary to another list in parallel. Sortex (see 25.30) sorts a list and returns the sorting permutation.

25.29 SortParallel

SortParallel( list1 , list2 )
SortParallel( list1 , list2 , func )

SortParallel sorts the list list1 in increasing order just as Sort (see 25.28) does. In parallel it applies the same exchanges that are necessary to sort list1 to the list list2 , which must of course have at least as many elements as list1 does.

gap> list1 := [ 5, 4, 6, 1, 7, 5 ];;
gap> list2 := [ 2, 3, 5, 7, 8, 9 ];;
gap> SortParallel( list1, list2 );
gap> list1;
[ 1, 4, 5, 5, 6, 7 ]
gap> list2;
[ 7, 3, 2, 9, 5, 8 ]  # [ 7, 3, 9, 2, 5, 8 ] is also possible

Sortex (see 25.30) sorts a list and returns the sorting permutation.

25.30 Sortex

Sortex( list )

Sortex sorts the list list and returns the permutation that must be applied to list to obtain the sorted list.

gap> list1 := [ 5, 4, 6, 1, 7, 5 ];;
gap> list2 := Copy( list1 );;
gap> perm := Sortex( list1 );
(1,3,5,6,4)
gap> list1;
[ 1, 4, 5, 5, 6, 7 ]
gap> Permuted( list2, perm );
[ 1, 4, 5, 5, 6, 7 ]

Permuted (see 25.31) allows you to rearrange a list according to a given permutation.

25.31 Permuted

Permuted( list, perm )

Permuted returns a new list new that contains the elements of the list list permuted according to the permutation perm. That is new[i^perm] = list[i].

    gap> Permuted( [ 5, 4, 6, 1, 7, 5 ], (1,3,5,6,4) );
    [ 1, 4, 5, 5, 6, 7 ]

Sortex (see 25.30) allows you to compute the permutation that must be applied to a list to get the sorted list.

25.32 Product

Product( list )
Product( list, func )

In the first form Product returns the product of the elements of the list list, which must have no holes. If list is empty, the integer 1 is returned.

In the second form Product applies the function func to each element of the list list, which must have no holes, and multiplies the results. If the list is empty, the integer 1 is returned.

    gap> Product( [ 2, 3, 5, 7, 11, 13, 17, 19 ] );
    9699690
    gap> Product( [1..10], x->x^2 );
    13168189440000
    gap> Product( [ (1,2), (1,3), (1,4), (2,3), (2,4), (3,4) ] );
    (1,4)(2,3)

Sum (see 25.33) computes the sum of the elements of a list.

25.33 Sum

Sum( list )

Sum( list, func )

In the first form Sum returns the sum of the elements of the list list, which must have no holes. If list is empty 0 is returned.

In the second form Sum applies the function func to each element of the list list, which must have no holes, and sums the results. If the list is empty 0 is returned.

    gap> Sum( [ 2, 3, 5, 7, 11, 13, 17, 19 ] );
    77
    gap> Sum( [1..10], x->x^2 );
    385
    gap> Sum( [ [1,2], [3,4], [5,6] ] );
    [ 9, 12 ]

Product (see 25.32) computes the product of the elements of a list.

25.34 Maximum

Maximum( obj1 , obj2 ..  )
Maximum( list )

Maximum returns the maximum of its arguments, i.e., that argument obj_{i} for which obj_{k} <= obj_{i} for all k. In its second form Maximum takes a list list and returns the maximum of the elements of this list.

Typically the arguments or elements of the list respectively will be integers, but actually they can be objects of an arbitrary type. This works because any two objects can be compared using the < operator.

    gap> Maximum( -123, 700, 123, 0, -1000 );
    700
    gap> Maximum( [ -123, 700, 123, 0, -1000 ] );
    700
    gap> Maximum( [ 1, 2 ], [ 0, 15 ], [ 1, 5 ], [ 2, -11 ] );

[ 2, -11 ] # lists are compared elementwise

25.35 Minimum

Minimum( obj1 , obj2 ..  )
Minimum( list )

Minimum returns the minimum of its arguments, i.e., that argument obj_{i} for which obj_{i} <= obj_{k} for all k. In its second form Minimum takes a list list and returns the minimum of the elements of this list.

Typically the arguments or elements of the list respectively will be integers, but actually they can be objects of an arbitrary type. This works because any two objects can be compared using the < operator.

    gap> Minimum( -123, 700, 123, 0, -1000 );
    -1000
    gap> Minimum( [ -123, 700, 123, 0, -1000 ] );
    -1000
    gap> Minimum( [ 1, 2 ], [ 0, 15 ], [ 1, 5 ], [ 2, -11 ] );

[ 0, 15 ] # lists are compared elementwise

25.36 Iterated

Iterated( list, f )

Iterated returns the result of the iterated application of the function f , which must take two arguments, to the elements of list. More precisely Iterated returns the result of the following application, f (..f ( f ( list[1], list[2] ), list[3] ),..,list[n] ).

    gap> Iterated( [ 126, 66, 105 ], Gcd );
    3

25.37 RandomList

RandomList( list )

RandomList returns a random element of the list list. The results are equally distributed, i.e., all elements are equally likely to be selected.

    gap> RandomList( [1..200] );

192
gap> RandomList( [1..200] );
152
gap> RandomList( [ [ 1, 2 ], 3, [ 4, 5 ], 6 ] );
[ 4, 5 ]
RandomSeed( n )

RandomSeed seeds the pseudo random number generator RandomList. Thus to reproduce a computation exactly you can call RandomSeed each time before you start the computation. When GAP is started the pseudo random number generator is seeded with 1.

gap> RandomSeed(1);  RandomList([1..100]);  RandomList([1..100]);
96
76
gap> RandomSeed(1);  RandomList([1..100]);  RandomList([1..100]);
96
76

RandomList is called by all random functions for domains (see 4.16).

534 CHAPTER 25. LISTS

Sets

A very important mathematical concept, maybe the most important of all, are sets. Mathematically a set is an abstract object such that each object is either an element of the set or it is not. So a set is a collection like a list, and in fact GAP uses lists to represent sets. Note that this of course implies that GAP only deals with finite sets.

Unlike a list a set must not contain an element several times. It simply makes no sense to say that an object is twice an element of a set, because an object is either an element of a set, or it is not. Therefore the list that is used to represent a set has no duplicates, that is, no two elements of such a list are equal.

Also unlike a list a set does not impose any ordering on the elements. Again it simply makes no sense to say that an object is the first or second etc. element of a set, because, again, an object is either an element of a set, or it is not. Since ordering is not defined for a set we can put the elements in any order into the list used to represent the set. We put the elements sorted into the list, because this ordering is very practical. For example if we convert a list into a set we have to remove duplicates, which is very easy to do after we have sorted the list, since then equal elements will be next to each other.

In short sets are represented by sorted lists without holes and duplicates in GAP. Such a list is in this document called a proper set. Note that we guarantee this representation, so you may make use of the fact that a set is represented by a sorted list in your functions.

In some contexts (for example see 40), we also want to talk about multisets. A multiset is like a set, except that an element may appear several times in a multiset. Such multisets are represented by sorted lists with holes that may have duplicates.

The first section in this chapter describes the functions to test if an object is a set and to convert objects to sets (see 26.1 and 26.2).

The next section describes the function that tests if two sets are equal (see 26.3).

The next sections describe the destructive functions that compute the standard set operations for sets (see 26.4, 26.5, 26.6, 26.7, and 26.8).

The last section tells you more about sets and their internal representation (see 26.10).

All set theoretic functions, especially Intersection and Union, also accept sets as arguments. Thus all functions described in chapter 4 are applicable to sets (see 26.9).

Since sets are just a special case of lists, all the operations and functions for lists, especially the membership test (see 25.13), can be used for sets just as well (see 25).

26.1 IsSet

IsSet( obj )

IsSet returns true if the object obj is a set and false otherwise. An object is a set if it is a sorted lists without holes or duplicates. Will cause an error if evaluation of obj is an unbound variable.

    gap> IsSet( [] );
    true
    gap> IsSet( [ 2, 3, 5, 7, 11 ] );
    true
    gap> IsSet( [, 2, 3,, 5,, 7,,,, 11 ] );

false # this list contains holes

    gap> IsSet( [ 11, 7, 5, 3, 2 ] );

false # this list is not sorted

    gap> IsSet( [ 2, 2, 3, 5, 5, 7, 11, 11 ] );

false # this list contains duplicates

    gap> IsSet( 235711 );

false # this argument is not even a list

26.2 Set

Set( list )

Set returns a new proper set, which is represented as a sorted list without holes or duplicates, containing the elements of the list list.

Set returns a new list even if the list list is already a proper set, in this case it is equivalent to ShallowCopy (see 39.12). Thus the result is a new list that is not identical to any other list. The elements of the result are however identical to elements of list. If list contains equal elements, it is not specified to which of those the element of the result is identical (see 25.8).

    gap> Set( [3,2,11,7,2,,5] );
    [ 2, 3, 5, 7, 11 ]
    gap> Set( [] );
    [  ]

26.3 IsEqualSet

IsEqualSet( list1 , list2 )

IsEqualSet returns true if the two lists list1 and list2 are equal when viewed as sets, and false otherwise. list1 and list2 are equal if every element of list1 is also an element of list2 and if every element of list2 is also an element of list1 .

If both lists are proper sets then they are of course equal if and only if they are also equal as lists. Thus IsEqualSet( list1 , list2 ) is equivalent to Set( list1 ) = Set( list2 ) (see 26.2), but the former is more efficient.

gap> IsEqualSet( [2,3,5,7,11], [11,7,5,3,2] );
true
gap> IsEqualSet( [2,3,5,7,11], [2,3,5,7,11,13] );
false

26.4 AddSet

AddSet( set, elm )

AddSet adds elm, which may be an elment of an arbitrary type, to the set set, which must be a proper set, otherwise an error will be signalled. If elm is already an element of the set set, the set is not changed. Otherwise elm is inserted at the correct position such that set is again a set afterwards.

gap> s := [2,3,7,11];;
gap> AddSet( s, 5 );  s;
[ 2, 3, 5, 7, 11 ]
gap> AddSet( s, 13 );  s;
[ 2, 3, 5, 7, 11, 13 ]
gap> AddSet( s, 3 );  s;
[ 2, 3, 5, 7, 11, 13 ]

RemoveSet (see 26.5) is the counterpart of AddSet.

26.5 RemoveSet

RemoveSet( set, elm )

RemoveSet removes the element elm, which may be an object of arbitrary type, from the set set, which must be a set, otherwise an error will be signalled. If elm is not an element of set nothing happens. If elm is an element it is removed and all the following elements in the list are moved one position forward.

gap> s := [ 2, 3, 4, 5, 6, 7 ];;
gap> RemoveSet( s, 6 );
gap> s;
[ 2, 3, 4, 5, 7 ]
gap> RemoveSet( s, 10 );
gap> s;
[ 2, 3, 4, 5, 7 ]

AddSet (see 26.4) is the counterpart of RemoveSet.

26.6 UniteSet

UniteSet( set1 , set2 )

UniteSet unites the set set1 with the set set2 . This is equivalent to adding all the elements in set2 to set1 (see 26.4). set1 must be a proper set, otherwise an error is signalled. set2 may also be list that is not a proper set, in which case UniteSet silently applies Set to it first (see 26.2). UniteSet returns nothing, it is only called to change set1 .

gap> set := [ 2, 3, 5, 7, 11 ];;

    gap> UniteSet( set, [ 4, 8, 9 ] );  set;
    [ 2, 3, 4, 5, 7, 8, 9, 11 ]
    gap> UniteSet( set, [ 16, 9, 25, 13, 16 ] );  set;
    [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 25 ]

The function UnionSet (see 26.9) is the nondestructive counterpart to the destructive pro-

cedure UniteSet.

26.7 IntersectSet

IntersectSet( set1 , set2 )

IntersectSet intersects the set set1 with the set set2 . This is equivalent to removing all the elements that are not in set2 from set1 (see 26.5). set1 must be a set, otherwise an error is signalled. set2 may be a list that is not a proper set, in which case IntersectSet silently applies Set to it first (see 26.2). IntersectSet returns nothing, it is only called to change set1 .

    gap> set := [ 2, 3, 4, 5, 7, 8, 9, 11, 13, 16 ];;
    gap> IntersectSet( set, [ 3, 5, 7, 9, 11, 13, 15, 17 ] );  set;
    [ 3, 5, 7, 9, 11, 13 ]
    gap> IntersectSet( set, [ 9, 4, 6, 8 ] );  set;
    [ 9 ]

The function IntersectionSet (see 26.9) is the nondestructive counterpart to the destructive procedure IntersectSet.

26.8 SubtractSet

SubtractSet( set1 , set2 )

SubtractSet subtracts the set set2 from the set set1 . This is equivalent to removing all the elements in set2 from set1 (see 26.5). set1 must be a proper set, otherwise an error is signalled. set2 may be a list that is not a proper set, in which case SubtractSet applies Set to it first (see 26.2). SubtractSet returns nothing, it is only called to change set1 .

    gap> set := [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];;
    gap> SubtractSet( set, [ 6, 10 ] );  set;
    [ 2, 3, 4, 5, 7, 8, 9, 11 ]
    gap> SubtractSet( set, [ 9, 4, 6, 8 ] );  set;
    [ 2, 3, 5, 7, 11 ]

The function Difference (see 4.14) is the nondestructive counterpart to destructive the

procedure SubtractSet.

26.9 Set Functions for Sets

As was already mentioned in the introduction to this chapter all domain functions also accept sets as arguments. Thus all functions described in the chapter 4 are applicable to sets. This section describes those functions where it might be helpful to know the implementation of those functions for sets.

IsSubset( set1 , set2 )

This is implemented by IsSubsetSet, which you can call directly to save a little bit of time. Either argument to IsSubsetSet may also be a list that is not a proper set, in which case IsSubset silently applies Set (see 26.2) to it first.

Union( set1 , set2 )

This is implemented by UnionSet, which you can call directly to save a little bit of time. Note that UnionSet only accepts two sets, unlike Union, which accepts several sets or a list of sets. The result of UnionSet is a new set, represented as a sorted list without holes or duplicates. Each argument to UnionSet may also be a list that is not a proper set, in which case UnionSet silently applies Set (see 26.2) to this argument. UnionSet is implemented in terms of its destructive counterpart UniteSet (see 26.6).

Intersection( set1 , set2 )

This is implemented by IntersectionSet, which you can call directly to save a little bit of time. Note that IntersectionSet only accepts two sets, unlike Intersection, which accepts several sets or a list of sets. The result of IntersectionSet is a new set, represented as a sorted list without holes or duplicates. Each argument to IntersectionSet may also be a list that is not a proper set, in which case IntersectionSet silently applies Set (see 26.2) to this argument. IntersectionSet is implemented in terms of its destructive counterpart

IntersectSet (see 26.7).

The result of IntersectionSet and UnionSet is always a new list, that is not identical to any other list. The elements of that list however are identical to the corresponding elements of set1 . If set1 is not a proper list it is not specified to which of a number of equal elements in set1 the element in the result is identical (see 25.8).

26.10 More about Sets

In the previous section we defined a proper set as a sorted list without holes or duplicates. This representation is not only nice to use, it is also a good internal representation supporting efficient algorithms. For example the in operator can use binary instead of a linear search since a set is sorted. For another example Union only has to merge the sets.

However, all those set functions also allow lists that are not proper sets, silently making a copy of it and converting this copy to a set. Suppose all the functions would have to test their arguments every time, comparing each element with its successor, to see if they are proper sets. This would chew up most of the performance advantage again. For example suppose in would have to run over the whole list, to see if it is a proper set, so it could use the binary search. That would be ridiculous.

To avoid this a list that is a proper set may, but need not, have an internal flag set that tells those functions that this list is indeed a proper set. Those functions do not have to check this argument then, and can use the more efficient algorithms. This section tells you when a proper set obtains this flag, so you can write your functions in such a way that you make best use of the algorithms.

The results of Set, Difference, Intersection and Union are known to be sets by construction, and thus have the flag set upon creation.

If an argument to IsSet, IsEqualSet, IsSubset, Set, Difference, Intersection or Union

is a proper set, that does not yet have the flag set, those functions will notice that and set the flag for this set. Note that in will use linear search if the right operand does not have the flag set, will therefore not detect if it is a proper set and will, unlike the functions above, never set the flag.

If you change a proper set, that does have this flag set, by assignment, Add or Append the set will generally lose it flag, even if the change is such that the resulting list is still a proper set. However if the set has more than 100 elements and the value assigned or added is not a list and not a record and the resulting list is still a proper set than it will keep the flag. Note that changing a list that is not a proper set will never set the flag, even if the resulting list is a proper set. Such a set will obtain the flag only if it is passed to a set function.

Suppose you have built a proper set in such a way that it does not have the flag set, and that you now want to perform lots of membership tests. Then you should call IsSet with that set as an argument. If it is indeed a proper set IsSet will set the flag, and the subsequent in operations will use the more efficient binary search. You can think of the call to IsSet as a hint to GAP that this list is a proper set.

There is no way you can set the flag for an ordinary list without going through the checking in IsSet. The internal functions depend so much on the fact that a list with this flag set is indeed sorted and without holes and duplicates that the risk would be too high to allow setting the flag without such a check.

Boolean Lists

This chapter describes boolean lists. A boolean list is a list that has no holes and contains only boolean values, i.e., true and false. In function names we call boolean lists blist for brevity.

Boolean lists can be used in various ways, but maybe the most important application is their use for the description of subsets of finite sets. Suppose set is a finite set, represented as a list. Then a subset sub of set is represented by a boolean list blist of the same length as set such that blist[i] is true if set[i] is in sub and false otherwise.

This package contains functions to switch between the representations of subsets of a finite set either as sets or as boolean lists (see 27.1, 27.2), to test if a list is a boolean list (see 27.3), and to count the number of true entries in a boolean list (see 27.4).

Next there are functions for the standard set operations for the subsets represented by boolean lists (see 27.5, 27.6, 27.7, and 27.8). There are also the corresponding destructive procedures that change their first argument (see 27.9, 27.10, and 27.11). Note that there is no function to add or delete a single element to a subset represented by a boolean list, because this can be achieved by assigning true or false to the corresponding position in the boolean list (see 25.5).

Since boolean lists are just a special case of lists, all the operations and functions for lists, can be used for boolean lists just as well (see 25). For example Position (see 25.14) can be used to find the true entries in a boolean list, allowing you to loop over the elements of the subset represented by the boolean list.

There is also a section about internal details (see 27.12).

27.1 BlistList

BlistList( list, sub )

BlistList returns a new boolean list that describes the list sub as a sublist of the list list, which must have no holes. That is BlistList returns a boolean list blist of the same length as list such that blist[i] is true if list[i] is in sub and false otherwise.

list need not be a proper set (see 26), even though in this case BlistList is most efficient. In particular list may contain duplicates. sub need not be a proper sublist of list, i.e., sub may contain elements that are not in list. Those elements of course have no influence on the result of BlistList.

gap> BlistList( [1..10], [2,3,5,7] );
[ false, true, true, false, true, false, true, false, false, false ]
gap> BlistList( [1,2,3,4,5,2,8,6,4,10], [4,8,9,16] );
[ false, false, false, true, false, false, true, false, true, false ]

ListBlist (see 27.2) is the inverse function to BlistList.

27.2 ListBlist

ListBlist( list, blist )

ListBlist returns the sublist sub of the list list, which must have no holes, represented by the boolean list blist, which must have the same length as list. sub contains the element list[i] if blist[i] is true and does not contain the element if blist[i] is false. The order of the elements in sub is the same as the order of the corresponding elements in list.

gap> ListBlist([1..8],[false,true,true,true,true,false,true,true]);
[ 2, 3, 4, 5, 7, 8 ]
gap> ListBlist( [1,2,3,4,5,2,8,6,4,10],
> [false,false,false,true,false,false,true,false,true,false] );
[ 4, 8, 4 ]

BlistList (see 27.1) is the inverse function to ListBlist.

27.3 IsBlist

IsBlist( obj )

IsBlist returns true if obj , which may be an object of arbitrary type, is a boolean list and false otherwise. A boolean list is a list that has no holes and contains only true and

false.
gap> IsBlist( [ true, true, false, false ] );
true
gap> IsBlist( [] );
true
gap> IsBlist( [false,,true] );

false # has holes

gap> IsBlist( [1,1,0,0] );

false # contains not only boolean values

gap> IsBlist( 17 );

false # is not even a list

27.4 SizeBlist

SizeBlist( blist )

SizeBlist returns the number of entries of the boolean list blist that are true. This is the size of the subset represented by the boolean list blist.

gap> SizeBlist( [ true, true, false, false ] );

27.5 IsSubsetBlist

IsSubsetBlist( blist1 , blist2 )

IsSubsetBlist returns true if the boolean list blist2 is a subset of the boolean list list1 , which must have equal length, and false otherwise. blist2 is a subset if blist1 if blist1 [i] = blist1 [i] or blist2 [i] for all i.

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> IsSubsetBlist( blist1, blist2 );
    false
    gap> blist2 := [ true, false, false, false ];;
    gap> IsSubsetBlist( blist1, blist2 );
    true

27.6 UnionBlist

UnionBlist( blist1 , blist2 ..  )
UnionBlist( list )

In the first form UnionBlist returns the union of the boolean lists blist1 , blist2 , etc., which must have equal length. The union is a new boolean list such that union[i] = blist1 [i]

or blist2 [i] or ...

In the second form list must be a list of boolean lists blist1 , blist2 , etc., which must have equal length, and Union returns the union of those boolean list.

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> UnionBlist( blist1, blist2 );
    [ true, true, true, false ]

Note that UnionBlist is implemented in terms of the procedure UniteBlist (see 27.9).

27.7 IntersectionBlist

IntersectionBlist( blist1 , blist2 ..  )
IntersectionBlist( list )

In the first form IntersectionBlist returns the intersection of the boolean lists blist1 , blist2 , etc., which must have equal length. The intersection is a new boolean list such that inter[i] = blist1 [i] and blist2 [i] and ...

In the second form list must be a list of boolean lists blist1 , blist2 , etc., which must have equal length, and IntersectionBlist returns the intersection of those boolean lists.

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> IntersectionBlist( blist1, blist2 );
    [ true, false, false, false ]

Note that IntersectionBlist is implemented in terms of the procedure IntersectBlist (see 27.10).

27.8 DifferenceBlist

DifferenceBlist( blist1 , blist2 )

DifferenceBlist returns the asymmetric set difference of the two boolean lists blist1 and blist2 , which must have equal length. The asymmetric set difference is a new boolean list such that union[i] = blist1 [i] and not blist2 [i].

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> DifferenceBlist( blist1, blist2 );
    [ false, true, false, false ]

Note that DifferenceBlist is implemented in terms of the procedure SubtractBlist (see 27.11).

27.9 UniteBlist

UniteBlist( blist1 , blist2 )

UniteBlist unites the boolean list blist1 with the boolean list blist2 , which must have the same length. This is equivalent to assigning blist1 [i] := blist1 [i] or blist2 [i] for all i. UniteBlist returns nothing, it is only called to change blist1 .

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> UniteBlist( blist1, blist2 );
    gap> blist1;
    [ true, true, true, false ]

The function UnionBlist (see 27.6) is the nondestructive counterpart to the procedure

UniteBlist.

27.10 IntersectBlist

IntersectBlist( blist1 , blist2 )

IntersectBlist intersects the boolean list blist1 with the boolean list blist2 , which must have the same length. This is equivalent to assigning blist1 [i]:= blist1 [i] and blist2 [i] for all i. IntersectBlist returns nothing, it is only called to change blist1 .

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> IntersectBlist( blist1, blist2 );
    gap> blist1;
    [ true, false, false, false ]

The function IntersectionBlist (see 27.7) is the nondestructive counterpart to the pro-

cedure IntersectBlist.

27.11 SubtractBlist

SubtractBlist( blist1 , blist2 )

SubtractBlist subtracts the boolean list blist2 from the boolean list blist1 , which must have equal length. This is equivalent to assigning blist1 [i] := blist1 [i] and not blist2 [i] for all i. SubtractBlist returns nothing, it is only called to change blist1 .

    gap> blist1 := [ true, true, false, false ];;
    gap> blist2 := [ true, false, true, false ];;
    gap> SubtractBlist( blist1, blist2 );
    gap> blist1;
    [ false, true, false, false ]

The function DifferenceBlist (see 27.8) is the nondestructive counterpart to the procedure

SubtractBlist.

27.12 More about Boolean Lists

In the previous section (see 27) we defined a boolean list as a list that has no holes and contains only true and false. There is a special internal representation for boolean lists that needs only 1 bit for every entry. This bit is set if the entry is true and reset if the entry is false. This representation is of course much more compact than the ordinary representation of lists, which needs 32 bits per entry.

Not every boolean list is represented in this compact representation. It would be too much work to test every time a list is changed, whether this list has become a boolean list. This section tells you under which circumstances a boolean list is represented in the compact representation, so you can write your functions in such a way that you make best use of the compact representation.

The results of BlistList, UnionBlist, IntersectionBlist and DifferenceBlist are

known to be boolean lists by construction, and thus are represented in the compact representation upon creation.

If an argument of IsBlist, IsSubsetBlist, ListBlist, UnionBlist, IntersectionBlist,
DifferenceBlist, UniteBlist, IntersectBlist and SubtractBlist is a list represented

in the ordinary representation, it is tested to see if it is in fact a boolean list. If it is not, IsBlist returns false and the other functions signal an error. If it is, the representation of the list is changed to the compact representation.

If you change a boolean list that is represented in the compact representation by assignment (see 25.5) or Add (see 25.6) in such a way that the list remains a boolean list it will remain represented in the compact representation. Note that changing a list that is not represented in the compact representation, whether it is a boolean list or not, in such a way that the resulting list becomes a boolean list, will never change the representation of the list.

546 CHAPTER 27. BOOLEAN LISTS

Strings and Characters

A character is simply an object in GAP that represents an arbitrary character from the character set of the operating system. Character literals can be entered in GAP by enclosing the character in singlequotes '.

gap> 'a';
'a'
gap> '*';
'*'

A string is simply a dense list of characters. Strings are used mainly in filenames and error messages. A string literal can either be entered simply as the list of characters or by writing the characters between doublequotes ". GAP will always output strings in the latter format.

gap> s1 := ['H','a','l','l','o',' ','w','o','r','l','d','.'];
"Hallo world."
gap> s2 := "Hallo world.";
"Hallo world."
gap> s1 = s2;
true
gap> s3 := "";

"" # the empty string

gap> s3 = [];
true

Note that a string is just a special case of a list. So everything that is possible for lists (see 25) is also possible for strings. Thus you can access the characters in such a string (see 25.3), test for membership (see 25.13), etc. You can even assign to such a string (see 25.5). Of course unless you assign a character in such a way that the list stays dense, the resulting list will no longer be a string.

gap> Length( s2 );
12
gap> s2[2];
'a'

    gap> 'e' in s2;
    false
    gap> s2[2] := 'e';;  s2;
    "Hello world."

If a string is displayed as result of an evaluation (see 3.1), it is displayed with enclosing doublequotes. However, if a string is displayed by Print, PrintTo, or AppendTo (see 3.14, 3.15, 3.16) the enclosing doublequotes are dropped.

    gap> s2;
    "Hello world."
    gap> Print( s2 );
    Hello world.gap>

There are a number of special character sequences that can be used between the single quote of a character literal or between the doublequotes of a string literal to specify characters, which may otherwise be inaccessible. They consist of two characters. The first is a backslash \. The second may be any character. The meaning is given in the following list n newline character. This is the character that, at least on UNIX systems, separates

lines in a text file. Printing of this character in a string has the effect of moving the cursor down one line and back to the beginning of the line.

" doublequote character. Inside a string a doublequote must be escaped by the

backslash, because it is otherwise interpreted as end of the string. ' singlequote character. Inside a character a singlequote must escaped by the

backslash, because it is otherwise interpreted as end of the character. \ backslash character. Inside a string a backslash must be escaped by another

backslash, because it is otherwise interpreted as first character of an escape sequence. b backspace character. Printing this character should have the effect of moving

the cursor back one character. Whether it works or not is system dependent and should not be relied upon.

r carriage return character. Printing this character should have the effect of moving the cursor back to the beginning of the same line. Whether this works or not is again system dependent.

c flush character. This character is not printed. Its purpose is to flush the output queue. Usually GAP waits until it sees a newline before it prints a string. If you want to display a string that does not include this character use \c.

other For any other character the backslash is simply ignored. Again, if the line is displayed as result of an evaluation, those escape sequences are displayed in the same way that they are input. They are displayed in their special way only by Print,

PrintTo, or AppendTo.
    gap> "This is one line.\nThis is another line.\n";
    "This is one line.\nThis is another line.\n"
    gap> Print( last );
    This is one line.
    This is another line.

It is not allowed to enclose a newline inside the string. You can use the special character sequence \n to write strings that include newline characters. If, however, a string is too long to fit on a single line it is possible to continue it over several lines. In this case the last character of each line, except the last must be a backslash. Both backslash and newline are thrown away. Note that the same continuation mechanism is available for identifiers and integers.

    gap> "This is a very long string that does not fit on a line \
    gap> and is therefore continued on the next line.";
    "This is a very long string that does not fit on a line and is therefo\
    re continued on the next line."

# note that the output is also continued, but at a different place This chapter contains sections describing the function that creates the printable representation of a string (see 28.1), the functions that create new strings (see 28.2, 28.3), the functions that tests if an object is a string (see 28.5), the string comparisons (see 28.4), and the function that returns the length of a string (see 28.6).

28.1 String

String( obj )
String( obj , length )

String returns a representation of the obj , which may be an object of arbitrary type, as a string. This string should approximate as closely as possible the character sequence you see if you print obj . If length is given it must be an integer. The absolute value gives the minimal length of the result. If the string representation of obj takes less than that many characters it is filled with blanks. If length is positive it is filled on the left, if length is negative it is filled on the right.

    gap> String( 123 );
    "123"
    gap> String( [1,2,3] );
    "[ 1, 2, 3 ]"
    gap> String( 123, 10 );
    "       123"
    gap> String( 123, -10 );
    "123       "
    gap> String( 123, 2 );
    "123"

28.2 ConcatenationString

ConcatenationString( string1 , string2 )

ConcatenationString returns the concatenation of the two strings string1 and string2 . This is a new string that starts with the characters of string1 and ends with the characters of string2 .

    gap> ConcatenationString( "Hello ", "world.\n" );
    "Hello world.\n"

Because strings are now lists, Concatenation (see 25.17) does exactly the right thing, and the function ConcatenationString is obsolete.

28.3 SubString

SubString( string, from, to )

SubString returns the substring of the string string that begins at position from and continues to position to. The characters at these two positions are included. Indexing is done with origin 1, i.e., the first character is at position 1. from and to must be integers and are both silently forced into the range 1..LengthString(string) (see 28.6). If to is less than from the substring is empty.

    gap> SubString( "Hello world.\n", 1, 5 );
    "Hello"
    gap> SubString( "Hello world.\n", 5, 1 );
    ""

Because strings are now lists, substrings can also be extracted with string{[from..to]} (see 25.3). SubString forces from and to into the range 1..Length(string), which the above does not, but apart from that SubString is obsolete.

28.4 Comparisons of Strings

string1 = string2 , string1 <> string2

The equality operator = evaluates to true if the two strings string1 and string2 are equal and false otherwise. The inequality operator <> returns true if the two strings string1 and string2 are not equal and false otherwise.

    gap> "Hello world.\n" = "Hello world.\n";
    true
    gap> "Hello World.\n" = "Hello world.\n";

false # string comparison is case sensitive

    gap> "Hello world." = "Hello world.\n";

false # the first string has no newline

    gap> "Goodbye world.\n" = "Hello world.\n";
    false
    gap> [ 'a', 'b' ] = "ab";
    true

string1 < string2 , string1 <= string2 , string1 > string2 , string1 => string2

The operators <, <=, >, and => evaluate to true if the string string1 is less than, less than or equal to, greater than, greater than or equal to the string string2 respectively. The ordering of strings is lexicographically according to the order implied by the underlying, system dependent, character set.

You can also compare objects of other types, for example integers or permutations with strings. As strings are dense character lists they compare with other objects as lists do, i.e., they are never equal to those objects, records (see 39) are greater than strings, and objects of every other type are smaller than strings.

    gap> "Hello world.\n" < "Hello world.\n";

false # the strings are equal

    gap> "Hello World.\n" < "Hello world.\n";

true # in ASCII uppercase letters come before lowercase letters

gap> "Hello world." < "Hello world.\n";

true # prefixes are always smaller

gap> "Goodbye world.\n" < "Hello world.\n";

true # G comes before H, in ASCII at least

28.5 IsString

IsString( obj )

IsString returns true if the object obj , which may be an object of arbitrary type, is a string and false otherwise. Will cause an error if obj is an unbound variable.

gap> IsString( "Hello world.\n" );
true
gap> IsString( "123" );
true
gap> IsString( 123 );
false
gap> IsString( [ '1', '2', '3' ] );
true
gap> IsString( [ '1', '2', , '4' ] );

false # strings must be dense

gap> IsString( [ '1', '2', 3 ] );

false # strings must only contain characters

28.6 LengthString

LengthString( string )

LengthString returns the length of the string string. The length of a string is the number of characters in the string. Escape sequences (see 28) are just a two character representation for a single character, and are thus counted as single character by LengthString.

gap> LengthString( "" );
0
gap> LengthString( "Hello" );
5
gap> LengthString( "Hello world.\n" );
13

Because strings are now lists, Length (see 25.4) does exactly the right thing, and the function

LengthString is obsolete.

552 CHAPTER 28. STRINGS AND CHARACTERS

Ranges

A range is a dense list of integers, such that the difference between consecutive elements is a nonzero constant. Ranges can be abbreviated with the syntactic construct [ first, second .. last ] or, if the difference between consecutive elements is 1, as [ first .. last ].

If first > last, [first,second..last] is the empty list, which by definition is also a range. If first = last, [first,second..last] is a singleton list, which is a range too. Note that last - first must be divisable by the increment second - first, otherwise an error is signalled.

Note that a range is just a special case of a list. So everything that is possible for lists (see 25) is also possible for ranges. Thus you can access elements in such a range (see 25.3), test for membership (see 25.13), etc. You can even assign to such a range (see 25.5). Of course, unless you assign last + second-first to the entry range[Length(range)+1], the resulting list will no longer be a range.

Most often ranges are used in connection with the for-loop (see 2.17). Here the construct for var in [first..last] do statements od replaces the for var from first to last do statements od, which is more usual in other programming languages.

Note that a range is at the same time also a set (see 26), because it contains no holes or duplicates and is sorted, and also a vector (see 30), because it contains no holes and all elements are integers.

    gap> r := [10..20];
    [ 10 .. 20 ]
    gap> Length( r );
    11
    gap> r[3];
    12
    gap> 17 in r;
    true
    gap> r[12] := 25;; r;
    [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25 ]
    gap> r := [1,3..17];
    [ 1, 3 .. 17 ]

gap> Length( r );
9
gap> r[4];
7
gap> r := [0,-1..-9];
[ 0, -1 .. -9 ]
gap> r[5];
-4
gap> r := [ 1, 4 .. 32 ];
Error, Range: <high>-<low> must be divisable by <inc>
gap> s := [];;  for i  in [10..20]  do Add( s, i^2 );  od;  s;
[ 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400 ]

The first section in this chapter describes the function that tests if a list is a range (see 29.1). The other section tells you more about the internal representation of ranges (see 29.2).

29.1 IsRange

IsRange( obj )

IsRange returns true if obj , which may be an object of any type, is a range and false otherwise. A range is a list without holes such that the elements are integers with a constant increment. Will cause an error if obj is an unassigned variable.

gap> IsRange( [1,2,3] );

true # this list is a range

gap> IsRange( [7,5,3,1] );

true # this list is a range

gap> IsRange( [1,2,4,5] );

false # this list is a set and a vector, but not a range

gap> IsRange( [1,,3,,5,,7] );

false # this list contains holes

gap> IsRange( 1 );

false # is not even a list

gap> IsRange( [] );

true # the empty list is a range by definition

gap> IsRange( [1] );

true # singleton lists are a range by definition too

29.2 More about Ranges

For some lists the kernel knows that they are in fact ranges. Those lists are represented internally in a compact way instead of the ordinary way. This is important since this representation needs only 12 bytes for the entire list while the ordinary representation needs 4length bytes. Note that a list that is represented in the ordinary way might still be a range. It is just that GAP does not know this. This section tells you under which circumstances a range is represented in the compact way, so you can write your program in such a way that you make best use of this compact representation for ranges.

Lists created by the syntactic construct [ first, second .. last ] are of course known to be ranges and are represented in the compact way.

If you call IsRange for a list represented the ordinary way that is indeed a range, IsRange will note this, change the representation from the ordinary to the compact representation, and then return true;

If you change a range that is represented in the compact way, by assignment, Add or Append, the range will be converted to the ordinary representation, even if the change is such that the resulting list is still a proper range.

Suppose you have built a proper range in such a way that it is represented in the ordinary way and that you now want to convert it to the compact representation to save space. Then you should call IsRange with that list as an argument. If it is indeed a proper range, IsRange will convert it to the compact representation. You can think of the call to IsRange as a hint to GAP that this list is a proper range.

556 CHAPTER 29. RANGES

Vectors

A important concept in algebra is the vector space over a field F. A vector space V is a set of vectors, for which an addition u + v and a multiplication by scalars, i.e., elements from F, sv must be defined. A base of V is a list of vectors, such that every vector in V can be uniquely written as linear combination of the base vectors. If the base if finite, its size is called the dimension of V. Using a base it can be shown that V is isomorphic to the set n-tuples of elements with the componentwise addition and multiplication.

This comment suggests the representation that is actually used in GAP. A GAP vector is a list without holes whose elements all come from a common field. We call the length of the list the dimension of the vector. This is a little bit lax, because the dimension is a property of the vector space, not of the vector, but should seldom cause confusion.

The first possibility for this field are the rationals (see 12). We call a list without holes whose elements are all rationals a rational vector, which is a bit lax too, but should again cause no confusion. For example [ 1/2, 0, -1/3, 2 ] is a rational vector of dimension 4.

The second possibility are cyclotomics (see 13). Note that the rationals are the prime field of cyclotomic fields and therefore rational vectors are just a special case of cyclotomic vectors. An example of a cyclotomic vector is [ E(3)+E(3)^2, 1, E(15) ].

Third the common field may be a finite field (see 17). Note that it is not enough that all elements are finite field elements of the same characteristic, the common finite field containing all elements must be representable in GAP, i.e., must have at most 2^{16} elements. An example of such a vector over the finite field GF (3^{4}) with 81 elements is [ Z(3^4)^3,

Z(3^2)^5, Z(3^4)^11 ].

Finally a list all of whose elements are records is also considered a vector. In that case the records should all have an operations record with the necessary functions +, -, *, ^. This allows for vectors over library and/or user defined fields (or rings) such as a polynomial ring (see 18).

The first section in this chapter describes the operations applicable to vectors (see 30.1).

The next section describes the function that tests if an object is a vector (see 30.2).

The next section describes the function that returns a canonical multiple of a vector (see 30.3).

The last section tells you more about the internal representation of vectors (see 30.4).

Because vectors are just a special case of lists, all the operations and functions for lists are applicable to vectors also (see chapter 25). This especially includes accessing elements of a vector (see 25.3), changing elements of a vector (see 25.5), and comparing vectors (see 25.11).

Vectorspaces are a special category of domains and are described by vectorspace records (see chapter 9).

Vectors play an important role for matrices (see chapter 32), which are implemented as lists of vectors.

30.1 Operations for Vectors

vec1 + vec2

In this form the addition operator + evaluates to the sum of the two vectors vec1 and vec2 , which must have the same dimension and lie in a common field. The sum is a new vector where each entry is the sum of the corresponding entries of the vectors. As an exception it is also possible to add an integer vector to a finite field vector, in which case the integers are interpreted as scalar * GF .one.

scalar + vec vec + scalar

In this form + evaluates to the sum of the scalar scalar and the vector vec, which must lie in a common field. The sum is a new vector where each entry is the sum of the scalar and the corresponding entry of the vector. As an exception it is also possible to add an integer scalar to a finite field vector, in which case the integer is interpreted as scalar * GF .one.

    gap> [ 1, 2, 3 ] + [ 1/2, 1/3, 1/4 ];
    [ 3/2, 7/3, 13/4 ]
    gap> [ 1/2, 3/2, 1/2 ] + 1/2;
    [ 1, 2, 1 ]

vec1 - vec2 scalar - vec vec - scalar

The difference operator - returns the componentwise difference of its two operands and is defined subject to the same restrictions as +.

    gap> [ 1, 2, 3 ] - [ 1/2, 1/3, 1/4 ];
    [ 1/2, 5/3, 11/4 ]
    gap> [ 1/2, 3/2, 1/2 ] - 1/2;
    [ 0, 1, 0 ]

vec1 * vec2

In this form the multiplication operator * evaluates to the product of the two vectors vec1 and vec2 , which must have the same dimension and lie in a common field. The product is the sum of the products of the corresponding entries of the vectors. As an exception it is also possible to multiply an integer vector to a finite field vector, in which case the integers are interpreted as scalar * GF .one. scalar * vec vec * scalar

In this form * evaluates to the product of the scalar scalar and the vector vec, which must lie in a common field. The product is a new vector where each entry is the product of the scalar and the corresponding entry of the vector. As an exception it is also possible to multiply an integer scalar to a finite field vector, in which case the integer is interpreted as scalar *GF .one.

gap> [ 1, 2, 3 ] * [ 1/2, 1/3, 1/4 ];
23/12
gap> [ 1/2, 3/2, 1/2 ] * 2;
[ 1, 3, 1 ]

Further operations with vectors as operands are defined by the matrix operations (see 32.1).

30.2 IsVector

IsVector( obj )

IsVector returns true if obj , which may be an object of arbitrary type, is a vector and false else. A vector is a list without holes, whose elements all come from a common field.

gap> IsVector( [ 0, -3, -2, 0, 6 ] );
true
gap> IsVector( [ Z(3^4)^3, Z(3^2)^5, Z(3^4)^13 ] );
true
gap> IsVector( [ 0, Z(2^3)^3, Z(2^3) ] );

false # integers are not finite field elements

gap> IsVector( [ , 2, 3,, 5,, 7 ] );

false # list that have holes are not vectors

gap> IsVector( 0 );

false # not even a list

30.3 NormedVector

NormedVector( vec )

NormedVector returns the scalar multiple of vec such that the first nonzero entry of vec is the one from the field over which the vector is defined. If vec contains only zeroes a copy of it is returned.

gap> NormedVector( [ 0, -3, -2, 0, 6 ] );
[ 0, 1, 2/3, 0, -2 ]
gap> NormedVector( [ 0, 0 ] );
[ 0, 0 ]
gap> NormedVector( [ Z(3^4)^3, Z(3^2)^5, Z(3^4)^13 ] );
[ Z(3)^0, Z(3^4)^47, Z(3^2) ]

30.4 More about Vectors

In the first section of this chapter we defined a vector as a list without holes whose elements all come from a common field. This representation is quite nice to use. However, suppose that GAP would have to check that a list is a vector every time this vector appears as operand in a addition or multiplication. This would be quite wasteful.

To avoid this a list that is a vector may, but need not, have an internal flag set that tells the operations that this list is indeed a vector. Then this operations do not have to check this operand and can perform the operation right away. This section tells you when a vector obtains this flag, so you can write your functions in such a way that you make best use of this feature.

The results of vector operations, i.e., binary operations that involve vectors, are known by construction to be vectors, and thus have the flag set upon creation.

If the operand of one of the binary operation is a list that does not yet have the flag set, those operations will check that this operand is indeed a vector and set the flag if it is. If it is not a vector and not a matrix an error is signalled.

If the argument to IsVector is a list that does not yet have this flag set, IsVector will test if all elements come from a common field. If they do, IsVector will set the flag. Thus on the one hand IsVector is a test whether the argument is a vector. On the other hand IsVector can be used as a hint to GAP that a certain list is indeed a vector.

If you change a vector, that does have this flag set, by assignment, Add, or Append, the vectors will loose its flag, even if the change is such that the resulting list is still a vector. However if the vector is a vector over a finite field and you assign an element from the same finite field the vector will keep its flag. Note that changing a list that is not a vector will never set the flag, even if the resulting list is a vector. Such a vector will obtain the flag only if it appears as operand in a binary operation, or is passed to IsVector.

Vectors over finite fields have one additional feature. If they are known to be vectors, not only do they have the flag set, but also are they represented differently. This representation is much more compact. Instead of storing every element separately and storing for every element separately in which field it lies, the field is only stored once. This representation takes up to 10 times less memory.

Row Spaces

The material described in this chapter is subject to change. Row spaces are a subcategory of vector spaces (see 9). The following sections describe how row spaces are created (see 31.1 ) and how factorspaces of row spaces are constructed (see 31.8). The following sections describe the additional functions for factorspaces of row spaces. The functions described in this chapter are implemented in the file LIBNAME/"rowspace.g".

31.1 RowSpace

RowSpace( generators, field ) RowSpace returns the row space that is generated by the vectors generators over the field field. It is highly recommended that the elements in generators in fact are GAP vectors. RowSpace( generators, field, zero ) Whenever the list generators is empty, this call of RowSpace has to be used. In that case the necessary components in the record representing the row space are set up using the third argument zero as well. RowSpace( dimension, field ) The standard row space of dimension dimension over the field field is constructed and returned when using this version of the call to RowSpace. The elements of the returned row space are all the lists of length dimension with entries in field.

31.2 IsRowSpace

IsRowSpace( obj )

IsRowSpace returns true if obj , which can be an object of arbitrary type, is a row space and false otherwise.

31.3 Row Space Records

In addition to the record components described in 9.3 the following components may be present in the record of a row space V .

isRowSpace

is always true.

31.4 Set Functions for Row Spaces

v in V

Membership is tested using SiftedVector (see 31.7).

Intersection( V , W )

The intersection of two row spaces is computed using the Zassenhaus-algorithm, thus avoiding the generation of the set of elements of V and W .

31.5 VectorSpace Functions for Row Spaces

Row spaces are a special type of vector spaces for which better algorithms may be known. The following functions differ in their implementation from their equivalent for general vector spaces.

Base( V )

The base of a row space is determined with the Gauss algorithm (see 31.6).

31.6 Base for Row Spaces

RowSpaceOps.Base( V )

If V is a row space, a base might be computed using the full Gauss-algorithm on the matrix that is built row by row from the generators of V . The non-zero rows of the resulting matrix form a base for V , which even is a canonical one. RowSpaceOps.Base computes and returns such a base using the function BaseMat (see 32.13).

31.7 SiftedVector

SiftedVector( V , v )

SiftedVector shifts the given vector v through the base of V which is obtained using the function Base. For the default function RowSpaceOps.SiftedVector, shifting a vector through a base assumes that the matrix of that base is in upper triangular form so that the entries at the heads of the base elements may successively be extinguished by subtracting an appropriate multiple of the base vector. RowSpaceOps.SiftedVector returns the result of this process. This function is used to decide whether a given vector v lies in a row space V , as v lies in V , iff the residuum is equal to V .zero. The residuum is the result of the shifting process.

31.8 Row Space Operations

The following operator can be used for row spaces.

V mod W

mod returns a record representing a factorspace, which can be used as an argument to some of the functions that are applicable for row spaces (see 9.10).

564 CHAPTER 31. ROW SPACES

Matrices

Matrices are an important tool in algebra. A matrix nicely represents a homomorphism between two vector spaces with respect to a choice of bases for the vector spaces. Also matrices represent systems of linear equations. In GAP matrices are represented by list of vectors (see 30). The vectors must all have the same length, and their elements must lie in a common field. The field may be the field of rationals (see 12), a cyclotomic field (see 13), a finite field (see 17), or a library and/or user defined field (or ring) such as a polynomial ring (see 18). The first section in this chapter describes the operations applicable to matrices (see 32.1). The next sections describes the function that tests whether an object is a matrix (see 32.2). The next sections describe the functions that create certain matrices (see 32.3, 32.4, 32.5, and 32.6). The next sections describe functions that compute certain characteristic values of matrices (see 32.7, 32.8, 32.9, 32.10, and 32.11). The next sections describe the functions that are related to the interpretation of a matrix as a system of linear equations (see 32.12, 32.13, 32.14, and 32.15). The last two sections describe the functions that diagonalize an integer matrix (see 32.16 and 32.17). Because matrices are just a special case of lists, all operations and functions for lists are applicable to matrices also (see chapter 25). This especially includes accessing elements of a matrix (see 25.3), changing elements of a matrix (see 25.5), and comparing matrices (see 25.11).

32.1 Operations for Matrices

mat + scalar scalar + mat This forms evaluates to the sum of the matrix mat and the scalar scalar. The elements of mat and scalar must lie in a common field. The sum is a new matrix where each entry is the sum of the corresponding entry of mat and scalar. mat1 + mat2 This form evaluates to the sum of the two matrices mat1 and mat2 , which must have the same dimensions and whose elements must lie in a common field. The sum is a new matrix where each entry is the sum of the corresponding entries of mat1 and mat2 . mat - scalar scalar - mat mat1 - mat2

The definition for the - operator are similar to the above definitions for the + operator, except that - subtracts of course.

mat * scalar scalar * mat

This forms evaluate to the product of the matrix mat and the scalar scalar. The elements of mat and scalar must lie in a common field. The product is a new matrix where each entry is the product of the corresponding entries of mat and scalar.

vec * mat

This form evaluates to the product of the vector vec and the matrix mat. The length of vec and the number of rows of mat must be equal. The elements of vec and mat must lie in a common field. If vec is a vector of length n and mat is a matrix with n rows and m columns, the product is a new vector of length m. The element at position i is the sum of vec[l] * mat[l][i] with l running from 1 to n.

mat * vec

This form evaluates to the product of the matrix mat and the vector vec. The number of columns of mat and the length of vec must be equal. The elements of mat and vec must lie in a common field. If mat is a matrix with m rows and n columns and vec is a vector of length n, the product is a new vector of length m. The element at position i is the sum of mat[i][l] * vec[l] with l running from 1 to n.

mat1 * mat2

This form evaluates to the product of the two matrices mat1 and mat2 . The number of columns of mat1 and the number of rows of mat2 must be equal. The elements of mat1 and mat2 must lie in a common field. If mat1 is a matrix with m rows and n columns and mat2 is a matrix with n rows and o columns, the result is a new matrix with m rows and o columns. The element in row i at position k of the product is the sum of mat1 [i][l] * mat2 [l][k] with l running from 1 to n.

mat1 / mat2 scalar / mat mat / scalar vec / mat

In general left / right is defined as left * right^-1. Thus in the above forms the right operand must always be invertable.

mat ^int

This form evaluates to the int-th power of the matrix mat. mat must be a square matrix, int must be an integer. If int is negative, mat must be invertible. If int is 0, the result is the identity matrix, even if mat is not invertible.

mat1 ^mat2

This form evaluates to the conjugation of the matrix mat1 by the matrix mat2 , i.e., to mat2 ^-1 * mat1 * mat2 . mat2 must be invertible and mat1 must be such that these product can be computed. vec ^mat

This is in every respect equivalent to vec * mat. This operations reflects the fact that matrices operate on the vector space by multiplication from the right.

scalar + matlist matlist + scalar scalar - matlist matlist - scalar scalar * matlist matlist * scalar matlist / scalar

A scalar scalar may also be added, subtracted, multiplied with, or divide into a whole list of matrices matlist. The result is a new list of matrices where each matrix is the result of performing the operation with the corresponding matrix in matlist.

mat * matlist matlist * mat

A matrix mat may also be multiplied with a whole list of matrices matlist. The result is a new list of matrices, where each matrix is the product of mat and the corresponding matrix in matlist.

matlist / mat

This form evaluates to matlist * mat^-1. mat must of course be invertable.

vec * matlist

This form evaluates to the product of the vector vec and the list of matrices mat. The length l of vec and matlist must be equal. All matrices in matlist must have the same dimensions. The elements of vec and the elements of the matrices in matlist must lie in a common field. The product is the sum of vec[i] * matlist[i] with i running from 1 to l.

Comm( mat1 , mat2 )

Comm returns the commutator of the matrices mat1 and mat2 , i.e., mat1 ^-1 * mat2 ^-1 * mat1 * mat2 . mat1 and mat2 must be invertable and such that these product can be computed.

There is one exception to the rule that the operands or their elements must lie in common field. It is allowed that one operand is a finite field element, a finite field vector, a finite field matrix, or a list of finite field matrices, and the other operand is an integer, an integer vector, an integer matrix, or a list of integer matrices. In this case the integers are interpreted as int * GF .one, where GF is the finite field (see 17.3).

For all the above operations the result is new, i.e., not identical to any other list (see 25.8). This is the case even if the result is equal to one of the operands, e.g., if you add zero to a matrix.

32.2 IsMat

IsMat( obj )

IsMat return true if obj , which can be an object of arbitrary type, is a matrix and false otherwise. Will cause an error if obj is an unbound variable.

gap> IsMat( [ [ 1, 0 ], [ 0, 1 ] ] );

true # a matrix is a list of vectors

gap> IsMat( [ [ 1, 2, 3, 4, 5 ] ] );
true
gap> IsMat( [ [ Z(2)^0, 0*Z(2) ], [ 0*Z(2), Z(2)^0 ] ] );
true
gap> IsMat( [ [ Z(2)^0, 0 ], [ 0, Z(2)^0 ] ] );

false # Z(2)^0 and 0 do not lie in a common field

gap> IsMat( [ 1, 0 ] );

false # a vector is not a matrix

gap> IsMat( 1 );

false # neither is a scalar

32.3 IdentityMat

IdentityMat( n )
IdentityMat( n, F )

IdentityMat returns the identity matrix with n rows and n columns over the field F . If no field is given, IdentityMat returns the identity matrix over the field of rationals. Each call to IdentityMat returns a new matrix, so it is safe to modify the result.

gap> IdentityMat( 3 );
[ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ]
gap> PrintArray( last );
[ [  1,  0,  0 ],
  [  0,  1,  0 ],
  [  0,  0,  1 ] ]
gap> PrintArray( IdentityMat( 3, GF(2) ) );
[ [  Z(2)^0,  0*Z(2),  0*Z(2) ],
  [  0*Z(2),  Z(2)^0,  0*Z(2) ],
  [  0*Z(2),  0*Z(2),  Z(2)^0 ] ]

32.4 NullMat

NullMat( m, n )
NullMat( m, n, F )

NullMat returns the null matrix with m rows and n columns over the field F . If no field is given, NullMat returns the null matrix over the field of rationals. Each call to NullMat returns a new matrix, so it is safe to modify the result.

gap> PrintArray( NullMat( 2, 3 ) );
[ [  0,  0,  0 ],
  [  0,  0,  0 ] ]
gap> PrintArray( NullMat( 2, 2, GF(2) ) );
[ [  0*Z(2),  0*Z(2) ],
  [  0*Z(2),  0*Z(2) ] ]

32.5 TransposedMat

TransposedMat( mat )

TransposedMat returns the transposed of the matrix mat. The transposed matrix is a new matrix trn, such that trn[i][k] is mat[k][i].

gap> TransposedMat( [ [ 1, 2 ], [ 3, 4 ] ] );
[ [ 1, 3 ], [ 2, 4 ] ]
gap> TransposedMat( [ [ 1..5 ] ] );
[ [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ] ]

32.6 KroneckerProduct

KroneckerProduct( mat1 , mat2 )

KroneckerProduct returns the Kronecker product of the two matrices mat1 and mat2 . If mat1 is a m by n matrix and mat2 is a o by p matrix, the Kronecker product is a m*o by n*p matrix, such that the entry in row i1 *m+i2 at position k1 *n+k2 is mat1 [i1 ][k1 ] * mat2 [i2 ][k2 ].

gap> mat1 := [ [ 0, -1, 1 ], [ -2, 0, -2 ] ];;
gap> mat2 := [ [ 1, 1 ], [ 0, 1 ] ];;
gap> PrintArray( KroneckerProduct( mat1, mat2 ) );
[ [   0,   0,  -1,  -1,   1,   1 ],
  [   0,   0,   0,  -1,   0,   1 ],
  [  -2,  -2,   0,   0,  -2,  -2 ],
  [   0,  -2,   0,   0,   0,  -2 ] ]

32.7 DimensionsMat

DimensionsMat( mat )

DimensionsMat returns the dimensions of the matrix mat as a list of two integers. The first entry is the number of rows of mat, the second entry is the number of columns.

gap> DimensionsMat( [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] );
[ 2, 3 ]
gap> DimensionsMat( [ [ 1 .. 5 ] ] );
[ 1, 5 ]

32.8 TraceMat

TraceMat( mat )

TraceMat returns the trace of the square matrix mat. The trace is the sum of all entries on the diagonal of mat.

gap> TraceMat( [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] );
15
gap> TraceMat( IdentityMat( 4, GF(2) ) );
0*Z(2)

32.9 DeterminantMat

DeterminantMat( mat )

DeterminantMat returns the determinant of the square matrix mat. The determinant is defined by ∑_{p∈Symm(n)} sign(p) ∏_{i=1}^{n} mat[i][i^{p}].

    gap> DeterminantMat( [ [ 1, 2 ], [ 3, 4 ] ] );
    -2
    gap> DeterminantMat( [ [ 0*Z(3), Z(3)^0 ], [ Z(3)^0, Z(3) ] ] );
    Z(3)

Note that DeterminantMat does not use the above definition to compute the result. Instead it performs a Gaussian elimination. For large rational matrices this may take very long, because the entries may become very large, even if the final result is a small integer.

32.10 RankMat

RankMat( mat )

RankMat returns the rank of the matrix mat. The rank is defined as the dimension of the vector space spanned by the rows of mat. It follows that a n by n matrix is invertible exactly if its rank is n.

    gap> RankMat( [ [ 4, 1, 2 ], [ 3, -1, 4 ], [ -1, -2, 2 ] ] );
    2

Note that RankMat performs a Gaussian elimination. For large rational matrices this may take very long, because the entries may become very large.

32.11 OrderMat

OrderMat( mat )

OrderMat returns the order of the invertible square matrix mat. The order ord is the smallest positive integer such that mat^ord is the identity.

    gap> OrderMat( [ [ 0*Z(2), 0*Z(2), Z(2)^0 ],
    >                 [ Z(2)^0, Z(2)^0, 0*Z(2) ],
    >                 [ Z(2)^0, 0*Z(2), 0*Z(2) ] ] );
    4

OrderMat first computes ord1 such that the first standard basis vector is mapped by mat^ord1 onto itself. It does this by applying mat repeatedly to the first standard basis vector. Then it computes mat1 as mat1 ^ord1 . Then it computes ord2 such that the second standard basis vector is mapped by mat1 ^ord2 onto itself. This process is repeated until all basis vectors are mapped onto themselves. OrderMat warns you that the order may be infinite, when it finds that the order must be larger than 1000.

32.12 TriangulizeMat

TriangulizeMat( mat )

TriangulizeMat brings the matrix mat into upper triangular form. Note that mat is changed and that nothing is returned. A matrix is in upper triangular form when the first nonzero entry in each row is one and lies further to the right than the first nonzero entry in the previous row. Furthermore, above the first nonzero entry in each row all entries are zero. Note that the matrix will have trailing zero rows if the rank of mat is not maximal. The rows of the resulting matrix span the same vectorspace than the rows of the original matrix mat.

    gap> m := [ [ 0, -3, -1 ], [ -3, 0, -1 ], [ 2, -2, 0 ] ];;
    gap> TriangulizeMat( m ); m;
    [ [ 1, 0, 1/3 ], [ 0, 1, 1/3 ], [ 0, 0, 0 ] ]

Note that for large rational matrices TriangulizeMat may take very long, because the entries may become very large during the Gaussian elimination, even if the final result contains only small integers.

32.13 BaseMat

BaseMat( mat )

BaseMat returns a standard base for the vector space spanned by the rows of the matrix mat. The standard base is in upper triangular form. That means that the first nonzero vector in each row is one and lies further to the right than the first nonzero entry in the previous row. Furthermore, above the first nonzero entry in each row all entries are zero.

    gap> BaseMat( [ [ 0, -3, -1 ], [ -3, 0, -1 ], [ 2, -2, 0 ] ] );
    [ [ 1, 0, 1/3 ], [ 0, 1, 1/3 ] ]

Note that for large rational matrices BaseMat may take very long, because the entries may become very large during the Gaussian elimination, even if the final result contains only small integers.

32.14 NullspaceMat

NullspaceMat( mat )

NullspaceMat returns a base for the nullspace of the matrix mat. The nullspace is the set of vectors vec such that vec * mat is the zero vector. The returned base is the standard base for the nullspace (see 32.13).

    gap> NullspaceMat( [ [ 2, -4, 1 ], [ 0, 0, -4 ], [ 1, -2, -1 ] ] );
    [ [ 1, 3/4, -2 ] ]

Note that for large rational matrices NullspaceMat may take very long, because the entries may become very large during the Gaussian elimination, even if the final result only contains small integers.

32.15 SolutionMat

SolutionMat( mat, vec )

SolutionMat returns one solution of the equation x * mat = vec or false if no such solution exists.

    gap> SolutionMat( [ [ 2, -4, 1 ], [ 0, 0, -4 ], [ 1, -2, -1 ] ],
    >                  [ 10, -20, -10 ] );
    [ 5, 15/4, 0 ]
    gap> SolutionMat( [ [ 2, -4, 1 ], [ 0, 0, -4 ], [ 1, -2, -1 ] ],

    >                  [ 10, 20, -10 ] );
    false

Note that for large rational matrices SolutionMat may take very long, because the entries may become very large during the Gaussian elimination, even if the final result only contains small integers.

32.16 DiagonalizeMat

DiagonalizeMat( mat )

DiagonalizeMat transforms the integer matrix mat by multiplication with unimodular (i.e., determinant +1 or -1) integer matrices from the left and from the right into diagonal form (i.e., only diagonal entries are nonzero). Note that DiagonalizeMat changes mat and returns nothing. If there are several diagonal matrices to which mat is equivalent, it is not specified which one is computed, except that all zero entries on the diagonal are collected at the lower right end (see 32.17).

    gap> m := [ [ 0, -1, 1 ], [ -2, 0, -2 ], [ 2, -2, 4 ] ];;
    gap> DiagonalizeMat( m );  m;
    [ [ 1, 0, 0 ], [ 0, 2, 0 ], [ 0, 0, 0 ] ]

Note that for large integer matrices DiagonalizeMat may take very long, because the entries may become very large during the computation, even if the final result only contains small integers.

32.17 ElementaryDivisorsMat

ElementaryDivisorsMat( mat )

ElementaryDivisors returns a list of the elementary divisors, i.e., the unique d with d[i] divides d[i+1] and mat is equivalent to a diagonal matrix with the elements d[i] on the diagonal (see 32.16).

    gap> m := [ [ 0, -1, 1 ], [ -2, 0, -2 ], [ 2, -2, 4 ] ];;
    gap> ElementaryDivisorsMat( m );
    [ 1, 2, 0 ]

Matrix Rings

A matrix ring is a ring of square matrices (see chapter 32). In GAP you can define matrix rings of matrices over each of the fields that GAP supports, i.e., the rationals, cyclotomic extensions of the rationals, and finite fields (see chapters 12, 13, and 17). You define a matrix ring in GAP by calling Ring (see 5.2) passing the generating matrices as arguments.

    gap> m1 := [ [ Z(3)^0, Z(3)^0,   Z(3) ],
    >            [   Z(3), 0*Z(3),   Z(3) ],
    >            [ 0*Z(3),   Z(3), 0*Z(3) ] ];;
    gap> m2 := [ [   Z(3),   Z(3), Z(3)^0 ],
    >            [   Z(3), 0*Z(3),   Z(3) ],
    >            [ Z(3)^0, 0*Z(3),   Z(3) ] ];;
    gap> m := Ring( m1, m2 );
    Ring( [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), 0*Z(3), Z(3) ],
      [ 0*Z(3), Z(3), 0*Z(3) ] ],
    [ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
      [ Z(3)^0, 0*Z(3), Z(3) ] ] )
    gap> Size( m );
    2187

However, currently GAP can only compute with finite matrix rings with a multiplicative neutral element (a one). Also computations with large matrix rings are not done very efficiently. We hope to improve this situation in the future, but currently you should be careful not to try too large matrix rings. Because matrix rings are just a special case of domains all the set theoretic functions such as Size and Intersection are applicable to matrix rings (see chapter 4 and 33.1). Also matrix rings are of course rings, so all ring functions such as Units and IsIntegralRing are applicable to matrix rings (see chapter 5 and 33.2).

33.1 Set Functions for Matrix Rings

All set theoretic functions described in chapter 4 use their default function for matrix rings currently. This means, for example, that the size of a matrix ring is computed by computing the set of all elements of the matrix ring with an orbit-like algorithm. Thus you should not try to work with too large matrix rings.

33.2 Ring Functions for Matrix Rings

As already mentioned in the introduction of this chapter matrix rings are after all rings. All ring functions such as Units and IsIntegralRing are thus applicable to matrix rings. This section describes how these functions are implemented for matrix rings. Functions not mentioned here inherit the default group methods described in the respective sections.

IsUnit( R, m )

A matrix is a unit in a matrix ring if its rank is maximal (see 32.10).

Matrix Groups

A matrix group is a group of invertable square matrices (see chapter 32). In GAP you can define matrix groups of matrices over each of the fields that GAP supports, i.e., the rationals, cyclotomic extensions of the rationals, and finite fields (see chapters 12, 13, and 17).

You define a matrix group in GAP by calling Group (see 7.9) passing the generating matrices as arguments.

    gap> m1 := [ [ Z(3)^0, Z(3)^0,   Z(3) ],
    >            [   Z(3), 0*Z(3),   Z(3) ],
    >            [ 0*Z(3),   Z(3), 0*Z(3) ] ];;
    gap> m2 := [ [   Z(3),   Z(3), Z(3)^0 ],
    >            [   Z(3), 0*Z(3),   Z(3) ],
    >            [ Z(3)^0, 0*Z(3),   Z(3) ] ];;
    gap> m := Group( m1, m2 );
    Group( [ [ Z(3)^0, Z(3)^0, Z(3) ], [ Z(3), 0*Z(3), Z(3) ],
      [ 0*Z(3), Z(3), 0*Z(3) ] ],
    [ [ Z(3), Z(3), Z(3)^0 ], [ Z(3), 0*Z(3), Z(3) ],
      [ Z(3)^0, 0*Z(3), Z(3) ] ] )

However, currently GAP can only compute with finite matrix groups. Also computations with large matrix groups are not done very efficiently. We hope to improve this situation in the future, but currently you should be careful not to try too large matrix groups.

Because matrix groups are just a special case of domains all the set theoretic functions such as Size and Intersection are applicable to matrix groups (see chapter 4 and 34.1).

Also matrix groups are of course groups, so all the group functions such as Centralizer and DerivedSeries are applicable to matrix groups (see chapter 7 and 34.2).

34.1 Set Functions for Matrix Groups

As already mentioned in the introduction of this chapter matrix groups are domains. All set theoretic functions such as Size and Intersections are thus applicable to matrix groups. This section describes how these functions are implemented for matrix groups. Functions not mentioned here either inherit the default group methods described in 7.107 or the default method mentioned in the respective sections.

To compute with a matrix group m, GAP computes the operation of the matrix group on the underlying vector space (more precisely the union of the orbits of the parent of m on the standard basis vectors). Then it works with the thus defined permutation group p, which is of course isomorphic to m, and finally translates the results back into the matrix group.

obj in m

To test if an object obj lies in a matrix group m, GAP first tests whether obj is a invertable square matrix of the same dimensions as the matrices of m. If it is, GAP tests whether obj permutes the vectors in the union of the orbits of m on the standard basis vectors. If it does, GAP takes this permutation and tests whether it lies in p.

Size( m )

To compute the size of the matrix group m, GAP computes the size of the isomorphic permutation group p.

Intersection( m1 , m2 )

To compute the intersection of two subgroups m1 and m2 with a common parent matrix group m, GAP intersects the images of the corresponding permutation subgroups p1 and p2 of p. Then it translates the generators of the intersection of the permutation subgroups back to matrices. The intersection of m1 and m2 is the subgroup of m generated by those matrices. If m1 and m2 do not have a common parent group, or if only one of them is a matrix group and the other is a set of matrices, the default method is used (see 4.12).

34.2 Group Functions for Matrix Groups

As already mentioned in the introduction of this chapter matrix groups are after all group. All group functions such as Centralizer and DerivedSeries are thus applicable to matrix groups. This section describes how these functions are implemented for matrix groups. Functions not mentioned here either inherit the default group methods described in the respective sections.

To compute with a matrix group m, GAP computes the operation of the matrix group on the underlying vector space (more precisely the union of the orbits of the parent of m on the standard basis vectors). Then it works with the thus defined permutation group p, which is of course isomorphic to m, and finally translates the results back into the matrix group.

Centralizer( m, u )
Normalizer( m, u )
SylowSubgroup( m, p )
ConjugacyClasses( m )

This functions all work by solving the problem in the permutation group p and translating the result back.

PermGroup( m )

This computes a permutation representation of m by letting m operate on the union of the orbits of m (not its parent) on the standard basis vectors.

Stabilizer( m, v )

The stabilizer of a vector v that lies in the union of the orbits of the parent of m on the standard basis vectors is computed by finding the stabilizer of the corresponding point in the permutation group p and translating this back. Other stabilizers are computed with the default method (see 8.22).

RepresentativeOperation( m, v1 , v2 )

If v1 and v2 are vectors that both lie in the union of the orbits of the parent group of m on the standard basis vectors, RepresentativeOperation finds a permutation in p that takes the point corresponding to v1 to the point corresponding to v2 . If no such permutation exists, it returns false. Otherwise it translates the permutation back to a matrix.

RepresentativeOperation( m, m1 , m2 )

If m1 and m2 are matrices in m, RepresentativeOperation finds a permutation in p that conjugates the permutation corresponding to m1 to the permutation corresponding to m2 . If no such permutation exists, it returns false. Otherwise it translates the permutation back to a matrix.

34.3 Matrix Group Records

A group is represented by a record that contains information about the group. A matrix group record contains the following components in addition to those described in section 7.111.

isMatGroup

always true.

If a permutation representation for a matrix group m is known it is stored in the following components.

permGroupP

contains the permutation group representation of m.

permDomain

contains the union of the orbits of the parent of m on the standard basis vectors.

578 CHAPTER 34. MATRIX GROUPS

Group Libraries

When you start GAP it already knows several groups. Currently GAP initially knows the following groups. Some basic groups, such as cyclic groups or symmetric groups, the primitive groups of degree at most 50, the solvable groups of size at least 100, the 2-groups of size at most 256, and the 3-groups of size at most 729.

Each of the set of groups above is called a library. The whole set of groups that GAP knows initially is called the GAP collection of group libraries. There is usually no relation between the groups in the different libraries.

Most libraries are accessed in a uniform manner. For each library there is a so called selection function that allows you to select the list of groups that satisfy given criterias from a library. The example function allows yo to select one group that satisfies given criterias from the library. The low-level extraction function allows you to extract a single group from a library, using a simple indexing scheme.

The first section in this chapter describes the library of basic groups (see 35.1). This library is special, because it is not accessed in the same way as the other libraries. The next sections describe the general appearance of the selection, example, and extraction function (see 35.2, 35.3, and 35.4). The last sections in this chapter describe the group libraries currently available in GAP (see 35.5, 35.6, 35.7, 35.9).

Note that a system administrator may choose to install all, or only several, or even none of the libraries. So some of the libraries mentioned below may not be available on your installation.

35.1 The Basic Groups Library

CyclicGroup( n )
CyclicGroup( D, n )

In the first form CyclicGroup returns the cyclic group of size n as a permutation group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and CyclicGroup returns the cyclic group of size n as a group of elements of that type.

    gap> c12 := CyclicGroup( 12 );
    Group( ( 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12) )

    gap> c105 := CyclicGroup( AgWords, 5*3*7 );
    Group( c105_1, c105_2, c105_3 )
    gap> Order(c105,c105.1); Order(c105,c105.2); Order(c105,c105.3);
    105
    35
    7
AbelianGroup( sizes )
AbelianGroup( D, sizes )

In the first form AbelianGroup returns the abelian group C_{sizes[1]} ∗ C_{sizes[2]} ∗ ...∗ C_{sizes[n]}, where sizes must be a list of positive integers, as a permutation group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and AbelianGroup returns the abelian group as a group of elements of this type.

    gap> g := AbelianGroup( AgWords, [ 2, 3, 7 ] );
    Group( a, b, c )
    gap> Size( g );
    42
    gap> IsAbelian( g );
    true
The default function GroupElementsOps.AbelianGroup uses the functions CyclicGroup

and DirectProduct (see 7.92) to construct the abelian group.

ElementaryAbelianGroup( n )
ElementaryAbelianGroup( D, n )

In the first form ElementaryAbelianGroup returns the elementary abelian group of size n as a permutation group. n must be a positive prime power of course. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and ElementaryAbelianGroup returns the elementary abelian group as a group of elements of this type.

    gap> ElementaryAbelianGroup( 16 );
    Group( (1,2), (3,4), (5,6), (7,8) )
    gap> ElementaryAbelianGroup( AgWords, 3 ^10 );
    Group( m59049_1, m59049_2, m59049_3, m59049_4, m59049_5, m59049_6,
    m59049_7, m59049_8, m59049_9, m59049_10 )
The default function GroupElementsOps.ElementaryAbelianGroup uses CyclicGroup and

DirectProduct (see 7.92 to construct the elementary abelian group.

DihedralGroup( n )
DihedralGroup( D, n )

In the first form DihedralGroup returns the dihedral group of size n as a permutation group. n must be a positive even integer. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and DihedralGroup returns the dihedral group as a group of elements of this type.

    gap> DihedralGroup( 12 );

    Group( (1,2,3,4,5,6), (2,6)(3,5) )

PolyhedralGroup( p, q )
PolyhedralGroup( D, p, q )

In the first form PolyhedralGroup returns the polyhedral group of size p * q as a permutation group. p and q must be positive integers and there must exist a nontrivial p-th root of unity modulo every prime factor of q. In the second form D must be a domain of group elements, e.g., Permutations or Words, and PolyhedralGroup returns the polyhedral group as a group of elements of this type.

    gap> PolyhedralGroup( 3, 13 );
    Group( ( 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13), ( 2, 4,10)( 3, 7, 6)
    ( 5,13,11)( 8, 9,12) )
    gap> Size( last );
    39

SymmetricGroup( d )
SymmetricGroup( D, d )

In the first form SymmetricGroup returns the symmetric group of degree d as a permutation group. d must be a positive integer. In the second form D must be a domain of group elements, e.g., Permutations or Words, and SymmetricGroup returns the symmetric group as a group of elements of this type.

    gap> SymmetricGroup( 8 );
    Group( (1,8), (2,8), (3,8), (4,8), (5,8), (6,8), (7,8) )
    gap> Size( last );
    40320

AlternatingGroup( d )
AlternatingGroup( D, d )

In the first form AlternatingGroup returns the alternating group of degree d as a permutation group. d must be a positive integer. In the second form D must be a domain of group elements, e.g., Permutations or Words, and AlternatingGroup returns the alternating group as a group of elements of this type.

    gap> AlternatingGroup( 8 );
    Group( (1,2,8), (2,3,8), (3,4,8), (4,5,8), (5,6,8), (6,7,8) )
    gap> Size( last );
    20160

GeneralLinearGroup( n, q )
GeneralLinearGroup( D, n, q )

In the first form GeneralLinearGroup returns the general linear group GL(n, q) as a matrix group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and GeneralLinearGroup returns GL(n, q) as a group of elements of that type.

    gap> g:= GeneralLinearGroup( 2, 4 ); Size( g );

    GL(2,4)
    180

SpecialLinearGroup( n, q )
SpecialLinearGroup( D, n, q )

In the first form SpecialLinearGroup returns the special linear group SL(n, q) as a matrix group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and SpecialLinearGroup returns SL(n, q) as a group of elements of that type.

    gap> g:= SpecialLinearGroup( 3, 4 ); Size( g );
    SL(3,4)
    60480

SymplecticGroup( n, q )
SymplecticGroup( D, n, q )

In the first form SymplecticGroup returns the symplectic group SP (n, q) as a matrix group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and SymplecticGroup returns SP (n, q) as a group of elements of that type.

    gap> g:= SymplecticGroup( 4, 2 ); Size( g );
    SP(4,2)
    720

GeneralUnitaryGroup( n, q )
GeneralUnitaryGroup( D, n, q )

In the first form GeneralUnitaryGroup returns the general unitary group GU(n, q) as a matrix group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and GeneralUnitaryGroup returns GU(n, q) as a group of elements of that type.

    gap> g:= GeneralUnitaryGroup( 3, 3 ); Size( g );
    GU(3,3)
    24192

SpecialUnitaryGroup( n, q )
SpecialUnitaryGroup( D, n, q )

In the first form SpecialUnitaryGroup returns the special unitary group SU(n, q) as a matrix group. In the second form D must be a domain of group elements, e.g., Permutations or AgWords, and SpecialUnitaryGroup returns SU(n, q) as a group of elements of that type.

    gap> g:= SpecialUnitaryGroup( 3, 3 ); Size( g );
    SU(3,3)
    6048

35.2 Selection Functions

AllLibraryGroups( fun1 , val1 , fun2 , val2 , ... )

For each group library there is a selection function. This function allows you to select all groups from the library that have a given set of properties.

The name of the selection functions always begins with All and always ends with Groups. Inbetween is a name that hints at the nature of the group library. For example the selection function for the library of all primitive groups of degree at most 50 (see 35.5) is called AllPrimitiveGroups, and the selection function for the library of all 2-groups of size at most 256 (see 35.7) is called AllTwoGroups.

These functions take an arbitrary number of pairs of arguments. The first argument in such a pair is a function that can be applied to the groups in the library, and the second argument is either a single value that this function must return in order to have this group included in the selection, or a list of such values.

For example

    AllPrimitiveGroups( DegreeOperation,  [10..15],
                        Size,             [1..100],
                        IsAbelian,        false    );

should return a list of all primitive groups with degree between 10 and 15, size less than 100, that are not abelian.

Thus the AllPrimitiveGroups behaves as if it was implemented by a function similar to the one defined below, where PrimitiveGroupsList is a list of all primitive groups. Note, in the definition below we assume for simplicity that AllPrimitiveGroups accepts exactly 4 arguments. It is of course obvious how to change this definition so that the function would accept a variable number of arguments.

    AllPrimitiveGroups := function ( fun1, val1, fun2, val2 )
        local    groups, g, i;
        groups := [];
        for i  in [ 1 .. Length( PrimitiveGroupsList ) ] do
            g := PrimitiveGroupsList[i];
            if      fun1(g) = val1  or IsList(val1) and fun1(g) in val1
                and fun2(g) = val2  or IsList(val2) and fun2(g) in val2
            then
                Add( group, g );
            fi;
        od;
        return groups;
    end;

Note that the real selection functions are considerably more difficult, to improve the efficiency. Most important, each recognizes a certain set of functions and handles those properties using an index (see 1.26).

35.3 Example Functions

OneLibraryGroup( fun1 , val1 , fun2 , val2 , ... )

For each group library there is a example function. This function allows you to find one group from the library that has a given set of properties.

The name of the example functions always begins with One and always ends with Group. Inbetween is a name that hints at the nature of the group library. For example the example function for the library of all primitive groups of degree at most 50 (see 35.5) is called OnePrimitiveGroup, and the example function for the library of all 2-groups of size at most 256 (see 35.7) is called OneTwoGroup.

These functions take an arbitrary number of pairs of arguments. The first argument in such a pair is a function that can be applied to the groups in the library, and the second argument is either a single value that this function must return in order to have this group returned by the example function, or a list of such values.

For example

    OnePrimitiveGroup( DegreeOperation,  [10..15],
                        Size,             [1..100],
                        IsAbelian,        false    );

should return one primitive group with degree between 10 and 15, size less than 100, that is not abelian.

Thus the OnePrimitiveGroup behaves as if it was implemented by a function similar to the one defined below, where PrimitiveGroupsList is a list of all primitive groups. Note, in the definition below we assume for simplicity that OnePrimitiveGroup accepts exactly 4 arguments. It is of course obvious how to change this definition so that the function would accept a variable number of arguments.

    OnePrimitiveGroup := function ( fun1, val1, fun2, val2 )
        local    g, i;
        for i  in [ 1 .. Length( PrimitiveGroupsList ) ] do
            g := PrimitiveGroupsList[i];
            if      fun1(g) = val1  or IsList(val1) and fun1(g) in val1
                and fun2(g) = val2  or IsList(val2) and fun2(g) in val2
            then
                return g;
            fi;
        od;
        return false;
    end;

Note that the real example functions are considerably more difficult, to improve the efficiency. Most important, each recognizes a certain set of functions and handles those properties using an index (see 1.26).

35.4 Extraction Functions

For each group library there is an extraction function. This function allows you to extract single groups from the library.

The name of the extraction function always ends with Group and begins with a name that hints at the nature of the library. For example the extraction function for the library of primitive groups (see 35.5) is called PrimitiveGroup, and the extraction function for the library of all 2-groups of size at most 256 (see 35.7) is called TwoGroup.

What arguments the extraction function accepts, and how they are interpreted is described in the sections that describe the individual group libraries.

For example

    PrimitiveGroup( 10, 4 );

returns the 4-th primitive group of degree 10.

The reason for the extraction function is as follows. A group library is usually not stored as a list of groups. Instead a more compact representation for the groups is used. For example the groups in the library of 2-groups are represented by 4 integers. The extraction function hides this representation from you, and allows you to access the group library as if it was a table of groups (two dimensional in the above example).

35.5 The Primitive Groups Library

This group library contains all primitive groups of degree at most 50. There are a total of 406 such groups. Actually to be a little bit more precise, there are 406 inequivalent primitive operations on at most 50 points. Quite a few of the 406 groups are isomorphic.

AllPrimitiveGroups( fun1 , val1 , fun2 , val2 , ...  )

AllPrimitiveGroups returns a list containing all primitive groups that have the properties given as arguments. Each property is specified by passing a pair of arguments, the first being a function, which will be applied to all groups in the library, and the second being a value or a list of values, that this function must return in order to have this group included in the list returned by AllPrimitiveGroups.

The first argument must be DegreeOperation and the second argument either a degree or a list of degree, otherwise AllPrimitiveGroups will print a warning to the effect that the library contains only groups with degrees between 1 and 50.

    gap> l := AllPrimitiveGroups( Size, 120, IsSimple, false );
    #W  AllPrimitiveGroups: degree automatically restricted to [1..50]
    [ S(5), PGL(2,5), S(5) ]
    gap> List( l, g -> g.generators );
    [ [ (1,2,3,4,5), (1,2) ], [ (1,2,3,4,5), (2,3,5,4), (1,6)(3,4) ],
      [ ( 1, 8)( 2, 5, 6, 3)( 4, 9, 7,10), ( 1, 5, 7)( 2, 9, 4)( 3, 8,10)
         ] ]
OnePrimitiveGroup( fun1 , val1 , fun2 , val2 , ...  )

OnePrimitiveGroup returns one primitive group that has the properties given as argument. Each property is specified by passing a pair of arguments, the first being a function, which will be applied to all groups in the library, and the second being a value or a list of values, that this function must return in order to have this group returned by OnePrimitiveGroup. If no such group exists, false is returned.

The first argument must be DegreeOperation and the second argument either a degree or a list of degree, otherwise OnePrimitiveGroup will print a warning to the effect that the library contains only groups with degrees between 1 and 50.

    gap> g := OnePrimitiveGroup( DegreeOperation,5, IsSolvable,false );

    A(5)
    gap> Size( g );
    60

AllPrimitiveGroups and OnePrimitiveGroup recognize the following functions and handle

them usually quite efficient. DegreeOperation, Size, Transitivity, and IsSimple. You

should pass those functions first, e.g., it is more efficient to say AllPrimitiveGroups(

Size,120 , IsAbelian,false ) than to say AllPrimitiveGroups( IsAbelian,false,

Size,120 ) (see 1.26).

PrimitiveGroup( deg, nr )

PrimitiveGroup returns the nr-th primitive group of degree deg. Both deg and nr must be positive integers. The primitive groups of equal degree are sorted with respect to their size, so for example PrimitiveGroup( deg, 1 ) is the smallest primitive group of degree deg, e.g, the cyclic group of size deg, if deg is a prime. Primitive groups of equal degree and size are in no particular order.

    gap>  g := PrimitiveGroup( 8, 1 );
    AGL(1,8)
    gap> g.generators;
    [ (1,2,3,4,5,6,7), (1,8)(2,4)(3,7)(5,6) ]

Apart from the usual components described in 7.111, the group records returned by the above functions have the following components.

transitivity

degree of transitivity of G.

isSharpTransitive

true if G is sharply G.transitivity-fold transitive and false otherwise.

isKPrimitive

true if G is k-fold primitive, and false otherwise.

isOdd

false if G is a subgroup of the alternating group of degree G.degree and true otherwise.

isFrobeniusGroup

true if G is a Frobenius group and false otherwise.

This library was computed by Charles Sims. The list of primitive permutation groups of degree at most 20 was published in [Sim70]. The library was brought into GAP format by Martin Schönert. He assumes the responsibility for all mistakes.

35.6 The Solvable Groups Library

GAP has a library of the 1045 solvable groups of size between 2 and 100. The groups are from lists computed by M. Hall and J. K. Senior (size 64, see [HS64]), R. Laue (size 96, see [Lau82]) and J. Neubüser (other sizes, see [Neu67]).

AllSolvableGroups( fun1 , val1 , fun2 , val2 , ...  )

AllSolvableGroups returns a list containing all solvable groups that have the properties given as arguments. Each property is specified by passing a pair of arguments, the first being a function, which will be applied to all the groups in the library, and the second being a value or a list of values, that this function must return in order to have this group included in the list returned by AllSolvableGroups.

    gap> AllSolvableGroups(Size,24,IsNontrivialDirectProduct,false);
    [ 12.2, grp_24_11, D24, Q8+S3, Sl(2,3), S4 ]

OneSolvableGroup( fun1 , val1 , fun2 , val2 , ...  )

OneSolvableGroup returns a solvable group with the specified properties. Each property is specified by passing a pair of arguments, the first being a function, which will be applied to all the groups in the library, and the second being a value or a list of values, that this function must return in order to have this group returned by OneSolvableGroup. If no such group exists, false is returned.

    gap> OneSolvableGroup(Size,100,x->Size(DerivedSubgroup(x)),10);
    false
    gap> OneSolvableGroup(Size,24,IsNilpotent,false);
    S3x2^2

SolvableGroup( size, nr )

SolvableGroup returns the nr-th group of size size in the library. SolvableGroup will signal an error if size is not between 2 and 100, or if nr is larger than the number of solvable groups of size size. The group is returned as finite polycyclic group (see 24).

    gap> SolvableGroup( 32 , 15 );
    Q8x4

35.7 The 2-Groups Library

The library of 2-groups contains all the 2-groups of size dividing 256. There are a total of 58760 such groups, 1 of size 2, 2 of size 4, 5 of size 8, 14 of size 16, 51 of size 32, 267 of size 64, 2328 of size 128, and 56092 of size 256.

AllTwoGroups( fun1 , val1 , fun2 , val2 , ...  )

AllTwoGroups returns the list of all the 2-groups that have the properties given as arguments. Each property is specified by passing a pair of arguments, the first is a function that can be applied to each group, the second is either a single value or a list of values that the function must return in order to select that group.

    gap> l := AllTwoGroups( Size, 256, Rank, 3, pClass, 2 );
    [ Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ),
      Group( a1, a2, a3, a4, a5, a6, a7, a8 ) ]
    gap> List( l, g -> Length( ConjugacyClasses( g ) ) );
    [ 112, 88, 88, 88 ]

OneTwoGroup( fun1 , val1 , fun2 , val2 , ...  )

OneTwoGroup returns a single 2-group that has the properties given as arguments. Each property is specified by passing a pair of arguments, the first is a function that can be applied to each group, the second is either a single value or a list of values that the function must return in order to select that group.

    gap> g := OneTwoGroup( Size, [64..128], Rank, [2..3], pClass, 5 );
    #I  size restricted to [ 64, 128 ]
    Group( a1, a2, a3, a4, a5, a6 )
    gap> Size( g );
    64
    gap> Rank( g );
    2

AllTwoGroups and OneTwoGroup recognize the following functions and handle them usually very efficiently. Size, Rank for the rank of the Frattini quotient of the group, and pClass for the exponent-p class of the group. Note that Rank and pClass are dummy functions that can be used only in this context, i.e., they can not be applied to arbitrary groups.

TwoGroup( size, nr )

TwoGroup returns the nr-th group of size size. The group is returned as a finite polycyclic group (see 24). TwoGroup will signal an error if size is not a power of 2 between 2 and 256, or nr is larger than the number of groups of size size.

Within each size the following criteria have been used, in turn, to determine the index position of a group in the list

1 increasing generator number;

2 increasing exponent-2 class;

3 the position of its parent in the list of groups of appropriate size;

4 the list in which the Newman and O’Brien implementation of the p-group generation algorithm outputs the immediate descendants of a group.

    gap> g := TwoGroup( 32, 45 );
    Group( a1, a2, a3, a4, a5 )
    gap> Rank( g );
    4
    gap> pClass( g );
    2
    gap> g.abstractRelators;
    [ a1^2*a5^-1, a2^2, a2^-1*a1^-1*a2*a1, a3^2, a3^-1*a1^-1*a3*a1,
      a3^-1*a2^-1*a3*a2, a4^2, a4^-1*a1^-1*a4*a1, a4^-1*a2^-1*a4*a2,
      a4^-1*a3^-1*a4*a3, a5^2, a5^-1*a1^-1*a5*a1, a5^-1*a2^-1*a5*a2,
      a5^-1*a3^-1*a5*a3, a5^-1*a4^-1*a5*a4 ]

Apart from the usual components described in 7.111, the group records returned by the above functions have the following components.

rank

rank of Frattini quotient of G.

pclass

exponent-p class of G.

abstractGenerators

a list of abstract generators of G (see 21.1).

abstractRelators

a list of relators of G stored as words in the abstract generators.

Descriptions of the algorithms used in constructing the library data may be found in [O´ Br90, O´ Br91]. Using these techniques, a library was first prepared in 1987 by M.F. Newman and E.A. O’Brien; a partial description may be found in [NO89].

The library was brought into the GAP format by Werner Nickel, Alice Niemeyer, and E.A. O’Brien.

35.8 The 3-Groups Library

The library of 3-groups contains all the 3-groups of size dividing 729. There are a total of 594 such groups, 1 of size 3, 2 of size 9, 5 of size 27, 15 of size 81, 67 of size 243, and 504 of size 729.

AllThreeGroups( fun1 , val1 , fun2 , val2 , ...  )

AllThreeGroups returns the list of all the 3-groups that have the properties given as arguments. Each property is specified by passing a pair of arguments, the first is a function that can be applied to each group, the second is either a single value or a list of values that the function must return in order to select that group.

    gap> l := AllThreeGroups( Size, 243, Rank, [2..4], pClass, 3 );;
    gap> Length ( l );
    33
    gap>  List( l, g -> Length( ConjugacyClasses( g ) ) );
    [ 35, 35, 35, 35, 35, 35, 35, 243, 99, 99, 51, 51, 51, 51, 51, 51,
      51, 51, 99, 35, 243, 99, 99, 51, 51, 51, 51, 51, 35, 35, 35, 35, 35
     ]

OneThreeGroup( fun1 , val1 , fun2 , val2 , ...  )

OneThreeGroup returns a single 3-group that has the properties given as arguments. Each property is specified by passing a pair of arguments, the first is a function that can be applied to each group, the second is either a single value or a list of values that the function must return in order to select that group.

    gap> g := OneThreeGroup( Size, 729, Rank, 4, pClass, [3..5] );
    Group( a1, a2, a3, a4, a5, a6 )
    gap> IsAbelian( g );
    true

AllThreeGroups and OneThreeGroup recognize the following functions and handle them usually very efficiently. Size, Rank for the rank of the Frattini quotient of the group, and pClass for the exponent-p class of the group. Note that Rank and pClass are dummy functions that can be used only in this context, i.e., they cannot be applied to arbitrary groups.

ThreeGroup( size, nr )

ThreeGroup returns the nr-th group of size size. The group is returned as a finite polycyclic group (see 24). ThreeGroup will signal an error if size is not a power of 3 between 3 and 729, or nr is larger than the number of groups of size size.

Within each size the following criteria have been used, in turn, to determine the index position of a group in the list

1 increasing generator number;

2 increasing exponent-3 class;

3 the position of its parent in the list of groups of appropriate size;

4 the list in which the Newman and O’Brien implementation of the p-group generation algorithm outputs the immediate descendants of a group.

    gap> g := ThreeGroup( 243, 56 );
    Group( a1, a2, a3, a4, a5 )
    gap> pClass( g );
    3
    gap> g.abstractRelators;
    [ a1^3, a2^3, a2^-1*a1^-1*a2*a1*a4^-1, a3^3, a3^-1*a1^-1*a3*a1,
      a3^-1*a2^-1*a3*a2*a5^-1, a4^3, a4^-1*a1^-1*a4*a1*a5^-1,
      a4^-1*a2^-1*a4*a2, a4^-1*a3^-1*a4*a3, a5^3, a5^-1*a1^-1*a5*a1,
      a5^-1*a2^-1*a5*a2, a5^-1*a3^-1*a5*a3, a5^-1*a4^-1*a5*a4 ]

Apart from the usual components described in 7.111, the group records returned by the above functions have the following components.

rank

rank of Frattini quotient of G.

pclass

exponent-p class of G.

abstractGenerators

a list of abstract generators of G (see 21.1).

abstractRelators

a list of relators of G stored as words in the abstract generators.

Descriptions of the algorithms used in constructing the library data may be found in [O´ Br90, O´ Br91].

The library was generated and brought into GAP format by E.A. O’Brien and Colin Rhodes. David Baldwin, M.F. Newman, and Maris Ozols have contributed in various ways to this project and to correctly determining these groups. The library design is modelled on and borrows extensively from the 2-groups library, which was brought into GAP format by Werner Nickel, Alice Niemeyer, and E.A. O’Brien.

35.9 The Irreducible Solvable Linear Groups Library

The IrredSol group library provides access to the irreducible solvable subgroups of GL(n, p), where n > 1, p is prime and p^{n} < 256. The library contains exactly one member from each of the 370 conjugacy classes of such subgroups.

By well known theory, this library also doubles as a library of primitive solvable permutation groups of non-prime degree less than 256. To access the data in this form, you must first build the matrix group(s) of interest and then call the function

PrimitivePermGroupIrreducibleMatGroup( matgrp )

This function returns a permutation group isomorphic to the semidirect product of an irreducible matrix group (over a finite field) and its underlying vector space.

AllIrreducibleSolvableGroups( fun1 , val1 , fun2 , val2 , ...  )

AllIrreducibleSolvableGroups returns a list containing all irreducible solvable linear groups that have the properties given as arguments. Each property is specified by passing a pair of arguments, the first being a function which will be applied to all groups in the library, and the second being a value or a list of values that this function must return in order to have this group included in the list returned by AllIrreducibleSolvableGroups.

    gap> AllIrreducibleSolvableGroups( Dimension, 2,
    >                                   CharFFE, 3,
    >                                   Size, 8 );
    [ Group( [ [ 0*Z(3), Z(3)^0 ], [ Z(3)^0, 0*Z(3) ] ],
             [ [ Z(3), 0*Z(3) ], [ 0*Z(3), Z(3)^0 ] ],
             [ [ Z(3)^0, 0*Z(3) ], [ 0*Z(3), Z(3) ] ] ),
      Group( [ [ 0*Z(3), Z(3)^0 ], [ Z(3), 0*Z(3) ] ],
             [ [ Z(3)^0, Z(3) ], [ Z(3), Z(3) ] ] ),
      Group( [ [ 0*Z(3), Z(3)^0 ], [ Z(3)^0, Z(3) ] ] ) ]

OneIrreducibleSolvableGroup( fun1 , val1 , fun2 , val2 , ...  )

OneIrreducibleSolvableGroup returns one irreducible solvable linear group that has the properties given as arguments. Each property is specified by passing a pair of arguments, the first being a function which will be applied to all groups in the library, and the second being a value or a list of values that this function must return in order to have this group returned by OneIrreducibleSolvableGroup. If no such group exists, false is returned.

    gap> OneIrreducibleSolvableGroup( Dimension, 4,
    >                                  IsLinearlyPrimitive, false );
    Group( [ [ 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
             [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0 ],
             [ Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2) ],
             [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ] ],
           [ [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ],
             [ Z(2)^0, Z(2)^0, 0*Z(2), 0*Z(2) ],
             [ 0*Z(2), 0*Z(2), Z(2)^0, 0*Z(2) ],
             [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0 ] ],
           [ [ Z(2)^0, 0*Z(2), 0*Z(2), 0*Z(2) ],

             [ 0*Z(2), Z(2)^0, 0*Z(2), 0*Z(2) ],
             [ 0*Z(2), 0*Z(2), 0*Z(2), Z(2)^0 ],
             [ 0*Z(2), 0*Z(2), Z(2)^0, Z(2)^0 ] ] )
AllIrreducibleSolvableGroups and OneIrreducibleSolvableGroup recognize the fol-

lowing functions and handle them very efficiently (because the information is stored with the groups and so no computations are needed):Dimension for the linear degree, CharFFE for

the field characteristic, Size, IsLinearlyPrimitive, and MinimalBlockDimension. Note

that the last two are dummy functions that can be used only in this context. Their meaning is explained at the end of this section.

IrreducibleSolvableGroup( n, p, i )

IrreducibleSolvableGroup returns the i-th irreducible solvable subgroup of GL( n, p ). The irreducible solvable subgroups of GL(n, p) are ordered with respect to the following criteria

1. increasing size;

2. increasing guardian number.

If two groups have the same size and guardian, they are in no particular order. (See the library documentation or [Sho92] for the meaning of guardian.)

    gap> g := IrreducibleSolvableGroup( 3, 5, 12 );
    Group( [ [ 0*Z(5), Z(5)^2, 0*Z(5) ], [ Z(5)^2, 0*Z(5), 0*Z(5) ],
      [ 0*Z(5), 0*Z(5), Z(5)^2 ] ],
    [ [ 0*Z(5), Z(5)^0, 0*Z(5) ], [ 0*Z(5), 0*Z(5), Z(5)^0 ],
      [ Z(5)^0, 0*Z(5), 0*Z(5) ] ],
    [ [ Z(5)^2, 0*Z(5), 0*Z(5) ], [ 0*Z(5), Z(5)^0, 0*Z(5) ],
      [ 0*Z(5), 0*Z(5), Z(5)^2 ] ],
    [ [ Z(5)^0, 0*Z(5), 0*Z(5) ], [ 0*Z(5), Z(5)^2, 0*Z(5) ],
      [ 0*Z(5), 0*Z(5), Z(5)^2 ] ],
    [ [ Z(5), 0*Z(5), 0*Z(5) ], [ 0*Z(5), Z(5), 0*Z(5) ],
      [ 0*Z(5), 0*Z(5), Z(5) ] ] )

Apart from the usual components described in 7.111, the group records returned by the above functions have the following components.

size

size of G.

isLinearlyPrimitive

false if G preserves a direct sum decomposition of the underlying vector space, and true otherwise.

minimalBlockDimension

not bound if G is linearly primitive; otherwise equals the dimension of the blocks in an unrefinable system of imprimitivity for G.

This library was computed and brought into GAP format by Mark Short. Descriptions of the algorithms used in computing the library data can be found in [Sho92].

Mappings

A mapping is an object that maps each element of its source to a value in its range.

Precisely, a mapping is a triple. The source is a set of objects. The range is another set of objects. The relation is a subset S of the cartesian product of the source with the range, such that for each element elm of the source there is exactly one element img of the range, so that the pair (elm, img) lies in S. This img is called the image of elm under the mapping, and we say that the mapping maps elm to img.

A multi valued mapping is an object that maps each element of its source to a set of values in its range.

Precisely, a multi valued mapping is a triple. The source is a set of objects. The range is another set of objects. The relation is a subset S of the cartesian product of the source with the range. For each element elm of the source the set img such that the pair (elm, img) lies in S is called the set of images of elm under the mapping, and we say that the mapping maps elm to this set.

Thus a mapping is a special case of a multi valued mapping where the set of images of each element of the source contains exactly one element, which is then called the image of the element under the mapping.

Mappings are created by mapping constructors such as MappingByFunction (see 36.18)

or NaturalHomomorphism (see 7.103).

This chapter contains sections that describe the functions that test whether an object is a mapping (see 36.1), whether a mapping is single valued (see 36.2), and the various functions that test if such a mapping has a certain property (see 36.3, 36.4, 36.5, 37.1, 37.2, 37.3, 37.4, 37.3, and 37.6).

Next this chapter contains functions that describe how mappings are compared (see 36.6) and the operations that are applicable to mappings (see 36.7).

Next this chapter contains sections that describe the functions that deal with the images and preimages of elements under mappings (see 36.8, 36.9, 36.10, 36.11, 36.12, and 36.13).

Next this chapter contains sections that describe the functions that compute the composition of two mappings, the power of a mapping, the inverse of a mapping, and the identity mapping on a certain domain (see 36.14, 36.15, 36.16, and 36.17).

Finally this chapter also contains a section that describes how mappings are represented internally (see 36.19). The functions described in this chapter are in the file libname/"mapping.g".

36.1 IsGeneralMapping

IsGeneralMapping( obj )

IsGeneralMapping returns true if the object obj is a mapping (possibly multi valued) and false otherwise.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsGeneralMapping( p4 );
true
gap> IsGeneralMapping( InverseMapping( p4 ) );

true # note that the inverse mapping is multi valued

gap> IsGeneralMapping( x -> x^4 );

false # a function is not a mapping See 36.18 for the definition of MappingByFunction and 36.16 for InverseMapping.

36.2 IsMapping

IsMapping( map )

IsMapping returns true if the general mapping map is single valued and false otherwise. Signals an error if map is not a general mapping.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsMapping( p4 );
true
gap> IsMapping( InverseMapping( p4 ) );

false # note that the inverse mapping is multi valued

gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsMapping( p5 );
true
gap> IsMapping( InverseMapping( p5 ) );

true # p5 is a bijection IsMapping first tests if the flag map.isMapping is bound. If the flag is bound, it returns its value. Otherwise it calls map.operations.IsMapping( map ), remembers the returned value in map.isMapping, and returns it.

The default function called this way is MappingOps.IsMapping, which computes the sets of images of all the elements in the source of map, provided this is finite, and returns true if all those sets have size one. Look in the index under IsMapping to see for which mappings this function is overlaid.

36.3 IsInjective

IsInjective( map )

IsInjective returns true if the mapping map is injective and false otherwise. Signals an error if map is a multi valued mapping.

A mapping map is injective if for each element img of the range there is at most one element elm of the source that map maps to img.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> IsInjective( p4 );
    false
    gap> IsInjective( InverseMapping( p4 ) );
    Error, <map> must be a single valued mapping
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> IsInjective( p5 );
    true
    gap> IsInjective( InverseMapping( p5 ) );

true # p5 is a bijection

IsInjective first tests if the flag map.isInjective is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.isInjective( map ), remembers the returned value in map.isInjective, and returns it.

The default function called this way is MappingOps.IsInjective, which compares the sizes of the source and image of map, and returns true if they are equal (see 36.8). Look in the index under IsInjective to see for which mappings this function is overlaid.

36.4 IsSurjective

IsSurjective( map )

IsSurjective returns true if the mapping map is surjective and false otherwise. Signals an error if map is a multi valued mapping.

A mapping map is surjective if for each element img of the range there is at least one element elm of the source that map maps to img.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );

MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsSurjective( p4 );
false
gap> IsSurjective( InverseMapping( p4 ) );
Error, <map> must be a single valued mapping
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsSurjective( p5 );
true
gap> IsSurjective( InverseMapping( p5 ) );

true # p5 is a bijection

IsSurjective first tests if the flag map.isSurjective is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsSurjective( map ), remembers the returned value in map.isSurjective, and returns it.

The default function called this way is MappingOps.IsSurjective, which compares the sizes of the range and image of map, and returns true if they are equal (see 36.8). Look in the index under IsSurjective to see for which mappings this function is overlaid.

36.5 IsBijection

IsBijection( map )

IsBijection returns true if the mapping map is a bijection and false otherwise. Signals an error if map is a multi valued mapping.

A mapping map is a bijection if for each element img of the range there is exactly one element elm of the source that map maps to img. We also say that map is bijective.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsBijection( p4 );
false
gap> IsBijection( InverseMapping( p4 ) );
Error, <map> must be a single valued mapping
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsBijection( p5 );
true
gap> IsBijection( InverseMapping( p5 ) );

true # p5 is a bijection IsBijection first tests if the flag map.isBijection is bound. If the flag is bound, it returns its value. Otherwise it calls map.operations.IsBijection( map ), remembers the returned value in map.isBijection, and returns it.

The default function called this way is MappingOps.IsBijection, which calls IsInjective and IsSurjective, and returns the logical and of the results. This function is seldom overlaid, because all the interesting work is done by IsInjective and IsSurjective.

36.6 Comparisons of Mappings

map1 = map2 map1 <> map2

The equality operator = applied to two mappings map1 and map2 evaluates to true if the two mappings are equal and to false otherwise. The unequality operator <> applied to two mappings map1 and map2 evaluates to true if the two mappings are not equal and to false otherwise. A mapping can also be compared with another object that is not a mapping, of course they are never equal.

Two mappings are considered equal if and only if their sources are equal, their ranges are equal, and for each elment elm of the source Images( map1 , elm ) is equal to Images( map2 , elm ) (see 36.9).

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> p13 := MappingByFunction( g, g, x -> x^13 );
    MappingByFunction( g, g, function ( x )
        return x ^13;
    end )
    gap> p4 = p13;
    false
    gap> p13 = IdentityMapping( g );
    true

map1 < map2 map1 <= map2 map1 > map2 map1 >= map2

The operators <, <=, >, and >= applied to two mappings evaluates to true if map1 is less than, less than or equal to, greater than, or greater than or equal to map2 and false otherwise. A mapping can also be compared with another object that is not a mapping, everything except booleans, lists, and records is smaller than a mapping.

If the source of map1 is less than the source of map2 , then map1 is considered to be less than map2 . If the sources are equal and the range of map1 is less than the range of map2 , then map1 is considered to be less than map2 . If the sources and the ranges are equal the mappings are compared lexicographically with respect to the sets of images of the elements of the source under the mappings.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> p4 < p5;

true # since (5,6,7) is the smallest nontrivial element of g # and the image of (5,6,7) under p4 is smaller # than the image of (5,6,7) under p5

The operator = calls map2 .operations.=( map1 , map2 ) and returns this value. The operator <> also calls map2 .operations.=( map1 , map2 ) and returns the logical not of this value.

The default function called this way is MappingOps.=, which first compares the sources of map1 and map2 , then, if they are equal, compares the ranges of map1 and map2 , and then, if both are equal and the source is finite, compares the images of all elements of the source under map1 and map2 . Look in the index under equality to see for which mappings this function is overlaid.

The operator < calls map2 .operations.<( map1 , map2 ) and returns this value. The operator <= calls map2 .operations.<( map2 , map1 ) and returns the logical not of this value. The operator > calls map2 .operations.<( map2 , map1 ) and returns this value. The operator >= calls map2 .operations.<( map1 , map2 ) and returns the logical not of this value.

The default function called this way is MappingOps.<, which first compares the sources of map1 and map2 , then, if they are equal, compares the ranges of map1 and map2 , and then, if both are equal and the source is finite, compares the images of all elements of the source under map1 and map2 . Look in the index under ordering to see for which mappings this function is overlaid.

36.7 Operations for Mappings

map1 * map2

The product operator * applied to two mappings map1 and map2 evaluates to the product of the two mappings, i.e., the mapping map that maps each element elm of the source of map1 to the value (elm ^map1 ) ^map2 . Note that the range of map1 must be a subset of the source of map2 . If map1 and map2 are homomorphisms then so is the result. This can also be expressed as CompositionMapping( map2 , map1 ) (see 36.14). Note that the arguments of CompositionMapping are reversed.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )

    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> p20 := p4 * p5;
    CompositionMapping( MappingByFunction( g, g, function ( x )
        return x ^5;
    end ), MappingByFunction( g, g, function ( x )
        return x ^4;
    end ) )

list * map map * list

As with every other type of group elements a mapping map can also be multiplied with a list of mappings list. The result is a new list, such that each entry is the product of the corresponding entry of list with map (see 25.12).

elm ^map

The power operator ^applied to an element elm and a mapping map evaluates to the image of elm under map, i.e., the element of the range to which map maps elm. Note that map must be a single valued mapping, a multi valued mapping is not allowed (see 36.9). This can also be expressed as Image( map, elm ) (see 36.8).

    gap> (1,2,3,4) ^p4;
    ()
    gap> (2,4)(5,6,7) ^p20;
    (5,7,6)

map ^0

The power operator ^applied to a mapping map, for which the range must be a subset of the source, and the integer 0 evaluates to the identity mapping on the source of map, i.e., the mapping that maps each element of the source to itself. If map is a homomorphism then so is the result. This can also be expressed as IdentityMapping( map.source ) (see 36.17).

    gap> p20 ^0;
    IdentityMapping( g )

map ^n

The power operator ^applied to a mapping map, for which the range must be a subset of the source, and an positive integer n evaluates to the n-fold composition of map. If map is a homomorphism then so is the result. This can also be expressed as PowerMapping( map, n ) (see 36.15).

    gap> p16 := p4 ^2;
    CompositionMapping( CompositionMapping( IdentityMapping( g ),
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end ) ), CompositionMapping( IdentityMapping( g ),
    MappingByFunction( g, g, function ( x )
        return x ^4;

    end ) ) )
    gap> p16 = MappingByFunction( g, g, x -> x^16 );
    true
bij ^-1

The power operator ^applied to a bijection bij and the integer -1 evaluates to the inverse mapping of bij , i.e., the mapping that maps each element img of the range of bij to the uniq element elm of the source of bij that maps to img. Note that bij must be a bijection, a mapping that is not a bijection is not allowed. This can also be expressed as InverseMapping( bij ) (see 36.16).

    gap> p5 ^-1;
    InverseMapping( MappingByFunction( g, g, function ( x )
        return x ^5;
    end ) )
    gap> p4 ^-1;
    Error, <lft> must be a bijection

bij ^z

The power operator ^applied to a bijection bij , for which the source and the range must be equal, and an integer z returns the z-fold composition of bij . If z is 0 or positive see above, if z is negative, this is equivalent to (bij ^-1) ^-z. If bij is an automorphism then so is the result.

aut1 ^aut2

The power operator ^applied to two automorphisms aut1 and aut2 , which must have equal sources (and thus ranges) returns the conjugate of aut1 by aut2 , i.e., aut2 ^-1 * aut1 * aut2 . The result if of course again an automorphism.

The operator * calls map2 .operations.*( map1 , map2 ) and returns this value.

The default function called this way is MappingOps.* which calls CompositionMapping to do the work. This function is seldom overlaid, since CompositionMapping does all the interesting work.

The operator ^calls map.operations.^( map1 , map2 ) and returns this value.

The default function called this way is MappingOps.^, which calls Image, IdentityMapping, InverseMapping, or PowerMapping to do the work. This function is seldom overlaid, since

Image, IdentityMapping, InverseMapping, and PowerMapping do all the interesting work.

36.8 Image

Image( map, elm )

In this form Image returns the image of the element elm of the source of the mapping map under map, i.e., the element of the range to which map maps elm. Note that map must be a single valued mapping, a multi valued mapping is not allowed (see 36.9). This can also be expressed as elm ^map (see 36.7).

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )

        return x ^4;
    end )
    gap> Image( p4, (2,4)(5,6,7) );
    (5,6,7)
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> Image( p5, (2,4)(5,6,7) );
    (2,4)(5,7,6)
Image( map, elms )

In this form Image returns the image of the set of elements elms of the source of the mapping map under map, i.e., set of images of the elements in elms. elms may be a proper set (see 26) or a domain (see 4). The result will be a subset of the range of map, either as a proper set or as a domain. Again map must be a single valued mapping, a multi valued mapping is not allowed (see 36.9).

    gap> Image( p4, Subgroup( g, [ (2,4), (5,6,7) ] ) );
    [ (), (5,6,7), (5,7,6) ]
    gap> Image( p5, [ (5,6,7), (2,4) ] );
    [ (5,7,6), (2,4) ]

Note that in the first example, the result is returned as a proper set, even though it is mathematically a subgroup. This is because p4 is not known to be a homomorphism, even though it is one.

Image( map )

In this form Image returns the image of the mapping map, i.e., the subset of element of the range of map that are actually values of map. Note that in this case the argument may also be a multi valued mapping.

    gap> Image( p4 );
    [ (), (5,6,7), (5,7,6) ]
    gap> Image( p5 ) = g;
    true

Image firsts checks in which form it is called. In the first case it calls map.operations.ImageElm( map, elm ) and returns this value. The default function called this way is MappingOps.ImageElm, which checks that map is indeed a single valued mapping, calls Images( map, elm ), and returns the single element of the set returned by Images. Look in the index under Image to see for which mappings this function is overlaid. In the second case it calls map.operations.ImageSet( map, elms ) and returns this value. The default function called this way is MappingOps.ImageSet, which checks that map is indeed a single valued mapping, calls Images( map, elms ), and returns this value. Look in the index under Image to see for which mappings this function is overlaid. In the third case it tests if the field map.image is bound. If this field is bound, it simply returns this value. Otherwise it calls map.operations.ImageSource( map ), remembers the returned value in map.image, and returns it.

The default function called this way is MappingOps.ImageSource, which calls Images( map, map.source ), and returns this value. This function is seldom overlaid, since all the

work is done by map.operations.ImagesSet.

36.9 Images

Images( map, elm )

In this form Images returns the set of images of the element elm in the source of the mapping map under map. map may be a multi valued mapping.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> i4 := InverseMapping( p4 );
InverseMapping( MappingByFunction( g, g, function ( x )
    return x ^4;
end ) )
gap> IsMapping( i4 );

false # i4 is multi valued

gap> Images( i4, () );
[ (), (2,4), (1,2)(3,4), (1,2,3,4), (1,3), (1,3)(2,4), (1,4,3,2),
  (1,4)(2,3) ]
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> i5 := InverseMapping( p5 );
InverseMapping( MappingByFunction( g, g, function ( x )
    return x ^5;
end ) )
gap> Images( i5, () );
[ () ]
Images( map, elms )

In this form Images returns the set of images of the set of elements elms in the source of map under map. map may be a multi valued mapping. In any case Images returns a set of elements of the range of map, either as a proper set (see 26) or as a domain (see 4).

gap> Images( i4, [ (), (5,6,7) ] );
[ (), (5,6,7), (2,4), (2,4)(5,6,7), (1,2)(3,4), (1,2)(3,4)(5,6,7),
  (1,2,3,4), (1,2,3,4)(5,6,7), (1,3), (1,3)(5,6,7), (1,3)(2,4),
  (1,3)(2,4)(5,6,7), (1,4,3,2), (1,4,3,2)(5,6,7), (1,4)(2,3),
  (1,4)(2,3)(5,6,7) ]
gap> Images( i5, [ (), (5,6,7) ] );
[ (), (5,7,6) ]

Images first checks in which form it is called. In the first case it calls map.operations.ImagesElm( map, elm ) and returns this value.

The default function called this way is MappingOps.ImagesElm, which just raises an error, since their is no default way to compute the images of an element under a mapping about which nothing is known. Look in the index under Images to see how images are computed for the various mappings.

In the second case it calls map.operations.ImagesSet( map, elms ) and returns this value.

The default function called this way is MappingOps.ImagesSet, which returns the union of the images of all the elements in the set elms. Look in the index under Images to see for which mappings this function is overlaid.

36.10 ImagesRepresentative

ImagesRepresentative( map, elm )

ImagesRepresentative returns a representative of the set of images of elm under map, i.e., a single element img, such that img in Images( map, elm ) (see 36.9). map may be a multi valued mapping.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> i4 := InverseMapping( p4 );
    InverseMapping( MappingByFunction( g, g, function ( x )
        return x ^4;
    end ) )
    gap> IsMapping( i4 );

false # i4 is multi valued

    gap> ImagesRepresentative( i4, () );
    ()
ImagesRepresentative calls
map.operations.ImagesRepresentative( map, elm ) and returns this value.

The default function called this way is MappingOps.ImagesRepresentative, which calls Images( map, elm ) and returns the first element in this set. Look in the index under ImagesRepresentative to see for which mappings this function is overlaid.

36.11 PreImage

PreImage( bij , img )

In this form PreImage returns the preimage of the element img of the range of the bijection bij under bij . The preimage is the unique element of the source of bij that is mapped by bij to img. Note that bij must be a bijection, a mapping that is not a bijection is not allowed (see 36.12).

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )

        return x ^4;
    end )
    gap> PreImage( p4, (5,6,7) );
    Error, <bij> must be a bijection, not an arbitrary mapping
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> PreImage( p5, (2,4)(5,6,7) );
    (2,4)(5,7,6)
PreImage( bij , imgs )

In this form PreImage returns the preimage of the elements imgs of the range of the bijection bij under bij . The primage of imgs is the set of all preimages of the elements in imgs. imgs may be a proper set (see 26.2) or a domain (see 4). The result will be a subset of the source of bij , either as a proper set or as a domain. Again bij must be a bijection, a mapping that is not a bijection is not allowed (see 36.12).

    gap> PreImage( p4, [ (), (5,6,7) ] );
    [ (), (5,6,7), (2,4), (2,4)(5,6,7), (1,2)(3,4), (1,2)(3,4)(5,6,7),
      (1,2,3,4), (1,2,3,4)(5,6,7), (1,3), (1,3)(5,6,7), (1,3)(2,4),
      (1,3)(2,4)(5,6,7), (1,4,3,2), (1,4,3,2)(5,6,7), (1,4)(2,3),
      (1,4)(2,3)(5,6,7) ]
    gap> PreImage( p5, Subgroup( g, [ (5,7,6), (2,4) ] ) );
    [ (), (5,6,7), (5,7,6), (2,4), (2,4)(5,6,7), (2,4)(5,7,6) ]
PreImage( map )

In this form PreImage returns the preimage of the mapping map. The preimage is the set of elements elm of the source of map that are actually mapped to at least one element, i.e., for which PreImages( map, elm ) is nonempty. Note that in this case the argument may be an arbitrary mapping (especially a multi valued one).

    gap> PreImage( p4 ) = g;
    true

PreImage firsts checks in which form it is called.

In the first case it calls bij .operations.PreImageElm( bij , elm ) and returns this value.

The default function called this way is MappingOps.PreImageElm, which checks that bij is indeed a bijection, calls PreImages( bij , elm ), and returns the single element of the set returned by PreImages. Look in the index under PreImage to see for which mappings this function is overlaid.

In the second case it calls bij .operations.PreImageSet( bij , elms ) and returns this value.

The default function called this way is MappingOps.PreImageSet, which checks that map is indeed a bijection, calls PreImages( bij , elms ), and returns this value. Look in the index under PreImage to see for which mappings this is overlaid.

In the third case it tests if the field map.preImage is bound. If this field is bound, it simply returns this value. Otherwise it calls map.operations.PreImageRange( map ), remembers the returned value in map.preImage, and returns it.

The default function called this way is MappingOps.PreImageRange, which calls PreImages( map, map.source ), and returns this value. This function is seldom overlaid, since all the

work is done by map.operations.PreImagesSet.

36.12 PreImages

PreImages( map, img )

In the first form PreImages returns the set of elements from the source of the mapping map that are mapped by map to the element img in the range of map, i.e., the set of elements elm such that img in Images( map, elm ) (see 36.9). map may be a multi valued mapping.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> PreImages( p4, (5,6,7) );
    [ (5,6,7), (2,4)(5,6,7), (1,2)(3,4)(5,6,7), (1,2,3,4)(5,6,7),
      (1,3)(5,6,7), (1,3)(2,4)(5,6,7), (1,4,3,2)(5,6,7),
      (1,4)(2,3)(5,6,7) ]
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> PreImages( p5, (2,4)(5,6,7) );
    [ (2,4)(5,7,6) ]
PreImages( map, imgs )

In the second form PreImages returns the set of all preimages of the elements in the set of elements imgs, i.e., the union of the preimages of the single elements of imgs. map may be a multi valued mapping.

    gap> PreImages( p4, [ (), (5,6,7) ] );
    [ (), (5,6,7), (2,4), (2,4)(5,6,7), (1,2)(3,4), (1,2)(3,4)(5,6,7),
      (1,2,3,4), (1,2,3,4)(5,6,7), (1,3), (1,3)(5,6,7), (1,3)(2,4),
      (1,3)(2,4)(5,6,7), (1,4,3,2), (1,4,3,2)(5,6,7), (1,4)(2,3),
      (1,4)(2,3)(5,6,7) ]
    gap> PreImages( p5, [ (), (5,6,7) ] );
    [ (), (5,7,6) ]

PreImages first checks in which form it is called.

In the first case it calls map.operations.PreImagesElm( map, img ) and returns this value.

The default function called this way is MappingOps.PreImagesElm, which runs through all elements of the source of map, if it is finite, and returns the set of those that have img in their images. Look in the index under PreImages to see for which mappings this function is overlaid.

In the second case if calls map.operations.PreImagesSet( map, imgs ) and returns this value.

The default function called this way is MappingOps.PreImagesSet, which returns the union of the preimages of all the elements of the set imgs. Look in the index under PreImages to see for which mappings this function is overlaid.

36.13 PreImagesRepresentative

PreImagesRepresentative( map, img )

PreImagesRepresentative returns an representative of the set of preimages of img under map, i.e., a single element elm, such that img in Images( map, elm ) (see 36.9).

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> PreImagesRepresentative( p4, (5,6,7) );
    (5,6,7)
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> PreImagesRepresentative( p5, (2,4)(5,6,7) );
    (2,4)(5,7,6)
PreImagesRepresentative calls
map.operations.PreImagesRepresentative( map, img ) and returns this value.

The default function called this way is MappingOps.PreImagesRepresentative, which calls PreImages( map, img ) and returns the first element in this set. Look in the index under PreImagesRepresentative to see for which mappings this function is overlaid.

36.14 CompositionMapping

CompositionMapping( map1 ..  )

CompositionMapping returns the composition of the mappings map1 , map2 , etc. where the range of each mapping must be a subset of the source of the previous mapping. The mappings need not be single valued mappings, i.e., multi valued mappings are allowed.

The composition of map1 and map2 is the mapping map that maps each element elm of the source of map2 to Images( map1 , Images( map2 , elm ) ). If map1 and map2 are single valued mappings this can also be expressed as map2 * map1 (see 36.7). Note the reversed operands.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )

    return x ^5;
end )
gap> p20 := CompositionMapping( p4, p5 );
CompositionMapping( MappingByFunction( g, g, function ( x )
    return x ^4;
end ), MappingByFunction( g, g, function ( x )
    return x ^5;
end ) )
gap> (2,4)(5,6,7) ^p20;
(5,7,6)
CompositionMapping calls
map2 .operations.CompositionMapping( map1 , map2 ) and returns this value.

The default function called this way is MappingOps.CompositionMapping, which constructs a new mapping com. This new mapping remembers map1 and map2 as its factors in com.map1 and com.map2 and delegates everything to them. For example to compute

Images( com, elm ), com.operations.ImagesElm calls Images( com.map1, Images(

com.map2, elm ) ). Look in the index under CompositionMapping to see for which mappings this function is overlaid.

36.15 PowerMapping

PowerMapping( map, n )

PowerMapping returns the n-th power of the mapping map. map must be a mapping whose range is a subset of its source. n must be a nonnegative integer. map may be a multi valued mapping.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> p16 := p4 ^2;
CompositionMapping( CompositionMapping( IdentityMapping( g ),
MappingByFunction( g, g, function ( x )
    return x ^4;
end ) ), CompositionMapping( IdentityMapping( g ),
MappingByFunction( g, g, function ( x )
    return x ^4;
end ) ) )
gap> p16 = MappingByFunction( g, g, x -> x^16 );
true
PowerMapping calls map.operations.PowerMapping( map, n ) and returns this value.

The default function called this way is MappingOps.PowerMapping, which computes the power using a binary powering algorithm, IdentityMapping, and CompositionMapping. This function is seldom overlaid, because CompositionMapping does the interesting work.

36.16 InverseMapping

InverseMapping( map )

InverseMapping returns the inverse mapping of the mapping map. The inverse mapping inv is a mapping with source map.range, range map.source, such that each element elm of its source is mapped to the set PreImages( map, elm ) (see 36.12). map may be a multi valued mapping.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> i4 := InverseMapping( p4 );
    InverseMapping( MappingByFunction( g, g, function ( x )
        return x ^4;
    end ) )
    gap> Images( i4, () );
    [ (), (2,4), (1,2)(3,4), (1,2,3,4), (1,3), (1,3)(2,4), (1,4,3,2),
      (1,4)(2,3) ]

InverseMapping first tests if the field map.inverseMapping is bound. If the field is bound, it returns its value. Otherwise it calls map.operations.InverseMapping( map ), remembers the returned value in map.inverseMapping, and returns it.

The default function called this way is MappingOps.InverseMapping, which constructs a new mapping inv. This new mapping remembers map as its own inverse mapping in inv.inverseMapping and delegates everything to it. For example to compute Image(

inv, elm ), inv.operations.ImageElm calls PreImage(inv.inverseMapping,elm). Spe-

cial types of mappings will overlay this default function with more efficient functions.

36.17 IdentityMapping

IdentityMapping( D )

IdentityMapping returns the identity mapping on the domain D.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> i := IdentityMapping( g );
    IdentityMapping( g )
    gap> (1,2,3,4) ^i;
    (1,2,3,4)
    gap> IsBijection( i );
    true
IdentityMapping calls D.operations.IdentityMapping( D ) and returns this value.

The functions usually called this way are GroupOps.IdentityMapping if the domain D is a group and FieldOps.IdentityMapping if the domain D is a field.

36.18 MappingByFunction

MappingByFunction( D, E, fun )

MappingByFunction returns a mapping map with source D and range E such that each element d of D is mapped to the element fun(d), where fun is a GAP function.

    gap> g := Group( (1,2,3,4), (1,2) );;  g.name := "g";;
    gap> m := MappingByFunction( g, g, x -> x^2 );
    MappingByFunction( g, g, function ( x )
        return x ^2;
    end )
    gap> (1,2,3) ^m;
    (1,3,2)
    gap> IsHomomorphism( m );
    false

MappingByFunction constructs the mapping in the obvious way. For example the image of an element under map is simply computed by applying fun to the element.

36.19 Mapping Records

A mapping map is represented by a record with the following components

isGeneralMapping

always true, indicating that this is a general mapping.

source

the source of the mapping, i.e., the set of elements to which the mapping can be applied.

range

the range of the mapping, i.e., a set in which all value of the mapping lie.

The following entries are optional. The functions with the corresponding names will generally test if they are present. If they are then their value is simply returned. Otherwise the functions will perform the computation and add those fields to those record for the next time.

isMapping

true if map is a single valued mapping and false otherwise.

isInjective
     isSurjective
     isBijection
     isHomomorphism
     isMonomorphism
     isEpimorphism
     isIsomorphism
     isEndomorphism
     isAutomorphism

true if map has the corresponding property and false otherwise.

preImage

the subset of map.source of elements pre that are actually mapped to at least one element, i.e., for which Images( map, pre ) is nonempty.

image

the subset of map.range of the elements img that are actually values of the mapping, i.e., for which PreImages( map, img ) is nonempty.

inverseMapping

the inverse mapping of map. This is a mapping from map.range to map.source that maps each element img to the set PreImages( map, img ).

The following entry is optional. It must be bound only if the inverse of map is indeed a single valued mapping.

inverseFunction

the inverse function of map.

The following entry is optional. It must be bound only if map is a homomorphism.

kernel

the elements of map.source that are mapped to the identity element of map.range.

Homomorphisms

An important special class of mappings are homomorphisms.

A mapping map is a homomorphism if the source and the range are domains of the same category, and map respects their structure. For example, if both source and range are groups and for each x, y in the source (xy)^{map} = x^{map}y^{map}, then map is a group homomorphism.

GAP currently supports field and group homomorphisms (see 6.13, 7.99).

Homomorphism are created by homomorphism constructors, which are ordinary GAP functions that return homomorphisms, such as FrobeniusAutomorphism (see 17.11) or

NaturalHomomorphism (see 7.103).

The first section in this chapter describes the function that tests whether a mapping is a homomorphism (see 37.1). The next sections describe the functions that test whether a homomorphism has certain properties (see 37.2, 37.3, 37.4, 37.5, and 37.6). The last section describes the function that computes the kernel of a homomorphism (see 37.7).

Because homomorphisms are just a special case of mappings all operations and functions described in chapter 36 are applicable to homomorphisms. For example, the image of an element elm under a homomorphism hom can be computed by elm ^hom (see 36.7).

37.1 IsHomomorphism

IsHomomorphism( map )

IsHomomorphism returns true if the mapping map is a homomorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping map is a homomorphism if the source and the range are sources of the same category, and map respects the structure. For example, if both source and range are groups and for each x, y in the source (xy)^{map} = x^{map}y^{map}, then map is a homomorphism.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )

gap> IsHomomorphism( p4 );
true
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsHomomorphism( p5 );
true
gap> p6 := MappingByFunction( g, g, x -> x^6 );
MappingByFunction( g, g, function ( x )
    return x ^6;
end )
gap> IsHomomorphism( p6 );
false

IsHomomorphism first tests if the flag map.isHomomorphism is bound. If the flag is bound, it returns its value. Otherwise it calls map.source.operations.IsHomomorphism( map ), remembers the returned value in map.isHomomorphism, and returns it. The functions usually called this way are IsGroupHomomorphism if the source of map is a group and IsFieldHomomorphism if the source of map is a field (see 7.100, 6.14).

37.2 IsMonomorphism

IsMonomorphism( map )

IsMonomorphism returns true if the mapping map is a monomorphism and false otherwise. Signals an error if map is a multi valued mapping. A mapping is a monomorphism if it is an injective homomorphism (see 36.3, 37.1).

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsMonomorphism( p4 );
false
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsMonomorphism( p5 );
true

IsMonomorphism first test if the flag map.isMonomorphism is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsMonomorphism( map ), remembers the returned value in map.isMonomorphism, and returns it. The default function called this way is MappingOps.IsMonomorphism, which calls the functions IsInjective and IsHomomorphism, and returns the logical and of the results. This function is seldom overlaid, because all the interesting work is done in IsInjective and

IsHomomorphism.

37.3 IsEpimorphism

IsEpimorphism( map )

IsEpimorphism returns true if the mapping map is an epimorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping is an epimorphism if it is an surjective homomorphism (see 36.4, 37.1).

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsEpimorphism( p4 );
false
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsEpimorphism( p5 );
true

IsEpimorphism first test if the flag map.isEpimorphism is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsEpimorphism( map ), remembers the returned value in map.isEpimorphism, and returns it.

The default function called this way is MappingOps.IsEpimorphism, which calls the functions IsSurjective and IsHomomorphism, and returns the logical and of the results. This function is seldom overlaid, because all the interesting work is done in IsSurjective and

IsHomomorphism.

37.4 IsIsomorphism

IsIsomorphism( map )

IsIsomorphism returns true if the mapping map is an isomorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping is an isomorphism if it is a bijective homomorphism (see 36.5, 37.1).

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsIsomorphism( p4 );
false
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsIsomorphism( p5 );

    true

IsIsomorphism first test if the flag map.isIsomorphism is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsIsomorphism( map ), remembers the returned value in map.isIsomorphism, and returns it.

The default function called this way is MappingOps.IsIsomorphism, which calls the func-

tions IsInjective, IsSurjective, and IsHomomorphism, and returns the logical and of

the results. This function is seldom overlaid, because all the interesting work is done in

IsInjective, IsSurjective, and IsHomomorphism.

37.5 IsEndomorphism

IsEndomorphism( map )

IsEndomorphism returns true if the mapping map is a endomorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping is an endomorphism if it is a homomorphism (see 37.1) and the range is a subset of the source.

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
    gap> p4 := MappingByFunction( g, g, x -> x^4 );
    MappingByFunction( g, g, function ( x )
        return x ^4;
    end )
    gap> IsEndomorphism( p4 );
    true
    gap> p5 := MappingByFunction( g, g, x -> x^5 );
    MappingByFunction( g, g, function ( x )
        return x ^5;
    end )
    gap> IsEndomorphism( p5 );
    true

IsEndomorphism first test if the flag map.isEndomorphism is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsEndomorphism( map ), remembers the returned value in map.isEndomorphism, and returns it.

The default function called this way is MappingOps.IsEndomorphism, which tests if the range is a subset of the source, calls IsHomomorphism, and returns the logical and of the results. This function is seldom overlaid, because all the interesting work is done in IsSubset

and IsHomomorphism.

37.6 IsAutomorphism

IsAutomorphism( map )

IsAutomorphism returns true if the mapping map is an automorphism and false otherwise. Signals an error if map is a multi valued mapping.

A mapping is an automorphism if it is an isomorphism where the source and the range are equal (see 37.4, 37.5).

    gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;

gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> IsAutomorphism( p4 );
false
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> IsAutomorphism( p5 );
true

IsAutomorphism first test if the flag map.isAutomorphism is bound. If the flag is bound, it returns this value. Otherwise it calls map.operations.IsAutomorphism( map ), remembers the returned value in map.isAutomorphism, and returns it. The default function called this way is MappingOps.IsAutomorphism, which calls the functions IsEndomorphism and IsBijection, and returns the logical and of the results. This function is seldom overlaid, because all the interesting work is done in IsEndomorphism and

IsBijection.

37.7 Kernel

Kernel( hom )

Kernel returns the kernel of the homomorphism hom. The kernel is usually returned as a source, though in some cases it might be returned as a proper set. The kernel is the set of elements that are mapped hom to the identity element of hom.range, i.e., to hom.range.identity if hom is a group homomorphism, and to hom.range.zero if hom is a ring or field homomorphism. The kernel is a substructure of hom.source.

gap> g := Group( (1,2,3,4), (2,4), (5,6,7) );;  g.name := "g";;
gap> p4 := MappingByFunction( g, g, x -> x^4 );
MappingByFunction( g, g, function ( x )
    return x ^4;
end )
gap> Kernel( p4 );
Subgroup( g, [ (1,2,3,4), (1,4)(2,3) ] )
gap> p5 := MappingByFunction( g, g, x -> x^5 );
MappingByFunction( g, g, function ( x )
    return x ^5;
end )
gap> Kernel( p5 );
Subgroup( g, [  ] )

Kernel first tests if the field hom.kernel is bound. If the field is bound it returns its value. Otherwise it calls hom.source.operations.Kernel( hom ), remembers the returned value in hom.kernel, and returns it. The functions usually called this way from the dispatcher are KernelGroupHomomorphism

and KernelFieldHomomorphism (see 7.101, 6.15).

616 CHAPTER 37. HOMOMORPHISMS

Booleans

The two boolean values are true and false. They stand for the logical values of the same name. They appear mainly as values of the conditions in if-statements and while-loops.

This chapter contains sections describing the operations that are available for the boolean values (see 38.1, 38.2).

Further this chapter contains a section about the function IsBool (see 38.3). Note that it is a convention that the name of a function that tests a property, and returns true and false according to the outcome, starts with Is, as in IsBool.

38.1 Comparisons of Booleans

bool1 = bool2 , bool1 <> bool2

The equality operator = evaluates to true if the two boolean values bool1 and bool2 are equal, i.e., both are true or both are false, and false otherwise. The inequality operator <> evaluates to true if the two boolean values bool1 and bool2 are different and false otherwise. This operation is also called the exclusive or, because its value is true if exactly one of bool1 or bool2 is true.

You can compare boolean values with objects of other types. Of course they are never equal.

    gap> true = false;
    false
    gap> false = (true = false);
    true
    gap> true <> 17;
    true

bool1 < bool2 , bool1 <= bool2 , bool1 > bool2 , bool1 >= bool2

The operators <, <=, >, and => evaluate to true if the boolean value bool1 is less than, less than or equal to, greater than, and greater than or equal to the boolean value bool2 . The ordering of boolean values is defined by true < false.

You can compare boolean values with objects of other types. Integers, rationals, cyclotomics, permutations, and words are smaller than boolean values. Objects of the other types, i.e., functions, lists, and records are larger.

    gap> true < false;
    true
    gap> false >= true;
    true
    gap> 17 < true;
    true
    gap> true < [17];
    true

38.2 Operations for Booleans

bool1 or bool2

The logical operator or evaluates to true if at least one of the two boolean operands bool1 and bool2 is true and to false otherwise.

or first evaluates bool1 . If the value is neither true nor false an error is signalled. If the value is true, then or returns true without evaluating bool2 . If the value is false, then or evaluates bool2 . Again, if the value is neither true nor false an error is signalled. Otherwise or returns the value of bool2 . This short-circuited evaluation is important if the value of bool1 is true and evaluation of bool2 would take much time or cause an error.

or is associative, i.e., it is allowed to write b1 or b2 or b3 , which is interpreted as (b1 or b2 ) or b3 . or has the lowest precedence of the logical operators. All logical operators have lower precedence than the comparison operators =, <, in, etc.

    gap> true or false;
    true
    gap> false or false;
    false
    gap> i := -1;;  l := [1,2,3];;
    gap> if i <= 0 or l[i] = false  then Print("aha\n");  fi;

aha # no error, because l[i] is not evaluated

bool1 and bool2

The logical operator and evaluates to true if both boolean operands bool1 and bool2 are true and to false otherwise.

and first evaluates bool1 . If the value is neither true nor false an error is signalled. If the value is false, then and returns false without evaluating bool2 . If the value is true, then and evaluates bool2 . Again, if the value is neither true nor false an error is signalled. Otherwise and returns the value of bool2 . This short-circuited evaluation is important if the value of bool1 is false and evaluation of bool2 would take much time or cause an error.

and is associative, i.e., it is allowed to write b1 and b2 and b3 , which is interpreted as (b1 and b2 ) and b3 . and has higher precedence than the logical or operator, but lower than the unary logical not operator. All logical operators have lower precedence than the comparison operators =, <, in, etc.

    gap> true and false;

false
gap> true and true;
true
gap> false and 17;

false # is no error, because 17 is never looked at

not bool

The logical operator not returns true if the boolean value bool is false and true otherwise. An error is signalled if bool does not evaluate to true or false.

not has higher precedence than the other logical operators, or and and. All logical operators have lower precedence than the comparison operators =, <, in, etc.

gap> not true;
false
gap> not false;
true

38.3 IsBool

IsBool( obj )

IsBool returns true if obj , which may be an object of an arbitrary type, is a boolean value and false otherwise. IsBool will signal an error if obj is an unbound variable.

gap> IsBool( true );
true
gap> IsBool( false );
true
gap> IsBool( 17 );
false

620 CHAPTER 38. BOOLEANS

Records

Records are next to lists the most important way to collect objects together. A record is a collection of components. Each component has a unique name, which is an identifier that distinguishes this component, and a value, which is an object of arbitrary type. We often abbreviate value of a component to element. We also say that a record contains its elements. You can access and change the elements of a record using its name. Record literals are written by writing down the components in order between rec( and ), and separating them by commas ,. Each component consists of the name, the assignment operator :=, and the value. The empty record, i.e., the record with no components, is written as rec().

gap> rec( a := 1, b := "2" ); # a record with two components

    rec(
      a := 1,
      b := "2" )
    gap> rec( a := 1, b := rec( c := 2 ) );  # record may contain records
    rec(
      a := 1,
      b := rec(
          c := 2 ) )

Records usually contain elements of various types, i.e., they are usually not homogeneous like lists. The first section in this chapter tells you how you can access the elements of a record (see 39.1). The next sections tell you how you can change the elements of a record (see 39.2 and 39.3). The next sections describe the operations that are available for records (see 39.4, 39.5, 39.6, and 39.7). The next section describes the function that tests if an object is a record (see 39.8). The next sections describe the functions that test whether a record has a component with a given name, and delete such a component (see 39.9 and 39.10). Those functions are also applicable to lists (see chapter 25). The final sections describe the functions that create a copy of a record (see 39.11 and 39.12). Again those functions are also applicable to lists (see chapter 25).

39.1 Accessing Record Elements

rec.name

The above construct evaluates to the value of the record component with the name name in the record rec. Note that the name is not evaluated, i.e., it is taken literal.

    gap> r := rec( a := 1, b := 2 );;
    gap> r.a;
    1
    gap> r.b;
    2

rec.(name)

This construct is similar to the above construct. The difference is that the second operand name is evaluated. It must evaluate to a string or an integer otherwise an error is signalled. The construct then evaluates to the element of the record rec whose name is, as a string, equal to name.

    gap> old := rec( a := 1, b := 2 );;
    gap> new := rec();
    rec(
       )
    gap> for i  in RecFields( old )  do
    >        new.(i) := old.(i);
    >    od;
    gap> new;
    rec(
      a := 1,
      b := 2 )

If rec does not evaluate to a record, or if name does not evaluate to a string, or if rec.name is unbound, an error is signalled. As usual you can leave the break loop (see 3.2) with quit;. On the other hand you can return a result to be used in place of the record element

by return expr;.

39.2 Record Assignment

rec.name := obj ;

The record assignment assigns the object obj , which may be an object of arbitrary type, to the record component with the name name, which must be an identifier, of the record rec. That means that accessing the element with name name of the record rec will return obj after this assignment. If the record rec has no component with the name name, the record is automatically extended to make room for the new component.

    gap> r := rec( a := 1, b := 2 );;
    gap> r.a := 10;;  r;
    rec(
      a := 10,
      b := 2 )
    gap> r.c := 3;;  r;

    rec(
      a := 10,
      b := 2,
      c := 3 )

The function IsBound (see 39.9) can be used to test if a record has a component with a certain name, the function Unbind (see 39.10) can be used to remove a component with a certain name again.

Note that assigning to a record changes the record. The ability to change an object is only available for lists and records (see 39.3).

rec.(name) = obj ;

This construct is similar to the above construct. The difference is that the second operand name is evaluated. It must evaluate to a string or an integer otherwise an error is signalled. The construct then assigns obj to the record component of the record rec whose name is, as a string, equal to name.

If rec does not evaluate to a record, name does not evaluate to a string, or obj is a call to a function that does not return a value, e.g., Print (see 3.14), an error is signalled. As usual you can leave the break loop (see 3.2) with quit;. On the other hand you can continue the assignment by returning a record in the first case, a string in the second, or an object to be assigned in the third, using return expr;.

39.3 Identical Records

With the record assignment (see 39.2) it is possible to change a record. The ability to change an object is only available for lists and records. This section describes the semantic consequences of this fact.

You may think that in the following example the second assignment changes the integer, and that therefore the above sentence, which claimed that only records and lists can be changed, is wrong.

    i := 3;
    i :=  i + 1;

But in this example not the integer 3 is changed by adding one to it. Instead the variable i is changed by assigning the value of i+1, which happens to be 4, to i. The same thing happens in the following example

    r := rec( a := 1 );
    r := rec( a := 1, b := 2 );

The second assignment does not change the first record, instead it assigns a new record to the variable r. On the other hand, in the following example the record is changed by the second assignment.

    r := rec( a := 1 );
    r.b := 2;

To understand the difference first think of a variable as a name for an object. The important point is that a record can have several names at the same time. An assignment var := record; means in this interpretation that var is a name for the object record. At the end of the following example r2 still has the value rec( a := 1 ) as this record has not been changed and nothing else has been assigned to r2.

    r1 := rec( a := 1 );
    r2 := r1;
    r1 := rec( a := 1, b := 2 );

But after the following example the record for which r2 is a name has been changed and thus the value of r2 is now rec( a := 1, b := 2 ).

    r1 := rec( a := 1 );
    r2 := r1;
    r1.b := 2;

We shall say that two records are identical if changing one of them by a record assignment also changes the other one. This is slightly incorrect, because if two records are identical, there are actually only two names for one record. However, the correct usage would be very awkward and would only add to the confusion. Note that two identical records must be equal, because there is only one records with two different names. Thus identity is an equivalence relation that is a refinement of equality.

Let us now consider under which circumstances two records are identical.

If you enter a record literal then the record denoted by this literal is a new record that is not identical to any other record. Thus in the following example r1 and r2 are not identical, though they are equal of course.

    r1 := rec( a := 1 );
    r2 := rec( a := 1 );

Also in the following example, no records in the list l are identical.

    l := [];
    for i  in [1..10]  do
        l[i] := rec( a := 1 );
    od;

If you assign a record to a variable no new record is created. Thus the record value of the variable on the left hand side and the record on the right hand side of the assignment are identical. So in the following example r1 and r2 are identical records.

    r1 := rec( a := 1 );
    r2 := r1;

If you pass a record as argument, the old record and the argument of the function are identical. Also if you return a record from a function, the old record and the value of the function call are identical. So in the following example r1 and r2 are identical record

    r1 := rec( a := 1 );
    f := function ( r )  return r;  end;
    r2 := f( r1 );

The functions Copy and ShallowCopy (see 39.11 and 39.12) accept a record and return a new record that is equal to the old record but that is not identical to the old record. The difference between Copy and ShallowCopy is that in the case of ShallowCopy the corresponding elements of the new and the old records will be identical, whereas in the case of Copy they will only be equal. So in the following example r1 and r2 are not identical records.

    r1 := rec( a := 1 );
    r2 := Copy( r1 );

If you change a record it keeps its identity. Thus if two records are identical and you change one of them, you also change the other, and they are still identical afterwards. On the other hand, two records that are not identical will never become identical if you change one of them. So in the following example both r1 and r2 are changed, and are still identical.

    r1 := rec( a := 1 );
    r2 := r1;
    r1.b := 2;

39.4 Comparisons of Records

rec1 = rec2 rec1 <> rec2

The equality operator = returns true if the record rec1 is equal to the record rec2 and false otherwise. The inequality operator <> returns true if the record rec1 is not equal to rec2 and false otherwise.

Usually two records are considered equal, if for each component of one record the other record has a component of the same name with an equal value and vice versa. You can also compare records with other objects, they are of course different, unless the record has a special comparison function (see below) that says otherwise.

    gap> rec( a := 1, b := 2 ) = rec( b := 2, a := 1 );
    true
    gap> rec( a := 1, b := 2 ) = rec( a := 2, b := 1 );
    false
    gap> rec( a := 1 ) = rec( a := 1, b := 2 );
    false
    gap> rec( a := 1 ) = 1;
    false

However a record may contain a special operations record that contains a function that is called when this record is an operand of a comparison. The precise mechanism is as follows. If the operand of the equality operator = is a record, and if this record has an element with the name operations that is a record, and if this record has an element with the name = that is a function, then this function is called with the operands of = as arguments, and the value of the operation is the result returned by this function. In this case a record may also be equal to an object of another type if this function says so. It is probably not a good idea to define a comparison function in such a way that the resulting relation is not an equivalence relation, i.e., not reflexive, symmetric, and transitive. Note that there is no corresponding <> function, because left <> right is implemented as not left = right.

The following example shows one piece of the definition of residue classes, using record operations. Of course this is far from a complete implementation (see 1.30). Note that the = must be quoted, so that it is taken as an identifier (see 2.5).

    gap> ResidueClassOps := rec( );;

gap> ResidueClassOps.\= := function ( l, r )
>   return (l.modulus = r.modulus)
>     and (l.representative-r.representative) mod l.modulus = 0;
> end;;
gap> ResidueClass := function ( representative, modulus )
>   return rec(
>     representative := representative,
>     modulus        := modulus,
>     operations     := ResidueClassOps );
> end;;
gap> l := ResidueClass( 13, 23 );;
gap> r := ResidueClass( -10, 23 );;
gap> l = r;
true
gap> l = ResidueClass( 10, 23 );
false

rec1 <rec2 rec1 <= rec2 rec1 > rec2 rec1 >= rec2

The operators <, <=, >, and >= evaluate to true if the record rec1 is less than, less than or equal to, greater than, and greater than or equal to the record rec2 , and to false otherwise.

To compare records we imagine that the components of both records are sorted according to their names. Then the records are compared lexicographically with unbound elements considered smaller than anything else. Precisely one record rec1 is considered less than another record rec2 if rec2 has a component with name name2 and either rec1 has no component with this name or rec1 .name2 <rec2 .name2 and for each component of rec1 with name name1 <name2 rec2 has a component with this name and rec1 .name1 = rec2 .name1 . Records may also be compared with objects of other types, they are greater than anything else, unless the record has a special comparison function (see below) that says otherwise.

gap> rec( a := 1, b := 2 ) < rec( b := 2, a := 1 );

false # they are equal

gap> rec( a := 1, b := 2 ) < rec( a := 2, b := 0 );

true # the a elements are compared first and 1 is less than 2

gap> rec( a := 1 ) < rec( a := 1, b := 2 );

true # unbound is less than 2

gap> rec( a := 1 ) < rec( a := 0, b := 2 );

false # the a elements are compared first and 0 is less than 1

gap> rec( b := 1 ) < rec( b := 0, a := 2 );

true # the a-s are compared first and unbound is less than 2

gap> rec( a := 1 ) < 1;

false # other objects are less than records

However a record may contain a special operations record that contains a function that is called when this record is an operand of a comparison. The precise mechanism is as follows. If the operand of the equality operator < is a record, and if this record has an element with the name operations that is a record, and if this record has an element with the name < that is a function, then this function is called with the operands of < as arguments, and the value of the operation is the result returned by this function. In this case a record may also be smaller than an object of another type if this function says so. It is probably not a good idea to define a comparison function in such a way that the resulting relation is not an ordering relation, i.e., not antisymmetric, and transitive. Note that there are no corresponding <=, >, and >= functions, since those operations are implemented as not right <left, right <left, and not left <right respectively.

The following example shows one piece of the definition of residue classes, using record operations. Of course this is far from a complete implementation (see 1.30). Note that the < must be quoted, so that it is taken as an identifier (see 2.5).

    gap> ResidueClassOps := rec( );;
    gap> ResidueClassOps.\< := function ( l, r )
    >   if l.modulus <> r.modulus  then
    >     Error("<l> and <r> must have the same modulus");
    >   fi;
    >   return   l.representative mod l.modulus
    >          < r.representative mod r.modulus;
    > end;;
    gap> ResidueClass := function ( representative, modulus )
    >   return rec(
    >     representative := representative,
    >     modulus        := modulus,
    >     operations     := ResidueClassOps );
    > end;;
    gap> l := ResidueClass( 13, 23 );;
    gap> r := ResidueClass( -1, 23 );;
    gap> l < r;

true # 13 is less than 22

    gap> l < ResidueClass( 10, 23 );

false # 10 is less than 13

39.5 Operations for Records

Usually no operations are defined for record. However a record may contain a special operations record that contains functions that are called when this record is an operand of a binary operation. This mechanism is detailed below for the addition.

obj + rec, rec + obj

If either operand is a record, and if this record contains an element with name operations that is a record, and if this record in turn contains an element with the name + that is a function, then this function is called with the two operands as arguments, and the value of the addition is the value returned by that function. If both operands are records with such a function rec.operations.+, then the function of the right operand is called. If either operand is a record, but neither operand has such a function rec.operations.+, an error is signalled.

obj - rec, rec - obj obj * rec, rec * obj obj / rec, rec / obj obj mod rec, rec mod obj obj ^rec, rec ^obj

This is evaluated similar, but the functions must obviously be called -, *, /, mod, ^respectively.

The following example shows one piece of the definition of a residue classes, using record operations. Of course this is far from a complete implementation (see 1.30). Note that the * must be quoted, so that it is taken as an identifier (see 2.5).

gap> ResidueClassOps := rec( );;
gap> ResidueClassOps.\* := function ( l, r )
>   if l.modulus <> r.modulus  then
>     Error("<l> and <r> must have the same modulus");
>   fi;
>   return rec(
>     representative := (l.representative * r.representative)
>                       mod l.modulus,
>     modulus        := l.modulus,
>     operations     := ResidueClassOps );
> end;;
gap> ResidueClass := function ( representative, modulus )
>   return rec(
>     representative := representative,
>     modulus        := modulus,
>     operations     := ResidueClassOps );
> end;;
gap> l := ResidueClass( 13, 23 );;
gap> r := ResidueClass( -1, 23 );;
gap> s := l * r;
rec(
  representative := 10,
  modulus := 23,
  operations := rec(
      * := function ( l, r ) ... end ) )

39.6 In for Records

element in rec

Usually the membership test is only defined for lists. However a record may contain a special operations record, that contains a function that is called when this record is the right operand of the in operator. The precise mechanism is as follows.

If the right operand of the in operator is a record, and if this record contains an element with the name operations that is a record, and if this record in turn contains an element with the name in that is a function, then this function is called with the two operands as arguments, and the value of the membership test is the value returned by that function. The function should of course return true or false.

The following example shows one piece of the definition of residue classes, using record operations. Of course this is far from a complete implementation (see 1.30). Note that the in must be quoted, so that it is taken as an identifier (see 2.5).

gap> ResidueClassOps := rec( );;
gap> ResidueClassOps.\in := function ( l, r )
>   if IsInt( l )  then
>     return (l - r.representative) mod r.modulus = 0;
>   else
>     return false;
>   fi;
> end;;
gap> ResidueClass := function ( representative, modulus )
>   return rec(
>     representative := representative,
>     modulus        := modulus,
>     operations     := ResidueClassOps );
> end;;
gap> l := ResidueClass( 13, 23 );;
gap> -10 in l;
true
gap> 10 in l;
false

39.7 Printing of Records

Print( rec )

If a record is printed by Print (see 3.14, 3.15, and 3.16) or by the main loop (see 3.1), it is usually printed as record literal, i.e., as a sequence of components, each in the format name := value, separated by commas and enclosed in rec( and ).

gap> r := rec();;  r.a := 1;;  r.b := 2;;
gap> r;
rec(
  a := 1,
  b := 2 )

But if the record has an element with the name operations that is a record, and if this record has an element with the name Print that is a function, then this function is called with the record as argument. This function must print whatever the printed representation of the record should look like.

The following example shows one piece of the definition of residue classes, using record operations. Of course this is far from a complete implementation (see 1.30). Note that it is typical for records that mimic group elements to print as a function call that, when evaluated, will create this group element record.

gap> ResidueClassOps := rec( );;
gap> ResidueClassOps.Print := function ( r )
>   Print( "ResidueClass( ",
>               r.representative mod r.modulus, ", ",

>               r.modulus, " )" );
> end;;
gap> ResidueClass := function ( representative, modulus )
>   return rec(
>     representative := representative,
>     modulus        := modulus,
>     operations     := ResidueClassOps );
> end;;
gap> l := ResidueClass( 33, 23 );
ResidueClass( 10, 23 )

39.8 IsRec

IsRec( obj )

IsRec returns true if the object obj , which may be an object of arbitrary type, is a record, and false otherwise. Will signal an error if obj is a variable with no assigned value.

gap> IsRec( rec( a := 1, b := 2 ) );
true
gap> IsRec( IsRec );
false

39.9 IsBound

IsBound( rec.name )
IsBound( list[n] )

In the first form IsBound returns true if the record rec has a component with the name name, which must be an ident and false otherwise. rec must evaluate to a record, otherwise an error is signalled.

In the second form IsBound returns true if the list list has a element at the position n, and false otherwise. list must evaluate to a list, otherwise an error is signalled.

gap> r := rec( a := 1, b := 2 );;
gap> IsBound( r.a );
true
gap> IsBound( r.c );
false
gap> l := [  , 2, 3,  , 5,  , 7,  ,  ,   , 11 ];;
gap> IsBound( l[7] );
true
gap> IsBound( l[4] );
false
gap> IsBound( l[101] );
false

Note that IsBound is special in that it does not evaluate its argument, otherwise it would always signal an error when it is supposed to return false.

39.10 Unbind

Unbind( rec.name )
Unbind( list[n] )

In the first form Unbind deletes the component with the name name in the record rec. That is, after execution of Unbind, rec no longer has a record component with this name. Note that it is not an error to unbind a nonexisting record component. rec must evaluate to a record, otherwise an error is signalled.

In the second form Unbind deletes the element at the position n in the list list. That is, after execution of Unbind, list no longer has an assigned value at the position n. Note that it is not an error to unbind a nonexisting list element. list must evaluate to a list, otherwise an error is signalled.

    gap> r := rec( a := 1, b := 2 );;
    gap> Unbind( r.a );  r;
    rec(
      b := 2 )
    gap> Unbind( r.c );  r;
    rec(
      b := 2 )
    gap> l := [  , 2, 3,  5,  , 7,  ,  ,  , 11 ];;
    gap> Unbind( l[3] );  l;
    [ , 2,, 5,, 7,,,, 11 ]
    gap> Unbind( l[4] );  l;
    [ , 2,,,, 7,,,, 11 ]

Note that Unbind does not evaluate its argument, otherwise there would be no way for Unbind to tell which component to remove in which record, because it would only receive the value of this component.

39.11 Copy

Copy( obj )

Copy returns a copy new of the object obj . You may apply Copy to objects of any type, but for objects that are not lists or records Copy simply returns the object itself.

For lists and records the result is a new list or record that is not identical to any other list or record (see 25.8 and 39.3). This means that you may modify this copy new by assignments (see 25.5 and 39.2) or by adding elements to it (see 25.6 and 25.7), without modifying the original object obj .

    gap> list1 := [ 1, 2, 3 ];;
    gap> list2 := Copy( list1 );
    [ 1, 2, 3 ]
    gap> list2[1] := 0;;  list2;
    [ 0, 2, 3 ]
    gap> list1;
    [ 1, 2, 3 ]

That Copy returns the object itself if it is not a list or a record is consistent with this definition, since there is no way to change the original object obj by modifying new, because in fact there is no way to change the object new.

Copy basically executes the following code for lists, and similar code for records.

new := [];
for i  in [1..Length(obj)]  do
    if IsBound(obj[i])  then
        new[i] := Copy( obj[i] );
    fi;
od;

Note that Copy recursively copies all elements of the object obj . If you only want to copy the top level use ShallowCopy (see 39.12).

gap> list1 := [ [ 1, 2 ], [ 3, 4 ] ];;
gap> list2 := Copy( list1 );
[ [ 1, 2 ], [ 3, 4 ] ]
gap> list2[1][1] := 0;;  list2;
[ [ 0, 2 ], [ 3, 4 ] ]
gap> list1;
[ [ 1, 2 ], [ 3, 4 ] ]

The above code is not entirely correct. If the object obj contains a list or record twice this list or record is not copied twice, as would happen with the above definition, but only once. This means that the copy new and the object obj have exactly the same structure when view as a general graph.

gap> sub := [ 1, 2 ];;  list1 := [ sub, sub ];;
gap> list2 := Copy( list1 );
[ [ 1, 2 ], [ 1, 2 ] ]
gap> list2[1][1] := 0;;  list2;
[ [ 0, 2 ], [ 0, 2 ] ]
gap> list1;
[ [ 1, 2 ], [ 1, 2 ] ]

39.12 ShallowCopy

ShallowCopy( obj )

ShallowCopy returns a copy of the object obj . You may apply ShallowCopy to objects of any type, but for objects that are not lists or records ShallowCopy simply returns the object itself.

For lists and records the result is a new list or record that is not identical to any other list or record (see 25.8 and 39.3). This means that you may modify this copy new by assignments (see 25.5 and 39.2) or by adding elements to it (see 25.6 and 25.7), without modifying the original object obj .

gap> list1 := [ 1, 2, 3 ];;
gap> list2 := ShallowCopy( list1 );
[ 1, 2, 3 ]
gap> list2[1] := 0;;  list2;

    [ 0, 2, 3 ]
    gap> list1;
    [ 1, 2, 3 ]

That ShallowCopy returns the object itself if it is not a list or a record is consistent with this definition, since there is no way to change the original object obj by modifying new, because in fact there is no way to change the object new.

ShallowCopy basically executes the following code for lists, and similar code for records.

    new := [];
    for i  in [1..Length(obj)]  do
        if IsBound(obj[i])  then
            new[i] := obj[i];
        fi;
    od;

Note that ShallowCopy only copies the top level. The subojects of the new object new are identical to the corresponding subobjects of the object obj . If you want to copy recursively use Copy (see 39.11).

39.13 RecFields

RecFields( rec )

RecFields returns a list of strings corresponding to the names of the record components of the record rec.

    gap> r := rec( a := 1, b := 2 );;
    gap> RecFields( r );
    [ "a", "b" ]

Note that you cannot use the string result in the ordinary way to access or change a record component. You must use the rec.(name) construct (see 39.1 and 39.2).

634 CHAPTER 39. RECORDS

Combinatorics

This chapter describes the functions that deal with combinatorics. We mainly concentrate on two areas. One is about selections, that is the ways one can select elements from a set. The other is about partitions, that is the ways one can partition a set into the union of pairwise disjoint subsets.

First this package contains various functions that are related to the number of selections from a set (see 40.1, 40.2) or to the number of partitions of a set (see 40.3, 40.4, 40.5). Those numbers satisfy literally thousands of identities, which we do no mention in this document, for a thorough treatment see [GKP90].

Then this package contains functions to compute the selections from a set (see 40.6), ordered selections, i.e., selections where the order in which you select the elements is important (see 40.7), selections with repetitions, i.e., you are allowed to select the same element more than once (see 40.8) and ordered selections with repetitions (see 40.9).

As special cases of ordered combinations there are functions to compute all permutations (see 40.10), all fixpointfree permutations (see 40.11) of a list and the number of permutations that fit a given 1-0 matrix (see 40.12).

This package also contains functions to compute partitions of a set (see 40.13), partitions of an integer into the sum of positive integers (see 40.14, 40.16) and ordered partitions of an integer into the sum of positive integers (see 40.15).

Finally this package also contains some miscellaneous functions (see 40.21, 40.22 and 40.23).

All these functions are in the file "LIBNAME/combinat.g".

40.1 Factorial

Factorial( n )

Factorial returns the factorial n! of the positive integer n, which is defined as the product 1 ∗ 2 ∗ 3 ∗ .. ∗ n.

n! is the number of permutations of a set of n elements. 1/n! is the coefficient of x^{n} in the formal series e^{x}, which is the generating function for factorial.

    gap> List( [0..10], Factorial );

    [ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 ]
    gap> Factorial( 30 );
    265252859812191058636308480000000

PermutationsList (see 40.10) computes the set of all permutations of a list.

40.2 Binomial

Binomial( n, k )

Binomial returns the binomial coefficient \binom{n}{k} of integers n and k, which is defined as n!/(k!(n − k)!) (see 40.1). We define \binom{0}{0} = 1, \binom{n}{k} = 0 if k < 0 or n < k, and \binom{n}{k} = (−1)^{k}\binom{−n+k−1}{k} if n < 0, which is consistent with \binom{n}{k} = \binom{n−1}{k} + \binom{n−1}{k−1}.

\binom{n}{k} is the number of combinations with k elements, i.e., the number of subsets with k elements, of a set with n elements. \binom{n}{k} is the coefficient of the term x^{k} of the polynomial (x + 1)^{n}, which is the generating function for \binom{n}{∗}, hence the name.

    gap> List( [0..4], k->Binomial( 4, k ) );

[ 1, 4, 6, 4, 1 ] # Knuth calls this the trademark of Binomial

    gap> List( [0..6], n->List( [0..6], k->Binomial( n, k ) ) );;
    gap> PrintArray( last );

[ [ 1, 0, 0, 0, 0, 0, 0 ], # the lower triangle is [ 1, 1, 0, 0, 0, 0, 0 ], # called Pascal’s triangle

      [   1,   2,   1,   0,   0,   0,   0 ],
      [   1,   3,   3,   1,   0,   0,   0 ],
      [   1,   4,   6,   4,   1,   0,   0 ],
      [   1,   5,  10,  10,   5,   1,   0 ],
      [   1,   6,  15,  20,  15,   6,   1 ] ]
    gap> Binomial( 50, 10 );
    10272278170

NrCombinations (see 40.6) is the generalization of Binomial for multisets. Combinations (see 40.6) computes the set of all combinations of a multiset.

40.3 Bell

Bell( n )

Bell returns the Bell number B(n). The Bell numbers are defined by B(0) = 1 and the recurrence B(n + 1) = ∑_{k=0}^{n} \binom{n}{k}B(k).

B(n) is the number of ways to partition a set of n elements into pairwise disjoint nonempty subsets (see 40.13). This implies of course that B(n) = ∑_{k=0}^{n} S_{2}(n, k) (see 40.5). B(n)/n! is the coefficient of x^{n} in the formal series e^{e^{x}−1}, which is the generating function for B(n).

    gap> List( [0..6], n -> Bell( n ) );
    [ 1, 1, 2, 5, 15, 52, 203 ]
    gap> Bell( 14 );
    190899322

40.4 Stirling1

Stirling1( n, k )

Stirling1 returns the Stirling number of the first kind S_{1}(n, k) of the integers n and k. Stirling numbers of the first kind are defined by S_{1}(0, 0) = 1, S_{1}(n, 0) = S_{1}(0, k) = 0 if n, k <> 0 and the recurrence S_{1}(n, k) = (n − 1)S_{1}(n − 1, k) + S_{1}(n − 1, k − 1).

S_{1}(n, k) is the number of permutations of n points with k cycles. Stirling numbers of the first kind appear as coefficients in the series n!\binom{x}{n} = ∑_{k=0}^{n} S_{1}(n, k)x^{k} which is the generating function for Stirling numbers of the first kind. Note the similarity to x^{n} = ∑_{k=0}^{n} S_{2}(n, k)k!\binom{x}{k} (see 40.5). Also the definition of S_{1} implies S_{1}(n, k) = S_{2}(−k, −n) if n, k < 0. There are many formulae relating Stirling numbers of the first kind to Stirling numbers of the second kind, Bell numbers, and Binomial numbers.

    gap> List( [0..4], k->Stirling1( 4, k ) );

[ 0, 6, 11, 6, 1 ] # Knuth calls this the trademark of S_{1}

    gap> List( [0..6], n->List( [0..6], k->Stirling1( n, k ) ) );;
    gap> PrintArray( last );

[ [ 1, 0, 0, 0, 0, 0, 0 ], # Note the similarity

      [    0,    1,    0,    0,    0,    0,    0 ],  # with Pascal’s
      [    0,    1,    1,    0,    0,    0,    0 ],  # triangle for the
      [    0,    2,    3,    1,    0,    0,    0 ],  # Binomial numbers
      [    0,    6,   11,    6,    1,    0,    0 ],
      [    0,   24,   50,   35,   10,    1,    0 ],
      [    0,  120,  274,  225,   85,   15,    1 ] ]
    gap> Stirling1(50,10);
    101623020926367490059043797119309944043405505380503665627365376

40.5 Stirling2

Stirling2( n, k )

Stirling2 returns the Stirling number of the second kind S_{2}(n, k) of the integers n and k. Stirling numbers of the second kind are defined by S_{2}(0, 0) = 1, S_{2}(n, 0) = S_{2}(0, k) = 0 if n, k <> 0 and the recurrence S_{2}(n, k) = kS_{2}(n − 1, k) + S_{2}(n − 1, k − 1).

S_{2}(n, k) is the number of ways to partition a set of n elements into k pairwise disjoint nonempty subsets (see 40.13). Stirling numbers of the second kind appear as coefficients in the expansion of x^{n} = ∑_{k=0}^{n} S_{2}(n, k)k!\binom{x}{k}. Note the similarity to n!\binom{x}{n} = ∑_{k=0}^{n} S_{1}(n, k)x^{k} (see 40.4). Also the definition of S_{2} implies S_{2}(n, k) = S_{1}(−k, −n) if n, k < 0. There are many formulae relating Stirling numbers of the second kind to Stirling numbers of the first kind, Bell numbers, and Binomial numbers.

    gap> List( [0..4], k->Stirling2( 4, k ) );

[ 0, 1, 7, 6, 1 ] # Knuth calls this the trademark of S_{2}

    gap> List( [0..6], n->List( [0..6], k->Stirling2( n, k ) ) );;
    gap> PrintArray( last );

[ [ 1, 0, 0, 0, 0, 0, 0 ], # Note the similarity with [ 0, 1, 0, 0, 0, 0, 0 ], # Pascal’s triangle for [ 0, 1, 1, 0, 0, 0, 0 ], # the Binomial numbers

      [   0,   1,   3,   1,   0,   0,   0 ],

      [   0,   1,   7,   6,   1,   0,   0 ],
      [   0,   1,  15,  25,  10,   1,   0 ],
      [   0,   1,  31,  90,  65,  15,   1 ] ]
    gap> Stirling2( 50, 10 );
    26154716515862881292012777396577993781727011

40.6 Combinations

Combinations( mset )
Combinations( mset, k )
NrCombinations( mset )
NrCombinations( mset, k )

In the first form Combinations returns the set of all combinations of the multiset mset. In the second form Combinations returns the set of all combinations of the multiset mset with k elements. In the first form NrCombinations returns the number of combinations of the multiset mset. In the second form NrCombinations returns the number of combinations of the multiset mset with k elements. A combination of mset is an unordered selection without repetitions and is represented by a sorted sublist of mset. If mset is a proper set, there are \binom{|mset|}{k} (see 40.2) combinations with k elements, and the set of all combinations is just the powerset of mset, which contains all subsets of mset and has cardinality 2^{|mset|}.

    gap> Combinations( [1,2,2,3] );
    [ [  ], [ 1 ], [ 1, 2 ], [ 1, 2, 2 ], [ 1, 2, 2, 3 ], [ 1, 2, 3 ],
      [ 1, 3 ], [ 2 ], [ 2, 2 ], [ 2, 2, 3 ], [ 2, 3 ], [ 3 ] ]
    gap> NrCombinations( [1..52], 5 );

2598960 # number of different hands in a game of poker The function Arrangements (see 40.7) computes ordered selections without repetitions, UnorderedTuples (see 40.8) computes unordered selections with repetitions and Tuples (see 40.9) computes ordered selections with repetitions.

40.7 Arrangements

Arrangements( mset )
Arrangements( mset, k )
NrArrangements( mset )
NrArrangements( mset, k )

In the first form Arrangements returns the set of arrangements of the multiset mset. In the second form Arrangements returns the set of all arrangements with k elements of the multiset mset. In the first form NrArrangements returns the number of arrangements of the multiset mset. In the second form NrArrangements returns the number of arrangements with k elements of the multiset mset. An arrangement of mset is an ordered selection without repetitions and is represented by a list that contains only elements from mset, but maybe in a different order. If mset is a proper set there are |mset|!/(|mset| − k)! (see 40.1) arrangements with k elements.

As an example of arrangements of a multiset, think of the game Scrabble. Suppose you have the six characters of the word settle and you have to make a four letter word. Then the possibilities are given by

    gap> Arrangements( ["s","e","t","t","l","e"], 4 );
    [ [ "e", "e", "l", "s" ], [ "e", "e", "l", "t" ],
      [ "e", "e", "s", "l" ], [ "e", "e", "s", "t" ],

# 96 more possibilities

      [ "t", "t", "s", "e" ], [ "t", "t", "s", "l" ] ]

Can you find the five proper English words, where lets does not count? Note that the fact that the list returned by Arrangements is a proper set means in this example that the possibilities are listed in the same order as they appear in the dictionary.

    gap> NrArrangements( ["s","e","t","t","l","e"] );
    523

The function Combinations (see 40.6) computes unordered selections without repetitions, UnorderedTuples (see 40.8) computes unordered selections with repetitions and Tuples (see 40.9) computes ordered selections with repetitions.

40.8 UnorderedTuples

UnorderedTuples( set, k )
NrUnorderedTuples( set, k )

UnorderedTuples returns the set of all unordered tuples of length k of the set set. NrUnorderedTuples returns the number of unordered tuples of length k of the set set. An unordered tuple of length k of set is a unordered selection with repetitions of set and is represented by a sorted list of length k containing elements from set. There are \binom{|set|+k−1}{k} (see 40.2) such unordered tuples. Note that the fact that UnOrderedTuples returns a set implies that the last index runs fastest. That means the first tuple contains the smallest element from set k times, the second tuple contains the smallest element of set at all positions except at the last positions, where it contains the second smallest element from set and so on. As an example for unordered tuples think of a poker-like game played with 5 dices. Then each possible hand corresponds to an unordered five-tuple from the set [1..6]

    gap> NrUnorderedTuples( [1..6], 5 );
    252
    gap> UnorderedTuples( [1..6], 5 );
    [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 1, 2 ], [ 1, 1, 1, 1, 3 ],
      [ 1, 1, 1, 1, 4 ], [ 1, 1, 1, 1, 5 ], [ 1, 1, 1, 1, 6 ],

# 99 more tuples

      [ 1, 3, 4, 5, 6 ], [ 1, 3, 4, 6, 6 ], [ 1, 3, 5, 5, 5 ],

# 99 more tuples

      [ 3, 3, 4, 4, 5 ], [ 3, 3, 4, 4, 6 ], [ 3, 3, 4, 5, 5 ],

# 39 more tuples

      [ 5, 5, 6, 6, 6 ], [ 5, 6, 6, 6, 6 ], [ 6, 6, 6, 6, 6 ] ]

The function Combinations (see 40.6) computes unordered selections without repetitions, Arrangements (see 40.7) computes ordered selections without repetitions and Tuples (see 40.9) computes ordered selections with repetitions.

40.9 Tuples

Tuples( set, k )
NrTuples( set, k )

Tuples returns the set of all ordered tuples of length k of the set set. NrTuples returns the number of all ordered tuples of length k of the set set. An ordered tuple of length k of set is an ordered selection with repetition and is represented by a list of length k containing elements of set. There are |set|^{k} such ordered tuples. Note that the fact that Tuples returns a set implies that the last index runs fastest. That means the first tuple contains the smallest element from set k times, the second tuple contains the smallest element of set at all positions except at the last positions, where it contains the second smallest element from set and so on.

    gap> Tuples( [1,2,3], 2 );
    [ [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 2 ], [ 2, 3 ],
      [ 3, 1 ], [ 3, 2 ], [ 3, 3 ] ]
    gap> NrTuples( [1..10], 5 );
    100000

Tuples(set,k) can also be viewed as the k-fold cartesian product of set (see 25.21). The function Combinations (see 40.6) computes unordered selections without repetitions, Arrangements (see 40.7) computes ordered selections without repetitions, and finally the function UnorderedTuples (see 40.8) computes unordered selections with repetitions.

40.10 PermutationsList

PermutationsList( mset )
NrPermutationsList( mset )

PermutationsList returns the set of permutations of the multiset mset. NrPermutationsList returns the number of permutations of the multiset mset. A permutation is represented by a list that contains exactly the same elements as mset, but possibly in different order. If mset is a proper set there are |mset|! (see 40.1) such permutations. Otherwise if the first elements appears k_{1} times, the second element appears k_{2} times and so on, the number of permutations is |mset|!/(k_{1}!k_{2}!..), which is sometimes called multinomial coefficient.

    gap> PermutationsList( [1,2,3] );
    [ [ 1, 2, 3 ], [ 1, 3, 2 ], [ 2, 1, 3 ], [ 2, 3, 1 ], [ 3, 1, 2 ],
      [ 3, 2, 1 ] ]
    gap> PermutationsList( [1,1,2,2] );
    [ [ 1, 1, 2, 2 ], [ 1, 2, 1, 2 ], [ 1, 2, 2, 1 ], [ 2, 1, 1, 2 ],
      [ 2, 1, 2, 1 ], [ 2, 2, 1, 1 ] ]
    gap> NrPermutationsList( [1,2,2,3,3,3,4,4,4,4] );
    12600

The function Arrangements (see 40.7) is the generalization of PermutationsList that allows you to specify the size of the permutations. Derangements (see 40.11) computes permutations that have no fixpoints.

40.11 Derangements

Derangements( list )
NrDerangements( list )

Derangements returns the set of all derangements of the list list.

NrDerangements returns the number of derangements of the list list.

A derangement is a fixpointfree permutation of list and is represented by a list that contains exactly the same elements as list, but in such an order that the derangement has at no position the same element as list. If the list list contains no element twice there are exactly |list|!(1/2! − 1/3! + 1/4! − ..(−1)^{n}/n!) derangements.

Note that the ratio NrPermutationsList([1..n])/NrDerangements([1..n]), which is

n!/(n!(1/2! − 1/3! + 1/4! − ..(−1)^{n}/n!)) is an approximation for the base of the natural logarithm e = 2.7182818285, which is correct to about n digits.

As an example of derangements suppose that you have to send four different letters to four different people. Then a derangement corresponds to a way to send those letters such that no letter reaches the intended person.

    gap> Derangements( [1,2,3,4] );
    [ [ 2, 1, 4, 3 ], [ 2, 3, 4, 1 ], [ 2, 4, 1, 3 ], [ 3, 1, 4, 2 ],
      [ 3, 4, 1, 2 ], [ 3, 4, 2, 1 ], [ 4, 1, 2, 3 ], [ 4, 3, 1, 2 ],
      [ 4, 3, 2, 1 ] ]
    gap> NrDerangements( [1..10] );
    1334961
    gap> Int( 10^7*NrPermutationsList([1..10])/last );
    27182816
    gap> Derangements( [1,1,2,2,3,3] );
    [ [ 2, 2, 3, 3, 1, 1 ], [ 2, 3, 1, 3, 1, 2 ], [ 2, 3, 1, 3, 2, 1 ],
      [ 2, 3, 3, 1, 1, 2 ], [ 2, 3, 3, 1, 2, 1 ], [ 3, 2, 1, 3, 1, 2 ],
      [ 3, 2, 1, 3, 2, 1 ], [ 3, 2, 3, 1, 1, 2 ], [ 3, 2, 3, 1, 2, 1 ],
      [ 3, 3, 1, 1, 2, 2 ] ]
    gap> NrDerangements( [1,2,2,3,3,3,4,4,4,4] );
    338

The function PermutationsList (see 40.10) computes all permutations of a list.

40.12 Permanent

Permanent( mat )

Permanent returns the permanent of the matrix mat. The permanent is defined by ∑_{p∈Symm(n)} ∏_{i=1}^{n} mat[i][i^{p}].

Note the similarity of the definition of the permanent to the definition of the determinant. In fact the only difference is the missing sign of the permutation. However the permanent is quite unlike the determinant, for example it is not multilinear or alternating. It has however important combinatorical properties.

    gap> Permanent( [[0,1,1,1],
    >                [1,0,1,1],

>                [1,1,0,1],
>                [1,1,1,0]] );
9  # inefficient way to compute NrDerangements([1..4])
gap> Permanent( [[1,1,0,1,0,0,0],
>                [0,1,1,0,1,0,0],
>                [0,0,1,1,0,1,0],
>                [0,0,0,1,1,0,1],
>                [1,0,0,0,1,1,0],
>                [0,1,0,0,0,1,1],
>                [1,0,1,0,0,0,1]] );

24 # 24 permutations fit the projective plane of order 2

40.13 PartitionsSet

PartitionsSet( set )
PartitionsSet( set, k )
NrPartitionsSet( set )
NrPartitionsSet( set, k )

In the first form PartitionsSet returns the set of all unordered partitions of the set set. In the second form PartitionsSet returns the set of all unordered partitions of the set set into k pairwise disjoint nonempty sets.

In the first form NrPartitionsSet returns the number of unordered partitions of the set set. In the second form NrPartitionsSet returns the number of unordered partitions of the set set into k pairwise disjoint nonempty sets.

An unordered partition of set is a set of pairwise disjoint nonempty sets with union set and is represented by a sorted list of such sets. There are B(|set|) (see 40.3) partitions of the set set and S_{2}(|set|, k) (see 40.5) partitions with k elements.

gap> PartitionsSet( [1,2,3] );
[ [ [ 1 ], [ 2 ], [ 3 ] ], [ [ 1 ], [ 2, 3 ] ], [ [ 1, 2 ], [ 3 ] ],
  [ [ 1, 2, 3 ] ], [ [ 1, 3 ], [ 2 ] ] ]
gap> PartitionsSet( [1,2,3,4], 2 );
[ [ [ 1 ], [ 2, 3, 4 ] ], [ [ 1, 2 ], [ 3, 4 ] ],
  [ [ 1, 2, 3 ], [ 4 ] ], [ [ 1, 2, 4 ], [ 3 ] ],
  [ [ 1, 3 ], [ 2, 4 ] ], [ [ 1, 3, 4 ], [ 2 ] ],
  [ [ 1, 4 ], [ 2, 3 ] ] ]
gap> NrPartitionsSet( [1..6] );
203
gap> NrPartitionsSet( [1..10], 3 );
9330

Note that PartitionsSet does currently not support multisets and that there is currently no ordered counterpart.

40.14 Partitions

Partitions( n )
Partitions( n, k )

NrPartitions( n )
NrPartitions( n, k )

In the first form Partitions returns the set of all (unordered) partitions of the positive integer n. In the second form Partitions returns the set of all (unordered) partitions of the positive integer n into sums with k summands.

In the first form NrPartitions returns the number of (unordered) partitions of the positive integer n. In the second form NrPartitions returns the number of (unordered) partitions of the positive integer n into sums with k summands.

An unordered partition is an unordered sum n = p_{1}+p_{2}+..+p_{k} of positive integers and is represented by the list p = [p_{1}, p_{2}, .., p_{k}], in nonincreasing order, i.e., p_{1} >= p_{2} >= .. >= p_{k}. We write p ⊢ n. There are approximately E^{π\sqrt{2/3n}}/4\sqrt{3}n such partitions.

It is possible to associate with every partition of the integer n a conjugacy class of permutations in the symmetric group on n points and vice versa. Therefore p(n) := NrP artitions(n) is the number of conjugacy classes of the symmetric group on n points.

Ramanujan found the identities p(5i + 4) = 0 mod 5, p(7i + 5) = 0 mod 7 and p(11i + 6) = 0 mod 11 and many other fascinating things about the number of partitions.

Do not call Partitions with an n much larger than 40, in which case there are 37338 partitions, since the list will simply become too large.

    gap> Partitions( 7 );
    [ [ 1, 1, 1, 1, 1, 1, 1 ], [ 2, 1, 1, 1, 1, 1 ], [ 2, 2, 1, 1, 1 ],
      [ 2, 2, 2, 1 ], [ 3, 1, 1, 1, 1 ], [ 3, 2, 1, 1 ], [ 3, 2, 2 ],
      [ 3, 3, 1 ], [ 4, 1, 1, 1 ], [ 4, 2, 1 ], [ 4, 3 ], [ 5, 1, 1 ],
      [ 5, 2 ], [ 6, 1 ], [ 7 ] ]
    gap> Partitions( 8, 3 );
    [ [ 3, 3, 2 ], [ 4, 2, 2 ], [ 4, 3, 1 ], [ 5, 2, 1 ], [ 6, 1, 1 ] ]
    gap> NrPartitions( 7 );
    15
    gap> NrPartitions( 100 );
    190569292

The function OrderedPartitions (see 40.15) is the ordered counterpart of Partitions.

40.15 OrderedPartitions

OrderedPartitions( n )
OrderedPartitions( n, k )
NrOrderedPartitions( n )
NrOrderedPartitions( n, k )

In the first form OrderedPartitions returns the set of all ordered partitions of the positive integer n. In the second form OrderedPartitions returns the set of all ordered partitions of the positive integer n into sums with k summands.

In the first form NrOrderedPartitions returns the number of ordered partitions of the positive integer n. In the second form NrOrderedPartitions returns the number of ordered partitions of the positive integer n into sums with k summands.

An ordered partition is an ordered sum n = p_{1} + p_{2} + .. + p_{k} of positive integers and is represented by the list [p_{1}, p_{2}, .., p_{k}]. There are totally 2^{n−1} ordered partitions and \binom{n−1}{k−1} (see 40.2) partitions with k summands.

Do not call OrderedPartitions with an n larger than 15, the list will simply become too large.

    gap> OrderedPartitions( 5 );
    [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 2 ], [ 1, 1, 2, 1 ], [ 1, 1, 3 ],
      [ 1, 2, 1, 1 ], [ 1, 2, 2 ], [ 1, 3, 1 ], [ 1, 4 ], [ 2, 1, 1, 1 ],
      [ 2, 1, 2 ], [ 2, 2, 1 ], [ 2, 3 ], [ 3, 1, 1 ], [ 3, 2 ],
      [ 4, 1 ], [ 5 ] ]
    gap> OrderedPartitions( 6, 3 );
    [ [ 1, 1, 4 ], [ 1, 2, 3 ], [ 1, 3, 2 ], [ 1, 4, 1 ], [ 2, 1, 3 ],
      [ 2, 2, 2 ], [ 2, 3, 1 ], [ 3, 1, 2 ], [ 3, 2, 1 ], [ 4, 1, 1 ] ]
    gap> NrOrderedPartitions(20);
    524288

The function Partitions (see 40.14) is the unordered counterpart of OrderedPartitions.

40.16 RestrictedPartitions

RestrictedPartitions( n, set )
RestrictedPartitions( n, set, k )
NrRestrictedPartitions( n, set )
NrRestrictedPartitions( n, set, k )

In the first form RestrictedPartitions returns the set of all restricted partitions of the positive integer n with the summands of the partition coming from the set set. In the second form RestrictedPartitions returns the set of all partitions of the positive integer n into sums with k summands with the summands of the partition coming from the set set.

In the first form NrRestrictedPartitions returns the number of restricted partitions of the positive integer n with the summands coming from the set set. In the second form NrRestrictedPartitions returns the number of restricted partitions of the positive integer n into sums with k summands with the summands of the partition coming from the set set.

A restricted partition is like an ordinary partition (see 40.14) an unordered sum n = p_{1} + p_{2} + .. + p_{k} of positive integers and is represented by the list p = [p_{1}, p_{2}, .., p_{k}], in nonincreasing order. The difference is that here the p_{i} must be elements from the set set, while for ordinary partitions they may be elements from [1..n].

    gap> RestrictedPartitions( 8, [1,3,5,7] );
    [ [ 1, 1, 1, 1, 1, 1, 1, 1 ], [ 3, 1, 1, 1, 1, 1 ], [ 3, 3, 1, 1 ],
      [ 5, 1, 1, 1 ], [ 5, 3 ], [ 7, 1 ] ]
    gap> NrRestrictedPartitions( 50, [1,5,10,25,50] );
    50

The last example tells us that there are 50 ways to return 50 cent change using 1, 5, 10 cent coins, quarters and halfdollars.

40.17 SignPartition

SignPartition( pi )

returns the sign of a permutation with cycle structure pi.

    gap> SignPartition([6,5,4,3,2,1]);
    -1

This function actually describes a homomorphism of the symmetric group S_{n} into the cyclic group of order 2, whose kernel is exactly the alternating group A_{n} (see 19.6). Partitions of sign 1 are called even partitions while partitions of sign −1 are called odd.

40.18 AssociatedPartition

AssociatedPartition( pi )

returns the associated partition of the partition pi.

    gap> AssociatedPartition([4,2,1]);
    [ 3, 2, 1, 1 ]
    gap> AssociatedPartition([6]);
    [ 1, 1, 1, 1, 1, 1 ]

The associated partition of a partition pi is defined to be the partition belonging to the transposed of the Young diagram of pi.

40.19 PowerPartition

PowerPartition( pi, k )

returns the partition corresponding to the k-th power of a permutation with cycle structure pi.

    gap> PowerPartition([6,5,4,3,2,1], 3);
    [ 5, 4, 2, 2, 2, 2, 1, 1, 1, 1 ]

Each part l of pi is replaced by d = gcd(l, k) parts l/d. So if pi is a partition of n then pi^{k} also is a partition of n. PowerPartition describes the powermap of symmetric groups.

40.20 PartitionTuples

PartitionTuples( n, r )

returns the list of all r–tuples of partitions that together partition n.

    gap> PartitionTuples(3, 2);
    [ [ [ 1, 1, 1 ], [  ] ], [ [ 1, 1 ], [ 1 ] ], [ [ 1 ], [ 1, 1 ] ],
      [ [  ], [ 1, 1, 1 ] ], [ [ 2, 1 ], [  ] ], [ [ 1 ], [ 2 ] ],
      [ [ 2 ], [ 1 ] ], [ [  ], [ 2, 1 ] ], [ [ 3 ], [  ] ],
      [ [  ], [ 3 ] ] ]

r–tuples of partitions describe the classes and the characters of wreath products of groups with r conjugacy classes with the symmetric group S_{n}.

40.21 Fibonacci

Fibonacci( n )

Fibonacci returns the nth number of the Fibonacci sequence. The Fibonacci sequence F_{n} is defined by the initial conditions F_{1} = F_{2} = 1 and the recurrence relation F_{n+2} = F_{n+1} + F_{n}. For negative n we define F_{n} = (−1)^{n+1}F_{−n}, which is consistent with the recurrence relation.

Using generating functions one can prove that F_{n} = ϕ^{n} − 1/ϕ^{n}, where ϕ is (\sqrt{5} + 1)/2, i.e., one root of x^{2} − x − 1 = 0. Fibonacci numbers have the property Gcd(F_{m}, F_{n}) = F_{Gcd(m,n)}. But a pair of Fibonacci numbers requires more division steps in Euclid’s algorithm (see 5.26) than any other pair of integers of the same size. Fibonnaci(k) is the special case

Lucas(1,-1,k)[1] (see 40.22).
    gap> Fibonacci( 10 );
    55
    gap> Fibonacci( 35 );
    9227465
    gap> Fibonacci( -10 );
    -55

40.22 Lucas

Lucas( P, Q, k )

Lucas returns the k-th values of the Lucas sequence with parameters P and Q, which must be integers, as a list of three integers.

Let α, β be the two roots of x^{2} − P x + Q then we define Lucas(P, Q, k)[1] = U_{k} = (α^{k} − β^{k})/(α − β) and Lucas(P, Q, k)[2] = V_{k} = (α^{k} + β^{k}) and as a convenience Lucas(P, Q, k)[3] = Q^{k}.

The following recurrence relations are easily derived from the definition U_{0} = 0, U_{1} = 1, U_{k} = P U_{k−1} − QU_{k−2} and V_{0} = 2, V_{1} = P, V_{k} = P V_{k−1} − QV_{k−2}. Those relations are actually used to define Lucas if α = β.

Also the more complex relations used in Lucas can be easily derived U_{2k} = U_{k}V_{k}, U_{2k+1} = (P U_{2k} + V_{2k})/2 and V_{2k} = V_{k}^{2} − 2Q^{k}, V_{2k+1} = ((P^{2} − 4Q)U_{2k} + P V_{2k})/2.

Fibonnaci(k) (see 40.21) is simply Lucas(1,-1,k)[1]. In an abuse of notation, the sequence Lucas(1,-1,k)[2] is sometimes called the Lucas sequence.

    gap> List( [0..10], i->Lucas(1,-2,i)[1] );
    [ 0, 1, 1, 3, 5, 11, 21, 43, 85, 171, 341 ]  # 2^{k} − (−1)^{k})/3
    gap> List( [0..10], i->Lucas(1,-2,i)[2] );
    [ 2, 1, 5, 7, 17, 31, 65, 127, 257, 511, 1025 ]  # 2^{k} + (−1)^{k}
    gap> List( [0..10], i->Lucas(1,-1,i)[1] );
    [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]  # Fibonacci sequence
    gap> List( [0..10], i->Lucas(2,1,i)[1] );
    [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]  # the roots are equal

40.23 Bernoulli

Bernoulli( n )

Bernoulli returns the n-th Bernoulli number B_{n}, which is defined by B_{0} = 1 and B_{n} = − ∑_{k=0}^{n−1} \binom{n+1}{k}B_{k}/(n + 1).

B_{n}/n! is the coefficient of x^{n} in the power series of x/e^{x} − 1. Except for B_{1} = −1/2 the Bernoulli numbers for odd indices m are zero.

gap> Bernoulli( 4 );
-1/30
gap> Bernoulli( 10 );
5/66
gap> Bernoulli( 12 );

-691/2730 # there is no simple pattern in Bernoulli numbers

gap> Bernoulli( 50 );
495057205241079648212477525/66  # and they grow fairly fast

648 CHAPTER 40. COMBINATORICS

Tables of Marks

The concept of a table of marks was introduced by W. Burnside in his book Theory of Groups of Finite Order [Bur55]. Therefore a table of marks is sometimes called a Burnside matrix. The table of marks of a finite group G is a matrix whose rows and columns are labelled by the conjugacy classes of subgroups of G and where for two subgroups A and B the (A, B)– entry is the number of fixed points of B in the transitive action of G on the cosets of A in G. So the table of marks characterizes all permutation representations of G. Moreover, the table of marks gives a compact description of the subgroup lattice of G, since from the numbers of fixed points the numbers of conjugates of a subgroup B contained in a subgroup A can be derived. This chapter describes a function (see 41.4) which restores a table of marks from the GAP library of tables of marks (see 41.3) or which computes the table of marks for a given group from the subgroup lattice of that group. Moreover this package contains a function to display a table of marks (see 41.12), a function to check the consistency of a table of marks (see 41.11), functions which switch between several forms of representation (see 41.5, 41.6, 41.8, and 41.9), functions which derive information about the group from the table of marks (see 41.10, 41.13, 41.14, 41.15, 41.16, 41.17, 41.18, 41.19, 41.20, 41.21, and 41.22), and some functions for the generic construction of a table of marks (see 41.23, 41.24, and 41.25). The functions described in this chapter are implemented in the file LIBNAME/"tom.g".

41.1 More about Tables of Marks

Let G be a finite group with n conjugacy classes of subgroups C_{1}, …, C_{n} and representatives H_{i} ∈ C_{i}, i = 1, …, n. The table of marks of G is defined to be the n×n matrix M = (m_{ij}) where m_{ij} is the number of fixed points of the subgroup H_{j} in the action of G on the cosets of H_{i} in G. Since H_{j} can only have fixed points if it is contained in a one point stablizer the matrix M is lower triangular if the classes C_{i} are sorted according to the following condition; if H_{i} is contained in a conjugate of H_{j} then i ≤ j. Moreover, the diagonal entries m_{ii} are nonzero since m_{ii} equals the index of H_{i} in its normalizer in G. Hence M is invertible. Since any transitive action of G is equivalent to an action on the cosets of a subgroup of G, one sees that the table of marks completely characterizes permutation representations of G.

The entries m_{ij} have further meanings. If H_{1} is the trivial subgroup of G then each mark m_{i1} in the first column of M is equal to the index of H_{i} in G since the trivial subgroup fixes all cosets of H_{i}. If H_{n} = G then each m_{nj} in the last row of M is equal to 1 since there is only one coset of G in G. In general, m_{ij} equals the number of conjugates of H_{i} which contain H_{j}, multiplied by the index of H_{i} in its normalizer in G. Moreover, the number c_{ij} of conjugates of H_{j} which are contained in H_{i} can be derived from the marks m_{ij} via the formula

c_{ij} = \frac{m_{ij}m_{j1}}{m_{i1}m_{jj}} .

Both the marks m_{ij} and the numbers of subgroups c_{ij} are needed for the functions described in this chapter.

41.2 Table of Marks Records

A table of marks is represented by a record. This record has at least a component subs which is a list where for each conjugacy class of subgroups the class numbers of its subgroups are stored. These are exactly the positions in the corresponding row of the table of marks which have nonzero entries.

The marks themselves can be stored in the component marks which is a list that contains for each entry in the component subs the corresponding nonzero value of the table of marks.

The same information is, however, given by the three components nrSubs, length, and order, where nrSubs is a list which contains for each entry in the component subs the corresponding number of conjugates which are contained in a subgroup, length is a list which contains for each class of subgroups its length, and order is a list which contains for each class of subgroups their order.

So a table of marks consists either of the components subs and marks or of the components subs, nrSubs, length, and order. The functions Marks (see 41.5) and NrSubs (see 41.6) will derive one representation from the other when needed.

Additional information about a table of marks is needed by some functions. The class numbers of normalizers are stored in the component normalizer. The number of the derived subgroup of the whole group is stored in the component derivedSubgroup.

41.3 The Library of Tables of Marks

This package of functions comes together with a library of tables of marks. The library files are stored in a directory TOMNAME. The file TOMNAME/"tmprimar.tom" is the primary file of the library of tables of marks. It contains the information where to find a table and the function TomLibrary which restores a table from the library.

The secondary files are

    tmaltern.tom  tmmath24.tom  tmsuzuki.tom  tmunitar.tom
    tmlinear.tom  tmmisc.tom    tmsporad.tom  tmsymple.tom

The list TOMLIST contains for each table an entry with its name and the name of the file where it is stored.

A table of marks which is restored from the library will be stored as a component of the record TOM.

41.4 TableOfMarks

TableOfMarks( str )

If the argument str given to TableOfMarks is a string then TableOfMarks will search the library of tables of marks (see 41.3) for a table with name str. If such a table is found then TableOfMarks will return a copy of that table. Otherwise TableOfMarks will return false.

    gap> a5 := TableOfMarks( "A5" );
    rec(
      derivedSubgroup := 9,
      nrSubs := [ [ 1 ], [ 1, 1 ], [ 1, 1 ], [ 1, 3, 1 ], [ 1, 1 ],
          [ 1, 3, 1, 1 ], [ 1, 5, 1, 1 ], [ 1, 3, 4, 1, 1 ],
          [ 1, 15, 10, 5, 6, 10, 6, 5, 1 ] ],
      order := [ 1, 2, 3, 4, 5, 6, 10, 12, 60 ],
      subs := [ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 2, 4 ], [ 1, 5 ],
          [ 1, 2, 3, 6 ], [ 1, 2, 5, 7 ], [ 1, 2, 3, 4, 8 ],
          [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ],
      length := [ 1, 15, 10, 5, 6, 10, 6, 5, 1 ] )
    gap> TableOfMarks( "A10" );
    #W  TableOfMarks: no table of marks A10 found.
    false
TableOfMarks( grp )

If TableOfMarks is called with a group grp as its argument then the table of marks of that group will be computed and returned in the compressed format. The computation of the table of marks requires the knowledge of the complete subgroup lattice of the group grp. If the lattice is not yet known then it will be constructed (see 7.70). This may take a while if the group grp is large.

Moreover, as the Lattice command is involved the applicability of TableOfMarks underlies the same restrictions with respect to the soluble residuum of grp as described in section 7.70. The result of TableOfMarks is assigned to the component tableOfMarks of the group record grp, so that the next call to TableOfMarks with the same argument can just return this component tableOfMarks.

Warning: Note that TableOfMarks has changed with the release GAP 3.2. It now returns the table of marks in compressed form. However, you can apply the MatTom command (see 41.8) to convert it into the square matrix which was returned by TableOfMarks in GAP version 3.1.

    gap> alt5 := AlternatingPermGroup( 5 );;
    gap> TableOfMarks( alt5 );
    rec(
      subs := [ [ 1 ], [ 2, 1 ], [ 3, 1 ], [ 4, 1, 2 ], [ 5, 1 ],
          [ 6, 1, 2, 3 ], [ 7, 1, 2, 5 ], [ 8, 1, 2, 3, 4 ],

          [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ] ],
      marks := [ [ 60 ], [ 2, 30 ], [ 2, 20 ], [ 3, 15, 3 ], [ 2, 12 ],
          [ 1, 10, 2, 1 ], [ 1, 6, 2, 1 ], [ 1, 5, 1, 2, 1 ],
          [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ] )
    gap> last = alt5.tableOfMarks;
    true

For a pretty print display of a table of marks see 41.12.

41.5 Marks

Marks( tom )

Marks returns the list of lists of marks of the table of marks tom. If these are not yet stored in the component marks of tom then they will be computed and assigned to the component

marks.
    gap> Marks( a5 );
    [ [ 60 ], [ 30, 2 ], [ 20, 2 ], [ 15, 3, 3 ], [ 12, 2 ],
      [ 10, 2, 1, 1 ], [ 6, 2, 1, 1 ], [ 5, 1, 2, 1, 1 ],
      [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ]

41.6 NrSubs

NrSubs( tom )

NrSubs returns the list of lists of numbers of subgroups of the table of marks tom. If these are not yet stored in the component nrSubs of tom then they will be computed and assigned to the component nrSubs.

NrSubs also has to compute the orders and lengths from the marks.

    gap> NrSubs( a5 );
    [ [ 1 ], [ 1, 1 ], [ 1, 1 ], [ 1, 3, 1 ], [ 1, 1 ], [ 1, 3, 1, 1 ],
      [ 1, 5, 1, 1 ], [ 1, 3, 4, 1, 1 ], [ 1, 15, 10, 5, 6, 10, 6, 5, 1 ]
     ]

41.7 WeightsTom

WeightsTom( tom )

WeightsTom extracts the weights from a table of marks tom, i.e., the diagonal entries, indicating the index of a subgroup in its normalizer.

    gap> wt := WeightsTom( a5 );
    [ 60, 2, 2, 3, 2, 1, 1, 1, 1 ]

This information may be used to obtain the numbers of conjugate supergroups from the marks.

    gap> marks := Marks( a5 );;
    gap> List( [ 1 .. 9 ], x -> marks[x] / wt[x] );
    [ [ 1 ], [ 15, 1 ], [ 10, 1 ], [ 5, 1, 1 ], [ 6, 1 ], [ 10, 2, 1, 1 ],
      [ 6, 2, 1, 1 ], [ 5, 1, 2, 1, 1 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ]

41.8 MatTom

MatTom( tom )

MatTom produces a square matrix corresponding to the table of marks tom in compressed form. For large tables this may need a lot of space.

    gap> MatTom( a5 );
    [ [ 60, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 30, 2, 0, 0, 0, 0, 0, 0, 0 ],
      [ 20, 0, 2, 0, 0, 0, 0, 0, 0 ], [ 15, 3, 0, 3, 0, 0, 0, 0, 0 ],
      [ 12, 0, 0, 0, 2, 0, 0, 0, 0 ], [ 10, 2, 1, 0, 0, 1, 0, 0, 0 ],
      [ 6, 2, 0, 0, 1, 0, 1, 0, 0 ], [ 5, 1, 2, 1, 0, 0, 0, 1, 0 ],
      [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ]

41.9 TomMat

TomMat( mat )

Given a matrix mat which contains the marks of a group as its entries, TomMat will produce the corresponding table of marks record.

    gap> mat:=
    > [ [ 60, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 30, 2, 0, 0, 0, 0, 0, 0, 0 ],
    >   [ 20, 0, 2, 0, 0, 0, 0, 0, 0 ], [ 15, 3, 0, 3, 0, 0, 0, 0, 0 ],
    >   [ 12, 0, 0, 0, 2, 0, 0, 0, 0 ], [ 10, 2, 1, 0, 0, 1, 0, 0, 0 ],
    >   [ 6, 2, 0, 0, 1, 0, 1, 0, 0 ], [ 5, 1, 2, 1, 0, 0, 0, 1, 0 ],
    >   [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ];;
    gap> TomMat( mat );
    rec(
      subs := [ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 2, 4 ], [ 1, 5 ],
          [ 1, 2, 3, 6 ], [ 1, 2, 5, 7 ], [ 1, 2, 3, 4, 8 ],
          [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ],
      marks := [ [ 60 ], [ 30, 2 ], [ 20, 2 ], [ 15, 3, 3 ], [ 12, 2 ],
          [ 10, 2, 1, 1 ], [ 6, 2, 1, 1 ], [ 5, 1, 2, 1, 1 ],
          [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ] )
    gap> TomMat( IdentityMat( 7 ) );
    rec(
      subs := [ [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ] ],
      marks := [ [ 1 ], [ 1 ], [ 1 ], [ 1 ], [ 1 ], [ 1 ], [ 1 ] ] )

41.10 DecomposedFixedPointVector

DecomposedFixedPointVector( tom, fix )

Let the group with table of marks tom act as a permutation group on its conjugacy classes of subgroups, then fix is assumed to be a vector of fixed point numbers, i.e., a vector which contains for each class of subgroups the number of fixed points under that action. DecomposedFixedPointVector returns the decomposition of fix into rows of the table of marks. This decomposition corresponds to a decomposition of the action into transitive constituents. Trailing zeros in fix may be omitted.

    gap> DecomposedFixedPointVector( a5, [ 16, 4, 1, 0, 1, 1, 1 ] );

[ ,,,,, 1, 1 ]

The vector fix may be any vector of integers. The resulting decomposition, however, will not be integral, in general.

gap> DecomposedFixedPointVector( a5, [ 0, 0, 0, 0, 1, 1 ] );
[ 2/5, -1, -1/2,, 1/2, 1 ]

41.11 TestTom

TestTom( tom )

TestTom decomposes all tensor products of rows of the table of marks tom. It returns true if all decomposition numbers are nonnegative integers and false otherwise. This provides a strong consistency check for a table of marks.

gap> TestTom( a5 );
true

41.12 DisplayTom

DisplayTom( tom )

DisplayTom produces a formatted output for the table of marks tom. Each line of output begins with the number of the corresponding class of subgroups. This number is repeated if the output spreads over several pages.

gap> DisplayTom( a5 );
1:  60
2:  30 2
3:  20 . 2
4:  15 3 . 3
5:  12 . . . 2
6:  10 2 1 . . 1
7:   6 2 . . 1 . 1
8:   5 1 2 1 . . . 1
9:   1 1 1 1 1 1 1 1 1
DisplayTom( tom, arec )

In this form DisplayTom takes a record arec as an additional parameter. If this record has a component classes which contains a list of class numbers then only the rows and columns of the matrix corresponding to this list are printed.

gap> DisplayTom( a5, rec( classes := [ 1, 2, 3, 4, 8 ] ) );
1:  60
2:  30 2
3:  20 . 2
4:  15 3 . 3
8:   5 1 2 1 1

The record arec may also have a component form which enables the printing of tables of numbers of subgroups. If arec.form has the value "subgroups" then at position (i, j) the number of conjugates of H_{j} contained in H_{i} will be printed. If it has the value "supergroups" then at position (i, j) the number of conjugates of H_{i} which contain H_{j} will be printed.

gap> DisplayTom( a5, rec( form := "subgroups" ) );
1:  1
2:  1  1
3:  1  .  1
4:  1  3  . 1
5:  1  .  . . 1
6:  1  3  1 . .  1
7:  1  5  . . 1  . 1
8:  1  3  4 1 .  . . 1
9:  1 15 10 5 6 10 6 5 1
gap> DisplayTom( a5, rec( form := "supergroups" ) );
1:   1
2:  15 1
3:  10 . 1
4:   5 1 . 1
5:   6 . . . 1
6:  10 2 1 . . 1
7:   6 2 . . 1 . 1
8:   5 1 2 1 . . . 1
9:   1 1 1 1 1 1 1 1 1

41.13 NormalizerTom

NormalizerTom( tom, u )

NormalizerTom tries to find conjugacy class of the normalizer of a subgroup with class number u. It will return the list of class numbers of those subgroups which have the right size and contain the subgroup and all subgroups which clearly contain it as a normal subgroup. If the normalizer is uniquely determined by these conditions then only its class number will be returned. NormalizerTom should never return an empty list.

gap> NormalizerTom( a5, 4 );
8

The example shows that a subgroup with class number 4 in A_{5} (which is a Kleinan four group) is normalized by a subgroup in class 8. This class contains the subgroups of A_{5} which are isomorphic to A_{4}.

41.14 IntersectionsTom

IntersectionsTom( tom, a, b )

The intersections of the two conjugacy classes of subgroups with class numbers a and b, respectively, are determined by the decomposition of the tensor product of their rows of marks. IntersectionsTom returns this decomposition.

gap> IntersectionsTom( a5, 8, 8 );
[ ,, 1,,,,, 1 ]

Any two subgroups of class number 8 (A_{4}) of A_{5} are either equal and their intersection has again class number 8, or their intersection has class number 3, and is a cyclic subgroup of order 3.

41.15 IsCyclicTom

IsCyclicTom( tom, n )

A subgroup is cyclic if and only if the sum over the corresponding row of the inverse table of marks is nonzero (see [Ker91], page 125). Thus we only have to decompose the corresponding idempotent.

    gap> for i in [ 1 .. 6 ] do
    > Print( i, ": ", IsCyclicTom(a5, i), "  " );
    > od;  Print( "\n" );
    1: true  2: true  3: true  4: false  5: true  6: false

41.16 FusionCharTableTom

FusionCharTableTom( tbl, tom )

FusionCharTableTom determines the fusion of the classes of elements from the character table tbl into classes of cyclic subgroups on the table of marks tom.

    gap> a5c := CharTable( "A5" );;
    gap> fus := FusionCharTableTom( a5c, a5 );
    [ 1, 2, 3, 5, 5 ]

41.17 PermCharsTom

PermCharsTom( tom, fus )

PermCharsTom reads the list of permutation characters from the table of marks tom. It therefore has to know the fusion map fus which sends each conjugacy class of elements of the group to the conjugacy class of subgroups they generate.

    gap> PermCharsTom( a5, fus );
    [ [ 60, 0, 0, 0, 0 ], [ 30, 2, 0, 0, 0 ], [ 20, 0, 2, 0, 0 ],
      [ 15, 3, 0, 0, 0 ], [ 12, 0, 0, 2, 2 ], [ 10, 2, 1, 0, 0 ],
      [ 6, 2, 0, 1, 1 ], [ 5, 1, 2, 0, 0 ], [ 1, 1, 1, 1, 1 ] ]

41.18 MoebiusTom

MoebiusTom( tom )

MoebiusTom computes the Möbius values both of the subgroup lattice of the group with table of marks tom and of the poset of conjugacy classes of subgroups. It returns a record where the component mu contains the Möbius values of the subgroup lattice, and the component nu contains the Möbius values of the poset. Moreover, according to a conjecture of Isaacs et al. (see [HIÖ89], [Pah93]), the values on the poset of conjugacy classes are derived from those of the subgroup lattice. These theoretical values are returned in the component ex. For that computation, the derived subgroup must be known in the component derivedSubgroup of tom. The numbers of those subgroups where the theoretical value does not coincide with the actual value are returned in the component hyp.

    gap> MoebiusTom( a5 );
    rec(

      mu := [ -60, 4, 2,,, -1, -1, -1, 1 ],
      nu := [ -1, 2, 1,,, -1, -1, -1, 1 ],
      ex := [ -60, 4, 2,,, -1, -1, -1, 1 ],
      hyp := [  ] )

41.19 CyclicExtensionsTom

CyclicExtensionsTom( tom, p )

According to A. Dress [Dre69], two columns of the table of marks tom are equal modulo the prime p if and only if the corresponding subgroups are connected by a chain of normal extensions of order p. CyclicExtensionsTom returns the classes of this equivalence relation.

This information is not used by NormalizerTom although it might give additional restrictions in the search of normalizers.

    gap> CyclicExtensionsTom( a5, 2 );
    [ [ 1, 2, 4 ], [ 3, 6 ], [ 5, 7 ], [ 8 ], [ 9 ] ]

41.20 IdempotentsTom

IdempotentsTom( tom )

IdempotentsTom returns the list of idempotents of the integral Burnside ring described by the table of marks tom. According to A. Dress [Dre69], these idempotents correspond to the classes of perfect subgroups, and each such idempotent is the characteristic function of all those subgroups which arise by cyclic extension from the corresponding perfect subgroup.

    gap> IdempotentsTom( a5 );
    [ 1, 1, 1, 1, 1, 1, 1, 1, 9 ]

41.21 ClassTypesTom

ClassTypesTom( tom )

ClassTypesTom distinguishes isomorphism types of the classes of subgroups of the table of marks tom as far as this is possible. Two subgroups are clearly not isomorphic if they have different orders. Moreover, isomorphic subgroups must contain the same number of subgroups of each type.

The types are represented by numbers. ClassTypesTom returns a list which contains for each class of subgroups its corresponding number.

    gap> a6 := TableOfMarks( "A6" );;
    gap> ClassTypesTom( a6 );
    [ 1, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 9, 10, 11, 11, 12, 13, 13, 14, 15,
      15, 16 ]

41.22 ClassNamesTom

ClassNamesTom( tom )

ClassNamesTom constructs generic names for the conjugacy classes of subgroups of the table of marks tom.

In general, the generic name of a class of non–cyclic subgroups consists of three parts, "(order)", " {type}", and "letter", and hence has the form "(order) {type}letter", where order indicates the order of the subgroups, type is a number that distinguishes different types of subgroups of the same order, and letter is a letter which distinguishes classes of subgroups of the same type and order. The type of a subgroup is determined by the numbers of its subgroups of other types (see 41.21). This is slightly weaker than isomorphism.

The letter is omitted if there is only one class of subgroups of that order and type, and the type is omitted if there is only one class of that order. Moreover, the braces round the type are omitted if the type number has only one digit.

For classes of cyclic subgoups, the parentheses round the order and the type are omitted. Hence the most general form of their generic names is "order letter". Again, the letter is omitted if there is only one class of cyclic subgroups of that order.

    gap> ClassNamesTom( a6 );
    [ "1", "2", "3a", "3b", "5", "4", "(4)_2a", "(4)_2b", "(6)a", "(6)b",
      "(9)", "(10)", "(8)", "(12)a", "(12)b", "(18)", "(24)a", "(24)b",
      "(36)", "(60)a", "(60)b", "(360)" ]

41.23 TomCyclic

TomCyclic( n )

TomCyclic constructs the table of marks of the cyclic group of order n. A cyclic group of order n has as its subgroups for each divisor d of n a cyclic subgroup of order d. The record which is returned has an additional component name where for each subgroup its order is given as a string.

    gap> c6 := TomCyclic( 6 );
    rec(
      name := [ "1", "2", "3", "6" ],
      subs := [ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 2, 3, 4 ] ],
      marks := [ [ 6 ], [ 3, 3 ], [ 2, 2 ], [ 1, 1, 1, 1 ] ] )
    gap> DisplayTom( c6 );
    1:  6
    2:  3 3
    3:  2 . 2
    4:  1 1 1 1

41.24 TomDihedral

TomDihedral( m )

TomDihedral constructs the table of marks of the dihedral group of order m. For each divisor d of m, a dihedral group of order m = 2n contains subgroups of order d according to the following rule. If d is odd and divides n then there is only one cyclic subgroup of order d. If d is even and divides n then there are a cyclic subgroup of order d and two classes of dihedral subgroups of order d which are cyclic, too, in the case d = 2, see example below). Otherwise, (i.e. if d does not divide n, there is just one class of dihedral subgroups of order d.

    gap> d12 := TomDihedral( 12 );

rec(
  name := [ "1", "2", "D_{2}a", "D_{2}b", "3", "D_{4}", "6",
      "D_{6}a", "D_{6}b", "D_{12}" ],
  subs := [ [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 5 ],
      [ 1, 2, 3, 4, 6 ], [ 1, 2, 5, 7 ], [ 1, 3, 5, 8 ],
      [ 1, 4, 5, 9 ], [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] ],
  marks := [ [ 12 ], [ 6, 6 ], [ 6, 2 ], [ 6, 2 ], [ 4, 4 ],
      [ 3, 3, 1, 1, 1 ], [ 2, 2, 2, 2 ], [ 2, 2, 2, 2 ],
      [ 2, 2, 2, 2 ], [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] ] )
gap> DisplayTom( d12 );
 1:  12
 2:   6 6
 3:   6 . 2
 4:   6 . . 2
 5:   4 . . . 4
 6:   3 3 1 1 . 1
 7:   2 2 . . 2 . 2
 8:   2 . 2 . 2 . . 2
 9:   2 . . 2 2 . . . 2
10:   1 1 1 1 1 1 1 1 1 1

41.25 TomFrobenius

TomFrobenius( p, q )

TomFrobenius computes the table of marks of a Frobenius group of order pq, where p is a prime and q divides p − 1.

gap> f20 := TomFrobenius( 5, 4 );
rec(
  name := [ "1", "2", "4", "5:1", "5:2", "5:4" ],
  subs := [ [ 1 ], [ 1, 2 ], [ 1, 2, 3 ], [ 1, 4 ], [ 1, 2, 4, 5 ],
      [ 1, 2, 3, 4, 5, 6 ] ],
  marks :=
   [ [ 20 ], [ 10, 2 ], [ 5, 1, 1 ], [ 4, 4 ], [ 2, 2, 2, 2 ],
      [ 1, 1, 1, 1, 1, 1 ] ] )
gap> DisplayTom( f20 );
1:  20
2:  10 2
3:   5 1 1
4:   4 . . 4
5:   2 2 . 2 2
6:   1 1 1 1 1 1

660 CHAPTER 41. TABLES OF MARKS

Character Tables

This chapter contains

the introduction of GAP character tables (see 42.1, 42.2, 42.3, 42.4, 42.5, 42.6) and some conventions for their usage (see 42.7),

the description how to construct or get character tables (see 42.8, 42.9; for the contents of the table library, see Chapter 46), matrix representations (see 42.22).

the description of some functions which give information about the conjugacy classes of character tables, that is, to compute classlengths (see 42.24), inverse classes (see 42.25) and classnames (see 42.26), structure constants (see 42.27, 42.28, 42.29), the set of real classes (see 42.30), orbits of the Galois group on the classes (see 42.31) and roots of classes (see 42.32),

the description how character tables or parts of them can be displayed (see 42.34) and sorted (see 42.35, 42.36, 42.37).

the description of functions which compute the automorphism group of a matrix (see 42.38) or character table (see 42.39), or which compute permutations relating permutation equivalent matrices (see 42.40) or character tables (see 42.41),

the description of functions which get fusions from and store fusions on tables (see 42.42, 42.43, 42.44),

the description of the interface between GAP and the MOC3 system (see 42.45, 42.46, 42.47, 42.48, 42.49, 42.50 ), and of a function which converts GAP tables to CAS tables (see 42.51).

This chapter does not contain information about

functions to construct characters (see Chapter 44), or functions to construct and use maps (see Chapter 45).

For some elaborate examples how character tables are handled in GAP, see 1.25.

42.1 Some Notes on Character Theory in GAP

It seems to be necessary to state some basic facts –and maybe warnings– at the beginning of the character theory package. This holds for people who are familiar with character theory because there is no global reference on computational character theory, although there are many papers on this topic, like [NPP84] or [LP91]. It holds, however, also for people who are familiar with GAP because the general concept of categories and domains (see 1.23 and chapter 4) plays no important role here –we will justify this later in this section.

Intuitively, characters of the finite group G can be thought of as certain mappings defined on G, with values in the complex number field; the set of all characters of G forms a semiring with addition and multiplication both defined pointwise, which is embedded in the ring of generalized (or virtual) characters in the natural way. A Z–basis of this ring, and also a vector space base of the vector space of class functions, is given by the irreducible characters.

At this stage one could ask where there is a problem, since all these algebraic structures are supported by GAP, as is described in chapters 4, 5, 9, 36, and others.

Now, we first should say that characters are not implemented as mappings, that there are no GAP domains denoting character rings, and that a character table is not a domain.

For computations with characters of a finite group G with n conjugacy classes, say, we fix an order of the classes, and then identify each class with its position according to this order. Each character of G will be represented as list of length n where at the i–th position the character value for elements of the i–th class is stored. Note that we do not need to know the conjugacy classes of G physically, even our “knowledge” of the group may be implicit in the sense that e.g. we know how many classes of involutions G has, and which length these classes have, but we never have seen an element of G, or a presentation or representation of G. This allows to work with the character tables of very large groups, e.g., of the so–called monster, where GAP has no chance to work with the group.

As a consequence, also other information involving characters is given implicitly. For example, we can talk about the kernel of a character not as a group but as a list of classes (more exactly: a list of their positions according to the order of classes) forming this kernel; we can deduce the group order, the contained cyclic subgroups and so on, but we do not get the group itself.

Characters are one kind of class functions, and we also represent general class functions as lists. Two important kinds of these functions which are not characters are power maps and fusion maps. The k–th power map maps each class to the class of k–th powers of its elements, the corresponding list contains at each position the position of the image. A subgroup fusion map between the classes of a subgroup H of G and the classes of G maps each class c of H to that class of G that contains c; if we know only the character tables of the two groups, this means with respect to a fixed embedding of H in G.

So the data mainly consist of lists, and typical calculations with character tables are more or less loops over these lists. For example, the known scalar product of two characters χ, ψ of G given by

[χ, ψ] = \frac{1}{|G|} ∑_{g∈G} χ(g)ψ(g^{−1})

can be written as

    Sum( [1..n], i -> t.classes[i]*chi[i]*GaloisCyc(psi[i],-1) );

where t.classes is the list of classlengths, and chi, psi are the lists corresponding to χ, ψ, respectively. Characters, classlengths, element orders, power maps, fusion maps and other information about a group is stored in a common character table record just to avoid confusion, not to indicate an algebraic structure (which would mean a domain in the sense of GAP).

A character table is not determined by something similar to generators for groups or rings in GAP where other components (the knowledge about the domain) is stored for the sake of efficiency. In many situations one works with incomplete tables or preliminary tables which are, strictly speaking, no character tables but shall be handled like character tables. Moreover, the correctness or even the consistency of a character table is hard to prove. Thus it is not sufficient to view a character table as a black box, and to get information about it using a few property test functions. In fact there are very few functions that return character tables or that are property tests. Most GAP functions dealing with character tables return class functions, or lists of them, or information about class functions. For that, GAP directly accesses the components of the table record, and the user will have to look at the record components, too, in order to put the pieces of the puzzle together, and to decide how to go on.

So it is not easy to say what a character table is; it describes some properties of the underlying group, and it describes them in a rather abstract way. Also GAP does not know whether or not a list is a character, it will e.g. regard a list with all entries equal to 1 as the trivial character if it is passed to a function that expects characters.

It is one of the advantages of character theory that after one has translated a problem concerning groups into a problem concerning their character tables the calculations are mostly simple. For example, one can often prove that a group is a Galois group over the rationals using calculations of structure constants that can be computed from the character table, and informations on (the character tables of) maximal subgroups.

In this kind of problems the translation back to the group is just an interpretation by the user, it does not take place in GAP. At the moment, the only interface between handling groups and handling character tables is the fixed order of conjugacy classes.

Note that algebraic structures are not of much interest in character theory. The main reason for this is that we have no homomorphisms since we need not to know anything about the group multiplication.

42.2 Character Table Records

For GAP, a character table is any record which has the components centralizers and name (see 42.4).

There are three different but very similar types of character tables in GAP, namely ordinary tables, Brauer tables and generic tables. Generic tables are described in Chapter 43. Brauer tables are defined and stored relative to ordinary tables, so they will be described in 42.3, and we start with ordinary tables.

You may store arbitrary information on an ordinary character table, but these are the only fields used by GAP functions:

centralizers

the list of centralizer orders which should be positive integers

name

the name of the table (a string); it is used for fusions (see below), programs for generic tables (see chapter 43) and for access to library tables (see 42.9, 46.1)

order

the group order, a positive integer; in most cases, it is equal to centralizers[1]

classes

the lengths of conjugacy classes, a list of positive integers

orders

the list of representative orders

powermap

a list where at position p, if bound, the p-th powermap is stored; the p-th powermap is a -possibly parametrized- map (see 45.1)

fusions

a list of records which describe the fusions into other character tables, that is subgroup fusions and factor fusions; any record has fields name (the name of the destination table) and map (a list of images for the classes, it may be parametrized (see 45.1)); if there are different fusions with same destination table, the field specification is used to distinguish them; optional fields are type (a string that is "normal" for normal subgroup fusions and "factor" for factor fusions) and text (a string with information about the fusion)

fusionsource

a list of the names of those tables which contain a fusion into the actual table

irreducibles

a list of irreducible characters (see below)

irredinfo

a list of records with information about irreducibles, usual entries are indicator, pblock and charparam (see 44.7, 44.6, 43); if the field irreducibles is sorted using 42.35, the irredinfo field is sorted, too. So any information about irreducibles should be stored here.

projectives

(only for ATLAS tables, see 46.2) a list of records, each with fields name (of the table of a covering group) and chars (a list of –in general not all– faithful irreducibles of the covering group)

permutation

the actual permutation of the classes (see 42.7, 42.36)

classparam

a list of parameter values specifying the classes of tables constructed via specialisation of a generic character table (see chapter 43)

classtext

a list of additional information about the conjugacy classes (e.g. representatives of the class for matrix groups or permutation groups)

text

a string containing information about the table; these are e.g. its source (see Chapter 46), the tests it has passed (1.o.r. for the test of orthogonality, pow[p] for the construction of the p-th powermap, DEC for the decomposition of ordinary characters in Brauer characters), and choices made without loss of generality where possible

automorphisms

the permutation group of column permutations preserving the set irreducibles (see 42.38, 42.39)

classnames

a list of names for the classes, a string each (see 42.26)

classnames for each entry clname in classnames, a field tbl.clname that has the position of clname in classnames as value (see 42.26)

operations

a record with fields Print (see 42.34) and ScalarProduct (see 44.1); the default value

of the operations field is CharTableOps (see 42.6)
CAS

a list of records, each with fields permchars, permclasses (both permutations), name and eventually text and classtext; application of the two permutations to irreducibles and classes yields the original CAS library table with name name and text text (see 46.4)

libinfo

a record with fields othernames and eventually CASnames which are all admissible names of the table (see 42.9); using these records, the list LIBLIST.ORDINARY can be constructed from the library using MakeLIBLIST (see 46.5)

group

the group the table belongs to; if the table was computed using CharTable (see 42.9) then this component holds the group, with conjugacy classes sorted compatible with the columns of the table

Note that tables in library files may have different format (see chapter 46).

This is a typical example of a character table, first the “naked” record, then the displayed version:

gap> t:= CharTable( "2.A5" );
rec( text := "origin: ATLAS of finite groups, tests: 1.o.r., pow[2,3,5\
]", centralizers := [ 120, 120, 4, 6, 6, 10, 10, 10, 10
], powermap := [ , [ 1, 1, 2, 4, 4, 8, 8, 6, 6 ],
 [ 1, 2, 3, 1, 2, 8, 9, 6, 7 ],, [ 1, 2, 3, 4, 5, 1, 2, 1, 2 ]
], fusions := [ rec(
     name := "A5",
     map := [ 1, 1, 2, 3, 3, 4, 4, 5, 5 ],
     type := "factor" ), rec(
     name := "2.A5.2",
     map := [ 1, 2, 3, 4, 5, 6, 7, 6, 7 ],
     type := "normal" ) ], projectionsource :=
[ "A5" ], irreducibles := [ [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
 [ 3, 3, -1, 0, 0, -E(5)-E(5)^4, -E(5)-E(5)^4, -E(5)^2-E(5)^3,
     -E(5)^2-E(5)^3 ],
 [ 3, 3, -1, 0, 0, -E(5)^2-E(5)^3, -E(5)^2-E(5)^3, -E(5)-E(5)^4,
     -E(5)-E(5)^4 ], [ 4, 4, 0, 1, 1, -1, -1, -1, -1 ],
 [ 5, 5, 1, -1, -1, 0, 0, 0, 0 ],

  [ 2, -2, 0, -1, 1, E(5)+E(5)^4, -E(5)-E(5)^4, E(5)^2+E(5)^3,
      -E(5)^2-E(5)^3 ],
  [ 2, -2, 0, -1, 1, E(5)^2+E(5)^3, -E(5)^2-E(5)^3, E(5)+E(5)^4,
      -E(5)-E(5)^4 ], [ 4, -4, 0, 1, -1, -1, 1, -1, 1 ],
  [ 6, -6, 0, 0, 0, 1, -1, 1, -1 ] ], irredinfo := [ rec(
      pblock := [ , 1, 1,, 1 ] ), rec(
      pblock := [ , 1, 2,, 1 ] ), rec(
      pblock := [ , 1, 3,, 1 ] ), rec(
      pblock := [ , 2, 1,, 1 ] ), rec(
      pblock := [ , 1, 1,, 2 ] ), rec(
      pblock := [ , 1, 4,, 3 ] ), rec(
      pblock := [ , 1, 4,, 3 ] ), rec(
      pblock := [ , 2, 4,, 3 ] ), rec(
      pblock := [ , 1, 5,, 3 ] ) ], automorphisms := Group( (6,8)
(7,9) ), libinfo := rec(
  othernames :=
   [ "2.L2(4)", "2.L2(5)", "2.A1(4)", "2.A1(5)", "2.U2(4)",
      "2.U2(5)", "2.S2(4)", "2.S2(5)", "2.O3(4)", "2.O3(5)",
      "2.O4-(2)" ],
  firstname := "2.A5",
  filename := "ctoalter" ), name := "2.A5", order := 120, orders :=
[ 1, 2, 4, 3, 6, 5, 10, 5, 10 ], fusionsource := [  ], projections :=
[  ], classes := [ 1, 1, 30, 20, 20, 12, 12, 12, 12
 ], operations := CharTableOps )
gap> DisplayCharTable( t );
2.A5
   2  3  3  2  1  1   1   1   1   1
   3  1  1  .  1  1   .   .   .   .
   5  1  1  .  .  .   1   1   1   1
     1a 2a 4a 3a 6a  5a 10a  5b 10b
  2P 1a 1a 2a 3a 3a  5b  5b  5a  5a
  3P 1a 2a 4a 1a 2a  5b 10b  5a 10a
  5P 1a 2a 4a 3a 6a  1a  2a  1a  2a
X.1   1  1  1  1  1   1   1   1   1
X.2   3  3 -1  .  .   A   A  *A  *A
X.3   3  3 -1  .  .  *A  *A   A   A
X.4   4  4  .  1  1  -1  -1  -1  -1
X.5   5  5  1 -1 -1   .   .   .   .
X.6   2 -2  . -1  1  -A   A -*A  *A
X.7   2 -2  . -1  1 -*A  *A  -A   A
X.8   4 -4  .  1 -1  -1   1  -1   1
X.9   6 -6  .  .  .   1  -1   1  -1
A = -E(5)-E(5)^4

      = (1-ER(5))/2 = -b5

42.3 Brauer Table Records

Brauer table records are similar to the records which represent ordinary character tables. They contain many of the well–known record components, like name, centralizers, irreducibles etc.; but there are two kinds of differences:

First, the operations record is BrauerTableOps instead of CharTableOps (see 42.6). Second, there are two extra components, namely

ordinary, which contains the ordinary character table corresponding to the Brauer table, and

block, which reflects the block information; it is a list of records with components

defect

the defect of the block,

ordchars

a list of integers indexing the ordinary irreducibles in the block,

modchars

a list of integers indexing the Brauer characters in the block,

basicset

a list of integers indexing the ordinary irreducibles of a basic set; note that the indices refer to the positions in the whole irreducibles list of the ordinary table, not to the positions in the block,

decinv

the inverse of the restriction of the decomposition matrix of the block to the basic set given by the basicset component, and possibly

brauertree

if exists, a list that represents the decomposition matrix which in this case is viewed as incidence matrix of a tree (the so–called Brauer tree); the entries of the list correspond to the edges of the tree, they refer to positions in the block, not in the whole irreducibles list of the tables. Brauer trees are mainly used to store the information in a more compact way than by decomposition matrices, planar embeddings etc. are not (or not yet) included.

Note that Brauer tables in the library have different format (see 46.5).

We give an example:

    gap> CharTable( "M11mod11" );
    rec( name := "M11mod11", text := "origin: modular ATLAS of finite grou\
    ps, tests: DEC, TENS", prime := 11, order := 7920, centralizers :=
    [ 7920, 48, 18, 8, 5, 6, 8, 8 ], orders := [ 1, 2, 3, 4, 5, 6, 8, 8
     ], classes := [ 1, 165, 440, 990, 1584, 1320, 990, 990
     ], powermap := [ , [ 1, 1, 3, 2, 5, 3, 4, 4 ],
      [ 1, 2, 1, 4, 5, 2, 7, 8 ],, [ 1, 2, 3, 4, 1, 6, 8, 7 ],,,,,,
      [ 1, 2, 3, 4, 5, 6, 7, 8 ] ], fusions := [ rec(
          name := "M11",

map := [ 1, 2, 3, 4, 5, 6, 7, 8 ],
type := "choice" ) ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1 ], [ 9, 1, 0, 1, -1, -2, -1, -1 ],
[ 10, -2, 1, 0, 0, 1, E(8)+E(8)^3, -E(8)-E(8)^3 ],
[ 10, -2, 1, 0, 0, 1, -E(8)-E(8)^3, E(8)+E(8)^3 ],
[ 11, 3, 2, -1, 1, 0, -1, -1 ], [ 16, 0, -2, 0, 1, 0, 0, 0 ],
[ 44, 4, -1, 0, -1, 1, 0, 0 ], [ 55, -1, 1, -1, 0, -1, 1, 1 ]
], irredinfo := [ rec(
 ), rec(
 ), rec(
 ), rec(
 ), rec(
 ), rec(
 ), rec(
 ), rec(
 ) ], blocks := [ rec(
defect := 1,
ordchars := [ 1, 2, 3, 4, 6, 7, 9 ],
modchars := [ 1, 2, 3, 4, 6 ],
decinv :=
 [ [ 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0 ], [ 0, 0, 1, 0, 0 ],
    [ 0, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 1 ] ],
basicset := [ 1, 2, 3, 4, 6 ],
brauertree :=
 [ [ 1, 2 ], [ 2, 7 ], [ 3, 7 ], [ 4, 7 ], [ 5, 6, 7 ] ] ), rec(
defect := 0,
ordchars := [ 5 ],
modchars := [ 5 ],
decinv := [ [ 1 ] ],
basicset := [ 5 ] ), rec(
defect := 0,
ordchars := [ 8 ],
modchars := [ 7 ],
decinv := [ [ 1 ] ],
basicset := [ 8 ] ), rec(
defect := 0,
ordchars := [ 10 ],
modchars := [ 8 ],
decinv := [ [ 1 ] ],
basicset := [ 10 ] )
], ordinary := CharTable( "M11" ), operations := BrauerTableOps, auto\
morphisms := Group( (7,8) ) )

42.4 IsCharTable

IsCharTable( obj )

returns true if obj is a record with fields centralizers (a list) and name (a string), otherwise it returns false.

    gap> IsCharTable( rec( centralizers:= [ 2, 2 ], name:= "C2" ) );
    true

42.5 TestCharTable

TestCharTable( tbl )

checks the character table tbl

     if tbl.centralizers, tbl.classes, tbl.orders and the entries of tbl.powermap have

same length,

if the product of tbl.centralizers[i] with tbl.classes[i] is equal to tbl.order,

     if tbl.orders[i] divides tbl.centralizers[i],

if the entries of tbl.classnames and the corresponding record fields are consistent, if the first orthogonality relation for tbl.irreducibles is satisfied, if the centralizers agree with the sums of squared absolute values of tbl.irreducibles and if powermaps and representative orders are consistent.

If no inconsistency occurs, true is returned, otherwise each error is signalled, and false is returned at the end.

    gap> t:= CharTable("A5");; TestCharTable(t);
    true
    gap> t.irreducibles[2]:= t.irreducibles[3] - t.irreducibles[1];;
    gap> TestCharTable(t);
    #E TestCharTable(A5): Scpr( ., X[2], X[1] ) = -1
    #E TestCharTable(A5): Scpr( ., X[2], X[2] ) = 2
    #E TestCharTable(A5): Scpr( ., X[3], X[2] ) = 1
    #E TestCharTable(A5): centralizer orders inconsistent with irreducibles
    false

42.6 Operations Records for Character Tables

Although a character table is not a domain (see 42.1), it needs an operations record. That for ordinary character tables is CharTableOps, that for Brauer tables is BrauerTableOps. At the moment, these records contain only the fields Print and ScalarProduct. Character tables are printed using the Print field, one can e.g. replace the default Print by 42.34

DisplayCharTable.

Whenever a library function calls the scalar product this is the ScalarProduct field of the operations record, so one can replace the default function (which is 44.1 ScalarProduct) by a more efficient one for special cases.

42.7 Conventions for Character Tables

The following few conventions should be noted:

The identity element is expected to be in the first class.

Characters are lists of cyclotomics (see Chapter 13) or unknowns (see chapter 16); they do not physically “belong” to a table, so when necessary, functions “regard” them as characters of a table which is given as another parameter.

Conversely , most functions that take a character table as a parameter and work with characters expect these characters as a parameter, too.

Some functions, however, expect the characters to be stored in the irreducibles field of the table (e.g. 42.5 TestCharTable) or allow application either to a list of characters given by a parameter or to the irreducibles field (e.g. 44.7 Indicator) if this parameter is missing.

The trivial character need not be the first one in a list of characters.

Sort convention: Whenever 42.36 SortClassesCharTable or 42.37 SortCharTable is used to sort the classes of a character table, the fusions into that table are not adjusted; only the permutation field of the sorted table will be actualized.

If one handles fusions only using 42.42 GetFusionMap and 42.43 StoreFusion, the maps are adjusted automatically with respect to the value of the field permutation of the destination table. So one should not change this field by hand. Fusion maps that are entered explicitly (e.g. because they are not stored on a table) are expected to be sorted, they will not be adjusted.

42.8 Getting Character Tables

There are in general four different ways to get a character table which GAP already “knows”: You can either

read a file that contains the table record, construct the table using generic formulae, derive it from known tables or use a presentation or representation of the group.

The first two methods are used by 42.9 CharTable. For the conception of generic character tables, see chapter 43. Note that library files often contain something that is much different from the tables returned by CharTable, see chapter 46.

As for the third method, some generic ways to derive a character table are implemented: One can obtain it as table of a factor group where the table of the group is given (see 42.12), for given tables the table of the direct product can be constructed (see 42.14), the restriction of a table to the p-regular classes can be formed (see 42.16), for special cases, an isoclinic table of a given table can be constructed (see 42.17), the splitting and fusion of classes may be viewed as a generic process (see 42.18, 42.19).

At the moment, for the last method there are algorithms dealing with arbitrary groups (see 42.9), and with finite polycyclic groups with special properties (see 42.23).

Note that whenever fusions between tables occur in these functions, they are stored on the concerned tables, and the fusionsource fields are updated (see 42.2).

42.9 CharTable

CharTable( G )
CharTable( tblname )

CharTable( series, parameter1 , parameter2 ... )

CharTable( G )

returns the character table of the group G. If G.name is bound, the table is baptized the same. Otherwise it is given the name "" (empty string). This is necessary since every character table needs a name in GAP (see 42.4).

CharTable first computes the linear characters, using the commutator factor group. If irreducible characters are missing afterwards, they are computed using the algorithm of Dixon and Schneider (see [Dix67] and [Sch90]).

    gap> M11 := Group((1,2,3,4,5,6,7,8,9,10,11), (3,7,11,8)(4,10,5,6));;
    gap> M11.name := "M11";;
    gap> CharTable(M11);
    rec( order := 7920, centralizers :=
    [ 7920, 8, 48, 8, 8, 5, 6, 18, 11, 11 ], orders :=
    [ 1, 4, 2, 8, 8, 5, 6, 3, 11, 11 ], classes :=
    [ 1, 990, 165, 990, 990, 1584, 1320, 440, 720, 720 ], irreducibles :=
    [ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
      [ 10, 2, 2, 0, 0, 0, -1, 1, -1, -1 ],
      [ 10, 0, -2, E(8)+E(8)^3, -E(8)-E(8)^3, 0, 1, 1, -1, -1 ],
      [ 10, 0, -2, -E(8)-E(8)^3, E(8)+E(8)^3, 0, 1, 1, -1, -1 ],
      [ 11, -1, 3, -1, -1, 1, 0, 2, 0, 0 ],
      [ 16, 0, 0, 0, 0, 1, 0, -2, E(11)^2+E(11)^6+E(11)^7+E(11)^8+E(11)^10
            , E(11)+E(11)^3+E(11)^4+E(11)^5+E(11)^9 ],
      [ 16, 0, 0, 0, 0, 1, 0, -2, E(11)+E(11)^3+E(11)^4+E(11)^5+E(11)^9,
          E(11)^2+E(11)^6+E(11)^7+E(11)^8+E(11)^10 ],
      [ 44, 0, 4, 0, 0, -1, 1, -1, 0, 0 ],
      [ 45, 1, -3, -1, -1, 0, 0, 0, 1, 1 ],
      [ 55, -1, -1, 1, 1, 0, -1, 1, 0, 0 ]
     ], operations := CharTableOps, name := "M11", powermap :=
    [ , [ 1, 3, 1, 2, 2, 6, 8, 8, 10, 9 ],
      [ 1, 2, 3, 4, 5, 6, 3, 1, 9, 10 ],,
      [ 1, 2, 3, 5, 4, 1, 7, 8, 9, 10 ],,
      [ 1, 2, 3, 5, 4, 6, 7, 8, 10, 9 ],,,,
      [ 1, 2, 3, 4, 5, 6, 7, 8, 1, 1 ] ], automorphisms := Group(
    ( 4, 5),
    ( 9,10) ), text := "origin: Dixon's Algorithm", permutation :=
    ( 2, 3, 8, 5,10, 6)( 4, 9, 7), group := M11 )

The columns of the table will be sorted in the same order, as the classes of the group, thus allowing a bijection between group and table. If the conjugacy classes are bound in G.conjugacyClasses the order is not changed. Otherwise the routine itself computes the classes. One can sort them in the canonical way, using SortClassesCharTable (see 42.36). If an entry G.charTable exists the routine uses information contained in this table. This also provides a facility for entering known characters, but then the user assumes responsibility for the correctness of the characters (There is little use in providing the trivial character to the routine). Note: The algorithm binds the record component automorphisms of the character table. This is a permutation group generated by the Galois-morphisms only. Because of this a component isMaybeIncomplete is set true in automorphisms, but will not be printed by the standard output routines! The computation of character tables needs to identify the classes of group elements very often, so it can be helpful to store a class list of all group elements. Since this is obviously limited by the group size, it is controlled by the global variable LARGEGROUPORDER, which is set by standard to 10000. If the group is smaller, the class map is stored. Otherwise each occuring element is identified individually. Limitations: At the moment there is a limitation to the group size given by the following condition: the routine computes in a prime field of size p. p is a prime number, such that the exponent of the group divides (p − 1) and such that 2\sqrt{|G|} < p. At the moment, GAP provides only prime fields up to size 65535. The routine also sets up a component G.dixon. Using this component, routines that identify classes, for example FusionConjugacyClasses, will work much faster. When interrupting the algorithm, however, a neccessary cleanup has not taken place. Thus you should call Unbind( G.dixon ) to avoid possible further confusion. This is also a good idea because G.dixon may become very large. When the computation by CharTable is complete, this record is shrunk to an acceptable size, something that could not be done when interrupting.

CharTable( tblname )

If the only parameter is a string tblname and this is an admissible name of a library table, CharTable returns this library table, otherwise false. A call of CharTable may cause to read some library files and to construct the table from the data in the files, see chapter 46 for the details. Admissible names for the ordinary character table tbl of the group grp are

• the ATLAS name if tbl is an ATLAS table (see 46.2), e.g., M22 for the table of the Mathieu group M_{22}, L2(13) for L_{2}(13) and 12 1.U4(3).2 1 for 12_{1}.U_{4}(3).2_{1},

• the names that were admissible for tables of grp in CAS if the CAS table library contained a table of grp, e.g., sl42 for the table of the alternating group A_{8} (but note that the table may be different from that in CAS, see 46.4) and

• some “relative” names:

For grp the n–th maximal subgroup (in decreasing group order) of a sporadic simple group with admissible name name, nameMn is admissible for tbl, e.g., J3M2 for the second maximal subgroup of the Janko group J_{3} which has the name J3. For grp a nontrivial Sylow normalizer of a sporadic simple group with admissible name name, where nontrivial means that the group is not contained in p:(p − 1), nameNp is an admissible name of tbl, e.g., J4N11 for the Sylow 11 normalizer of the Janko group J_{4}. In a few cases, the table of the Sylow p subgroup of grp is accessible by nameSylp where name is an admissible name of the table of grp, e.g., A11Syl2 for the Sylow 2 subgroup of the alternating group A_{11}.

In a few cases, the table of an element centralizer of grp is accessible by nameCcl where name is an admissible name of the table of grp, e.g., M11C2 for an involution centralizer in the Mathieu group M_{11}.

Admissible names for a Brauer table tbl (modulo the prime p) are all names namemodp where name is admissible for the corresponding ordinary table, e.g., M12mod11 for the 11 modular table of M_{12}, and L2(25).2 1mod3 for the 3 modular table of L_{2}(25).2_{1}.

Generic tables are accessible only by the name given by their name field (see below).

Case is not significant for table names, e.g., suzm3 and SuzM3 are both admissible names for the third maximal subgroup of the sporadic Suzuki group.

The admissible names reflect the structure of the libraries, see 46.1 and 46.5.

gap> CharTable( "A5.2" );; # returns the character table of the # symmetric group on five letters # (in ATLAS format)

    gap> CharTable( "Symmetric" );; # returns the generic table of the

# symmetric group

    gap> CharTable( "J5" );
    #E CharTableLibrary: no library table with name 'J5'
    false

If CharTable is called with more than one parameter, the first must be a string specifying a series of groups which is implemented via a generic character table (see chapter 43), e.g. "Symmetric" for the symmetric groups; the following parameters specialise the required member of the series:

    gap> CharTable( "Symmetric", 5 );; # the table of the symmetric

# group S_{5} (got by specializing # the generic table)

These are the valid calls of CharTable with parameter series:

    CharTable( "Alternating", n )

returns the table of the alternating group on n letters,

    CharTable( "Cyclic", n )

returns the table of the cyclic group of order n,

    CharTable( "Dihedral", 2n )

returns the table of the dihedral group of order 2n,

    CharTable( "GL", 2, q )

returns the table of the general linear group GL(2, q) for a prime power q,

    CharTable( "GU", 3, q )

returns the table of the general unitary group GU(3, q) for a prime power q,

    CharTable( "P:Q", [ p, q ] )

returns the table of the extension of the cyclic group of prime order p by a cyclic group of order q where q divides p − 1,

    CharTable( "PSL", 2, q )

returns the table of the projective special linear group PSL(2, q) for a prime power q,

    CharTable( "SL", 2, q )

returns the table of the special linear group SL(2, q) for a prime power q,

    CharTable( "SU", 3, q )

returns the table of the special unitary group SU(3, q) for a prime power q,

    CharTable( "Quaternionic", 4n )

returns the table of the quaternionic (dicyclic) group of order 4n,

    CharTable( "Suzuki", q )

returns the table of the Suzuki group Sz(q) =^{2} B_{2}(q) for q an odd power of 2,

    CharTable( "Symmetric", n )

returns the table of the symmetric group on n letters.

    CharTable( "WeylB", n )

returns the table of the Weyl group of type B_{n}.

    CharTable( "WeylD", n )

returns the table of the Weyl group of type D_{n}.

42.10 Advanced Methods for Dixon Schneider Calculations

The computation of character tables of very large groups may take quite some time. On the other hand, for the expert only a few irreducible characters may be needed, since the other ones can be computed using character theoretic methods like tensoring, induction, and restriction. Thus GAP provides also step-by-step routines for doing the calculations, that will allow to compute some characters, and stop before all are calculated. Note that there is no ’safety net’, i.e., the routines, being somehow internal, do no error checking, and assume the information given are correct.

When the global variable InfoCharTable1 if set to Print, information about the progress of splitting is printed. The default value of InfoCharTable1 is Ignore.

DixonInit( G )

does the setup for the computation of characters: It computes conjugacy classes, power maps and linear characters (in the case of AgGroups it also contains a call of CharTablePGroup). DixonInit returns a special record D (see below), which stores all informations needed for the further computations. The power maps are computed for all primes smaller than the exponent of G, thus allowing to induce the characters of all cyclic subgroups by InducedCyclic (see 44.22). For internal purposes, the algorithm uses a permuted arrangement of the classes and probably a different —but isomorphic— group. It is possible to obtain different informations about the progress of the splitting process as well as the partially computed character table from the record D.

DixontinI( D )

is the reverse function: It takes a Dixon record D and returns the old group G. It also does the cleanup of D. The returned group contains the component charTable, containing the character table as far as known. The classes are arranged in the same way, as the classes of G.

42.11. AN EXAMPLE OF ADVANCED DIXON SCHNEIDER CALCULATIONS 675

DixonSplit( D )

will do the main splitting task: It chooses a class and splits the character spaces using the corresponding class matrix. Characters are computed as far as possible.

CombinatoricSplit( D )

tries to split two-dimensional character spaces by combinatoric means. It is called automatically by DixonSplit. A separate call can be useful, when new characters have been found, that reduce the size of the character spaces.

IncludeIrreducibles( D, list )

If you have found irreducible characters by other means —like tensoring etc.— you must not include them in the character table yourself, but let them include, using this routine. Otherwise GAP would lose control of the characters yet known. The characters given in list must be according to the arrangement of classes in D. GAP will automatically take the closure of list under the galoisgroup and tensor products with one-dimensional characters.

SplitCharacters( D, list )

This routine decomposes the characters, given in list according to the character spaces found up to this point. By applying this routine to tensor products etc., it may result in characters with smaller norm, even irreducible ones. Since the recalculation of characters is only possible, if the degree is small enough, the splitting process is applied only to characters of sufficiently small degree. Some notes on the record D returned by DixonInit: This record stores several items of mainly internal interest. There are some entries, however, that may be useful to know about when using the advanced methods described above. The computation need not to take place in the original group, but in an isomorphic image W . This may be the same group as the group given, but — depending on the group — also a new one. Additionally the initialisation process will create a new list of the conjugacy classes with possibly different arrangement. For access to these informations, the following record components of the “Dixon Record”D might be of interest:

group

the group W ,

oldG

the group G, of which the character table is to be computed,

conjugacyClasses

classes of W ; this list contains the same classes as W .conjugacyClasses, only the arrangement is different,

charTable

contains the partially computed character table. The classes are arranged according

     to D.conjugacyClasses,
classPermutation

permutation to apply to the classes to obtain the old arrangement.

42.11 An Example of Advanced Dixon Schneider Calculations

First, we set

gap> InfoCharTable1 := Print;;

for printout of some internal results. We now define our group, which is isomorphic to PSL_{4}(3) (we use a permutation representation of PSL_{4}(3) instead of matrices since this will speed up the computations).

gap> g := PrimitiveGroup(40,5);
PSL(4,3)
gap> Size(g);
6065280
gap> d := DixonInit(g);;
#I  29 classes
gap> c := d.charTable;;

After the initialisation, one structure matrix is evaluated, yielding smaller spaces and several irreducible characters.

gap> DixonSplit(d);
#I  Matrix 2, Representative of Order 3, Centralizer: 5832
#I  Dimensions: [ 1, 12, 2, 2, 4, 2, 1, 1, 1, 1, 1 ]
#I  Two-dim space split
#I  Two-dim space split
#I  Two-dim space split

In this case spaces of the listed dimensions are a result of the splitting process. The three two dimensional spaces are split successfully by combinatoric means.

We obtain several characters by tensor products and notify them to the program. The tensor products of the nonlinear characters are reduced with the irreducible characters. The result is split according to the spaces found, which yields characters of smaller norms, but no new irreducibles.

gap> asp:= AntiSymmetricParts( c, c.irreducibles, 2 );;
gap> ro:= ReducedOrdinary( c, c.irreducibles, asp );;
gap> Length( ro.irreducibles );
3
gap> IncludeIrreducibles( d, ro.irreducibles );
gap> nlc:= Filtered( c.irreducibles, i -> i[1] > 1 );;
gap> t:= Tensored( nlc, nlc );;
gap> ro:= ReducedOrdinary( c, c.irreducibles, t );; ro.irreducibles;
[  ]
gap> List( ro.remainders, i -> ScalarProduct( c, i, i) );
[ 2, 2, 4, 4, 4, 4, 13, 13, 18, 18, 19, 21, 21, 36, 36, 29, 34, 34,
  42, 34, 48, 54, 62, 68, 68, 78, 84, 84, 88, 90, 159, 169, 169, 172,
  172, 266, 271, 271, 268, 274, 274, 280, 328, 373, 373, 456, 532,
  576, 679, 683, 683, 754, 768, 768, 890, 912, 962, 1453, 1453, 1601,
  1601, 1728, 1739, 1739, 1802, 2058, 2379, 2414, 2543, 2744, 2744,
  2920, 3078, 3078, 4275, 4275, 4494, 4760, 5112, 5115, 5115, 5414,
  6080, 6318, 7100, 7369, 7369, 7798, 8644, 10392, 12373, 12922,
  14122, 14122, 18948, 21886, 24641, 24641, 25056, 38942, 44950,
  78778 ]
gap> t := SplitCharacters( d, ro.remainders );;

gap> List( t, i -> ScalarProduct( c, i, i ) );
[ 2, 2, 4, 2, 2, 4, 4, 3, 6, 5, 5, 9, 9, 4, 12, 13, 18, 18, 18, 26,
  32, 32, 16, 42, 36, 84, 84, 88, 90, 159, 169, 169, 172, 172, 266,
  271, 271, 268, 274, 274, 280, 328, 373, 373, 456, 532, 576, 679,
  683, 683, 754, 768, 768, 890, 912, 962, 1453, 1453, 1601, 1601,
  1728, 1739, 1739, 1802, 2058, 2379, 2414, 2543, 2744, 2744, 2920,
  3078, 3078, 4275, 4275, 4494, 4760, 5112, 5115, 5115, 5414, 6080,
  6318, 7100, 7369, 7369, 7798, 8644, 10392, 12373, 12922, 14122,
  14122, 18948, 21886, 24641, 24641, 25056, 38942, 44950, 78778 ]

Finally we calculate the characters induced from all cyclic subgroups and obtain the missing irreducibles by applying the LLL-algorithm to them.

gap> ic:= InducedCyclic( c, "all" );;
gap> ro:= ReducedOrdinary( c, c.irreducibles, ic );;
gap> Length( ro.irreducibles );
0
gap> l:= LLL( c, ro.remainders );;
gap> Length( l.irreducibles );
13
gap> IncludeIrreducibles( d, l.irreducibles );
gap> Length( c.irreducibles );
29
gap> Length( c.classes );
29

As the last step, we return to our original group.

gap> g:= DixontinI( d );
#I  Total:1 matrices, [ 2 ]
PSL(4,3)
gap> c:= g.charTable;;
gap> List( c.irreducibles, i -> i[1] );
[ 1, 26, 26, 39, 52, 65, 65, 90, 234, 234, 260, 260, 260, 351, 390,
  416, 416, 416, 416, 468, 585, 585, 640, 640, 640, 640, 729, 780,
  1040 ]
gap> Sum( last, i -> i^2 );
6065280

42.12 CharTableFactorGroup

CharTableFactorGroup( tbl, classes of normal subgroup )

returns the table of the factor group of tbl with respect to a particular normal subgroup: If the list of irreducibles stored in tbl.irreducibles is complete, this normal subgroup is the normal closure of classes of normal subgroup; otherwise it is the intersection of kernels of those irreducibles stored on tbl which contain classes of normal subgroups in their kernel –that may cause strange results.

gap> s4:= CharTable( "Symmetric", 4 );;
gap> CharTableFactorGroup( s4, [ 3 ] );
rec( order := 6, name := "S4modulo[ 3 ]", centralizers :=

[ 6, 2, 3 ], powermap := [ , [ 1, 1, 3 ], [ 1, 2, 1 ] ], fusions :=
[  ], fusionsource := [ "S4" ], irreducibles :=
[ [ 1, -1, 1 ], [ 2, 0, -1 ], [ 1, 1, 1 ] ], irredinfo :=
[  ], orders := [ 1, 2, 3 ], classes :=
[ 1, 3, 2 ], operations := CharTableOps )
gap> s4.fusions;
[ rec(
      map := [ 1, 2, 1, 3, 2 ],
      type := "factor",
      name := "S4modulo[ 3 ]" ) ]

42.13 CharTableNormalSubgroup

CharTableNormalSubgroup( tbl, normal subgroup )

returns the restriction of the character table tbl to the classes in the list normal subgroup. This table is an approximation of the character table of this normal subgroup. It has fields

order, name, centralizers, orders, classes, powermap, irreducibles (contains the set

of those restrictions of irreducibles of tbl which are irreducible), and fusions (contains the fusion in tbl).

In most cases, some classes of the normal subgroup must be split, see 42.18.

gap> s5:= CharTable( "A5.2" );;
gap> s3:= CharTable( "Symmetric", 3 );;
gap> SortCharactersCharTable( s3 );;
gap> s5xs3:= CharTableDirectProduct( s5, s3 );;
gap> nsg:= [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ];;
gap> sub:= CharTableNormalSubgroup( s5xs3, nsg );;
#I CharTableNormalSubgroup: classes in [ 8 ] necessarily split
gap> sub;
rec( name := "Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ])",
 order := 360, centralizers := [ 360, 180, 24, 12, 18, 9, 15, 15/2,
  12, 4, 6 ], orders := [ 1, 3, 2, 6, 3, 3, 5, 15, 2, 4, 6
 ], powermap := [ , [ 1, 2, 1, 2, 5, 6, 7, 8, 1, 3, 5 ],
  [ 1, 1, 3, 3, 1, 1, 7, 7, 9, 10, 9 ],,
  [ 1, 2, 3, 4, 5, 6, 1, 2, 9, 10, 11 ] ], classes :=
[ 1, 2, 15, 30, 20, 40, 24, 48, 30, 90, 60
 ], operations := CharTableOps, irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1 ],
  [ 2, -1, 2, -1, 2, -1, 2, -1, 0, 0, 0 ],
  [ 6, 6, -2, -2, 0, 0, 1, 1, 0, 0, 0 ],
  [ 4, 4, 0, 0, 1, 1, -1, -1, 2, 0, -1 ],
  [ 4, 4, 0, 0, 1, 1, -1, -1, -2, 0, 1 ],
  [ 8, -4, 0, 0, 2, -1, -2, 1, 0, 0, 0 ],
  [ 5, 5, 1, 1, -1, -1, 0, 0, 1, -1, 1 ],
  [ 5, 5, 1, 1, -1, -1, 0, 0, -1, 1, -1 ],
  [ 10, -5, 2, -1, -2, 1, 0, 0, 0, 0, 0 ] ], fusions := [ rec(
      name := "A5.2xS3",

          map := [ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ] ) ] )

42.14 CharTableDirectProduct

CharTableDirectProduct( tbl1 , tbl2 )

returns the character table of the direct product of the groups given by the character tables tbl1 and tbl2 . The matrix of irreducibles is the Kronecker product (see 32.6) of tbl1 .irreducibles with

tbl2 .irreducibles.
    gap> c2:= CharTable( "Cyclic", 2 );; s2:= CharTable( "Symmetric", 2 );;
    gap> SortCharactersCharTable( s2 );;
    gap> v4:= CharTableDirectProduct( c2, s2 );
    rec( order := 4, name := "C2xS2", centralizers :=
    [ 4, 4, 4, 4 ], classparam := [ [ [ 1, 0 ], [ 1, [ 1, 1 ] ] ],
      [ [ 1, 0 ], [ 1, [ 2 ] ] ], [ [ 1, 1 ], [ 1, [ 1, 1 ] ] ],
      [ [ 1, 1 ], [ 1, [ 2 ] ] ] ], orders := [ 1, 2, 2, 2 ], powermap :=
    [ , [ 1, 1, 1, 1 ] ], irreducibles :=
    [ [ 1, 1, 1, 1 ], [ 1, -1, 1, -1 ], [ 1, 1, -1, -1 ],
      [ 1, -1, -1, 1 ] ], irredinfo := [ rec(
          charparam := [ [ 1, 0 ], [ 1, [ 1, 1 ] ] ] ), rec(
          charparam := [ [ 1, 0 ], [ 1, [ 1, 1 ] ] ] ), rec(
          charparam := [ [ 1, 1 ], [ 1, [ 1, 1 ] ] ] ), rec(
          charparam := [ [ 1, 1 ], [ 1, [ 1, 1 ] ] ] ) ], charparam :=
    [  ], fusionsource := [ "C2", "S2" ], fusions := [ rec(
          name := "C2",
          map := [ 1, 1, 2, 2 ],
          type := "factor" ), rec(
          name := "S2",
          map := [ 1, 2, 1, 2 ],
          type := "factor" ) ], classes :=
    [ 1, 1, 1, 1 ], operations := CharTableOps )
    gap> c2.fusions;
    [ rec(
          map := [ 1, 3 ],
          type := "normal",
          name := "C2xS2" ) ]

Note: The result will contain those p-th powermaps for primes p where both tbl1 and tbl2 contain the p-th powermap. Additionally, if one of the tables contains it, and p does not divide the order of the other table, and the p-th powermap is uniquely determined (see 45.12), it will be computed; then the table of the direct product will contain the p-th powermap, too.

42.15 CharTableWreathSymmetric

CharTableWreathSymmetric( tbl, n )

returns the character table of the wreath product of an arbitrary group G with the full symmetric group S_{n}, where tbl is the character table of G.

gap> c3:= CharTable("Cyclic", 3);;
gap> CharTableWreathSymmetric(c3, 2);
rec( order := 18, name := "C3wrS2", centralizers :=
[ 18, 9, 9, 18, 9, 18, 6, 6, 6 ], classes :=
[ 1, 2, 2, 1, 2, 1, 3, 3, 3 ], orders := [ 1, 3, 3, 3, 3, 3, 2, 6, 6
 ], irredinfo := [ rec(
      charparam := [ [ 1, 1 ], [  ], [  ] ] ), rec(
      charparam := [ [ 1 ], [ 1 ], [  ] ] ), rec(
      charparam := [ [ 1 ], [  ], [ 1 ] ] ), rec(
      charparam := [ [  ], [ 1, 1 ], [  ] ] ), rec(
      charparam := [ [  ], [ 1 ], [ 1 ] ] ), rec(
      charparam := [ [  ], [  ], [ 1, 1 ] ] ), rec(
      charparam := [ [ 2 ], [  ], [  ] ] ), rec(
      charparam := [ [  ], [ 2 ], [  ] ] ), rec(
      charparam := [ [  ], [  ], [ 2 ] ] ) ], classparam :=
[ [ [ 1, 1 ], [  ], [  ] ], [ [ 1 ], [ 1 ], [  ] ],
  [ [ 1 ], [  ], [ 1 ] ], [ [  ], [ 1, 1 ], [  ] ],
  [ [  ], [ 1 ], [ 1 ] ], [ [  ], [  ], [ 1, 1 ] ],
  [ [ 2 ], [  ], [  ] ], [ [  ], [ 2 ], [  ] ], [ [  ], [  ], [ 2 ] ]
 ], powermap := [ , [ 1, 3, 2, 6, 5, 4, 1, 4, 6 ],
  [ 1, 1, 1, 1, 1, 1, 7, 7, 7 ] ], irreducibles :=
[ [ 1, 1, 1, 1, 1, 1, -1, -1, -1 ],
  [ 2, -E(3)^2, -E(3), 2*E(3), -1, 2*E(3)^2, 0, 0, 0 ],
  [ 2, -E(3), -E(3)^2, 2*E(3)^2, -1, 2*E(3), 0, 0, 0 ],
  [ 1, E(3), E(3)^2, E(3)^2, 1, E(3), -1, -E(3), -E(3)^2 ],
  [ 2, -1, -1, 2, -1, 2, 0, 0, 0 ],
  [ 1, E(3)^2, E(3), E(3), 1, E(3)^2, -1, -E(3)^2, -E(3) ],
  [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 1, E(3), E(3)^2, E(3)^2, 1, E(3), 1, E(3), E(3)^2 ],
  [ 1, E(3)^2, E(3), E(3), 1, E(3)^2, 1, E(3)^2, E(3) ]
 ], operations := CharTableOps )
gap> DisplayCharTable(last);
C3wrS2
   2  1   .   .   1  .   1  1   1   1
   3  2   2   2   2  2   2  1   1   1
     1a  3a  3b  3c 3d  3e 2a  6a  6b
  2P 1a  3b  3a  3e 3d  3c 1a  3c  3e
  3P 1a  1a  1a  1a 1a  1a 2a  2a  2a
X.1   1   1   1   1  1   1 -1  -1  -1
X.2   2   A  /A   B -1  /B  .   .   .
X.3   2  /A   A  /B -1   B  .   .   .
X.4   1 -/A  -A  -A  1 -/A -1  /A   A
X.5   2  -1  -1   2 -1   2  .   .   .
X.6   1  -A -/A -/A  1  -A -1   A  /A

X.7   1   1   1   1  1   1  1   1   1
X.8   1 -/A  -A  -A  1 -/A  1 -/A  -A
X.9   1  -A -/A -/A  1  -A  1  -A -/A
A = -E(3)^2
  = (1+ER(-3))/2 = 1+b3
B = 2*E(3)
  = -1+ER(-3) = 2b3

The record field classtext contains the sequences of partitions that parametrize the classes as well as the characters of the wreath product. Note that this parametrization prevents the principal character from being the first one in the list irreducibles.

42.16 CharTableRegular

CharTableRegular( tbl, prime )

returns the character table consisting of the prime-regular classes of the character table tbl.

gap> a5:= CharTable( "Alternating", 5 );;
gap> CharTableRegular( a5, 2 );
rec( name := "Regular(A5,2)", prime := 2, order := 60, orders :=
[ 1, 3, 5, 5 ], centralizers := [ 60, 3, 5, 5 ], powermap :=
[ , [ 1, 2, 4, 3 ], [ 1, 1, 4, 3 ],, [ 1, 2, 1, 1 ] ], fusions :=
[ rec(
      map := [ 1, 3, 4, 5 ],
      type := "choice",
      name := "A5" ) ], ordinary := rec( name := "A5", order :=
60, centralizers := [ 60, 4, 3, 5, 5 ], orders := [ 1, 2, 3, 5, 5
 ], powermap := [ , [ 1, 1, 3, 5, 4 ], [ 1, 2, 1, 5, 4 ],,
  [ 1, 2, 3, 1, 1 ] ], irreducibles :=
[ [ 1, 1, 1, 1, 1 ], [ 4, 0, 1, -1, -1 ], [ 5, 1, -1, 0, 0 ],
  [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
  [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ] ], classparam :=
[ [ 1, [ 1, 1, 1, 1, 1 ] ], [ 1, [ 2, 2, 1 ] ], [ 1, [ 3, 1, 1 ] ],
  [ 1, [ [ 5 ], "+" ] ], [ 1, [ [ 5 ], "-" ] ] ], irredinfo := [ rec(
      charparam := [ 1, [ 1, 1, 1, 1, 1 ] ] ), rec(
      charparam := [ 1, [ 2, 1, 1, 1 ] ] ), rec(
      charparam := [ 1, [ 2, 2, 1 ] ] ), rec(
      charparam := [ 1, [ [ 3, 1, 1 ], "+" ] ] ), rec(
      charparam := [ 1, [ [ 3, 1, 1 ], "-" ] ] )
 ], text := "computed using generic character table for alternating gr\
oups", classes := [ 1, 15, 20, 12, 12
 ], operations := CharTableOps, fusionsource := [ "Regular(A5,2)"
 ] ), operations := CharTableOps, classes := [ 1, 20, 12, 12 ] )
gap> a5.fusionsource;
[ "Regular(A5,2)" ]

42.17 CharTableIsoclinic

CharTableIsoclinic( tbl )

CharTableIsoclinic( tbl, classes of normal subgroup )

If tbl is a character table of a group with structure 2.G.2 with a unique central subgroup of order 2 and a unique subgroup of index 2, CharTableIsoclinic( tbl ) returns the table of the isoclinic group (see [CCN^{+}85, Chapter 6, Section 7]); if the subgroup of index 2 is not unique, it must be specified by enumeration of its classes in classes of normal subgroup.

    gap> d8:= CharTable( "Dihedral", 8 );;
    gap> CharTableIsoclinic( d8, [ 1, 2, 3 ] );
    rec( name := "Isoclinic(D8)", order := 8, centralizers :=
    [ 8, 4, 8, 4, 4 ], orders := [ 1, 4, 2, 4, 4 ], fusions :=
    [  ], fusionsource := [  ], powermap := [ , [ 1, 3, 1, 3, 3 ]
     ], irreducibles := [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, -1, -1 ],
      [ 1, -1, 1, 1, -1 ], [ 1, -1, 1, -1, 1 ], [ 2, 0, -2, 0, 0 ]
     ], classes := [ 1, 2, 1, 2, 2 ], operations := CharTableOps )

42.18 CharTableSplitClasses

CharTableSplitClasses( tbl, fusionmap )
CharTableSplitClasses( tbl, fusionmap, exponent )

returns a character table where the classes of the character table tbl are split according to the fusion map fusionmap.

The two forms correspond to the two different situations to split classes:

CharTableSplitClasses( tbl, fusionmap )

If one constructs a normal subgroup (see 42.13), the order remains unchanged, powermaps, classlengths and centralizer orders are changed with respect to the fusion, representative orders and irreducibles are simply split. The “factor fusion” fusionmap to tbl is stored on the result.

# see example in 42.13

    gap> split:= CharTableSplitClasses(sub,[1,2,3,4,5,6,7,8,8,9,10,11]);
    rec( name := "Split(Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 2\
    0 ]),[ 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11 ])", order :=
    360, centralizers := [ 360, 180, 24, 12, 18, 9, 15, 15, 15, 12, 4, 6
     ], classes := [ 1, 2, 15, 30, 20, 40, 24, 24, 24, 30, 90, 60
     ], orders := [ 1, 3, 2, 6, 3, 3, 5, 15, 15, 2, 4, 6 ], powermap :=
    [ , [ 1, 2, 1, 2, 5, 6, 7, [ 8, 9 ], [ 8, 9 ], 1, 3, 5 ],
      [ 1, 1, 3, 3, 1, 1, 7, 7, 7, 10, 11, 10 ],,
      [ 1, 2, 3, 4, 5, 6, 1, 2, 2, 10, 11, 12 ] ], irreducibles :=
    [ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
      [ 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1 ],
      [ 2, -1, 2, -1, 2, -1, 2, -1, -1, 0, 0, 0 ],
      [ 6, 6, -2, -2, 0, 0, 1, 1, 1, 0, 0, 0 ],
      [ 4, 4, 0, 0, 1, 1, -1, -1, -1, 2, 0, -1 ],
      [ 4, 4, 0, 0, 1, 1, -1, -1, -1, -2, 0, 1 ],

      [ 8, -4, 0, 0, 2, -1, -2, 1, 1, 0, 0, 0 ],
      [ 5, 5, 1, 1, -1, -1, 0, 0, 0, 1, -1, 1 ],
      [ 5, 5, 1, 1, -1, -1, 0, 0, 0, -1, 1, -1 ],
      [ 10, -5, 2, -1, -2, 1, 0, 0, 0, 0, 0, 0 ] ], fusions := [ rec(
          name := "Rest(A5.2xS3,[ 1, 3, 4, 6, 7, 9, 10, 12, 14, 17, 20 ])",
          map := [ 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11 ] )
     ], operations := CharTableOps )

gap> # the table of (3 × A_{5}):2 (incomplete)

CharTableSplitClasses( tbl, fusionmap, exponent )

To construct a downward extension is somewhat more complicated, since the new order, representative orders, centralizer orders and classlengths are not known at the moment when the classes are split. So the order remains unchanged, centralizer orders will just be split, classlengths are divided by the number of image classes, and the representative orders become parametrized with respect to the exponent exponent of the normal subgroup. Power maps and irreducibles are computed from tbl and fusionmap, and the factor fusion fusionmap to tbl is stored on the result.

    gap> a5:= CharTable( "Alternating", 5 );;
    gap> CharTableSplitClasses( a5, [ 1, 1, 2, 3, 3, 4, 4, 5, 5 ], 2 );
    rec( name := "Split(A5,[ 1, 1, 2, 3, 3, 4, 4, 5, 5 ])", order :=
    60, centralizers := [ 60, 60, 4, 3, 3, 5, 5, 5, 5 ], classes :=
    [ 1/2, 1/2, 15, 10, 10, 6, 6, 6, 6 ], orders :=
    [ 1, 2, [ 2, 4 ], [ 3, 6 ], [ 3, 6 ], [ 5, 10 ], [ 5, 10 ],
      [ 5, 10 ], [ 5, 10 ] ], powermap :=
    [ , [ 1, 1, [ 1, 2 ], [ 4, 5 ], [ 4, 5 ], [ 8, 9 ], [ 8, 9 ],
          [ 6, 7 ], [ 6, 7 ] ],
      [ 1, 2, 3, [ 1, 2 ], [ 1, 2 ], [ 8, 9 ], [ 8, 9 ], [ 6, 7 ],
          [ 6, 7 ] ],,
      [ 1, 2, 3, [ 4, 5 ], [ 4, 5 ], [ 1, 2 ], [ 1, 2 ], [ 1, 2 ],
          [ 1, 2 ] ] ], irreducibles := [ [ 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
      [ 4, 4, 0, 1, 1, -1, -1, -1, -1 ], [ 5, 5, 1, -1, -1, 0, 0, 0, 0 ],
      [ 3, 3, -1, 0, 0, -E(5)-E(5)^4, -E(5)-E(5)^4, -E(5)^2-E(5)^3,
          -E(5)^2-E(5)^3 ],
      [ 3, 3, -1, 0, 0, -E(5)^2-E(5)^3, -E(5)^2-E(5)^3, -E(5)-E(5)^4,
          -E(5)-E(5)^4 ] ], fusions := [ rec(
          name := "A5",
          map := [ 1, 1, 2, 3, 3, 4, 4, 5, 5 ] )
     ], operations := CharTableOps )

Note that powermaps (and in the second case also the representative orders) may become parametrized maps (see Chapter 45).

The inverse process of splitting is the fusion of classes, see 42.19.

42.19 CharTableCollapsedClasses

CharTableCollapsedClasses( tbl, fusionmap )

returns a character table where all classes of the character table tbl with equal images under the map fusionmap are collapsed; the fields orders, classes, and the characters in irreducibles are the images under fusionmap, the powermaps are obtained on conjugation (see 45.9) with fusionmap, order remains unchanged, and centralizers arise from classes

and order.

The fusion to the returned table is stored on tbl.

gap> c3:= CharTable( "Cyclic", 3 );;
gap> t:= CharTableSplitClasses( c3, [ 1, 2, 2, 3, 3 ] );
rec( name := "Split(C3,[ 1, 2, 2, 3, 3 ])", order :=
3, centralizers := [ 3, 6, 6, 6, 6 ], classes :=
[ 1, 1/2, 1/2, 1/2, 1/2 ], orders := [ 1, 3, 3, 3, 3 ], powermap :=
[ ,, [ 1, 1, 1, 1, 1 ] ], irreducibles :=
[ [ 1, 1, 1, 1, 1 ], [ 1, E(3), E(3), E(3)^2, E(3)^2 ],
  [ 1, E(3)^2, E(3)^2, E(3), E(3) ] ], fusions := [ rec(
      name := "C3",
      map := [ 1, 2, 2, 3, 3 ] ) ], operations := CharTableOps )
gap> CharTableCollapsedClasses( t, [ 1, 2, 2, 3, 3 ] );
rec( name := "Collapsed(Split(C3,[ 1, 2, 2, 3, 3 ]),[ 1, 2, 2, 3, 3 ])\
", order := 3, centralizers := [ 3, 3, 3 ], orders :=
[ 1, 3, 3 ], powermap := [ ,, [ 1, 1, 1 ] ], fusionsource :=
[ "Split(C3,[ 1, 2, 2, 3, 3 ])" ], irreducibles :=
[ [ 1, 1, 1 ], [ 1, E(3), E(3)^2 ], [ 1, E(3)^2, E(3) ] ], classes :=
[ 1, 1, 1 ], operations := CharTableOps )

The inverse process of fusion is the splitting of classes, see 42.18.

42.20 CharDegAgGroup

CharDegAgGroup( G [, q ] )

CharDegAgGroup computes the degrees of irreducible characters of the finite polycyclic group G over the algebraic closed field of characteristic q. The default value for q is zero. The degrees are returned as a list of pairs, the first entry denoting a degree, and the second denoting its multiplicity.

gap> g:= SolvableGroup( 24, 15 );
S4
gap> CharDegAgGroup( g );

[ [ 1, 2 ], [ 2, 1 ], [ 3, 2 ] ] # two linear characters, one of

# degree 2, two of degree 3

gap> CharDegAgGroup( g, 3 );
[ [ 1, 2 ], [ 3, 2 ] ]

The algorithm bases on [Con90b]. It works for all solvable groups.

42.21 CharTableSSGroup

CharTableSSGroup( G )

CharTableSSGroup returns the character table of the supersolvable ag-group G and stores it in G.charTable. If G is not supersolvable not all irreducible characters migth be calculated and a warning will be printed out. The algorithm bases on [Con90a] and [Con90b].

All the characters calculated are monomial, so they are the induced of a linear character of some subgroup of G. For every character the subgroup it is induced from and the kernel the linear character has are written down in t.irredinfo[i].inducedFrom.subgroup and

t.irredinfo[i].inducedFrom.kernel.
    gap> CharTableSSGroup( SolvableGroup( 8 , 5 ) );
    rec( order := 8, classes := [ 1, 1, 2, 2, 2 ], powermap :=
    [ , [ 1, 1, 2, 2, 2 ]
     ], operations := CharTableOps, group := Q8, irreducibles :=
    [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, -1, -1 ], [ 1, 1, -1, 1, -1 ],
      [ 1, 1, -1, -1, 1 ], [ 2, -2, 0, 0, 0 ] ], orders :=
    [ 1, 2, 4, 4, 4 ], irredinfo := [ rec(
          inducedFrom := rec(
              subgroup := Q8,
              kernel := Q8 ) ), rec(
          inducedFrom := rec(
              subgroup := Q8,
              kernel := Subgroup( Q8, [ b, c ] ) ) ), rec(
          inducedFrom := rec(
              subgroup := Q8,
              kernel := Subgroup( Q8, [ a, c ] ) ) ), rec(
          inducedFrom := rec(
              subgroup := Q8,
              kernel := Subgroup( Q8, [ a*b, c ] ) ) ), rec(
          inducedFrom := rec(
              subgroup := Subgroup( Q8, [ b, c ] ),
              kernel := Subgroup( Q8, [  ] ) ) ) ], centralizers :=
    [ 8, 8, 4, 4, 4 ], name := "Q8" )

42.22 MatRepresentationsPGroup

MatRepresentationsPGroup( G )
MatRepresentationsPGroup( G [, int ] )

MatRepresentationsPGroup( G ) returns a list of homomorphisms from the finite polycyclic group G to irreducible complex matrix groups. These matrix groups form a system of representatives of the complex irreducible representations of G.

MatRepresentationsPGroup( G, int ) returns only the int-th representation.

Let G be a finite polycyclic group with an abelian normal subgroup N such that the factorgroup G/N is supersolvable. MatRepresentationsPGroup uses the algorithm described in [Bau91]. Note that for such groups all such representations are equivalent to monomial ones, and in fact MatRepresentationsPGroup only returns monomial representations.

If G has not the property stated above, a system of representatives of irreducible representations and characters only for the factor group G/M can be computed using this algorithm, where M is the derived subgroup of the supersolvable residuum of G. In this case first a warning is printed. MatRepresentationsPGroup returns the irreducible representations of G with kernel containing M then.

    gap> g:= SolvableGroup( 6, 2 );

    S3
    gap> MatRepresentationsPGroup( g );
    [ GroupHomomorphismByImages( S3, Group( [ [ 1 ] ] ), [ a, b ],
        [ [ [ 1 ] ], [ [ 1 ] ] ] ), GroupHomomorphismByImages( S3, Group(
        [ [ -1 ] ] ), [ a, b ], [ [ [ -1 ] ], [ [ 1 ] ] ] ),
      GroupHomomorphismByImages( S3, Group( [ [ 0, 1 ], [ 1, 0 ] ],
        [ [ E(3), 0 ], [ 0, E(3)^2 ] ] ), [ a, b ],
        [ [ [ 0, 1 ], [ 1, 0 ] ], [ [ E(3), 0 ], [ 0, E(3)^2 ] ] ] ) ]

CharTablePGroup can be used to compute the character table of a group with the above properties (see 42.23).

42.23 CharTablePGroup

CharTablePGroup( G )

CharTablePGroup returns the character table of the finite polycyclic group G, and stores it in G.charTable. Do not change the order of G.conjugacyClasses after having called

CharTablePGroup.

Let G be a finite polycyclic group with an abelian normal subgroup N such that the factorgroup G/N is supersolvable. CharTablePGroup uses the algorithm described in [Bau91].

If G has not the property stated above, a system of representatives of irreducible representations and characters only for the factor group G/M can be computed using this algorithm, where M is the derived subgroup of the supersolvable residuum of G. In this case first a warning is printed. CharTablePGroup returns an incomplete table containing exactly those irreducibles with kernel containing M .

    gap> CharTablePGroup( SolvableGroup( 8, 4 ) );
    rec( order := 8, centralizers := [ 8, 8, 4, 4, 4 ], classes :=
    [ 1, 1, 2, 2, 2 ], orders := [ 1, 2, 2, 2, 4 ], irreducibles :=
    [ [ 1, 1, 1, 1, 1 ], [ 1, 1, -1, 1, -1 ], [ 1, 1, 1, -1, -1 ],
      [ 1, 1, -1, -1, 1 ], [ 2, -2, 0, 0, 0 ]
     ], operations := CharTableOps, powermap := [ , [ 1, 1, 1, 1, 2 ]
     ], name := "D8", group := D8 )

MatRepresentationsPGroup can be used to compute representatives of the complex irreducible representations (see 42.22).

42.24 InitClassesCharTable

InitClassesCharTable( tbl )

returns the list of conjugacy class lengths of the character table tbl, and assigns it to the field tbl.classes; the classlengths are computed from the centralizer orders of tbl.

InitClassesCharTable is called automatically for tables that are read from the library (see 42.9) or constructed as generic character tables (see 43).

    gap> t:= rec( centralizers:= [ 2, 2 ], name:= "C2" );;
    gap> InitClassesCharTable( t ); t;
    [ 1, 1 ]
    rec(

      centralizers := [ 2, 2 ],
      name := "C2",
      classes := [ 1, 1 ] )

42.25 InverseClassesCharTable

InverseClassesCharTable( tbl )

returns the list mapping each class of the character table tbl to its inverse class. This list can be regarded as (-1)-st powermap; it is computed using tbl.irreducibles.

    gap> InverseClassesCharTable( CharTable( "L3(2)" ) );
    [ 1, 2, 3, 4, 6, 5 ]

42.26 ClassNamesCharTable

ClassNamesCharTable( tbl )

ClassNamesCharTable computes names for the classes of the character table tbl as strings consisting of the order of an element of the class and and least one distinguishing letter.

The list of these names at the same time is returned by this function and stored on the table tbl as record field classnames.

Moreover for each class a record field with its name is constructed, containing the position of this name in the list classnames as its value.

    gap> c3:= CharTable( "Cyclic", 3 );;
    gap> ClassNamesCharTable( c3 );
    [ "1a", "3a", "3b" ]
    gap> c3;
    rec( name := "C3", order := 3, centralizers := [ 3, 3, 3 ], orders :=
    [ 1, 3, 3 ], powermap := [ ,, [ 1, 1, 1 ] ], irreducibles :=
    [ [ 1, 1, 1 ], [ 1, E(3), E(3)^2 ], [ 1, E(3)^2, E(3) ]
     ], classparam := [ [ 1, 0 ], [ 1, 1 ], [ 1, 2 ] ], irredinfo :=
    [ rec(
          charparam := [ 1, 0 ] ), rec(
          charparam := [ 1, 1 ] ), rec(
          charparam := [ 1, 2 ] )
     ], text := "computed using generic character table for cyclic groups",
    classes := [ 1, 1, 1 ], operations := CharTableOps, classnames :=
    [ "1a", "3a", "3b" ], 1a := 1, 3a := 2, 3b := 3 )

If the record field classnames of tbl is unbound, ClassNamesCharTable is automatically called by DisplayCharTable (see 42.34).

Note that once the class names are computed the resulting record fields are stored on tbl. They are not deleted by another call of ClassNamesCharTable.

42.27 ClassMultCoeffCharTable

ClassMultCoeffCharTable( tbl, c1 , c2 , c3 )

returns the class multiplication coefficient of the classes c1 , c2 and c3 of the group G with character table tbl.

    gap> t:= CharTable( "L3(2)" );;
    gap> ClassMultCoeffCharTable( t, 2, 2, 4 );
    4

The class multiplication coefficient c_{1,2,3} of the classes c1 , c2 and c3 equals the number of pairs (x, y) of elements x, y ∈ G such that x lies in class c1 , y lies in class c2 and their product xy is a fixed element of class c3 .

Also in the center of the group algebra these numbers are found as coefficients of the decomposition of the product of two class sums K_{i} and K_{j} into class sums,

K_{i}K_{j} = ∑_{k} c_{ijk}K_{k}.

Given the character table of a finite group G, whose classes are C_{1}, …, C_{r} with representatives g_{i} ∈ C_{i}, the class multiplication coefficients c_{ijk} can be computed by the following formula.

c_{ijk} = \frac{|C_{i}||C_{j}|}{|G|} ∑_{χ∈Irr(G)} \frac{χ(g_{i})χ(g_{j})\overline{χ(g_{k})}}{χ(1)}

On the other hand the knowledge of the class multiplication coefficients enables the computation of the character table (see 42.9).

42.28 MatClassMultCoeffsCharTable

MatClassMultCoeffsCharTable( tbl, class )

returns the matrix C_{i} = [a_{ijk}]_{j,k} of structure constants (see 42.27).

    gap> L3_2:= CharTable( "L3(2)" );;
    gap> MatClassMultCoeffsCharTable( t, 2 );
    [ [ 0, 1, 0, 0, 0, 0 ], [ 21, 4, 3, 4, 0, 0 ], [ 0, 8, 6, 8, 7, 7 ],
      [ 0, 8, 6, 1, 7, 7 ], [ 0, 0, 3, 4, 0, 7 ], [ 0, 0, 3, 4, 7, 0 ] ]

42.29 ClassStructureCharTable

ClassStructureCharTable( tbl, classes )

returns the so–called class structure of the classes in the list classes, for the character table tbl of the group G. The length of classes must be at least 2.

    gap> t:= CharTable( "M12" );;
    gap> ClassStructureCharTable( t, [2,6,9,13] );
    916185600
    gap> ClassStructureCharTable( t, [2,9,13] ); # equals the group order
    95040

Let C_{1}, …, C_{n} denote the conjugacy classes of G that are indexed by classes. The class structure n(C_{1}, C_{2}, …, C_{n}) equals the number of tuples (g_{1}, g_{2}, …, g_{n}) of elements g_{i} ∈ C_{i} with g_{1}g_{2} ⋯ g_{n} = 1. Note the difference to the definition of the class multiplication

coefficients in 42.27 ClassMultCoeffCharTable.

n(C_{1}, C_{2}, …, C_{n}) is computed using the formula

n(C_{1}, C_{2}, …, C_{n}) = \frac{|C_{1}||C_{2}| ⋯ |C_{n}|}{|G|} ∑_{χ∈Irr(G)} \frac{χ(g_{1})χ(g_{2}) ⋯ χ(g_{n})}{χ(1)^{n−2}}.

42.30 RealClassesCharTable

RealClassesCharTable( tbl )

returns a list of the real classes of the group G with character table tbl.

    gap> RealClassesCharTable(L3_2);
    [ 1, 2, 3, 4 ]

An element x ∈ G is called real, if it is conjugate with its inverse. And as x^{−1} = x^{o(x)−1}, this fact is tested by looking at the powermap of tbl. Real elements take only real character values.

42.31 ClassOrbitCharTable

ClassOrbitCharTable( tbl, class )

returns a list of classes containing elements of the cyclic subgroup generated by an element x of class class.

    gap> ClassOrbitCharTable(L3_2, 5);
    [ 5, 6 ]

Being all powers of x this data is recovered from the powermap of tbl.

42.32 ClassRootsCharTable

ClassRootsCharTable( tbl )

returns a list of the classes of all nontrivial p–th roots of the classes of tbl where for each class, p runs over the prime divisors of the representative order.

    gap> ClassRootsCharTable(L3_2);
    [ [ 2, 3, 5, 6 ], [ 4 ], [  ], [  ], [  ], [  ] ]

This information is found by looking at the powermap of tbl, too.

42.33 NrPolyhedralSubgroups

NrPolyhedralSubgroups( tbl, c1 , c2 , c3 )

returns the number and isomorphism type of polyhedral subgroups of the group with character table tbl which are generated by an element g of class c1 and an element h of class c2 with the property that the product gh lies in class c3 .

    gap> NrPolyhedralSubgroups(L3_2, 2, 2, 4);
    rec(

      number := 21,
      type := "D8" )

According to [NPP84, p. 233] the number of polyhedral subgroups of isomorphism type V_{4}, D_{2n}, A_{4}, S_{4} and A_{5} can be derived from the class multiplication coefficient (see 42.27) and the number of Galois conjugates of a class (see 42.31).

Note that the classes c1 , c2 and c3 in the parameter list must be ordered according to the order of the elements in these classes.

42.34 DisplayCharTable

DisplayCharTable( tbl )
DisplayCharTable( tbl, arec )

DisplayCharTable prepares the data contained in the character table tbl for a pretty columnwise output.

In the first form DisplayCharTable prints all irreducible characters of the table tbl, together with the orders of the centralizers in factorized form and the available powermaps.

Thus it can be used to echo character tables in interactive use, being the value of the record field Print of a record field operations of tbl (see 42.2, 42.6).

Each displayed character is given a name X.n.

The number of columns printed at one time depends on the actual linelength, which is restored by the function SizeScreen (see 3.19).

The first lines of the output describe the order of the centralizer of an element of the class factorized into its prime divisor.

The next line gives the name of the class. If the record field classnames of the table tbl is not bound, DisplayCharTable calls the function ClassNamesCharTable to determine the class names and to store them on the table tbl (see 42.26).

Preceded by a name Pn the next lines show the nth powermaps of tbl in terms of the former shown class names.

Every ambiguous or unknown (see 16.1) value of the table is displayed as a question mark

?.

Irrational character values are not printed explicitly because the lengths of their printed representation might disturb the view. Instead of that every irrational value is indicated by a name, which is a string of a least one capital letter.

Once a name for an irrational number is found, it is used all over the printed table. Moreover the complex conjugate and the star of an irrationality are represented by that very name preceded by a / resp. a *.

The printed character table is then followed by a legend, a list identifying the occurred symbols with their actual irrational value. Occasionally this identity is supplemented by a quadratic representation of the irrationality together with the corresponding ATLAS– notation.

    gap> a5:= CharTable("A5");;
    gap> DisplayCharTable(a5);
    A5

   2  2  2  .   .   .
   3  1  .  1   .   .
   5  1  .  .   1   1
     1a 2a 3a  5a  5b
  2P 1a 1a 3a  5b  5a
  3P 1a 2a 1a  5b  5a
  5P 1a 2a 3a  1a  1a
X.1   1  1  1   1   1
X.2   3 -1  .   A  *A
X.3   3 -1  .  *A   A
X.4   4  .  1  -1  -1
X.5   5  1 -1   .   .
A = -E(5)-E(5)^4
  = (1-ER(5))/2 = -b5

In the second form DisplayCharTable takes an argument record arec as an additional argument. This record can be used to change the default style for displaying a character as shown above. Its relevant fields are

chars

an integer or a list of integers to select a sublist of the irreducible characters of tbl, or a list of characters of tbl (in this case the letter "X" is replaced by "Y"),

classes

an integer or a list of integers to select a sublist of the classes of tbl,

centralizers

suppresses the printing of the orders of the centralizers if false,

powermap

an integer or a list of integers to select a subset of the available powermaps, or false to suppress the powermaps,

letter

a single capital letter (e. g. "P" for permutation characters) to replace "X",

indicator

true enables the printing of the second Schur indicator, a list of integers enables the printing of the corresponding indicators.

gap> arec:= rec( chars:= 4, classes:= [a5.3a..a5.5a],
> centralizers:= false, indicator:= true, powermap:= [2] );;
gap> Indicator( a5, 2 );;
gap> DisplayCharTable( a5, arec );
A5
        3a 5a
     2P 3a 5b
     2
X.4  +   1 -1

42.35 SortCharactersCharTable

SortCharactersCharTable( tbl )
SortCharactersCharTable( tbl, permutation )
SortCharactersCharTable( tbl, chars )
SortCharactersCharTable( tbl, chars, permutation )
SortCharactersCharTable( tbl, chars, "norm")
SortCharactersCharTable( tbl, chars, "degree")

If no list chars of characters of the character table tbl is entered, SortCharactersCharTable sorts tbl.irreducibles; then additionally the list tbl.irredinfo is permuted by the same permutation. Otherwise SortCharactersCharTable sorts the list chars.

There are four possibilities to sort characters: Besides the application of an explicitly given permutation (see 25.31), they can be sorted according to ascending norms (parameter "norm"), to ascending degree (parameter "degree") or both (no third parameter), i.e., characters with same norm are sorted according to ascending degree, and characters with smaller norm precede those with bigger norm.

If the trivial character is contained in the sorted list, it will be sorted to the first position. Rational characters always will precede other ones with same norm resp. same degree afterwards.

SortCharactersCharTable returns the permutation that was applied to chars.

    gap> t:= CharTable( "Symmetric", 5 );
    rec( name := "S5", order := 120, centralizers :=
    [ 120, 12, 8, 6, 6, 4, 5 ], orders := [ 1, 2, 2, 3, 6, 4, 5
     ], powermap := [ , [ 1, 1, 1, 4, 4, 3, 7 ], [ 1, 2, 3, 1, 2, 6, 7 ],
      , [ 1, 2, 3, 4, 5, 6, 1 ] ], irreducibles :=
    [ [ 1, -1, 1, 1, -1, -1, 1 ], [ 4, -2, 0, 1, 1, 0, -1 ],
      [ 5, -1, 1, -1, -1, 1, 0 ], [ 6, 0, -2, 0, 0, 0, 1 ],
      [ 5, 1, 1, -1, 1, -1, 0 ], [ 4, 2, 0, 1, -1, 0, -1 ],
      [ 1, 1, 1, 1, 1, 1, 1 ] ], classparam :=
    [ [ 1, [ 1, 1, 1, 1, 1 ] ], [ 1, [ 2, 1, 1, 1 ] ], [ 1, [ 2, 2, 1 ] ],
      [ 1, [ 3, 1, 1 ] ], [ 1, [ 3, 2 ] ], [ 1, [ 4, 1 ] ], [ 1, [ 5 ] ]
     ], irredinfo := [ rec(
          charparam := [ 1, [ 1, 1, 1, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 2, 1, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 2, 2, 1 ] ] ), rec(
          charparam := [ 1, [ 3, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 3, 2 ] ] ), rec(
          charparam := [ 1, [ 4, 1 ] ] ), rec(
          charparam := [ 1, [ 5 ] ] )
     ], text := "computed using generic character table for symmetric grou\
    ps", classes := [ 1, 10, 15, 20, 20, 30, 24
     ], operations := CharTableOps )
    gap> SortCharactersCharTable( t, t.irreducibles, "norm" );

(1,2,3,4,5,6,7) # sort the trivial character to the first position

    gap> SortCharactersCharTable( t );
    (4,5,7)

    gap> t.irreducibles;
    [ [ 1, 1, 1, 1, 1, 1, 1 ], [ 1, -1, 1, 1, -1, -1, 1 ],
      [ 4, -2, 0, 1, 1, 0, -1 ], [ 4, 2, 0, 1, -1, 0, -1 ],
      [ 5, -1, 1, -1, -1, 1, 0 ], [ 5, 1, 1, -1, 1, -1, 0 ],
      [ 6, 0, -2, 0, 0, 0, 1 ] ]

42.36 SortClassesCharTable

SortClassesCharTable( tbl )
SortClassesCharTable( tbl, "centralizers")
SortClassesCharTable( tbl, "representatives")
SortClassesCharTable( tbl, permutation )
SortClassesCharTable( chars, permutation )

The last form simply permutes the classes of all elements of chars with permutation. All other forms take a character table tbl as parameter, and SortClassesCharTable permutes the classes of tbl:

SortClassesCharTable( tbl, "centralizers")

sorts the classes according to descending centralizer orders,

SortClassesCharTable( tbl, "representatives")

sorts the classes according to ascending representative orders,

SortClassesCharTable( tbl )

sorts the classes according to ascending representative orders, and classes with equal representative orders according to descending centralizer orders,

SortClassesCharTable( tbl, permutation )

sorts the classes by application of permutation

After having calculated the permutation, SortClassesCharTable will adjust the following fields of tbl:

by application of the permutation: orders, centralizers, classes, print, all entries of

irreducibles, classtext, classparam, classnames, all fusion maps, all entries of the

chars lists in the records of projectives

by conjugation with the permutation: all powermaps, automorphisms,

by multiplication with the permutation: permutation,

and the fields corresponding to tbl.classnames (see 42.26).

The applied permutation is returned by SortClassesCharTable.

Note that many programs expect the class 1A to be the first one (see 42.7).

    gap> t:= CharTable( "Symmetric", 5 );
    rec( name := "S5", order := 120, centralizers :=
    [ 120, 12, 8, 6, 6, 4, 5 ], orders := [ 1, 2, 2, 3, 6, 4, 5
     ], powermap := [ , [ 1, 1, 1, 4, 4, 3, 7 ], [ 1, 2, 3, 1, 2, 6, 7 ],
      , [ 1, 2, 3, 4, 5, 6, 1 ] ], irreducibles :=
    [ [ 1, -1, 1, 1, -1, -1, 1 ], [ 4, -2, 0, 1, 1, 0, -1 ],
      [ 5, -1, 1, -1, -1, 1, 0 ], [ 6, 0, -2, 0, 0, 0, 1 ],
      [ 5, 1, 1, -1, 1, -1, 0 ], [ 4, 2, 0, 1, -1, 0, -1 ],

      [ 1, 1, 1, 1, 1, 1, 1 ] ], classparam :=
    [ [ 1, [ 1, 1, 1, 1, 1 ] ], [ 1, [ 2, 1, 1, 1 ] ], [ 1, [ 2, 2, 1 ] ],
      [ 1, [ 3, 1, 1 ] ], [ 1, [ 3, 2 ] ], [ 1, [ 4, 1 ] ], [ 1, [ 5 ] ]
     ], irredinfo := [ rec(
          charparam := [ 1, [ 1, 1, 1, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 2, 1, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 2, 2, 1 ] ] ), rec(
          charparam := [ 1, [ 3, 1, 1 ] ] ), rec(
          charparam := [ 1, [ 3, 2 ] ] ), rec(
          charparam := [ 1, [ 4, 1 ] ] ), rec(
          charparam := [ 1, [ 5 ] ] )
     ], text := "computed using generic character table for symmetric grou\
    ps", classes := [ 1, 10, 15, 20, 20, 30, 24
     ], operations := CharTableOps )
    gap> SortClassesCharTable( t, "centralizers" );
    (6,7)
    gap> SortClassesCharTable( t, "representatives" );
    (5,7)
    gap> t.centralizers; t.orders;
    [ 120, 12, 8, 6, 4, 5, 6 ]
    [ 1, 2, 2, 3, 4, 5, 6 ]

42.37 SortCharTable

SortCharTable( tbl, kernel )
SortCharTable( tbl, normalseries )
SortCharTable( tbl, facttbl, kernel )

sorts classes and irreducibles of the character table tbl, and returns a record with components columns and rows, which are the permutations applied to classes and characters.

The first form sorts the classes at positions contained in the list kernel to the beginning, and sorts all characters in tbl.irreducibles such that the first characters are those that contain kernel in their kernel.

The second form does the same successively for all kernels k_{i} in the list normalseries = [k_{1}, k_{2}, …, k_{n}] where k_{i} must be a sublist of k_{i+1} for 1 ≤ i ≤ n − 1.

The third form computes the table F of the factor group of tbl modulo the normal subgroup formed by the classes whose positions are contained in the list kernel; F must be permutation equivalent to the table facttbl (in the sense of 42.41), else false is returned. The classes of tbl are sorted such that the preimages of a class of F are consecutive, and that the succession of preimages is that of facttbl. tbl.irreducibles is sorted as by SortCharTable( tbl, kernel ). (Note that the transformation is only unique up to table automorphisms of F , and this need not be unique up to table automorphisms of tbl.)

All rearrangements of classes and characters are stable, i.e., the relative positions of classes and characters that are not distinguished by any relevant property is not changed.

SortCharTable uses 42.36 SortClassesCharTable and 42.35 SortCharactersCharTable.
    gap> t:= CharTable("Symmetric",4);;

gap> Set( List( t.irreducibles, KernelChar ) );
[ [ 1 ], [ 1, 2, 3, 4, 5 ], [ 1, 3 ], [ 1, 3, 4 ] ]
gap> SortCharTable( t, Permuted( last, (2,4,3) ) );
rec(
  columns := (2,4,3),
  rows := (1,2,4,5) )
gap> DisplayCharTable( t );
S4
   2  3  3  .  2  2
   3  1  .  1  .  .
     1a 2a 3a 2b 4a
  2P 1a 1a 3a 1a 2a
  3P 1a 2a 1a 2b 4a
X.1   1  1  1  1  1
X.2   1  1  1 -1 -1
X.3   2  2 -1  .  .
X.4   3 -1  . -1  1
X.5   3 -1  .  1 -1

42.38 MatAutomorphisms

MatAutomorphisms( mat, maps, subgroup )

returns the permutation group record representing the matrix automorphisms of the matrix mat that respect all lists in the list maps, i.e. representing the group of those permutations of columns of mat which acts on the set of rows of mat and additionally fixes all lists in maps.

subgroup is a list of permutation generators of a subgroup of this group. E.g. generators of the Galois automorphisms of a matrix of ordinary characters may be entered here.

gap> t:= CharTable( "Dihedral", 8 );
rec( name := "D8", order := 8, centralizers := [ 8, 4, 8, 4, 4
 ], orders := [ 1, 4, 2, 2, 2 ], powermap := [ , [ 1, 3, 1, 1, 1 ]
 ], irreducibles := [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, -1, -1 ],
  [ 1, -1, 1, 1, -1 ], [ 1, -1, 1, -1, 1 ], [ 2, 0, -2, 0, 0 ]
 ], classparam := [ [ 1, 0 ], [ 1, 1 ], [ 1, 2 ], [ 2, 0 ], [ 2, 1 ]
 ], irredinfo := [ rec(
      charparam := [ 1, 0 ] ), rec(
      charparam := [ 1, 1 ] ), rec(
      charparam := [ 1, 2 ] ), rec(
      charparam := [ 1, 3 ] ), rec(
      charparam := [ 2, 1 ] )
 ], text := "computed using generic character table for dihedral group\
s", classes := [ 1, 2, 1, 2, 2 ], operations := CharTableOps )
gap> MatAutomorphisms( t.irreducibles, [], Group( () ) );
Group( (4,5), (2,4) )

    gap> MatAutomorphisms( t.irreducibles, [ t.orders ], Group( () ) );
    Group( (4,5) )

42.39 TableAutomorphisms

TableAutomorphisms( tbl, chars )
TableAutomorphisms( tbl, chars, "closed")

returns a permutation group record for the group of those matrix automorphisms of chars (see 42.38) which are admissible by (i.e. which fix) the fields orders and all uniquely determined (i.e. not parametrized) maps in powermap of the character table tbl; the action on orders is the natural permutation, that on the powermaps is conjugation.

If chars is closed under galois conjugacy –this is always satisfied for ordinary character tables– the parameter ”closed” may be entered. In that case the subgroup of Galois automorphisms is computed by 13.15 GaloisMat.

    gap> t:= CharTable( "Dihedral", 8 );; # as in 42.38
    gap> TableAutomorphisms( t, t.irreducibles );
    Group( (4,5) )

42.40 TransformingPermutations

TransformingPermutations( mat1 , mat2 )

tries to construct a permutation π that transforms the set of rows of the matrix mat1 to the set of rows of the matrix mat2 by permutation of columns. If such a permutation exists, a record with fields columns, rows and group is returned, otherwise false: If

TransformingPermutations(mat1 , mat2 ) = r ≠ false then
Permuted( List(mat1 ,x->Permuted(x,r.columns)),r.rows ) = mat2 ,

and r.group is the group of matrix automorphisms of mat2 ; this group stabilizes the transformation, i.e. for g in that group and π the value of the columns field, also πg would be a valid permutation of columns.

    gap> mat1:= CharTable( "Alternating", 5 ).irreducibles;
    [ [ 1, 1, 1, 1, 1 ], [ 4, 0, 1, -1, -1 ], [ 5, 1, -1, 0, 0 ],
      [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ] ]
    gap> mat2:= CharTable( "A5" ).irreducibles;
    [ [ 1, 1, 1, 1, 1 ], [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]
    gap> TransformingPermutations( mat1, mat2 );
    rec(
      columns := (),
      rows := (2,4)(3,5),
      group := Group( (4,5) ) )

42.41 TransformingPermutationsCharTables

TransformingPermutationsCharTables( tbl1 , tbl2 )

tries to construct a permutation π that transforms the set of rows of tbl1 .irreducibles to the set of rows of tbl2 .irreducibles by permutation of columns (see 42.40) which also transforms the powermaps and the orders field. If such a permutation exists, a record with fields columns (a valid permutation of columns), rows (the permutation of tbl.irreducibles corresponding to that permutation) and group (the permutation group record of table automorphisms of tbl2 , see 42.39) is returned, otherwise false.

    gap> t1:= CharTable("Dihedral",8);;t2:= CharTable("Quaternionic",8);;
    gap> TransformingPermutations( t1.irreducibles, t2.irreducibles );
    rec(
      columns := (),
      rows := (),
      group := Group( (4,5), (2,4) ) )
    gap> TransformingPermutationsCharTables( t1, t2 );
    false
    gap> t1:= CharTable( "Dihedral", 6 );; t2:= CharTable("Symmetric",3);;
    gap> TransformingPermutationsCharTables( t1, t2 );
    rec(
      columns := (2,3),
      rows := (1,3,2),
      group := Group( () ) )

42.42 GetFusionMap

GetFusionMap( source, destination ) GetFusionMap( source, destination, specification )

For character tables source and destination, GetFusionMap( source, destination ) returns the map field of the fusion stored on the character table source that has the name field destination.name;

GetFusionMap( source, destination, specification ) gets that fusion that additionally has the specification field specification.

Both versions adjust the ordering of classes of destination using destination.permutation (see 42.36, 42.7). That is the reason why destination cannot be simply the name of the destination table.

If both source and destination are Brauer tables, GetFusionMap returns the fusion corresponding to that between the ordinary tables; for that, this fusion must be stored on

source.ordinary.

If no appropriate fusion is found, false is returned.

    gap> s:= CharTable( "L2(11)" );;
    gap> t:= CharTable( "J1" );;
    gap> SortClassesCharTable( t, ( 3, 4, 5, 6 ) );;
    gap> t.permutation;
    (3,4,5,6)
    gap> GetFusionMap( s, t );
    [ 1, 2, 4, 6, 5, 3, 10, 10 ]
    gap> s.fusions[5];

    rec(
      name := "J1",
      map := [ 1, 2, 3, 5, 4, 6, 10, 10 ],
      text :=
       "fusion is unique up to table automorphisms,\nthe representative is\
     equal to the fusion map on the CAS table" )

42.43 StoreFusion

StoreFusion( source, destination, fusion ) StoreFusion( source, destination, fusionmap )

For character tables source and destination, fusion must be a record containing at least the field map which is regarded as a fusion from source to destination. fusion is stored on source if no ambiguity arises, i.e. if there is not yet a fusion into destination stored on source or if any fusion into destination stored on source has a specification field different from that of fusion. The map field of fusion is adjusted by destination.permutation. (Thus the map will remain correct even if the classes of a concerned table are sorted, see 42.36 and 42.7; the correct fusion can be got using 42.42, so be careful!). Additionally, source.name is added to

destination.fusionsource.

The second form works like the first, with fusion = rec( map:= fusionmap ).

    gap> s:= CharTable( "A6.2_1" );; t:= CharTable( "A7.2" );;
    gap> fus:= RepresentativesFusions( s, SubgroupFusions( s, t ), t );
    [ [ 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13 ] ]
    gap> s.fusions; t.fusionsource;
    [  ]
    [ "2.A7.2", "3.A7.2", "6.A7.2", "A7" ]
    gap> StoreFusion( s, t, fus[1] );
    gap> s.fusions; t.fusionsource;
    [ rec(
          name := "A7.2",
          map := [ 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13 ] ) ]
    [ "2.A7.2", "3.A7.2", "6.A7.2", "A6.2_1", "A7" ]

42.44 FusionConjugacyClasses

FusionConjugacyClasses( subgroup, group )
FusionConjugacyClasses( group , factorgroup )

FusionConjugacyClasses returns a list denoting the fusion of conjugacy classes from the first group to the second one. If both groups have components charTable this list is written to the character tables, too.

    gap> g := SolvableGroup( 24, 14 );
    Sl(2,3)
    gap> FusionConjugacyClasses( g, g / Subgroup( g, [ g.4 ] ) );
    [ 1, 1, 2, 3, 3, 4, 4 ]
    gap> FusionConjugacyClasses( Subgroup( g, [ g.2, g.3, g.4 ] ), g );
    [ 1, 2, 3, 3, 3 ]

42.45 MAKElb11

MAKElb11( listofns )

prints field information for fields with conductor Q_{n} where n is in the list listofns;

MAKElb11( [ 3 .. 189 ] ) will print something very similar to Richard Parker’s file

lb11.
    gap> MAKElb11( [ 3, 4 ] );
       3   2   0   1   0
       4   2   0   1   0

42.46 ScanMOC

ScanMOC( list )

returns a record containing the information encoded in the list list, the components of the result are the labels in list. If list is in MOC2 format (10000–format) the names of components are 30000–numbers, if it is in MOC3 format the names of components have yABC–format.

    gap> ScanMOC( List( "y100y105ay110t28t22z" ) );
    rec(
      y105 := [ 0 ],
      y110 := [ 28, 22 ] )

42.47 MOCChars

MOCChars( tbl, gapchars )

returns translations of GAP format characters gapchars to MOC format. tbl must be a GAP format table or a MOC format table.

42.48 GAPChars

GAPChars( tbl, mocchars )

returns translations of MOC format characters mocchars to GAP format. tbl must be a GAP format table or a MOC format table.

mocchars may also be a list of integers, e.g., a component containing characters in a record produced by 42.46.

42.49 MOCTable

MOCTable( gaptbl )

MOCTable( gaptbl, basicset )

return the MOC format table record of the GAP table gaptbl, and stores it in the component MOCtbl of gaptbl.

The first form can be used for ordinary (G.0) tables only, for modular (G.p) tables one has to specify a basic set basicset of ordinary irreducibles which must be the list of positions of these characters in the irreducibles component of the corresponding ordinary table (which is stored in gaptbl.ordinary).

The result contains the information of gaptbl in a format similar to the MOC 3 format, the table itself can e.g. easily be printed out or be used to print out characters using 42.50.

The components of the result are name the string MOCTable(name) where name is the name of gaptbl,

isMOCformat  has value true,

GAPtbl the record gaptbl,

operations equal to MOCTableOps, containing just an appropriate Print function,

prime the characteristic of the field (label 30105 in MOC),

centralizers centralizer orders for cyclic subgroups (label 30130)

orders element orders for cyclic subgroups (label 30140)

fields at position i the number field generated by the character values of the i–th cyclic subgroup; the base component of each field is a Parker base, (the length of fields is equal to the value of label 30110 in MOC).

cycsubgps cycsubgps[i] = j means that class i of the GAP table belongs to the j–th cyclic subgroup of the GAP table,

repcycsub repcycsub[j] = i means that class i of the GAP table is the representative of the j–th cyclic subgroup of the GAP table. Note that the representatives of GAP table and MOC table need not agree!

galconjinfo a list [r_{1}, c_{1}, r_{2}, c_{2}, …, r_{n}, c_{n}] which means that the i–th class of the GAP table is the c_{i}–th conjugate of the representative of the r_{i}–th cyclic subgroup on the MOC table. (This is used to translate back to GAP format, stored under label 30160)

30170 (power maps) for each cyclic subgroup (except the trivial one) and each prime divisor of the representative order store four values, the number of the subgroup, the power, the number of the cyclic subgroup containing the image, and the power to which the representative must be raised to give the image class. (This is used only to construct the 30230 power map/embedding information.) In result.30170 only a list of lists (one for each cyclic subgroup) of all these values is stored, it will not be used by GAP.

tensinfo tensor product information, used to compute the coefficients of the Parker base for tensor products of characters (label 30210 in MOC). For a field with vector space base (v_{1}, v_{2}, …, v_{n}) the tensor product information of a cyclic subgroup in MOC (as computed by fct) is either 1 (for rational classes) or a sequence

nx_{1,1}y_{1,1}z_{1,1}x_{1,2}y_{1,2}z_{1,2} … x_{1,m_{1}}y_{1,m_{1}}z_{1,m_{1}}0x_{2,1} … z_{2,m_{2}}0 … x_{n,m_{n}}y_{n,m_{n}}z_{n,m_{n}}0

which means that the coefficient of v_{k} in the product

(∑_{i=1}^{n} a_{i}v_{i})(∑_{j=1}^{n} b_{j}v_{j})

is equal to

∑_{i=1}^{m_{k}} x_{k,i}a_{y_{k,i}}b_{z_{k,i}} .

On a MOC table in GAP the tensinfo component is a list of lists, each containing exactly the sequence

invmap inverse map to compute complex conjugate characters, label 30220 in MOC.

powerinfo field embeddings for p–th symmetrizations, p prime in [ 2 .. 19 ]; note that the necessary power maps must be stored on gaptbl to compute this component. (label 30230 in MOC)

30900 basic set of restricted ordinary irreducibles in the case of nonzero characteristic, all ordinary irreducibles else.

42.50 PrintToMOC

PrintToMOC( moctbl )
PrintToMOC( moctbl, chars )

The first form prints the MOC3 format of the character table moctbl which must be an character table in MOC format (as produced by 42.49). The second form prints a table in MOC3 format that contains the MOC format characters chars (as produced by 42.47) under label y900.

    gap> t:= CharTable( "A5mod3" );;
    gap> moct:= MOCTable( t, [ 1, 2, 3, 4 ] );;
    gap> PrintTo( "a5mod3", PrintToMOC( moct ), "\n" );

produces a file a5mod3 whose first characters are

    y100y105dy110edy130t60efy140bcfy150bbfcabbey160bbcbdbdcy170ccbbefbb

42.51 PrintToCAS

PrintToCAS( filename, tbl )
PrintToCAS( tbl, filename )

produces a file with name filename which contains a CAS library table of the GAP character table tbl; this file can be read into CAS using the get-command (see [NPP84]).

The line length in the file is at most the current value SizeScreen()[1] (see 3.19).

Only the fields name, text, order, centralizers, orders, print, powermap, classtext

(for partitions only), fusions, irredinfo, characters, irreducibles of tbl are considered.

If tbl.characters is bound, this list is taken as characters entry of the CAS table, otherwise tbl.irreducibles (if exists) will form the list characters of the CAS table.

    gap> PrintToCAS( "c2", CharTable( "Cyclic", 2 ) );

produces a file with name c2 containing the following data:

    'C2'
    00/00/00. 00.00.00.
    (2,2,0,2,-1,0)

text:
(#computed using generic character table for cyclic groups#),
order=2,
centralizers:(2,2),
reps:(1,2),
powermap:2(1,1),
characters:
(1,1)
(1,-1);
/// converted from GAP

Generic Character Tables

This chapter informs about the conception of generic character tables (see 43.1), it gives some examples of generic tables (see 43.2), and introduces the specialization function (see 43.3). The generic tables that are actually available in the GAP group collection are listed in 42.9, see also 46.1.

43.1 More about Generic Character Tables

Generic character tables provide a means for writing down the character tables of all groups in a (usually infinite) series of similar groups, e.g. the cyclic groups, the symmetric groups or the general linear groups GL(2, q). Let {G_{q}|q ∈ I}, where I is an index set, be such a series. The table of a member G_{q} could be computed using a program for this series which takes q as parameter, and constructs the table. It is, however, desirable to compute not only the whole table but to get a single character or just one character value without computation the table. E.g. both conjugacy classes and irreducible characters of the symmetric group S_{n} are in bijection with the partitions of n. Thus for given n, it makes sense to ask for the character corresponding to a particular partition, and its value at a partition:

    gap> t:= CharTable( "Symmetric" );;
    gap> t.irreducibles[1][1]( 5, [ 3, 2 ], [ 2, 2, 1 ] );

1 # a character value of S_{5}

    gap> t.orders[1]( 5, [ 2, 1, 1, 1 ] );

2 # a representative order in S_{5} Generic table in GAP means that such local evaluation is possible, so GAP can also deal with tables that are too big to be computed as a whole. In some cases there are methods to compute the complete table of small members G_{q} faster than local evaluation. If such an algorithm is part of the generic table, it will be used when the generic table is used to compute the whole table (see 43.3). While the numbers of conjugacy classes for the series are usually not bounded, there is a fixed finite number of types (equivalence classes) of conjugacy classes; very often the equivalence relation is isomorphism of the centralizer of the representatives.

For each type t of classes and a fixed q ∈ I, a parametrisation of the classes in t is a function that assigns to each conjugacy class of G_{q} in t a parameter by which it is uniquely determined. Thus the classes are indexed by pairs (t, p_{t}) for a type t and a parameter p_{t} for that type.

There has to be a fixed number of types of irreducibles characters of G_{q}, too. Like the classes, the characters of each type are parametrised.

In GAP, the parametrisations of classes and characters of the generic table is given by the record fields classparam and charparam; they are both lists of functions, each function representing the parametrisation of a type. In the specialized table, the field classparam contains the lists of class parameters, the character parameters are stored in the field charparam of the irredinfo records (see 42.2).

The centralizer orders, representative orders and all powermaps of the generic character table can be represented by functions in q, t and p_{t}; in GAP, however, they are represented by lists of functions in q and a class parameter where each function represents a type of classes. The value of a powermap at a particular class is a pair consisting of type and parameter that specifies the image class.

The values of the irreducible characters of G_{q} can be represented by functions in q, class type and parameter, character type and parameter; in GAP, they are represented by lists of lists of functions, each list of functions representing the characters of a type, the function (in q, character parameters and class parameters) representing the classes of a type in these characters.

Any generic table is a record like an ordinary character table (see 42.2). There are some fields which are used for generic tables only:

isGenericTable

always true

specializedname

function that maps q to the name of the table of G_{q}

domain

function that returns true if its argument is a valid q for G_{q} in the series

wholetable

function to construct the whole table, more efficient than the local evaluation for this purpose

The table of G_{q} can be constructed by specializing q and evaluating the functions in the generic table (see 43.3 and the examples given in 43.2).

The available generic tables are listed in 46.1 and 42.9.

43.2 Examples of Generic Character Tables

1. The generic table of the cyclic group:

For the cyclic group C_{q} = ⟨x⟩ of order q, there is one type of classes. The class parameters are integers k ∈ {0, …, q − 1}, the class of the parameter k consists of the group element x^{k}. Group order and centralizer orders are the identity function q ↦ q, independent of the parameter k.

The representative order function maps (q, k) to \frac{q}{gcd(q,k)}, the order of x^{k} in C_{q}; the p-th powermap is the function (q, k, p) ↦ [1, (kp mod q)].

There is one type of characters with parameters l ∈ {0, …, q−1}; for e_{q} a primitive complex q-th root of unity, the character values are χ_{l}(x^{k}) = e_{q}^{kl}.

The library file contains the following generic table:

    rec(name:="Cyclic",
    specializedname:=(q->ConcatenationString("C",String(q))),
    order:=(n->n),
    text:="generic character table for cyclic groups",
    centralizers:=[function(n,k) return n;end],
    classparam:=[(n->[0..n-1])],
    charparam:=[(n->[0..n-1])],
    powermap:=[function(n,k,pow) return [1,k*pow mod n];end],
    orders:=[function(n,k) return n/Gcd(n,k);end],
    irreducibles:=[[function(n,k,l) return E(n)^(k*l);end]],
    domain:=(n->IsInt(n) and n>0),
    libinfo:=rec(firstname:="Cyclic",othernames:=[]),
    isGenericTable:=true);

2. The generic table of the general linear group GL(2, q):

We have four types t_{1}, t_{2}, t_{3}, t_{4} of classes according to the rational canonical form of the elements: t_{1} scalar matrices, t_{2} nonscalar diagonal matrices, t_{3} companion matrices of (X − ρ)^{2} for elements ρ ∈ F_{q}^{∗} and t_{4} companion matrices of irreducible polynomials of degree 2 over F_{q}.

The sets of class parameters of the types are in bijection with F_{q}^{∗} for t_{1} and t_{3}, {{ρ, τ}; ρ, τ ∈ F_{q}^{∗}, ρ ≠ τ} for t_{2} and {{ϵ, ϵ^{q}}; ϵ ∈ F_{q^{2}} \ F_{q}} for t_{4}.

The centralizer order functions are q ↦ (q^{2} − 1)(q^{2} − q) for type t_{1}, q ↦ (q − 1)^{2} for type t_{2}, q ↦ q(q − 1) for type t_{3} and q ↦ q^{2} − 1 for type t_{4}.

The representative order function of t_{1} maps (q, ρ) to the order of ρ in F_{q}, that of t_{2} maps (q, {ρ, τ}) to the least common multiple of the orders of ρ and τ.

The file contains something similar to this table:

    rec(name:="GL2",
        specializedname:=(q->ConcatenationString("GL(2,",String(q),")")),
        order:= ( q -> (q^2-1)*(q^2-q) ),
        text:= "generic character table of GL(2,q),\
     see Robert Steinberg: The Representations of Gl(3,q), Gl(4,q),\
     PGL(3,q) and PGL(4,q), Canad. J. Math. 3 (1951)",
        classparam:= [ ( q -> [0..q-2] ), ( q -> [0..q-2] ),
                   ( q -> Combinations( [0..q-2], 2 ) ),
                   ( q -> Filtered( [1..q^2-2], x -> not (x mod (q+1) = 0)
                               and (x mod (q^2-1)) < (x*q mod (q^2-1)) ))],
        charparam:= [ ( q -> [0..q-2] ), ( q -> [0..q-2] ),
                  ( q -> Combinations( [0..q-2], 2 ) ),

          ( q -> Filtered( [1..q^2-2], x -> not (x mod (q+1) = 0)
                       and (x mod (q^2-1)) < (x*q mod (q^2-1)) ))],
centralizers := [ function( q, k ) return (q^2-1) * (q^2-q); end,
                  function( q, k ) return q^2-q; end,
                  function( q, k ) return (q-1)^2; end,
                  function( q, k ) return q^2-1; end],
orders:= [ function( q, k ) return (q-1)/Gcd( q-1, k ); end,
         ..., ..., ... ],
classtext:= [ ..., ..., ..., ... ],
powermap:=
       [ function( q, k, pow ) return [1, (k*pow) mod (q-1)]; end,
         ..., ..., ... ],
irreducibles := [[ function( q, k, l ) return E(q-1)^(2*k*l); end,
               function( q, k, l ) return E(q-1)^(2*k*l); end,
               ...,
               function( q, k, l ) return E(q-1)^(k*l); end    ],
                 [ ..., ..., ..., ... ],
                 [ ..., ..., ..., ... ],
                 [ ..., ..., ..., ... ]],
domain := ( q->IsInt(q) and q>1 and Length(Set(FactorsInt(q)))=1 ),
isGenericTable := true )

43.3 CharTableSpecialized

CharTableSpecialized( generic table, q )

returns a character table which is computed by evaluating the generic character table generic table at the parameter q.

gap> CharTableSpecialized( CharTable( "Cyclic" ), 5 );
rec( name := "C5", order := 5, centralizers := [ 5, 5, 5, 5, 5
], orders := [ 1, 5, 5, 5, 5 ], powermap := [ ,,,, [ 1, 1, 1, 1, 1 ]
], irreducibles :=
[ [ 1, 1, 1, 1, 1 ], [ 1, E(5), E(5)^2, E(5)^3, E(5)^4 ],
[ 1, E(5)^2, E(5)^4, E(5), E(5)^3 ],
[ 1, E(5)^3, E(5), E(5)^4, E(5)^2 ],
[ 1, E(5)^4, E(5)^3, E(5)^2, E(5) ] ], classparam :=
[ [ 1, 0 ], [ 1, 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ] ], irredinfo :=
[ rec(
  charparam := [ 1, 0 ] ), rec(
  charparam := [ 1, 1 ] ), rec(
  charparam := [ 1, 2 ] ), rec(
  charparam := [ 1, 3 ] ), rec(
  charparam := [ 1, 4 ] )
], text := "computed using generic character table for cyclic groups"\
, classes := [ 1, 1, 1, 1, 1 ], operations := CharTableOps )

Characters

The functions described in this chapter are used to handle characters (see Chapter 42). For this, in many cases one needs maps (see Chapter 45).

There are four kinds of functions:

First, those which get informations about characters; they compute the scalar product of characters (see 44.1, 44.2), decomposition matrices (see 44.3, 44.4), the kernel of a character (see 44.5), p-blocks (see 44.6), Frobenius-Schur indicators (see 44.7) and eigenvalues of the corresponding representations (see 44.8), and decide if a character might be a permutation character candidate (see 44.25).

Second, those which construct characters or virtual characters, that is, differences of characters; these functions compute reduced characters (see 44.9, 44.10), tensor products (see 44.11), symmetrisations (see 44.12, 44.13, 44.14, 44.15, 44.16, 44.17), and irreducibles differences of virtual characters (see 44.18). Further, one can compute restricted (see 44.19), inflated (see 44.20), induced (see 44.21, 44.22) characters and permutation character candidates (see 44.25, 44.30).

Third, functions which use general methods for lattices. These are the LLL algorithm to compute a lattice base consisting of short vectors (see 44.32, 44.33, 44.34), functions to compute all orthogonal embeddings of a lattice (see 44.35), and one for the special case of D_{n} lattices (see 44.39). A backtrack search for irreducible characters in the span of proper characters is performed by 44.37.

Last, those which find special elements of parametrized characters (see 45.1); they compute the set of contained virtual characters (see 44.40) or characters (see 44.41), the set of contained vectors which possibly are virtual characters (see 44.42, 44.44) or characters (see 44.43).

44.1 ScalarProduct

ScalarProduct( tbl, character1 , character2 )

returns the scalar product of character1 and character2 , regarded as characters of the character table tbl.

    gap> t:= CharTable( "A5" );;

    gap> ScalarProduct( t, t.irreducibles[1], [ 5, 1, 2, 0, 0 ] );
    1
    gap> ScalarProduct( t, [ 4, 0, 1, -1, -1 ], [ 5, -1, 1, 0, 0 ] );
    2/3

44.2 MatScalarProducts

MatScalarProducts( tbl, chars1 , chars2 )
MatScalarProducts( tbl, chars )

For a character table tbl and two lists chars1 , chars2 of characters, the first version returns the matrix of scalar products (see 44.1); we have

MatScalarProducts(tbl, chars1 , chars2 )[i][j] = ScalarProduct(tbl, chars1 [j], chars2 [i]),

i.e., row i contains the scalar products of chars2 [i] with all characters in chars1 .

The second form returns a lower triangular matrix of scalar products:

MatScalarProducts(tbl, chars)[i][j] = ScalarProduct(tbl, chars[j], chars[i])

for j ≤ i.

    gap> t:= CharTable( "A5" );;
    gap> chars:= Sublist( t.irreducibles, [ 2 .. 4 ] );;
    gap> chars:= Set( Tensored( chars, chars ) );;
    gap> MatScalarProducts( t, chars );
    [ [ 2 ], [ 1, 3 ], [ 1, 2, 3 ], [ 2, 2, 1, 3 ], [ 2, 1, 2, 2, 3 ],
      [ 2, 3, 3, 3, 3, 5 ] ]

44.3 Decomposition

Decomposition( A, B, depth )
Decomposition( A, B, "nonnegative")

For a m × n matrix A of cyclotomics that has rank m ≤ n, and a list B of cyclotomic vectors, each of dimension n, Decomposition tries to find integral solutions x of the linear equation systems x *A = B[i] by computing the p–adic series of hypothetical solutions.

Decomposition( A, B, depth ), where depth is a nonnegative integer, computes for every vector B[i] the initial part ∑_{k=0}^{depth} x_{k}p^{k} (all x_{k} integer vectors with entries bounded by ±\frac{p−1}{2}). The prime p is 83 first; if the reduction of A modulo p is singular, the next prime is chosen automatically.

A list X is returned. If the computed initial part really is a solution of x *A = B[i], we have X [i] = x, otherwise X [i] = false.

Decomposition( A, B, "nonnegative") assumes that the solutions have only nonnegative entries, and that the first column of A consists of positive integers. In this case the necessary number depth of iterations is computed; the i-th entry of the returned list is false if there exists no nonnegative integral solution of the system x *A = B[i], and it is the solution otherwise.

If A is singular, an error is signalled.

    gap> a5:= CharTable( "A5" );; a5m3:= CharTable( "A5mod3" );;
    gap> a5m3.irreducibles;
    [ [ 1, 1, 1, 1 ], [ 3, -1, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, -1, -1 ] ]
    gap> reg:= CharTableRegular( a5, 3 );;
    gap> chars:= Restricted( a5, reg, a5.irreducibles );
    [ [ 1, 1, 1, 1 ], [ 3, -1, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, -1, -1 ],
      [ 5, 1, 0, 0 ] ]
    gap> Decomposition( a5m3.irreducibles, chars, "nonnegative" );
    [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ],
      [ 1, 0, 0, 1 ] ]
    gap> last * a5m3.irreducibles = chars;
    true

For the subroutines of Decomposition, see 44.4.

44.4 Subroutines of Decomposition

Let A be a square integral matrix, p an odd prime. The reduction of A modulo p is \overline{A}, its entries are chosen in the interval [−\frac{p−1}{2}, \frac{p−1}{2}]. If \overline{A} is regular over the field with p elements, we can form A′ = \overline{A}^{−1}. Now consider the integral linear equation system xA = b, i.e., we look for an integral solution x. Define b_{0} = b, and then iteratively compute

x_{i} = (b_{i}A′) mod p, b_{i+1} = \frac{1}{p}(b_{i} − x_{i}A) fori = 0, 1, 2, ….

By induction, we get

p^{i+1}b_{i+1} + (∑_{j=0}^{i} p^{j}x_{j})A = b.

If there is an integral solution x, it is unique, and there is an index l such that b_{l+1} is zero and x = ∑_{j=0}^{l} p^{j}x_{j}.

There are two useful generalizations. First, A need not be square; it is only necessary that there is a square regular matrix formed by a subset of columns. Second, A does not need to be integral; the entries may be cyclotomic integers as well, in this case one has to replace each column of A by the columns formed by the coefficients (which are integers). Note that this preprocessing must be performed compatibly for A and b.

And these are the subroutines called by Decomposition:

LinearIndependentColumns( A )

returns for a matrix A a maximal list lic of positions such that the rank of List( A, x -> Sublist( x, lic ) ) is the same as the rank of A.

InverseMatMod( A, p )

returns for a square integral matrix A and a prime p a matrix A′ with the property that A′A is congruent to the identity matrix modulo p; if A is singular modulo p, false is returned.

PadicCoefficients( A, Amodpinv, b, p, depth )

returns the list [x_{0}, x_{1}, …, x_{l}, b_{l+1}] where l = depth or l is minimal with the property that b_{l+1} = 0.

IntegralizedMat( A )
IntegralizedMat( A, inforec )

return for a matrix A of cyclotomics a record intmat with components mat and inforec. Each family of galois conjugate columns of A is encoded in a set of columns of the rational matrix intmat.mat by replacing cyclotomics by their coefficients. intmat.inforec is a record containing the information how to encode the columns. If the only argument is A, the component inforec is computed that can be entered as second argument inforec in a later call of IntegralizedMat with a matrix B that shall be encoded compatible with A.

DecompositionInt( A, B, depth )

does the same as Decomposition (see 44.3), but only for integral matrices A, B, and nonnegative integers depth.

44.5 KernelChar

KernelChar( char )

returns the set of classes which form the kernel of the character char, i.e. the set of positions i with char[i] = char[1]. For a factor fusion map fus, KernelChar( fus ) is the kernel of the epimorphism.

    gap> s4:= CharTable( "Symmetric", 4 );;
    gap> s4.irreducibles;
    [ [ 1, -1, 1, 1, -1 ], [ 3, -1, -1, 0, 1 ], [ 2, 0, 2, -1, 0 ],
      [ 3, 1, -1, 0, -1 ], [ 1, 1, 1, 1, 1 ] ]
    gap> List( last, KernelChar );
    [ [ 1, 3, 4 ], [ 1 ], [ 1, 3 ], [ 1 ], [ 1, 2, 3, 4, 5 ] ]

44.6 PrimeBlocks

PrimeBlocks( tbl, prime )
PrimeBlocks( tbl, chars, prime )

For a character table tbl and a prime prime, PrimeBlocks( tbl, chars, prime ) returns a record with fields

block

a list of positive integers which has the same length as the list chars of characters, the entry n at position i in that list means that chars[i] belongs to the n-th prime-block

defect

a list of nonnegative integers, the value at position i is the defect of the i–th block.

PrimeBlocks( tbl, prime ) does the same for chars = tbl.irreducibles, and additionally the result is stored in the irredinfo field of tbl.

    gap> t:= CharTable( "A5" );;
    gap> PrimeBlocks( t, 2 ); PrimeBlocks( t, 3 ); PrimeBlocks( t, 5 );
    rec(
      block := [ 1, 1, 1, 2, 1 ],
      defect := [ 2, 0 ] )
    rec(
      block := [ 1, 2, 3, 1, 1 ],
      defect := [ 1, 0, 0 ] )
    rec(
      block := [ 1, 1, 1, 1, 2 ],
      defect := [ 1, 0 ] )

gap> InverseMap( last.block ); # distribution of characters to blocks

    [ [ 1, 2, 3, 4 ], 5 ]

If InfoCharTable2 = Print, the defects of the blocks and the heights of the contained characters are printed.

44.7 Indicator

Indicator( tbl, n )
Indicator( tbl, chars, n )
Indicator( modtbl, 2 )

For a character table tbl, Indicator( tbl, chars, n ) returns the list of n-th Frobenius Schur indicators for the list chars of characters.

Indicator( tbl, n ) does the same for chars = tbl.irreducibles, and additionally the result is stored in the field irredinfo of tbl.

Indicator( modtbl, 2 ) returns the list of 2nd indicators for the irreducible characters of the Brauer character table modtbl and stores the indicators in the irredinfo component of modtbl; this does not work for tables in characteristic 2.

    gap> t:= CharTable( "M11" );; Indicator( t, t.irreducibles, 2 );
    [ 1, 1, 0, 0, 1, 0, 0, 1, 1, 1 ]

44.8 Eigenvalues

Eigenvalues( tbl, char, class )

Let M be a matrix of a representation with character char which is a character of the table tbl, for an element in the conjugacy class class. Eigenvalues( tbl, char, class ) returns a list of length n = tbl.orders[ class ] where at position i the multiplicity of E(n)^i = e^{\frac{2πi}{n}} as eigenvalue of M is stored.

    gap> t:= CharTable( "A5" );;
    gap> chi:= t.irreducibles[2];
    [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ]

    gap> List( [ 1 .. 5 ], i -> Eigenvalues( t, chi, i ) );
    [ [ 3 ], [ 2, 1 ], [ 1, 1, 1 ], [ 0, 1, 1, 0, 1 ], [ 1, 0, 0, 1, 1 ] ]
List( [1..n], i -> E(n)^i) * Eigenvalues(tbl,char,class) ) is equal to char[ class
].

44.9 Reduced

Reduced( tbl, constituents, reducibles ) Reduced( tbl, reducibles ) returns a record with fields remainders and irreducibles, both lists: Let rems be the set of nonzero characters obtained from reducibles by subtraction of

∑_{χ∈constituents}\frac{ScalarProduct(tbl, χ, reducibles[i])}{ScalarProduct(tbl, χ, constituents[j])} · χ

from reducibles[i] in the first case or subtraction of

∑_{j≤i} \frac{ScalarProduct(tbl, reducibles[j], reducibles[i])}{ScalarProduct(tbl, reducibles[j], reducibles[j])} · reducibles[j]

in the second case. Let irrs be the list of irreducible characters in rems. rems is reduced with irrs and all found irreducibles until no new irreducibles are found. Then irreducibles is the set of all found irreducible characters, remainders is the set of all nonzero remainders. If one knows that reducibles are ordinary characters of tbl and constituents are irreducible ones, 44.10 ReducedOrdinary may be faster. Note that elements of remainders may be only virtual characters even if reducibles are ordinary characters.

    gap> t:= CharTable( "A5" );;
    gap> chars:= Sublist( t.irreducibles, [ 2 .. 4 ] );;
    gap> chars:= Set( Tensored( chars, chars ) );;
    gap> Reduced( t, chars );
    rec(
      remainders := [  ],
      irreducibles :=
       [ [ 1, 1, 1, 1, 1 ], [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
          [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, 1, -1, -1 ],
          [ 5, 1, -1, 0, 0 ] ] )

44.10 ReducedOrdinary

ReducedOrdinary( tbl, constituents, reducibles ) works like 44.9 Reduced, but assumes that the elements of constituents and reducibles are ordinary characters of the character table tbl. So scalar products are calculated only for those pairs of characters where the degree of the constituent is smaller than the degree of the reducible.

44.11 Tensored

Tensored( chars1 , chars2 )

returns the list of tensor products (i.e. pointwise products) of all characters in the list chars1 with all characters in the list chars2 .

    gap> t:= CharTable( "A5" );;
    gap> chars1:= Sublist( t.irreducibles, [ 1 .. 3 ] );;
    gap> chars2:= Sublist( t.irreducibles, [ 2 .. 3 ] );;
    gap> Tensored( chars1, chars2 );
    [ [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ],
      [ 9, 1, 0, -2*E(5)-E(5)^2-E(5)^3-2*E(5)^4,
          -E(5)-2*E(5)^2-2*E(5)^3-E(5)^4 ], [ 9, 1, 0, -1, -1 ],
      [ 9, 1, 0, -1, -1 ],
      [ 9, 1, 0, -E(5)-2*E(5)^2-2*E(5)^3-E(5)^4, -2*E(5)-E(5)^2-E(5)^3
             -2*E(5)^4 ] ]

Note that duplicate tensor products are not deleted; the tensor product of chars1 [i] with chars2 [j ] is stored at position (i − 1)Length(chars1 ) + j.

44.12 Symmetrisations

Symmetrisations( tbl, chars, Sn )
Symmetrisations( tbl, chars, n )

returns the list of nonzero symmetrisations of the characters chars, regarded as characters of the character table tbl, with the ordinary characters of the symmetric group of degree n; alternatively, the table of the symmetric group can be entered as Sn.

The symmetrisation χ^{[λ]} of the character χ of tbl with the character λ of the symmetric group S_{n} of degree n is defined by

χ^{[λ]}(g) = \frac{1}{n!} ∑_{ρ∈S_{n}} λ(ρ) ∏_{k=1}^{n} χ(g^{k})^{a_{k}(ρ)},

where a_{k}(ρ) is the number of cycles of length k in ρ.

For special symmetrisations, see 44.13, 44.14, 44.15 and 44.16, 44.17.

    gap> t:= CharTable( "A5" );;
    gap> chars:= Sublist( t.irreducibles, [ 1 .. 3 ] );;
    gap> Symmetrisations( t, chars, 3 );
    [ [ 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0 ], [ 1, 1, 1, 1, 1 ],
      [ 1, 1, 1, 1, 1 ], [ 8, 0, -1, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 10, -2, 1, 0, 0 ], [ 1, 1, 1, 1, 1 ],
      [ 8, 0, -1, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 10, -2, 1, 0, 0 ] ]

Note that the returned list may contain zero characters, and duplicate characters are not deleted.

44.13 SymmetricParts

SymmetricParts( tbl, chars, n )

returns the list of symmetrisations of the characters chars, regarded as characters of the character table tbl, with the trivial character of the symmetric group of degree n (see 44.12).

    gap> t:= CharTable( "A5" );;
    gap> SymmetricParts( t, t.irreducibles, 3 );
    [ [ 1, 1, 1, 1, 1 ], [ 10, -2, 1, 0, 0 ], [ 10, -2, 1, 0, 0 ],
      [ 20, 0, 2, 0, 0 ], [ 35, 3, 2, 0, 0 ] ]

44.14 AntiSymmetricParts

AntiSymmetricParts( tbl, chars, n )

returns the list of symmetrisations of the characters chars, regarded as characters of the character table tbl, with the alternating character of the symmetric group of degree n (see 44.12).

    gap> t:= CharTable( "A5" );;
    gap> AntiSymmetricParts( t, t.irreducibles, 3 );
    [ [ 0, 0, 0, 0, 0 ], [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 1, 1 ],
      [ 4, 0, 1, -1, -1 ], [ 10, -2, 1, 0, 0 ] ]

44.15 MinusCharacter

MinusCharacter( char, prime powermap, prime ) returns the (possibly parametrized, see chapter 45) character χ^{p−} for the character χ = char and a prime p = prime, where χ^{p−} is defined by χ^{p−}(g) = (χ(g)^{p} − χ(g^{p}))/p, and prime powermap is the (possibly parametrized) p-th powermap.

    gap> t:= CharTable( "S7" );; pow:= InitPowermap( t, 2 );;
    gap> Congruences( t, t.irreducibles, pow, 2 );; pow;
    [ 1, 1, 3, 4, [ 2, 9, 10 ], 6, 3, 8, 1, 1, [ 2, 9, 10 ], 3, 4, 6,
      [ 7, 12 ] ]
    gap> chars:= Sublist( t.irreducibles, [ 2 .. 5 ] );;
    gap> List( chars, x-> MinusCharacter( x, pow, 2 ) );
    [ [ 0, 0, 0, 0, [ 0, 1 ], 0, 0, 0, 0, 0, [ 0, 1 ], 0, 0, 0, [ 0, 1 ] ],
      [ 15, -1, 3, 0, [ -2, -1, 0 ], 0, -1, 1, 5, -3, [ 0, 1, 2 ], -1, 0,
          0, [ 0, 1 ] ],
      [ 15, -1, 3, 0, [ -1, 0, 2 ], 0, -1, 1, 5, -3, [ 1, 2, 4 ], -1, 0,
          0, 1 ],
      [ 190, -2, 1, 1, [ 0, 2 ], 0, 1, 1, -10, -10, [ 0, 2 ], -1, -1, 0,
          [ -1, 0 ] ] ]

44.16 OrthogonalComponents

OrthogonalComponents( tbl, chars, m )

If χ is a (nonlinear) character with indicator +1, a splitting of the tensor power χ^{m} is given by the so-called Murnaghan functions (see [Mur58]). These components in general have fewer irreducible constituents than the symmetrizations with the symmetric group of degree m (see 44.12).

OrthogonalComponents returns the set of orthogonal symmetrisations of the characters of the character table tbl in the list chars, up to the power m, where the integer m is one of {2, 3, 4, 5, 6}.

Note: It is not checked if all characters in chars do really have indicator +1; if there are characters with indicator 0 or −1, the result might contain virtual characters, see also 44.17.

The Murnaghan functions are implemented as in [Fra82].

    gap> t:= CharTable( "A8" );; chi:= t.irreducibles[2];
    [ 7, -1, 3, 4, 1, -1, 1, 2, 0, -1, 0, 0, -1, -1 ]
    gap> OrthogonalComponents( t, [ chi ], 4 );
    [ [ 21, -3, 1, 6, 0, 1, -1, 1, -2, 0, 0, 0, 1, 1 ],
      [ 27, 3, 7, 9, 0, -1, 1, 2, 1, 0, -1, -1, -1, -1 ],
      [ 105, 1, 5, 15, -3, 1, -1, 0, -1, 1, 0, 0, 0, 0 ],
      [ 35, 3, -5, 5, 2, -1, -1, 0, 1, 0, 0, 0, 0, 0 ],
      [ 77, -3, 13, 17, 2, 1, 1, 2, 1, 0, 0, 0, 2, 2 ],
      [ 189, -3, -11, 9, 0, 1, 1, -1, 1, 0, 0, 0, -1, -1 ],
      [ 330, -6, 10, 30, 0, -2, -2, 0, -2, 0, 1, 1, 0, 0 ],
      [ 168, 8, 8, 6, -3, 0, 0, -2, 2, -1, 0, 0, 1, 1 ],
      [ 35, 3, -5, 5, 2, -1, -1, 0, 1, 0, 0, 0, 0, 0 ],
      [ 182, 6, 22, 29, 2, 2, 2, 2, 1, 0, 0, 0, -1, -1 ] ]

44.17 SymplecticComponents

SymplecticComponents( tbl, chars, m )

If χ is a (nonlinear) character with indicator −1, a splitting of the tensor power χ^{m} is given in terms of the so-called Murnaghan functions (see [Mur58]). These components in general have fewer irreducible constituents than the symmetrizations with the symmetric group of degree m (see 44.12).

SymplecticComponents returns the set of symplectic symmetrisations of the characters of the character table tbl in the list chars, up to the power m, where the integer m is one of {2, 3, 4, 5}.

Note: It is not checked if all characters in chars do really have indicator −1; if there are characters with indicator 0 or +1, the result might contain virtual characters, see also 44.16.

    gap> t:= CharTable( "U3(3)" );; chi:= t.irreducibles[2];
    [ 6, -2, -3, 0, -2, -2, 2, 1, -1, -1, 0, 0, 1, 1 ]
    gap> SymplecticComponents( t, [ chi ], 4 );
    [ [ 14, -2, 5, -1, 2, 2, 2, 1, 0, 0, 0, 0, -1, -1 ],
      [ 21, 5, 3, 0, 1, 1, 1, -1, 0, 0, -1, -1, 1, 1 ],
      [ 64, 0, -8, -2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 ],
      [ 14, 6, -4, 2, -2, -2, 2, 0, 0, 0, 0, 0, -2, -2 ],
      [ 56, -8, 2, 2, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0 ],
      [ 70, -10, 7, 1, 2, 2, 2, -1, 0, 0, 0, 0, -1, -1 ],
      [ 189, -3, 0, 0, -3, -3, -3, 0, 0, 0, 1, 1, 0, 0 ],
      [ 90, 10, 9, 0, -2, -2, -2, 1, -1, -1, 0, 0, 1, 1 ],

      [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
      [ 126, 14, -9, 0, 2, 2, 2, -1, 0, 0, 0, 0, -1, -1 ] ]

44.18 IrreducibleDifferences

IrreducibleDifferences( tbl, chars1 , chars2 )
IrreducibleDifferences( tbl, chars1 , chars2 , scprmat )
IrreducibleDifferences( tbl, chars, "triangle")
IrreducibleDifferences( tbl, chars, "triangle", scprmat )

returns the list of irreducible characters which occur as difference of two elements of chars (if "triangle" is specified) or of an element of chars1 and an element of chars2 ; if scprmat is not specified it will be computed (see 44.2), otherwise we must have

scprmat[i][j] = ScalarProduct(tbl, chars[i], chars[j])

resp.

scprmat[i][j] = ScalarProduct(tbl, chars1 [i], chars2 [j])

.

    gap> t:= CharTable( "A5" );;
    gap> chars:= Sublist( t.irreducibles, [ 2 .. 4 ] );;
    gap> chars:= Set( Tensored( chars, chars ) );;
    gap> IrreducibleDifferences( t, chars, "triangle" );
    [ [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ] ]

44.19 Restricted

Restricted( tbl, subtbl, chars )

Restricted( tbl, subtbl, chars, specification ) Restricted( chars, fusionmap )

returns the restrictions, i.e. the indirections, of the characters in the list chars by a subgroup fusion map. This map can either be entered directly as fusionmap, or it must be stored on the character table subtbl and must have destination tbl; in the latter case the value of the specification field of the desired fusion may be specified as specification (see 42.42). If no such fusion is stored, false is returned.

The fusion map may be a parametrized map (see 45.1); any value that is not uniquely determined in a restricted character is set to an unknown (see 16.1); for parametrized indirection of characters, see 45.2.

Restriction and inflation are the same procedures, so Restricted and Inflated are identical, see 44.20.

    gap> s5:= CharTable( "A5.2" );; a5:= CharTable( "A5" );;
    gap> Restricted( s5, a5, s5.irreducibles );
    [ [ 1, 1, 1, 1, 1 ], [ 1, 1, 1, 1, 1 ], [ 6, -2, 0, 1, 1 ],
      [ 4, 0, 1, -1, -1 ], [ 4, 0, 1, -1, -1 ], [ 5, 1, -1, 0, 0 ],
      [ 5, 1, -1, 0, 0 ] ]
    gap> Restricted( s5.irreducibles, [ 1, 6, 2, 6 ] );

# restrictions to the cyclic group of order 4

    [ [ 1, 1, 1, 1 ], [ 1, -1, 1, -1 ], [ 6, 0, -2, 0 ], [ 4, 0, 0, 0 ],
      [ 4, 0, 0, 0 ], [ 5, -1, 1, -1 ], [ 5, 1, 1, 1 ] ]

44.20 Inflated

Inflated( factortbl, tbl, chars ) Inflated( factortbl, tbl, chars, specification ) Inflated( chars, fusionmap ) returns the inflations, i.e. the indirections of chars by a factor fusion map. This map can either be entered directly as fusionmap, or it must be stored on the character table tbl and must have destination factortbl; in the latter case the value of the specification field of the desired fusion may be specified as specification (see 42.42). If no such fusion is stored, false is returned. The fusion map may be a parametrized map (see 45.1); any value that is not uniquely determined in an inflated character is set to an unknown (see 16.1); for parametrized indirection of characters, see 45.2. Restriction and inflation are the same procedures, so Restricted and Inflated are identical, see 44.19.

    gap> s4:= CharTable( "Symmetric", 4 );;
    gap> s3:= CharTableFactorGroup( s4, [3] );;
    gap> s3.irreducibles;
    [ [ 1, -1, 1 ], [ 2, 0, -1 ], [ 1, 1, 1 ] ]
    gap> s4.fusions;
    [ rec(
          map := [ 1, 2, 1, 3, 2 ],
          type := "factor",
          name := "S4modulo[ 3 ]" ) ]
    gap> Inflated( s3, s4, s3.irreducibles );
    [ [ 1, -1, 1, 1, -1 ], [ 2, 0, 2, -1, 0 ], [ 1, 1, 1, 1, 1 ] ]

44.21 Induced

Induced( subtbl, tbl, chars ) Induced( subtbl, tbl, chars, specification ) Induced( subtbl, tbl, chars, fusionmap ) returns a set of characters induced from subtbl to tbl; the elements of the list chars will be induced. The subgroup fusion map can either be entered directly as fusionmap, or it must be stored on the table subtbl and must have destination tbl; in the latter case the value of the specification field may be specified by specification (see 42.42). If no such fusion is stored, false is returned. The fusion map may be a parametrized map (see 45.1); any value that is not uniquely determined in an induced character is set to an unknown (see 16.1).

    gap> Induced( a5, s5, a5.irreducibles );
    [ [ 2, 2, 2, 2, 0, 0, 0 ], [ 6, -2, 0, 1, 0, 0, 0 ],
      [ 6, -2, 0, 1, 0, 0, 0 ], [ 8, 0, 2, -2, 0, 0, 0 ],
      [ 10, 2, -2, 0, 0, 0, 0 ] ]

44.22 InducedCyclic

InducedCyclic( tbl )
InducedCyclic( tbl, "all")
InducedCyclic( tbl, classes )
InducedCyclic( tbl, classes, "all")

returns a set of characters of the character table tbl. They are characters induced from cyclic subgroups of tbl. If "all" is specified, all irreducible characters of those subgroups are induced, otherwise only the permutation characters are computed. If a list classes is specified, only those cyclic subgroups generated by these classes are considered, otherwise all classes of tbl are considered.

Note that the powermaps for primes dividing tbl.order must be stored on tbl; if any powermap for a prime not dividing tbl.order that is smaller than the maximal representative order is not stored, this map will be computed (see 45.12) and stored afterwards.

The powermaps may be parametrized maps (see 45.1); any value that is not uniquely determined in an induced character is set to an unknown (see 16.1). The representative orders of the classes to induce from must not be parametrized (see 45.1).

    gap> t:= CharTable( "A5" );; InducedCyclic( t, "all" );
    [ [ 12, 0, 0, 2, 2 ], [ 12, 0, 0, E(5)^2+E(5)^3, E(5)+E(5)^4 ],
      [ 12, 0, 0, E(5)+E(5)^4, E(5)^2+E(5)^3 ], [ 20, 0, -1, 0, 0 ],
      [ 20, 0, 2, 0, 0 ], [ 30, -2, 0, 0, 0 ], [ 30, 2, 0, 0, 0 ],
      [ 60, 0, 0, 0, 0 ] ]

44.23 CollapsedMat

CollapsedMat( mat, maps )

returns a record with fields mat and fusion: The fusion field contains the fusion that collapses the columns of mat that are identical also for all maps in the list maps, the mat field contains the image of mat under that fusion.

    gap> t.irreducibles;
    [ [ 1, 1, 1, 1, 1 ], [ 3, -1, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ],
      [ 3, -1, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]
    gap> t:= CharTable( "A5" );; RationalizedMat( t.irreducibles );
    [ [ 1, 1, 1, 1, 1 ], [ 6, -2, 0, 1, 1 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]
    gap> CollapsedMat( last, [] );
    rec(
      mat := [ [ 1, 1, 1, 1 ], [ 6, -2, 0, 1 ], [ 4, 0, 1, -1 ],
          [ 5, 1, -1, 0 ] ],
      fusion := [ 1, 2, 3, 4, 4 ] )
    gap> Restricted( last.mat, last.fusion );
    [ [ 1, 1, 1, 1, 1 ], [ 6, -2, 0, 1, 1 ], [ 4, 0, 1, -1, -1 ],
      [ 5, 1, -1, 0, 0 ] ]

44.24 Power

Power( powermap, chars, n )

returns the list of indirections of the characters chars by the n-th powermap; for a character χ in chars, this indirection is often called χ^{(n)}. The powermap is calculated from the (necessarily stored) powermaps of the prime divisors of n if it is not stored in powermap (see 45.30).

Note that χ^{(n)} is in general only a virtual characters.

    gap> t:= CharTable( "A5" );; Power( t.powermap, t.irreducibles, 2 );
    [ [ 1, 1, 1, 1, 1 ], [ 3, 3, 0, -E(5)^2-E(5)^3, -E(5)-E(5)^4 ],
      [ 3, 3, 0, -E(5)-E(5)^4, -E(5)^2-E(5)^3 ], [ 4, 4, 1, -1, -1 ],
      [ 5, 5, -1, 0, 0 ] ]
    gap> MatScalarProducts( t, t.irreducibles, last );
    [ [ 1, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 1 ], [ 1, 0, -1, 0, 1 ],
      [ 1, -1, -1, 1, 1 ], [ 1, -1, -1, 0, 2 ] ]

44.25 Permutation Character Candidates

For groups H, G with H ≤ G, the induced character (1_{G})^{H} is called the permutation character of the operation of G on the right cosets of H. If only the character table of G is known, one can try to get informations about possible subgroups of G by inspection of those characters π which might be permutation characters, using that such a character must have at least the following properties:

π(1) divides |G|,

[π, ψ] ≤ ψ(1) for each character ψ of G,

[π, 1_{G}] = 1,

π(g) is a nonnegative integer for g ∈ G,

π(g) is smaller than the centralizer order of g for 1 ≠ g ∈ G,

π(g) ≤ π(g^{m}) for g ∈ G and any integer m,

π(g) = 0 for every |g| not diving \frac{|G|}{π(1)},

π(1)|N_{G}(g)| divides |G|π(g), where |N_{G}(g)| denotes the normalizer order of ⟨g⟩.

Any character with these properties will be called a permutation character candidate from now on.

GAP provides some algorithms to compute permutation character candidates, see 44.30. Some information about the subgroup can computed from a permutation character using 44.27.

44.26 IsPermChar

IsPermChar( tbl, pi )
missing, like tests TestPerm1, TestPerm2, TestPerm3

44.27 PermCharInfo

PermCharInfo( tbl, permchars )

Let tbl be the character table of the group G, and permchars the permutation character (1_{U})^{G} for a subgroup U of G, or a list of such characters. PermCharInfo returns a record with components

contained

a list containing for each character in permchars a list containing at position i the number of elements of U that are contained in class i of tbl, this is equal to

permchar[i]|U|/tbl.centralizers[i],
bound

Let permchars[k] be the permutation character (1_{U})^{G}. Then the class length in U of an element in class i of tbl must be a multiple of the value bound[k][i] = |U|/ gcd(|U|, tbl.centralizers[i]),

display

a record that can be used as second argument of DisplayCharTable to display the permutation characters and the corresponding components contained and bound, for the classes where at least one character of permchars is nonzero,

ATLAS

list of strings containing for each character in permchars the decomposition into tbl.irreducibles in ATLAS notation.

gap> t:= CharTable("A6");;
gap> PermCharInfo( t, [ 15, 3, 0, 3, 1, 0, 0 ] );
rec(
 contained := [ [ 1, 9, 0, 8, 6, 0, 0 ] ],
 bound := [ [ 1, 3, 8, 8, 6, 24, 24 ] ],
 display := rec(
     classes := [ 1, 2, 4, 5 ],
     chars := [ [ 15, 3, 0, 3, 1, 0, 0 ], [ 1, 9, 0, 8, 6, 0, 0 ],
         [ 1, 3, 8, 8, 6, 24, 24 ] ],
     letter := "I" ),
 ATLAS := [ "1a+5b+9a" ] )
gap> DisplayCharTable( t, last.display );
A6
  2  3  3  .  2
  3  2  .  2  .
  5  1  .  .  .
    1a 2a 3b 4a
 2P 1a 1a 3b 2a
 3P 1a 2a 1a 4a
 5P 1a 2a 3b 4a
I.1  15  3  3  1
I.2   1  9  8  6

    I.3   1  3  8  6

44.28 Inequalities

Inequalities( tbl )

The condition π(g) ≥ 0 for every permutation character candidate π places restrictions on the multiplicities a_{i} of the irreducible constituents χ_{i} of π = ∑_{i=1}^{r} a_{i}χ_{i}. For every group element g holds ∑_{i=1}^{r} a_{i}χ_{i}(g) ≥ 0. The power map provides even stronger conditions.

This system of inequalities is kind of diagonalized, resulting in a system of inequalities restricting a_{i} in terms of a_{j}, j < i. These inequalities are used to construct characters with nonnegative values (see 44.30). PermChars either calls Inequalities or takes this information from the record field ineq of its argument record.

The number of inequalities arising in the process of diagonalization may grow very strong.

There are two strategies to perform this diagonalization. The default is to simply eliminate one unknown a_{i} after the other with decreasing i. In some cases it turns out to be better first to look which choice for the next unknown will yield the fewest new inequalities.

44.29 PermBounds

PermBounds( tbl )

All characters π satisfying π(g) > 0 and π(1) = d for a given degree d lie in a simplex described by these conditions. PermBounds computes the boundary points of this simplex for d = 0, from which the boundary points for any other d are easily derived. Some conditions from the powermap are also involved.

For this purpose a matrix similar to the rationalized character table has to be inverted.

These boundary points are used by PermChars (see 44.30) to construct all permutation character candidates of a given degree. PermChars either calls PermBounds or takes this information from the record field bounds of its argument record.

44.30 PermChars

PermChars( tbl )
PermChars( tbl, degree )
PermChars( tbl, arec )

GAP provides several algorithms to determine permutation character candidates from a given character table. The algorithm is selected from the choice of the record fields of the optional argument record arec. The user is encouraged to try different approaches especially if one choice fails to come to an end.

Regardless of the algorithm used in a special case, PermChars returns a list of all permutation character candidates with the properties given in arec. There is no guarantee that a character of this list is in fact a permutation character. But an empty list always means there is no permutation character with these properties (e.g. of a certain degree).

In the first form PermChars( tbl ) returns the list of all permutation characters of the group with character table tbl. This list might be rather long for big groups, and it might take much time. The algorithm depends on a preprocessing step, where the inequalities arising from the condition π(g) ≤ 0 are transformed into a system of inequalities that guides the search (see 44.28).

    gap> m11:= CharTable("M11");;

gap> PermChars(m11);; # will return the list of 39 permutation

# character candidates of M11.

There are two different search strategies for this algorithm. One simply constructs all characters with nonnegative values and then tests for each such character whether its degree is a divisor of the order of the group. This is the default. The other strategy uses the inequalities to predict if it is possible to find a character of a certain degree in the currently searched part of the search tree. To choose this strategy set the field mode of arec to "preview" and the field degree to the degree (or a list of degrees which might be all divisors of the order of the group) you want to look for. The record field ineq can take the inequalities from Inequalities if they are needed more than once. In the second form PermChars( tbl, degree ) returns the list of all permutation characters of degree degree. For that purpose a preprocessing step is performed where essentially the rationalized character table is inverted in order to determine boundary points for the simplex in which the permutation character candidates of a given degree must lie (see 44.29). Note that inverting big integer matrices needs a lot of time and space. So this preprocessing is restricted to groups with less than 100 classes, say.

    gap> PermChars(m11, 220);
    [ [ 220, 4, 4, 0, 0, 4, 0, 0, 0, 0 ],
      [ 220, 12, 4, 4, 0, 0, 0, 0, 0, 0 ],
      [ 220, 20, 4, 0, 0, 2, 0, 0, 0, 0 ] ]

In the third form PermChars( tbl, arec ) returns the list of all permutation characters which have the properties given in the argument record arec. If arec contains a degree in the record field degree then PermChars will behave exactly as in the second form.

    gap> PermChars(m11, rec(degree:= 220));
    [ [ 220, 4, 4, 0, 0, 4, 0, 0, 0, 0 ],
      [ 220, 12, 4, 4, 0, 0, 0, 0, 0, 0 ],
      [ 220, 20, 4, 0, 0, 2, 0, 0, 0, 0 ] ]

Alternatively arec may have the record fields chars and torso. arec.chars is a list of (in most cases all) rational irreducible characters of tbl which might be constituents of the required characters, and arec.torso is a list that contains some known values of the required characters at the right positions. Note: At least the degree arec.torso[1] must be an integer. If arec.chars does not contain all rational irreducible characters of G, it may happen that any scalar product of π with an omitted character is negative; there should be nontrivial reasons for excluding a character that is known to be not a constituent of π.

    gap> rat:= RationalizedMat(m11.irreducibles);;
    gap> PermChars(m11, rec(torso:= [220], chars:= rat));
    [ [ 220, 4, 4, 0, 0, 4, 0, 0, 0, 0 ],
      [ 220, 20, 4, 0, 0, 2, 0, 0, 0, 0 ],
      [ 220, 12, 4, 4, 0, 0, 0, 0, 0, 0 ] ]
    gap> PermChars(m11, rec(torso:= [220,,,,,2], chars:= rat));
    [ [ 220, 20, 4, 0, 0, 2, 0, 0, 0, 0 ] ]

44.31 Faithful Permutation Characters

PermChars( tbl, arec )

PermChars may as well determine faithful candidates for permutation characters. In that

case arec requires the fields normalsubgrp, nonfaithful, chars, lower, upper, and torso.

Let tbl be the character table of the group G, arec.normalsubgrp a list of classes forming a normal subgroup N of G. arec.nonfaithful is a permutation character candidate (see 44.25) of G with kernel N. arec.chars is a list of (in most cases all) rational irreducible characters of tbl.

PermChars computes all those permutation character candidates π having following properties:

arec.chars contains every rational irreducible constituent of π.

π[i] ≥ arec.lower[i] for all integer values of the list arec.lower.

π[i] ≤ arec.upper[i] for all integer values of the list arec.upper.

π[i] = arec.torso[i] for all integer values of the list arec.torso.

No irreducible constituent of π − arec.nonfaithful has N in its kernel.

If there exists a subgroup V of G, V ≥ N, with nonfaithful = (1_{V})^{G}, the last condition means that the candidates for those possible subgroups U with V = UN are constructed.

Note:At least the degree torso[1] must be an integer. If chars does not contain all rational irreducible characters of G, it may happen that any scalar product of π with an omitted character is negative; there should be nontrivial reasons for excluding a character that is known to be not a constituent of π.

44.32 LLLReducedBasis

LLLReducedBasis([L],vectors[,y][,"linearcomb"])

LLLReducedBasis provides an implementation of the LLL algorithm by Lenstra, Lenstra and Lovász (see [LLL82], [Poh87]). The implementation follows the description on pages 94f. in [Coh93].

LLLReducedBasis returns a record whose component basis is a list of LLL reduced linearly independent vectors spanning the same lattice as the list vectors.

L must be a lattice record whose scalar product function is stored in the component

operations.NoMessageScalarProduct or operations.ScalarProduct. It must be a func-

tion of three arguments, namely the lattice and the two vectors. If no lattice L is given the standard scalar product is taken.

In the case of option "linearcomb", the record contains also the components relations and transformation, which have the following meaning. relations is a basis of the relation space of vectors, i.e., of vectors x such that x *vectors is zero. transformation gives the expression of the new lattice basis in terms of the old, i.e., transformation *vectors equals the basis component of the result.

Another optional argument is y, the “sensitivity”of the algorithm, a rational number between \frac{1}{4} and 1 (the default value is \frac{3}{4}).

(The function 44.33 computes an LLL reduced Gram matrix.)

    gap> vectors:= [ [ 9, 1, 0, -1, -1 ], [ 15, -1, 0, 0, 0 ],
    >                [ 16, 0, 1, 1, 1 ], [ 20, 0, -1, 0, 0 ],
    >                [ 25, 1, 1, 0, 0 ] ];;
    gap> LLLReducedBasis( vectors, "linearcomb" );
    rec(
      basis :=
       [ [ 1, 1, 1, 1, 1 ], [ 1, 1, -2, 1, 1 ], [ -1, 3, -1, -1,
              -1 ], [ -3, 1, 0, 2, 2 ] ],
      relations := [ [ -1, 0, -1, 0, 1 ] ],
      transformation :=
       [ [ 0, -1, 1, 0, 0 ], [ -1, -2, 0, 2, 0 ], [ 1, -2, 0, 1, 0
             ], [ -1, -2, 1, 1, 0 ] ] )

44.33 LLLReducedGramMat

LLLReducedGramMat( G [,y] )

LLLReducedGramMat provides an implementation of the LLL algorithm by Lenstra, Lenstra and Lovász (see [LLL82], [Poh87]). The implementation follows the description on pages 94f. in [Coh93].

Let G the Gram matrix of the vectors (b_{1}, b_{2}, …, b_{n}); this means G is either a square symmetric matrix or lower triangular matrix (only the entries in the lower triangular half are used by the program).

LLLReducedGramMat returns a record whose component remainder is the Gram matrix of the LLL reduced basis corresponding to (b_{1}, b_{2}, …, b_{n}). If G was a lower triangular matrix then also the remainder component is a lower triangular matrix.

The result record contains also the components relations and transformation, which have the following meaning.

relations is a basis of the space of vectors (x_{1}, x_{2}, …, x_{n}) such that ∑_{i=1}^{n} x_{i}b_{i} is zero, and transformation gives the expression of the new lattice basis in terms of the old, i.e., transformation is the matrix T such that T · G · T^{tr} is the remainder component of the result.

The optional argument y denotes the “sensitivity of the algorithm, it must be a rational number between \frac{1}{4} and 1; the default value is y = \frac{3}{4}.

(The function 44.32 computes an LLL reduced basis.)

    gap> g:= [ [ 4, 6, 5, 2, 2 ], [ 6, 13, 7, 4, 4 ],
    >    [ 5, 7, 11, 2, 0 ], [ 2, 4, 2, 8, 4 ], [ 2, 4, 0, 4, 8 ] ];;
    gap> LLLReducedGramMat( g );
    rec(
      remainder :=
       [ [ 4, 2, 1, 2, -1 ], [ 2, 5, 0, 2, 0 ], [ 1, 0, 5, 0, 2 ],
          [ 2, 2, 0, 8, 2 ], [ -1, 0, 2, 2, 7 ] ],
      relation := [  ],
      transformation :=
       [ [ 1, 0, 0, 0, 0 ], [ -1, 1, 0, 0, 0 ], [ -1, 0, 1, 0, 0 ],

          [ 0, 0, 0, 1, 0 ], [ -2, 0, 1, 0, 1 ] ],
      scalarproducts := [ , [ 1/2 ], [ 1/4, -1/8 ], [ 1/2, 1/4, -2/25 ],
          [ -1/4, 1/8, 37/75, 8/21 ] ],
      bsnorms := [ 4, 4, 75/16, 168/25, 32/7 ] )

44.34 LLL

LLL( tbl, characters [, y] [, "sort"] [, "linearcomb"] )

calls the LLL algorithm (see 44.32) in the case of lattices spanned by (virtual) characters characters of the character table tbl (see 44.1). By finding shorter vectors in the lattice spanned by characters, i.e. virtual characters of smaller norm, in some cases LLL is able to find irreducible characters.

LLL returns a record with at least components irreducibles (the list of found irreducible characters), remainders (a list of reducible virtual characters), and norms (the list of norms of remainders). irreducibles together with remainders span the same lattice as characters.

There are some optional parameters:

y controls the sensitivity of the algorithm; the value of y must be between 1/4 and 1, the default value is 3/4.

"sort"

LLL sorts characters and the remainders component of the result according to the degrees.

"linearcomb"

The returned record contains components irreddecomp and reddecomp which are decomposition matrices of irreducibles and remainders, with respect to characters.

    gap> s4:= CharTable( "Symmetric", 4 );;
    gap> chars:= [ [ 8, 0, 0, -1, 0 ], [ 6, 0, 2, 0, 2 ],
    >     [ 12, 0, -4, 0, 0 ], [ 6, 0, -2, 0, 0 ], [ 24, 0, 0, 0, 0 ],
    >     [ 12, 0, 4, 0, 0 ], [ 6, 0, 2, 0, -2 ], [ 12, -2, 0, 0, 0 ],
    >     [ 8, 0, 0, 2, 0 ], [ 12, 2, 0, 0, 0 ], [ 1, 1, 1, 1, 1 ] ];;
    gap> LLL( s4, chars );
    rec(
      irreducibles :=
       [ [ 2, 0, 2, -1, 0 ], [ 1, 1, 1, 1, 1 ], [ 3, 1, -1, 0, -1 ],
          [ 3, -1, -1, 0, 1 ], [ 1, -1, 1, 1, -1 ] ],
      remainders := [  ],
      norms := [  ] )

44.35 OrthogonalEmbeddings

OrthogonalEmbeddings( G [, "positive"] [, maxdim ] )

computes all possible orthogonal embeddings of a lattice given by its Gram matrix G which must be a regular matrix (see 44.33). In other words, all solutions X of the problem

X^{tr}X = G

are calculated (see [Ple90]). Usually there are many solutions X but all their rows are chosen from a small set of vectors, so OrthogonalEmbeddings returns the solutions in an encoded form, namely as a record with components

vectors

the list [x_{1}, x_{2}, …, x_{n}] of vectors that may be rows of a solution; these are exactly those vectors that fulfill the condition x_{i}G^{−1}x_{i}^{tr} ≤ 1 (see 44.36), and we have G = ∑_{i=1}^{n} x_{i}^{tr}x_{i},

norms the list of values x_{i}G^{−1}x_{i}^{tr}, and

solutions

a list S of lists; the i–th solution matrix is Sublist( L, S[i] ), so the dimension of the i–th solution is the length of S[i].

The optional argument "positive" will cause OrthogonalEmbeddings to compute only vectors x_{i} with nonnegative entries. In the context of characters this is allowed (and useful) if G is the matrix of scalar products of ordinary characters. When OrthogonalEmbeddings is called with the optional argument maxdim (a positive integer), it computes only solutions up to dimension maxdim; this will accelerate the algorithm in some cases. G may be the matrix of scalar products of some virtual characters. From the characters and the embedding given by the matrix X, Decreased (see 44.38) may be able to compute irreducibles.

    gap> b := [ [  3, -1, -1 ], [ -1,  3, -1 ], [ -1, -1,  3 ] ];;
    gap> c:=OrthogonalEmbeddings(b);
    rec(
      vectors :=
       [ [ -1, 1, 1 ], [ 1, -1, 1 ], [ -1, -1, 1 ], [ -1, 1, 0 ],
          [ -1, 0, 1 ], [ 1, 0, 0 ], [ 0, -1, 1 ], [ 0, 1, 0 ],
          [ 0, 0, 1 ] ],
      norms := [ 1, 1, 1, 1/2, 1/2, 1/2, 1/2, 1/2, 1/2 ],
      solutions := [ [ 1, 2, 3 ], [ 1, 6, 6, 7, 7 ], [ 2, 5, 5, 8, 8 ],
          [ 3, 4, 4, 9, 9 ], [ 4, 5, 6, 7, 8, 9 ] ] )
    gap> Sublist( c.vectors, c.solutions[1] );
    [ [ -1, 1, 1 ], [ 1, -1, 1 ], [ -1, -1, 1 ] ]
OrthogonalEmbeddingsSpecialDimension

( tbl, reducibles, grammat [, "positive"], dim )

This form can be used if you want to find irreducible characters of the table tbl, where reducibles is a list of virtual characters, grammat is the matrix of their scalar products, and dim is the maximal dimension of an embedding. First all solutions up to dim are compute, and then 44.38 Decreased is called in order to find irreducible characters of tab. If reducibles consists of ordinary characters only, you should enter the optional argument "positive"; this imposes some conditions on the possible embeddings (see the description

of OrthogonalEmbeddings).

OrthogonalEmbeddingsSpecialDimension returns a record with components

irreducibles a list of found irreducibles, the intersection of all lists of irreducibles

found by Decreased, for all possible embeddings, and remainders a list of remaining reducible virtual characters

    gap> s6:= CharTable( "Symmetric", 6 );;
    gap> b:= InducedCyclic( s6, "all" );;
    gap> Add( b, [1,1,1,1,1,1,1,1,1,1,1] );
    gap> c:= LLL( s6, b ).remainders;;
    gap> g:= MatScalarProducts( s6, c, c );;
    gap> d:= OrthogonalEmbeddingsSpecialDimension( s6, c, g, 8 );
    rec(
      irreducibles :=
       [ [ 5, -3, 1, 1, 2, 0, -1, -1, -1, 0, 1 ], [ 5, 1, 1, -3, -1, 1,
              2, -1, -1, 0, 0 ], [ 10, -2, -2, 2, 1, 1, 1, 0, 0, 0, -1 ],
          [ 10, 2, -2, -2, 1, -1, 1, 0, 0, 0, 1 ] ],
      remainders :=
       [ [ 0, 4, 0, -4, 3, 1, -3, 0, 0, 0, -1 ], [ 4, 0, 0, 4, -2, 0, 1,
              -2, 2, -1, 1 ], [ 6, 2, 2, -2, 3, -1, 0, 0, 0, 1, -2 ],
          [ 14, 6, 2, 2, 2, 0, -1, 0, 0, -1, -1 ] ] )

44.36 ShortestVectors

ShortestVectors( G, m )
ShortestVectors( G, m, "positive")

computes all vectors x with xGx^{tr} ≤ m, where G is a matrix of a symmetric bilinear form, and m is a nonnegative integer. If the optional argument "positive" is entered, only those vectors x with nonnegative entries are computed. ShortestVectors returns a record with components vectors the list of vectors x, and norms the list of their norms according to the Gram matrix G.

    gap> g:= [ [ 2, 1, 1 ], [ 1, 2, 1 ], [ 1, 1, 2 ] ];;
    gap> ShortestVectors(g,4);
    rec(
      vectors := [ [ -1, 1, 1 ], [ 0, 0, 1 ], [ -1, 0, 1 ], [ 1, -1, 1 ],
          [ 0, -1, 1 ], [ -1, -1, 1 ], [ 0, 1, 0 ], [ -1, 1, 0 ],
          [ 1, 0, 0 ] ],
      norms := [ 4, 2, 2, 4, 2, 4, 2, 2, 2 ] )

This algorithm is used in 44.35 OrthogonalEmbeddings.

44.37 Extract

Extract( tbl, reducibles, grammat ) Extract( tbl, reducibles, grammat, missing ) tries to find irreducible characters by drawing conclusions out of a given matrix grammat of scalar products of the reducible characters in the list reducibles, which are characters of the table tbl. Extract uses combinatorial and backtrack means.

Note: Extract works only with ordinary characters!

missing number of characters missing to complete the tbl perhaps Extract may be accelerated by the specification of missing.

Extract returns a record extr with components solution and choice where solution is a list [M_{1}, …, M_{n}] of decomposition matrices that satisfy the equation

M_{i}^{tr} · X = Sublist(reducibles, extr.choice[i]) ,

for a matrix X of irreducible characters, and choice is a list of length n whose entries are lists of indices.

So each column stands for one of the reducible input characters, and each row stands for an irreducible character. You can use 44.38 Decreased to examine the solution for computable irreducibles.

    gap> s4 := CharTable( "Symmetric", 4 );;
    gap> y := [ [ 5, 1, 5, 2, 1 ], [ 2, 0, 2, 2, 0 ], [ 3, -1, 3, 0, -1 ],
    >  [ 6, 0, -2, 0, 0 ], [ 4, 0, 0, 1, 2 ] ];;
    gap> g := MatScalarProducts( s4, y, y );
    [ [ 6, 3, 2, 0, 2 ], [ 3, 2, 1, 0, 1 ], [ 2, 1, 2, 0, 0 ],
      [ 0, 0, 0, 2, 1 ], [ 2, 1, 0, 1, 2 ] ]
    gap> e:= Extract( s4, y, g, 5 );
    rec(
      solution :=
       [ [ [ 1, 1, 0, 0, 2 ], [ 1, 0, 1, 0, 1 ], [ 0, 1, 0, 1, 0 ],
              [ 0, 0, 1, 0, 1 ], [ 0, 0, 0, 1, 0 ] ] ],
      choice := [ [ 2, 5, 3, 4, 1 ] ] )

# continued in Decreased ( see 44.38 )

44.38 Decreased

Decreased( tbl, reducibles, mat ) Decreased( tbl, reducibles, mat, choice )

tries to solve the output of 44.35 OrthogonalEmbeddings or 44.37 Extract in order to find irreducible characters. tbl must be a character table, reducibles the list of characters used for the call of OrtgogonalEmbeddings or Extract, mat one solution, and in the case of a solution returned by Extract, choice must be the corresponding choice component.

Decreased returns a record with components

irreducibles

the list of found irreducible characters,

remainders

the remaining reducible characters, and

matrix

the decomposition matrix of the characters in the remainders component, which could not be solved.

# see example in 44.37 Extract

    gap> d := Decreased( s4, y, e.solution[1], e.choice[1] );

    rec(
      irreducibles :=
       [ [ 1, 1, 1, 1, 1 ], [ 3, -1, -1, 0, 1 ], [ 1, -1, 1, 1, -1 ],
          [ 3, 1, -1, 0, -1 ], [ 2, 0, 2, -1, 0 ] ],
      remainders := [  ],
      matrix := [  ] )

44.39 DnLattice

DnLattice( tbl, grammat, reducibles )

tries to find sublattices isomorphic to root lattices of type D_{n} (for n ≥ 5 or n = 4) in a lattice that is generated by the norm 2 characters reducibles, which must be characters of the table tbl. grammat must be the matrix of scalar products of reducibles, i.e., the Gram matrix of the lattice.

DnLattice is able to find irreducible characters if there is a lattice with n > 4. In the case n = 4 DnLattice only in some cases finds irreducibles.

DnLattice returns a record with components

irreducibles

the list of found irreducible characters,

remainders

the list of remaining reducible characters, and

gram

the Gram matrix of the characters in remainders.

The remaining reducible characters are transformed into a normalized form, so that the lattice-structure is cleared up for further treatment. So DnLattice might be useful even if it fails to find irreducible characters.

    gap> tbl:= CharTable( "Symmetric", 4 );;
    gap> y1:=[ [ 2, 0, 2, 2, 0 ], [ 4, 0, 0, 1, 2 ], [ 5, -1, 1, -1, 1 ],
    >          [ -1, 1, 3, -1, -1 ] ];;
    gap> g1:= MatScalarProducts( tbl, y1, y1 );
    [ [ 2, 1, 0, 0 ], [ 1, 2, 1, -1 ], [ 0, 1, 2, 0 ], [ 0, -1, 0, 2 ] ]
    gap> e:= DnLattice( tbl, g1, y1 );
    rec(
      gram := [  ],
      remainders := [  ],
      irreducibles :=
       [ [ 2, 0, 2, -1, 0 ], [ 1, -1, 1, 1, -1 ], [ 1, 1, 1, 1, 1 ],
          [ 3, -1, -1, 0, 1 ] ] )

DnLatticeIterative( tbl, arec )

was made for iterative use of DnLattice. arec must be either a list of characters of the table tbl, or a record with components

remainders

a list of characters of the character table tbl, and

norms

the norms of the characters in remainders,

e.g., a record returned by 44.34 LLL. DnLatticeIterative will select the characters of norm 2, call DnLattice, reduce the characters with found irreducibles, call DnLattice for the remaining characters, and so on, until no new irreducibles are found.

DnLatticeIterative returns (like 44.34 LLL) a record with components

irreducibles

the list of found irreducible characters,

remainders

the list of remaining reducible characters, and

norms

the list of norms of the characters in remainders.

    gap> tbl:= CharTable( "Symmetric", 4 );;
    gap> y1:= [ [ 2, 0, 2, 2, 0 ], [ 4, 0, 0, 1, 2 ],
    >   [ 5, -1, 1, -1, 1 ], [ -1, 1, 3, -1, -1 ], [ 6, -2, 2, 0, 0 ] ];;
    gap> DnLatticeIterative( tbl, y1);
    rec(
      irreducibles :=
       [ [ 2, 0, 2, -1, 0 ], [ 1, -1, 1, 1, -1 ], [ 1, 1, 1, 1, 1 ],
          [ 3, -1, -1, 0, 1 ] ],
      remainders := [  ],
      norms := [  ] )

44.40 ContainedDecomposables

ContainedDecomposables( constituents, moduls, parachar, func )

For a list of rational characters constituents and a parametrized rational character parachar (see 45.1), the set of all elements χ of parachar is returned that satisfy func(χ) (i.e., for that true is returned) and that “modulo moduls lie in the lattice spanned by constituents”. This means they lie in the lattice spanned by constituents and the set {moduls[i] · e_{i}; 1 ≤ i ≤ n}, where n is the length of parachar and e_{i} is the i-th vector of the standard base.

    gap> hs:= CharTable("HS");; s:= CharTable("HSM12");; s.name;
    "5:4xa5"
    gap> rat:= RationalizedMat(s.irreducibles);;
    gap> fus:= InitFusion( s, hs );
    [ 1, [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], 4, 5, 5, [ 5, 6, 7 ], [ 5, 6, 7 ],
      9, [ 8, 9 ], [ 8, 9 ], [ 8, 9, 10 ], [ 8, 9, 10 ], [ 11, 12 ],
      [ 17, 18 ], [ 17, 18 ], [ 17, 18 ], 21, 21, 22, [ 23, 24 ],
      [ 23, 24 ], [ 23, 24 ], [ 23, 24 ] ]

# restrict a rational character of hs by fus,

# see chapter 45:

    gap> rest:= CompositionMaps( hs.irreducibles[8], fus );
    [ 231, [ -9, 7 ], [ -9, 7 ], [ -9, 7 ], 6, 15, 15, [ -1, 15 ],
      [ -1, 15 ], 1, [ 1, 6 ], [ 1, 6 ], [ 1, 6 ], [ 1, 6 ], [ -2, 0 ],
      [ 1, 2 ], [ 1, 2 ], [ 1, 2 ], 0, 0, 1, 0, 0, 0, 0 ]

# all vectors in the lattice:

    gap> ContainedDecomposables( rat, s.centralizers, rest, x -> true );
    [ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, -9, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ] ]

# better filter, only characters (see 44.41):

    gap> ContainedDecomposables( rat, s.centralizers, rest,
    >                  x->NonnegIntScalarProducts(s,s.irreducibles,x) );
    [ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ] ]
An application of ContainedDecomposables is 44.41 ContainedCharacters.

For another strategy that works also for irrational characters, see 44.42.

44.41 ContainedCharacters

ContainedCharacters( tbl, constituents, parachar )

returns the set of all characters contained in the parametrized rational character parachar (see 45.1), that modulo centralizer orders lie in the linear span of the rational characters constituents of the character table tbl and that have nonnegative integral scalar products with all elements of constituents.

Note: This does not imply that an element of the returned list is necessary a linear combination of constituents.

    gap> s:= CharTable( "HSM12" );; hs:= CharTable( "HS" );;
    gap> rat:= RationalizedMat( s.irreducibles );;
    gap> fus:= InitFusion( s, hs );;
    gap> rest:= CompositionMaps( hs.irreducibles[8], fus );;
    gap> ContainedCharacters( s, rat, rest );
    [ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ] ]
ContainedCharacters calls 44.40 ContainedDecomposables.

44.42 ContainedSpecialVectors

ContainedSpecialVectors( tbl, chars, parachar, func )

returns the list of all elements vec of the parametrized character parachar (see 45.1), that have integral norm and integral scalar product with the principal character of the character table tbl and that satisfy func( tbl, chars, vec ), i.e., for that true is returned.

gap> s:= CharTable( "HSM12" );; hs:= CharTable( "HS" );;
gap> fus:= InitFusion( s, hs );;
gap> rest:= CompositionMaps( hs.irreducibles[8], fus );;

# no further condition:

gap> ContainedSpecialVectors( s, s.irreducibles, rest,
>                      function(tbl,chars,vec) return true; end );;
gap> Length( last );
24

# better filter: those with integral scalar products

gap> ContainedSpecialVectors( s, s.irreducibles, rest,
>                             IntScalarProducts );
[ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ],
  [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ],
  [ 231, 7, -9, -9, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ],
  [ 231, 7, -9, 7, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ] ]

# better filter: the scalar products must be nonnegative

gap> ContainedSpecialVectors( s, s.irreducibles, rest,
>                             NonnegIntScalarProducts );
[ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ],
  [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ] ]
Special cases of ContainedSpecialVectors are 44.43 ContainedPossibleCharacters and
44.44 ContainedPossibleVirtualCharacters.

ContainedSpecialVectors successively examines all vectors contained in parachar, thus it might not be useful if the indeterminateness exceeds 10^{6}. For another strategy that works for rational characters only, see 44.40.

44.43 ContainedPossibleCharacters

ContainedPossibleCharacters( tbl, chars, parachar )

returns the list of all elements vec of the parametrized character parachar (see 45.1), which have integral norm and integral scalar product with the principal character of the character table tbl and nonnegative integral scalar product with all elements of the list chars of characters of tbl.

# see example in 44.42

gap> ContainedPossibleCharacters( s, s.irreducibles, rest );
[ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ],
  [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
      0, 1, 0, 0, 0, 0 ] ]
ContainedPossibleCharacters calls 44.42 ContainedSpecialVectors.

ContainedPossibleCharacters successively examines all vectors contained in parachar, thus it might not be useful if the indeterminateness exceeds 10^{6}. For another strategy that works for rational characters only, see 44.40.

44.44 ContainedPossibleVirtualCharacters

ContainedPossibleVirtualCharacters( tbl, chars, parachar )

returns the list of all elements vec of the parametrized character parachar (see 45.1), which have integral norm and integral scalar product with the principal character of the character table tbl and integral scalar product with all elements of the list chars of characters of tbl.

# see example in 44.42

    gap> ContainedPossibleVirtualCharacters( s, s.irreducibles, rest );
    [ [ 231, 7, -9, -9, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, -1, -1, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, -9, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ],
      [ 231, 7, -9, 7, 6, 15, 15, 15, 15, 1, 6, 6, 1, 1, -2, 1, 2, 2, 0,
          0, 1, 0, 0, 0, 0 ] ]
ContainedPossibleVirtualCharacters calls 44.42 ContainedSpecialVectors.

ContainedPossibleVirtualCharacters successively examines all vectors that are contained in parachar, thus it might not be useful if the indeterminateness exceeds 10^{6}. For another strategy that works for rational characters only, see 44.40.

734 CHAPTER 44. CHARACTERS

Maps and Parametrized Maps

In this chapter, first the data structure of (parametrized) maps is introduced (see 45.1). Then a description of several functions which mainly deal with parametrized maps follows; these are

basic operations with paramaps (see 45.2, 45.3, 45.29, 45.4, 45.5, 45.6, 45.7, 45.8, 45.9),

functions which inform about ambiguity with respect to a paramap (see 45.10, 45.11),

functions used for the construction of powermaps and subgroup fusions (see 45.12, 45.13 and their subroutines 45.14, 45.15, 45.16, 45.17, 45.23, 45.18, 45.19, 45.20, 45.21, 45.22, 45.24, 45.26, 45.25, 45.28, 45.27, 45.30) and

the function 45.31.

45.1 More about Maps and Parametrized Maps

Besides the characters, powermaps are an important part of a character table. Often their computation is not easy, and in general they cannot be obtained from the matrix of irreducible characters, so it is useful to store them on the table.

If not only a single table is considered but different tables of groups and subgroups are used, also subgroup fusion maps must be known to get informations about the embedding or simply to induce or restrict characters.

These are examples of class functions which are called maps for short; in GAP, maps are lists: Characters are maps, the lists of element orders, centralizer orders, classlengths are maps, and for a permutation perm of classes, ListPerm( perm ) is a map.

When maps are constructed, in most cases one only knows that the image of any class is contained in a set of possible images, e.g. that the image of a class under a subgroup fusion is in the set of all classes with the same element order. Using further informations, like centralizer orders, powermaps and the restriction of characters, the sets of possible images can be diminished. In many cases, at the end the images are uniquely determined.

For this, many functions do not only work with maps but with parametrized maps (or short paramaps): These are lists whose entries are either the images themselves (i.e. integers for fusion maps, powermaps, element orders etc. and cyclotomics for characters) or lists of possible images. Thus maps are special paramaps. A paramap paramap can be identified with the set of all maps map with map[i] = paramap[i] or map[i] contained in paramap[i]; we say that map is contained in paramap then.

The composition of two paramaps is defined as the paramap that contains all compositions of elements of the paramaps. For example, the indirection of a character by a parametrized subgroup fusion map is the parametrized character that contains all possible restrictions of that character.

45.2 CompositionMaps

CompositionMaps( paramap2 , paramap1 )

CompositionMaps( paramap2 , paramap1 , class )

For parametrized maps paramap1 and paramap2 where paramap[i] is a bound position or a set of bound positions in paramap2 , CompositionMaps( paramap2 , paramap1 ) is a parametrized map with image CompositionMaps( paramap2 , paramap1 , class ) at position class.

If paramap1 [ class ] is unique, we have

CompositionMaps(paramap2 , paramap1 , class) = paramap2 [paramap1 [class]],

otherwise it is the union of paramap2 [i] for i in paramap1 [ class ].

    gap> map1:= [ 1, [ 2, 3, 4 ], [ 4, 5 ], 1 ];;
    gap> map2:= [ [ 1, 2 ], 2, 2, 3, 3 ];;
    gap> CompositionMaps( map2, map1 ); CompositionMaps( map1, map2 );
    [ [ 1, 2 ], [ 2, 3 ], 3, [ 1, 2 ] ]
    [ [ 1, 2, 3, 4 ], [ 2, 3, 4 ], [ 2, 3, 4 ], [ 4, 5 ], [ 4, 5 ] ]

Note: If you want to get indirections of characters which contain unknowns (see chapter 16) instead of sets of possible values, use 45.29 Indirected.

45.3 InverseMap

InverseMap( paramap )

InverseMap( paramap )[i] is the unique preimage or the set of all preimages of i under paramap, if there are any; otherwise it is unbound.

(We have CompositionMaps( paramap, InverseMap( paramap ) ) the identity map.)

    gap> t:= CharTable( "2.A5" );; f:= CharTable( "A5" );;
    gap> fus:= GetFusionMap( t, f );  # the factor fusion map
    [ 1, 1, 2, 3, 3, 4, 4, 5, 5 ]
    gap> inverse:= InverseMap( fus );
    [ [ 1, 2 ], 3, [ 4, 5 ], [ 6, 7 ], [ 8, 9 ] ]
    gap> CompositionMaps( fus, inverse );
    [ 1, 2, 3, 4, 5 ]
    gap> t.powermap[2];
    [ 1, 1, 2, 4, 4, 8, 8, 6, 6 ]

# transfer a powermap up to the factor group:

gap> CompositionMaps( fus, CompositionMaps( last, inverse ) );
[ 1, 1, 3, 5, 4 ]  # is equal to f.powermap[2]

# transfer a powermap down to the group:

gap> CompositionMaps( inverse, CompositionMaps( last, fus ) );
[ [ 1, 2 ], [ 1, 2 ], [ 1, 2 ], [ 4, 5 ], [ 4, 5 ], [ 8, 9 ],
  [ 8, 9 ], [ 6, 7 ], [ 6, 7 ] ]  # contains t.powermap[2]

45.4 ProjectionMap

ProjectionMap( map )

For each image i under the (necessarily not parametrized) map map, ProjectionMap( map )[i] is the smallest preimage of i.

(We have CompositionMaps( map, ProjectionMap( map ) ) the identity map.)
gap> ProjectionMap( [1,1,1,2,2,2,3,4,5,5,5,6,6,6,7,7,7] );
[ 1, 4, 7, 8, 9, 12, 15 ]

45.5 Parametrized

Parametrized( list )

returns the parametrized cover of list, i.e. the parametrized map with smallest indeterminateness that contains all maps in list. Parametrized is the inverse function of 45.6 in the

sense that Parametrized( ContainedMaps( paramap ) ) = paramap.
gap> Parametrized( [ [ 1, 3, 4, 6, 8, 10, 11, 11, 15, 14 ],
>                    [ 1, 3, 4, 6, 8, 10, 11, 11, 14, 15 ],
>                    [ 1, 3, 4, 7, 8, 10, 12, 12, 15, 14 ],
>                    [ 1, 3, 4, 7, 8, 10, 12, 12, 14, 15 ] ] );
[ 1, 3, 4, [ 6, 7 ], 8, 10, [ 11, 12 ], [ 11, 12 ], [ 14, 15 ],
  [ 14, 15 ] ]

45.6 ContainedMaps

ContainedMaps( paramap )

returns the set of all maps contained in the parametrized map paramap. ContainedMaps is the inverse function of 45.5 in the sense that Parametrized( ContainedMaps( paramap ) ) = paramap.

gap> ContainedMaps( [ 1, 3, 4, [ 6, 7 ], 8, 10, [ 11, 12 ], [ 11, 12 ],
> 14, 15 ] );
[ [ 1, 3, 4, 6, 8, 10, 11, 11, 14, 15 ],
  [ 1, 3, 4, 6, 8, 10, 11, 12, 14, 15 ],
  [ 1, 3, 4, 6, 8, 10, 12, 11, 14, 15 ],
  [ 1, 3, 4, 6, 8, 10, 12, 12, 14, 15 ],
  [ 1, 3, 4, 7, 8, 10, 11, 11, 14, 15 ],
  [ 1, 3, 4, 7, 8, 10, 11, 12, 14, 15 ],
  [ 1, 3, 4, 7, 8, 10, 12, 11, 14, 15 ],
  [ 1, 3, 4, 7, 8, 10, 12, 12, 14, 15 ] ]

45.7 UpdateMap

UpdateMap( char, paramap, indirected )

improves the paramap paramap using that indirected is the (possibly parametrized) indirection of the character char by paramap.

    gap> s:= CharTable( "S4(4).2" );; he:= CharTable( "He" );;
    gap> fus:= InitFusion( s, he );
    [ 1, 2, 2, [ 2, 3 ], 4, 4, [ 7, 8 ], [ 7, 8 ], 9, 9, 9, [ 10, 11 ],
      [ 10, 11 ], 18, 18, 25, 25, [ 26, 27 ], [ 26, 27 ], 2, [ 6, 7 ],
      [ 6, 7 ], [ 6, 7, 8 ], 10, 10, 17, 17, 18, [ 19, 20 ], [ 19, 20 ] ]
    gap> Filtered( s.irreducibles, x -> x[1] = 50 );
    [ [ 50, 10, 10, 2, 5, 5, -2, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1,
          10, 2, 2, 2, 1, 1, 0, 0, 0, -1, -1 ],
      [ 50, 10, 10, 2, 5, 5, -2, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1,
          -10, -2, -2, -2, -1, -1, 0, 0, 0, 1, 1 ] ]
    gap> UpdateMap( he.irreducibles[2], fus, last[1] + s.irreducibles[1] );
    gap> fus;
    [ 1, 2, 2, 3, 4, 4, 8, 7, 9, 9, 9, 10, 10, 18, 18, 25, 25,
      [ 26, 27 ], [ 26, 27 ], 2, [ 6, 7 ], [ 6, 7 ], [ 6, 7 ], 10, 10,
      17, 17, 18, [ 19, 20 ], [ 19, 20 ] ]

45.8 CommutativeDiagram

CommutativeDiagram( paramap1 , paramap2 , paramap3 , paramap4 ) CommutativeDiagram( paramap1 , paramap2 , paramap3 , paramap4 , improvements )

If

CompositionMaps(paramap2 , paramap1 ) = CompositionMaps(paramap4 , paramap3 )

shall hold, the consistency is checked and the four maps will be improved according to this condition.

If a record improvements with fields imp1, imp2, imp3, imp4 (all lists) is entered as parameter, only diagrams containing elements of impi as positions in the i-th paramap are considered.

CommutativeDiagram returns false if an inconsistency was found, otherwise a record is returned that contains four lists imp1, …, imp4, where impi is the list of classes where the i-th paramap was improved.

    gap> map1:= [ [ 1, 2, 3 ], [ 1, 3 ] ];;
    gap> map2:= [ [ 1, 2 ], 1, [ 1, 3 ] ];;
    gap> map3:= [ [ 2, 3 ], 3 ];; map4:= [ , 1, 2, [ 1, 2 ] ];;
    gap> CommutativeDiagram( map1, map2, map3, map4 );
    rec(
      imp1 := [ 2 ],
      imp2 := [ 1 ],
      imp3 := [  ],
      imp4 := [  ] )

gap> imp:= last;; map1; map2; map3; map4;
[ [ 1, 2, 3 ], 1 ]
[ 2, 1, [ 1, 3 ] ]
[ [ 2, 3 ], 3 ]
[ , 1, 2, [ 1, 2 ] ]
gap> CommutativeDiagram( map1, map2, map3, map4, imp );
rec(
  imp1 := [  ],
  imp2 := [  ],
  imp3 := [  ],
  imp4 := [  ] )

45.9 TransferDiagram

TransferDiagram( inside1 , between, inside2 ) TransferDiagram( inside1 , between, inside2 , improvements )

Like in 45.8, it is checked that

CompositionMaps(between, inside1 ) = CompositionMaps(inside2 , between)

holds for the paramaps inside1 , between and inside2 , that means the paramap between occurs twice in each commutative diagram.

Additionally, 45.20 CheckFixedPoints is called.

If a record improvements with fields impinside1, impbetween and impinside2 is specified, only those diagrams with elements of impinside1 as positions in inside1 , elements of impbetween as positions in between or elements of impinside2 as positions in inside2 are considered.

When an inconsistency occurs, the program immediately returns false; else it returns a record with lists impinside1, impbetween and impinside2 of found improvements.

gap> s:= CharTable( "2F4(2)" );; ru:= CharTable( "Ru" );;
gap> fus:= InitFusion( s, ru );;
gap> permchar:= Sum( Sublist( ru.irreducibles, [ 1, 5, 6 ] ) );;
gap> CheckPermChar( s, ru, fus, permchar );; fus;
[ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,
  [ 25, 26 ], [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ],
  [ 18, 19 ], [ 25, 26 ], [ 25, 26 ], 27, 27 ]
gap> TransferDiagram( s.powermap[2], fus, ru.powermap[2] );
rec(
  impinside1 := [  ],
  impbetween := [ 12, 23 ],
  impinside2 := [  ] )
gap> TransferDiagram( s.powermap[3], fus, ru.powermap[3] );
rec(
  impinside1 := [  ],
  impbetween := [ 14, 24, 25 ],
  impinside2 := [  ] )
gap> TransferDiagram( s.powermap[2], fus, ru.powermap[2], last );

rec(
  impinside1 := [  ],
  impbetween := [  ],
  impinside2 := [  ] )
gap> fus;
[ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, 15, 16, 18, 20, [ 25, 26 ],
  [ 25, 26 ], 5, 5, 6, 8, 14, 13, 19, 19, [ 25, 26 ], [ 25, 26 ], 27,
  27 ]

45.10 Indeterminateness

Indeterminateness( paramap )

returns the indeterminateness of paramap, i.e. the number of maps contained in the parametrized map paramap

gap> m11:= CharTable( "M11" );; m12:= CharTable( "M12" );;
gap> fus:= InitFusion( m11, m12 );
[ 1, [ 2, 3 ], [ 4, 5 ], [ 6, 7 ], 8, [ 9, 10 ], [ 11, 12 ],
  [ 11, 12 ], [ 14, 15 ], [ 14, 15 ] ]
gap> Indeterminateness( fus );
256
gap> TestConsistencyMaps( m11.powermap, fus, m12.powermap );; fus;
[ 1, 3, 4, [ 6, 7 ], 8, 10, [ 11, 12 ], [ 11, 12 ], [ 14, 15 ],
  [ 14, 15 ] ]
gap> Indeterminateness( fus );
32

45.11 PrintAmbiguity

PrintAmbiguity( list, paramap )

prints for each character in list its position, its indeterminateness with respect to paramap and the list of ambiguous classes

gap> s:= CharTable( "2F4(2)" );; ru:= CharTable( "Ru" );;
gap> fus:= InitFusion( s, ru );;
gap> permchar:= Sum( Sublist( ru.irreducibles, [ 1, 5, 6 ] ) );;
gap> CheckPermChar( s, ru, fus, permchar );; fus;
[ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,
  [ 25, 26 ], [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ],
  [ 18, 19 ], [ 25, 26 ], [ 25, 26 ], 27, 27 ]
gap> PrintAmbiguity( Sublist( ru.irreducibles, [ 1 .. 8 ] ), fus );
1 1 [  ]
2 16 [ 16, 17, 26, 27 ]
3 16 [ 16, 17, 26, 27 ]
4 32 [ 12, 14, 23, 24, 25 ]
5 4 [ 12, 23 ]
6 1 [  ]
7 32 [ 12, 14, 23, 24, 25 ]

8 1 [  ]
gap> Indeterminateness( fus );
512

45.12 Powermap

Powermap( tbl, prime )

Powermap( tbl, prime, parameters )

returns a list of possibilities for the prime-th powermap of the character table tbl.

The optional record parameters may have the following fields:

chars

a list of characters which are used for the check of kernels (see 45.16), the test of congruences (see 45.15) and the test of scalar products of symmetrisations (see 45.23); the default is tbl.irreducibles

powermap

a (parametrized) map which is an approximation of the desired map

decompose

a boolean; if true, the symmetrisations of chars must have all constituents in chars, that will be used in the algorithm; if chars is not bound and tbl.irreducibles is complete, the default value of decompose is true, otherwise false

quick

a boolean; if true, the subroutines are called with the option "quick"; especially, a unique map will be returned immediately without checking all symmetrisations; the default value is false

parameters

a record with fields maxamb, minamb and maxlen which control the subroutine 45.23: It only uses characters with actual indeterminateness up to maxamb, tests decomposability only for characters with actual indeterminateness at least minamb and admits a branch only according to a character if there is one with at most maxlen possible minus-characters.

# cf. example in 45.14

gap> t:= CharTable( "U4(3).4" );;
gap> pow:= Powermap( t, 2 );
[ [ 1, 1, 3, 4, 5, 2, 2, 8, 3, 4, 11, 12, 6, 14, 9, 1, 1, 2, 2, 3, 4,
     5, 6, 8, 9, 9, 10, 11, 12, 16, 16, 16, 16, 17, 17, 18, 18, 18,
     18, 20, 20, 20, 20, 22, 22, 24, 24, 25, 26, 28, 28, 29, 29 ] ]

45.13 SubgroupFusions

SubgroupFusions( subtbl, tbl )
SubgroupFusions( subtbl, tbl, parameters )

returns the list of all subgroup fusion maps from subtbl into tbl.

The optional record parameters may have the following fields:

chars

a list of characters of tbl which will be restricted to subtbl, (see 45.24); the default is tbl.irreducibles

subchars

a list of characters of subtbl which are constituents of the retrictions of chars, the default is subtbl.irreducibles

fusionmap

a (parametrized) map which is an approximation of the desired map

decompose

a boolean; if true, the restrictions of chars must have all constituents in subchars, that will be used in the algorithm; if subchars is not bound and subtbl.irreducibles is complete, the default value of decompose is true, otherwise false

permchar

a permutation character; only those fusions are computed which afford that permutation character (see 45.19)

quick

a boolean; if true, the subroutines are called with the option "quick"; especially, a unique map will be returned immediately without checking all symmetrisations; the default value is false

parameters

a record with fields maxamb, minamb and maxlen which control the subroutine 45.24: It only uses characters with actual indeterminateness up to maxamb, tests decomposability only for characters with actual indeterminateness at least minamb and admits a branch only according to a character if there is one with at most maxlen possible restrictions. # cf. example in 45.24

gap> s:= CharTable( "U3(3)" );; t:= CharTable( "J4" );;
gap> SubgroupFusions( s, t, rec( quick:= true ) );
[ [ 1, 2, 4, 4, 5, 5, 6, 10, 12, 13, 14, 14, 21, 21 ],
 [ 1, 2, 4, 4, 5, 5, 6, 10, 13, 12, 14, 14, 21, 21 ],
 [ 1, 2, 4, 4, 6, 6, 6, 10, 12, 13, 15, 15, 22, 22 ],
 [ 1, 2, 4, 4, 6, 6, 6, 10, 12, 13, 16, 16, 22, 22 ],
 [ 1, 2, 4, 4, 6, 6, 6, 10, 13, 12, 15, 15, 22, 22 ],
 [ 1, 2, 4, 4, 6, 6, 6, 10, 13, 12, 16, 16, 22, 22 ] ]

45.14 InitPowermap

InitPowermap( tbl, prime )

computes a (probably parametrized, see 45.1) first approximation of of the prime-th powermap of the character table tbl, using that for any class i of tbl, the following properties hold: The centralizer order of the image is a multiple of the centralizer order of i. If the element order of i is relative prime to prime, the centralizer orders of i and its image must be equal. If prime divides the element order x of the class i, the element order of its image must be x/prime; otherwise the element orders of i and its image must be equal. Of course, this is used only if the element orders are stored on the table.

If no prime-th powermap is possible because of these properties, false is returned. Otherwise InitPowermap returns the parametrized map.

# cf. example in 45.12

    gap> t:= CharTable( "U4(3).4" );;
    gap> pow:= InitPowermap( t, 2 );
    [ 1, 1, 3, 4, 5, [ 2, 16 ], [ 2, 16, 17 ], 8, 3, [ 3, 4 ],
      [ 11, 12 ], [ 11, 12 ], [ 6, 7, 18, 19, 30, 31, 32, 33 ], 14,
      [ 9, 20 ], 1, 1, 2, 2, 3, [ 3, 4, 5 ], [ 3, 4, 5 ],
      [ 6, 7, 18, 19, 30, 31, 32, 33 ], 8, 9, 9, [ 9, 10, 20, 21, 22 ],
      [ 11, 12 ], [ 11, 12 ], 16, 16, [ 2, 16 ], [ 2, 16 ], 17, 17,
      [ 6, 18, 30, 31, 32, 33 ], [ 6, 18, 30, 31, 32, 33 ],
      [ 6, 7, 18, 19, 30, 31, 32, 33 ], [ 6, 7, 18, 19, 30, 31, 32, 33 ],
      20, 20, [ 9, 20 ], [ 9, 20 ], [ 9, 10, 20, 21, 22 ],
      [ 9, 10, 20, 21, 22 ], 24, 24, [ 15, 25, 26, 40, 41, 42, 43 ],
      [ 15, 25, 26, 40, 41, 42, 43 ], [ 28, 29 ], [ 28, 29 ], [ 28, 29 ],
      [ 28, 29 ] ]

# continued in 45.15

InitPowermap is used by 45.12 Powermap.

45.15 Congruences

Congruences( tbl, chars, prime powermap, prime ) Congruences( tbl, chars, prime powermap, prime, "quick") improves the parametrized map prime powermap (see 45.1) that is an approximation of the prime-th powermap of the character table tbl: For G a group with character table tbl, g ∈ G and a character χ of tbl, the congruence

GaloisCyc(χ(g), prime) ≡ χ(g^{prime}) (mod prime)

holds; if the representative order of g is relative prime to prime, we have

GaloisCyc(χ(g), prime) = χ(g^{prime}).

Congruences checks these congruences for the (virtual ) characters in the list chars. If "quick" is specified, only those classes are considered for which prime powermap is ambiguous. If there are classes for which no image is possible, false is returned, otherwise Congruences returns true.

# see example in 45.14

    gap> Congruences( t, t.irreducibles, pow, 2 ); pow;
    true
    [ 1, 1, 3, 4, 5, [ 2, 16 ], [ 2, 16, 17 ], 8, 3, 4, 11, 12,
      [ 6, 7, 18, 19 ], 14, [ 9, 20 ], 1, 1, 2, 2, 3, 4, 5,
      [ 6, 7, 18, 19 ], 8, 9, 9, [ 10, 21 ], 11, 12, 16, 16, [ 2, 16 ],
      [ 2, 16 ], 17, 17, [ 6, 18 ], [ 6, 18 ], [ 6, 7, 18, 19 ],
      [ 6, 7, 18, 19 ], 20, 20, [ 9, 20 ], [ 9, 20 ], 22, 22, 24, 24,
      [ 15, 25, 26 ], [ 15, 25, 26 ], 28, 28, 29, 29 ]

# continued in 45.16

Congruences is used by 45.12 Powermap.

45.16 ConsiderKernels

ConsiderKernels( tbl, chars, prime powermap, prime )

ConsiderKernels( tbl, chars, prime powermap, prime, "quick")

improves the parametrized map prime powermap (see 45.1) that is an approximation of the prime-th powermap of the character table tbl:

For G a group with character table tbl, the kernel of each character in the list chars is a normal subgroup of G, so for every g ∈ Kernel(chi) we have g^{prime} ∈ Kernel(chi).

Depending on the order of the factor group modulo Kernel( chi ), there are two further properties:If the order is relative prime to prime, for each g ∉ Kernel(chi) the prime-th power is not contained in Kernel( chi ); if the order is equal to prime, the prime-th powers of all elements lie in Kernel( chi ).

If "quick" is specified, only those classes are considered for which prime powermap is ambiguous.

If Kernel( chi ) has an order not dividing tbl.order for an element chi of chars, or if no image is possible for a class, false is returned; otherwise ConsiderKernels returns true.

Note that chars must consist of ordinary characters, since the kernel of a virtual character is not defined.

# see example in 45.15

    gap> ConsiderKernels( t, t.irreducibles, pow, 2 ); pow;
    true
    [ 1, 1, 3, 4, 5, 2, 2, 8, 3, 4, 11, 12, [ 6, 7 ], 14, 9, 1, 1, 2, 2,
      3, 4, 5, [ 6, 7, 18, 19 ], 8, 9, 9, [ 10, 21 ], 11, 12, 16, 16,
      [ 2, 16 ], [ 2, 16 ], 17, 17, [ 6, 18 ], [ 6, 18 ],
      [ 6, 7, 18, 19 ], [ 6, 7, 18, 19 ], 20, 20, [ 9, 20 ], [ 9, 20 ],
      22, 22, 24, 24, [ 15, 25, 26 ], [ 15, 25, 26 ], 28, 28, 29, 29 ]

# continued in 45.23

ConsiderKernels is used by 45.12 Powermap.

45.17 ConsiderSmallerPowermaps

ConsiderSmallerPowermaps( tbl, prime powermap, prime )
ConsiderSmallerPowermaps( tbl, prime powermap, prime, "quick")

improves the parametrized map prime powermap (see chapter 45) that is an approximation of the prime-th powermap of the character table tbl:

If prime > tbl.orders[i] for a class i, try to improve prime powermap at class i using that for g in class i, g_{i}^{prime} = gprime mod tbl.orders[i]_{i} holds; so if the (prime mod tbl.orders[i])-th powermap at class i is determined by the maps stored in tbl.powermap, this information is used.

If "quick" is specified, only those classes are considered for which prime powermap is ambiguous.

If there are classes for which no image is possible, false is returned, otherwise true.

Note: If tbl.orders is unbound, true is returned without tests.

    gap> t:= CharTable( "U4(3).4" );; init:= InitPowermap( t, 5 );;
    gap> Indeterminateness( init );
    84537841287168
    gap> ConsiderSmallerPowermaps( t, init, 5 );;
    gap> Indeterminateness( init );
    195689447424
ConsiderSmallerPowermaps is used by 45.12 Powermap.

45.18 InitFusion

InitFusion( subtbl, tbl )

computes a (probably parametrized, see 45.1) first approximation of of the subgroup fusion from the character table subtbl into the character table tbl, using that for any class i of subtbl, the centralizer order of the image is a multiple of the centralizer order of i and the element order of i is equal to the element order of its image (used only if element orders are stored on the tables).

If no fusion map is possible because of these properties, false is returned. Otherwise InitFusion returns the parametrized map.

    gap> s:= CharTable( "2F4(2)" );; ru:= CharTable( "Ru" );;
    gap> fus:= InitFusion( s, ru );
    [ 1, 2, 2, 4, [ 5, 6 ], [ 5, 6, 7, 8 ], [ 5, 6, 7, 8 ], [ 9, 10 ],
      11, 14, 14, [ 13, 14, 15 ], [ 16, 17 ], [ 18, 19 ], 20, [ 25, 26 ],
      [ 25, 26 ], [ 5, 6 ], [ 5, 6 ], [ 5, 6 ], [ 5, 6, 7, 8 ],
      [ 13, 14, 15 ], [ 13, 14, 15 ], [ 18, 19 ], [ 18, 19 ], [ 25, 26 ],
      [ 25, 26 ], [ 27, 28, 29 ], [ 27, 28, 29 ] ]
InitFusion is used by 45.13 SubgroupFusions.

45.19 CheckPermChar

CheckPermChar( subtbl, tbl, fusionmap, permchar )

tries to improve the parametrized fusion fusionmap (see Chapter 45) from the character table subtbl into the character table tbl using the permutation character permchar that belongs to the required fusion: A possible image x of class i is excluded if class i is too large, and a possible image y of class i is the right image if y must be the image of all classes where y is a possible image.

CheckPermChar returns true if no inconsistency occurred, and false otherwise.

    gap> fus:= InitFusion( s, ru );; # cf. example in 45.18
    gap> permchar:= Sum( Sublist( ru.irreducibles, [ 1, 5, 6 ] ) );;
    gap> CheckPermChar( s, ru, fus, permchar );; fus;
    [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,
      [ 25, 26 ], [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ],
      [ 18, 19 ], [ 25, 26 ], [ 25, 26 ], 27, 27 ]
CheckPermChar is used by 45.13 SubgroupFusions.

45.20 CheckFixedPoints

CheckFixedPoints( inside1 , between, inside2 )

If the parametrized map (see 45.1) between transfers the parametrized map inside1 to inside2 , i.e. inside2 ∘ between = between ∘ inside1 , between must map fixed points of inside1 to fixed points of inside2 . Using this property, CheckFixedPoints tries to improve between and inside2 .

If an inconsistency occurs, false is returned. Otherwise, CheckFixedPoints returns the list of classes where improvements were found.

    gap> s:= CharTable( "L4(3).2_2" );; o7:= CharTable( "O7(3)" );;
    gap> fus:= InitFusion( s, o7 );;
    gap> CheckFixedPoints( s.powermap[5], fus, o7.powermap[5] );
    [ 48, 49 ]
    gap> fus:= InitFusion( s, o7 );; Sublist( fus, [ 48, 49 ] );
    [ [ 54, 55, 56, 57 ], [ 54, 55, 56, 57 ] ]
    gap> CheckFixedPoints( s.powermap[5], fus, o7.powermap[5] );
    [ 48, 49 ]
    gap> Sublist( fus, [ 48, 49 ] );
    [ [ 56, 57 ], [ 56, 57 ] ]
CheckFixedPoints is used by 45.13 SubgroupFusions.

45.21 TestConsistencyMaps

TestConsistencyMaps( powmap1 , fusmap, powmap2 )

TestConsistencyMaps( powmap1 , fusmap, powmap2 , fus imp )

Like in 45.9, it is checked that parametrized maps (see chapter 45) commute:

For all positions i where both powmap1 [i] and powmap2 [i] are bound,

CompositionMaps(fusmap, powmap1 [i]) = CompositionMaps(powmap2 [i], fusmap)

shall hold, so fusmap occurs in diagrams for all considered elements of powmap1 resp. powmap2 , and it occurs twice in each diagram.

If a set fus imp is specified, only those diagrams with elements of fus imp as preimages of fusmap are considered.

TestConsistencyMaps stores all found improvements in fusmap and elements of powmap1 and powmap2 . When an inconsistency occurs, the program immediately returns false; otherwise true is returned.

TestConsistencyMaps stops if no more improvements of fusmap are possible. E.g. if fusmap was unique from the beginning, the powermaps will not be improved. To transfer powermaps by fusions, use 45.9 TransferDiagram.

    gap> s:= CharTable( "2F4(2)" );; ru:= CharTable( "Ru" );;
    gap> fus:= InitFusion( s, ru );;
    gap> permchar:= Sum( Sublist( ru.irreducibles, [ 1, 5, 6 ] ) );;
    gap> CheckPermChar( s, ru, fus, permchar );; fus;
    [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,

      [ 25, 26 ], [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ],
      [ 18, 19 ], [ 25, 26 ], [ 25, 26 ], 27, 27 ]
    gap> TestConsistencyMaps( s.powermap, fus, ru.powermap );
    true
    gap> fus;
    [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, 15, 16, 18, 20, [ 25, 26 ],
      [ 25, 26 ], 5, 5, 6, 8, 14, 13, 19, 19, [ 25, 26 ], [ 25, 26 ], 27,
      27 ]
    gap> Indeterminateness( fus );
    16
TestConsistencyMaps is used by 45.13 SubgroupFusions.

45.22 ConsiderTableAutomorphisms

ConsiderTableAutomorphisms( parafus, tableautomorphisms )

improves the parametrized subgroup fusion map parafus (see 45.1): Let T be the permutation group that has the list tableautomorphisms as generators, let T_{0} be the subgroup of T that is maximal with the property that T_{0} operates on the set of fusions contained in parafus by permutation of images.

ConsiderTableAutomorphisms replaces orbits by representatives at suitable positions so that afterwards exactly one representative of fusion maps (that is contained in parafus) in every orbit under the operation of T_{0} is contained in parafus.

The list of positions where improvements were found is returned.

    gap> fus:= InitFusion( s, ru );;
    gap> permchar:= Sum( Sublist( ru.irreducibles, [ 1, 5, 6 ] ) );;
    gap> CheckPermChar( s, ru, fus, permchar );; fus;
    [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,
      [ 25, 26 ], [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ],
      [ 18, 19 ], [ 25, 26 ], [ 25, 26 ], 27, 27 ]
    gap> ConsiderTableAutomorphisms( fus, ru.automorphisms );
    [ 16 ]
    gap> fus;
    [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, [ 13, 15 ], 16, [ 18, 19 ], 20,
      25, [ 25, 26 ], 5, 5, 6, 8, 14, [ 13, 15 ], [ 18, 19 ], [ 18, 19 ],
      [ 25, 26 ], [ 25, 26 ], 27, 27 ]
ConsiderTableAutomorphisms is used by 45.13 SubgroupFusions.

45.23 PowermapsAllowedBySymmetrisations

PowermapsAllowedBySymmetrisations( tbl, subchars, chars, pow,

prime, parameters )

returns a list of (possibly parametrized, see 45.1) maps map which are contained in the parametrized map pow and which have the property that for all χ in the list chars of characters of the character table tbl, the symmetrizations

χ^{p−} = MinusCharacter(χ, map, prime)

(see 44.15) have nonnegative integral scalar products with all characters in the list subchars.

parameters must be a record with fields

maxlen

an integer that controls the position where branches take place

contained

a function, usually 44.41 or 44.43; for a symmetrization minus, it returns the list contained( tbl, subchars, minus )

minamb, maxamb

two arbitrary objects; contained is called only for symmetrizations minus with

                    minamb < Indeterminateness(minus) < maxamb
quick

a boolean; if it is true, the scalar products of uniquely determined symmetrizations are not checked.

pow will be improved, i.e. is changed by the algorithm.

If there is no character left which allows an immediate improvement but there are characters in chars with indeterminateness of the symmetrizations bigger than parameters.minamb, a branch is necessary. Two kinds of branches may occur: If parameters.contained( tbl, subchars, minus ) has length at most parameters.maxlen, the union of maps allowed by the characters in minus is computed; otherwise a suitable class c is taken which is significant for some character, and the union of all admissible maps with image x on c is computed, where x runs over pow[c].

# see example in 45.16

    gap> PowermapsAllowedBySymmetrisations(t,t.irreducibles,t.irreducibles,
    >      pow, 2, rec( maxlen:=10, contained:=ContainedPossibleCharacters,
    >      minamb:= 2, maxamb:= "infinity", quick:= false ) );
    [ [ 1, 1, 3, 4, 5, 2, 2, 8, 3, 4, 11, 12, 6, 14, 9, 1, 1, 2, 2, 3, 4,
          5, 6, 8, 9, 9, 10, 11, 12, 16, 16, 16, 16, 17, 17, 18, 18, 18,
          18, 20, 20, 20, 20, 22, 22, 24, 24, 25, 26, 28, 28, 29, 29 ] ]
    gap> t.powermap[2] = last[1];
    true

45.24 FusionsAllowedByRestrictions

FusionsAllowedByRestrictions( subtbl, tbl, subchars, chars,

fus, parameters )

returns a list of (possibly parametrized, see 45.1) maps map which are contained in the parametrized map fus and which have the property that for all χ in the list chars of characters of the character table tbl, the restrictions

                       χ_{subtbl} = CompositionMaps(χ, fus)

(see 45.2) have nonnegative integral scalar products with all characters in the list subchars.

parameters must be a record with fields

maxlen

an integer that controls the position where branches take place

contained

a function, usually 44.41 or 44.43; for a restriction rest, it returns the list contained( subtbl, subchars, rest );

minamb, maxamb

two arbitrary objects; contained is called only for restrictions rest with minamb

     <Indeterminateness( rest ) <maxamb;
quick

a boolean value; if it is true, the scalar products of uniquely determined restrictions are not checked.

fus will be improved, i.e. is changed by the algorithm.

If there is no character left which allows an immediate improvement but there are characters in chars with indeterminateness of the restrictions bigger than parameters.minamb, a branch is necessary. Two kinds of branches may occur: If parameters.contained( tbl, subchars, rest ) has length at most parameters.maxlen, the union of maps allowed by the characters in rest is computed; otherwise a suitable class c is taken which is significant for some character, and the union of all admissible maps with image x on c is computed, where x runs over fus[c].

    gap> s:= CharTable( "U3(3)" );; t:= CharTable( "J4" );;
    gap> fus:= InitFusion( s, t );;
    gap> TestConsistencyMaps( s.powermap, fus, t.powermap );;
    gap> ConsiderTableAutomorphisms( fus, t.automorphisms );; fus;
    [ 1, 2, 4, 4, [ 5, 6 ], [ 5, 6 ], [ 5, 6 ], 10, 12, [ 12, 13 ],
      [ 14, 15, 16 ], [ 14, 15, 16 ], [ 21, 22 ], [ 21, 22 ] ]
    gap> FusionsAllowedByRestrictions( s, t, s.irreducibles,
    >             t.irreducibles, fus, rec( maxlen:= 10,
    >             contained:= ContainedPossibleCharacters,
    >             minamb:= 2, maxamb:= "infinity", quick:= false ) );
    [ [ 1, 2, 4, 4, 5, 5, 6, 10, 12, 13, 14, 14, 21, 21 ],
      [ 1, 2, 4, 4, 6, 6, 6, 10, 12, 13, 15, 15, 22, 22 ],
      [ 1, 2, 4, 4, 6, 6, 6, 10, 12, 13, 16, 16, 22, 22 ] ]

# cf. example in 45.13

FusionsAllowedByRestrictions is used by 45.13 SubgroupFusions.

45.25 OrbitFusions

OrbitFusions( subtblautomorphisms, fusionmap, tblautomorphisms )

returns the orbit of the subgroup fusion map fusionmap under the operations of maximal admissible subgroups of the table automorphism groups of the character tables. subtblautomorphisms is a list of generators of the automorphisms of the subgroup table, tblautomorphisms is a list of generators of the automorphisms of the supergroup table.

    gap> s:= CharTable( "U3(3)" );; t:= CharTable( "J4" );;
    gap> GetFusionMap( s, t );
    [ 1, 2, 4, 4, 5, 5, 6, 10, 12, 13, 14, 14, 21, 21 ]

gap> OrbitFusions( s.automorphisms, last, t.automorphisms );
[ [ 1, 2, 4, 4, 5, 5, 6, 10, 12, 13, 14, 14, 21, 21 ],
  [ 1, 2, 4, 4, 5, 5, 6, 10, 13, 12, 14, 14, 21, 21 ] ]

45.26 OrbitPowermaps

OrbitPowermaps( powermap, matautomorphisms )

returns the orbit of the powermap powermap under the operation of the subgroup matautomorphisms of the maximal admissible subgroup of the matrix automorphisms of the corresponding character table.

gap> t:= CharTable( "3.McL" );;
gap> maut:= MatAutomorphisms( t.irreducibles, [], Group( () ) );
Group( (55,58)(56,59)(57,60)(61,64)(62,65)(63,66), (35,36), (26,29)
(27,30)(28,31)(49,52)(50,53)(51,54), (40,43)(41,44)(42,45), ( 2, 3)
( 5, 6)( 8, 9)(12,13)(15,16)(18,19)(21,22)(24,25)(27,28)(30,31)(33,34)
(38,39)(41,42)(44,45)(47,48)(50,51)(53,54)(56,57)(59,60)(62,63)
(65,66) )
gap> OrbitPowermaps( t.powermap[3], maut );
[ [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 8, 9, 37,
      37, 37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49,
      49, 14, 14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ],
  [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 9, 8, 37,
      37, 37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49,
      49, 14, 14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ] ]

45.27 RepresentativesFusions

RepresentativesFusions( subtblautomorphisms, listoffusionmaps, tblautomorphisms )

RepresentativesFusions( subtbl, listoffusionmaps, tbl )

returns a list of representatives of the list listoffusionmaps of subgroup fusion maps under the operations of maximal admissible subgroups of the table automorphism groups of the character tables. subtblautomorphisms is a list of generators of the automorphisms of the subgroup table, tblautomorphisms is a list of generators of the automorphisms of the supergroup table. if the parameters subtbl and tbl (character tables) are used, the values of

subtbl.automorphisms and subtbl.automorphisms will be taken.
gap> s:= CharTable( "2F4(2)" );; ru:= CharTable( "Ru" );;
gap> SubgroupFusions( s, ru );
[ [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, 15, 16, 18, 20, 25, 26, 5, 5,
      6, 8, 14, 13, 19, 19, 26, 25, 27, 27 ],
  [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, 15, 16, 18, 20, 26, 25, 5, 5,
      6, 8, 14, 13, 19, 19, 25, 26, 27, 27 ] ]
gap> RepresentativesFusions( s, last, ru );
[ [ 1, 2, 2, 4, 5, 7, 8, 9, 11, 14, 14, 15, 16, 18, 20, 25, 26, 5, 5,
      6, 8, 14, 13, 19, 19, 26, 25, 27, 27 ] ]

45.28 RepresentativesPowermaps

RepresentativesPowermaps( listofpowermaps, matautomorphisms ) returns a list of representatives of the list listofpowermaps of powermaps under the operation of a subgroup matautomorphisms of the maximal admissible subgroup of matrix automorphisms of irreducible characters of the corresponding character table.

gap> t:= CharTable( "3.McL" );;
gap> maut:= MatAutomorphisms( t.irreducibles, [], Group( () ) );
Group( (55,58)(56,59)(57,60)(61,64)(62,65)(63,66), (35,36), (26,29)
(27,30)(28,31)(49,52)(50,53)(51,54), (40,43)(41,44)(42,45), ( 2, 3)
( 5, 6)( 8, 9)(12,13)(15,16)(18,19)(21,22)(24,25)(27,28)(30,31)(33,34)
(38,39)(41,42)(44,45)(47,48)(50,51)(53,54)(56,57)(59,60)(62,63)
(65,66) )
gap> Powermap( t, 3 );
[ [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 9, 8, 37,
      37, 37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49,
      49, 14, 14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ],
  [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 8, 9, 37,
      37, 37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49,
      49, 14, 14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ] ]
gap> RepresentativesPowermaps( last, maut );
[ [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 8, 9, 37,
      37, 37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49,
      49, 14, 14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ] ]

45.29 Indirected

Indirected( char, paramap )

We have Indirected(char, paramap)[i] = char[paramap[i]],

if this value is unique; otherwise it is set unknown (see chapter 16). (For a parametrized indirection, see 45.2.)

gap> m12:= CharTable( "M12" );;
gap> fus:= [ 1, 3, 4, [ 6, 7 ], 8, 10, [ 11, 12 ], [ 11, 12 ],

> [ 14, 15 ], [ 14, 15 ] ];; # parametrized subgroup fusion

# from M_{11}

gap> chars:= Sublist( m12.irreducibles, [ 1 .. 6 ] );;
gap> List( chars, x -> Indirected( x, fus ) );
[ [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ],
  [ 11, 3, 2, Unknown(1), 1, 0, Unknown(2), Unknown(3), 0, 0 ],
  [ 11, 3, 2, Unknown(4), 1, 0, Unknown(5), Unknown(6), 0, 0 ],
  [ 16, 0, -2, 0, 1, 0, 0, 0, Unknown(7), Unknown(8) ],
  [ 16, 0, -2, 0, 1, 0, 0, 0, Unknown(9), Unknown(10) ],
  [ 45, -3, 0, 1, 0, 0, -1, -1, 1, 1 ] ]

45.30 Powmap

Powmap( powermap, n )

Powmap( powermap, n, class )

The first form returns the n-th powermap where powermap is the powermap of a character table (see 42.2). If the n-th position in powermap is bound, this map is returned, otherwise it is computed from the (necessarily stored) powermaps of the prime divisors of n.

The second form returns the image of class under the n-th powermap; for any valid class class, we have Powmap( powermap, n )[ class ] = Powmap( powermap, n, class ).

The entries of powermap may be parametrized maps (see 45.1).

    gap> t:= CharTable( "3.McL" );;
    gap> Powmap( t.powermap, 3 );
    [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 8, 9, 37, 37,
      37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49, 49, 14,
      14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ]
    gap> Powmap( t.powermap, 27 );
    [ 1, 1, 1, 4, 4, 4, 1, 1, 1, 1, 11, 11, 11, 14, 14, 14, 17, 17, 17,
      4, 4, 4, 4, 4, 4, 29, 29, 29, 26, 26, 26, 32, 32, 32, 1, 1, 37, 37,
      37, 40, 40, 40, 43, 43, 43, 11, 11, 11, 52, 52, 52, 49, 49, 49, 14,
      14, 14, 14, 14, 14, 37, 37, 37, 37, 37, 37 ]
    gap> Lcm( t.orders ); Powmap( t.powermap, last );
    27720
    [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]

45.31 ElementOrdersPowermap

ElementOrdersPowermap( powermap )

returns the list of element orders given by the maps in the powermap powermap. The entries at positions where the powermaps do not uniquely determine the element order are set to unknowns (see chapter 16).

    gap> t:= CharTable( "3.J3.2" );; t.powermap;
    [ , [ 1, 2, 1, 2, 5, 6, 7, 3, 4, 10, 11, 12, 5, 6, 8, 9, 18, 19, 17,
          10, 11, 12, 13, 14, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 1,
          3, 7, 8, 8, 13, 18, 19, 17, 23, 23, 28, 30 ],
      [ 1, 1, 3, 3, 1, 1, 1, 8, 8, 10, 10, 10, 3, 3, 15, 15, 7, 7, 7, 20,
          20, 20, 8, 8, 10, 10, 10, 30, 30, 28, 28, 32, 32, 32, 35, 36,
          35, 38, 39, 36, 37, 37, 37, 38, 38, 47, 46 ],,
      [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 2, 13, 14, 15, 16, 19, 17, 18,
          3, 4, 4, 23, 24, 5, 6, 6, 30, 31, 28, 29, 32, 34, 33, 35, 36,
          37, 38, 39, 40, 43, 41, 42, 44, 45, 47, 46 ],,,,,,,,,,,,
      [ 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, 1, 2, 1, 2, 32, 34, 33, 35,
          36, 37, 38, 39, 40, 41, 42, 43, 45, 44, 35, 35 ],,

[ 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, 1, 2, 2,
    35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 ] ]
gap> ElementOrdersPowermap( last );
[ 1, 3, 2, 6, 3, 3, 3, 4, 12, 5, 15, 15, 6, 6, 8, 24, 9, 9, 9, 10,
30, 30, 12, 12, 15, 15, 15, 17, 51, 17, 51, 19, 57, 57, 2, 4, 6, 8,
8, 12, 18, 18, 18, 24, 24, 34, 34 ]
gap> Unbind( t.powermap[17] ); ElementOrdersPowermap( t.powermap );
[ 1, 3, 2, 6, 3, 3, 3, 4, 12, 5, 15, 15, 6, 6, 8, 24, 9, 9, 9, 10,
30, 30, 12, 12, 15, 15, 15, Unknown(11), Unknown(12), Unknown(13),
Unknown(14), 19, 57, 57, 2, 4, 6, 8, 8, 12, 18, 18, 18, 24, 24,
Unknown(15), Unknown(16) ]

754 CHAPTER 45. MAPS AND PARAMETRIZED MAPS

Character Table Libraries

The utility of GAP for character theoretical tasks depends on the availability of many known character tables, so there is a lot of tables in the GAP group collection.

There are three different libraries of character tables, namely ordinary character tables, Brauer tables and generic character tables.

Of course, these libraries are “open” in the sense that they shall be extended. So we would be grateful for any further tables of interest sent to us for inclusion into our libraries.

This chapter mainly explains properties not of single tables but of the libraries and their structure; for the format of character tables, see 42.2, 42.3 and chapter 43.

The chapter informs about

• the actually available tables (see 46.1),

• the sublibraries of ATLAS tables (see 46.2) and CAS tables (see 46.4),

• the organization of the libraries (see 46.5),

• and how to extend a library (see 46.6).

46.1 Contents of the Table Libraries

As stated at the beginning of the chapter, there are three libraries of character tables: ordinary character tables, Brauer tables, and generic character tables.

Ordinary Character Tables

Two different aspects are useful to list up the ordinary character tables available to GAP the aspect of source of the tables and that of connections between the tables.

As for the source, there are two big sources, the ATLAS (see 46.2) and the CAS library of character tables. Many ATLAS tables are contained in the CAS library, and difficulties may arise because the succession of characters or classes in CAS tables and ATLAS tables are different, so see 46.4 and 42.2 for the relations between the (at least) two forms of the same table. A large subset of the CAS tables is the set of tables of Sylow normalizers of sporadic simple groups as published in [Ost86], so this may be viewed as another source.

To avoid confusions about the actual format of a table, authorship and so on, the text component of the table contains the information

     origin: ATLAS of finite groups for ATLAS tables (see 46.2)

origin: Ostermann for tables of [Ost86] and

origin: CAS library for any table of the CAS table library that is contained neither in the ATLAS nor in [Ost86].

If one is interested in the aspect of connections between the tables, i.e., the internal structure of the library of ordinary tables (which corresponds to the access to character tables, as described in 42.9), the contents can be listed up the following way:

We have

• all ATLAS tables (see 46.2), i.e. the tables of the simple groups which are contained in the ATLAS, and the tables of cyclic and bicyclic extensions of these groups;

• most tables of maximal subgroups of sporadic simple groups (not all for HN, Th, Fi23, Co1, F3+, B, M);

• some tables of maximal subgroups of other ATLAS tables (which?)

• most nontrivial Sylow normalizers of sporadic simple groups as printed in [Ost86], where nontrivial means that the group is not contained in p:(p − 1);

not yet possible for all!!

which are not contained? (J_{4}N2, Co_{1}N2, Co_{1}N5, all of F i_{23}, F i′_{24}, B, M, HN, and F i_{22}N2)

• some tables of element centralizers

• some tables of Sylow subgroups

• a few other tables, e.g. W(F4)

namely which?

Brauer Tables

This library contains the tables of the modular ATLAS which are yet known. Some of them still contain unknowns (see 16.1). Since there is ongoing work in computing new tables, this library is changed nearly every day.

These Brauer tables contain the information

    origin: modular ATLAS of finite groups

in their text component.

Generic Character Tables

At the moment, generic tables of the following groups are available in GAP (see 42.9):

• alternating groups

• cyclic groups, • dihedral groups,

• some linear groups,

• quaternionic (dicyclic) groups

• symmetric groups,

• wreath products of a group with a symmetric group (see 42.15),

• Weyl groups of types B_{n} and D_{n}

Not all these are really implemented as generic tables!!!

46.2 ATLAS Tables

The GAP group collection contains all character tables that are included in the Atlas of finite groups ([CCN^{+}85], from now on called ATLAS) and the Brauer tables contained in the modular ATLAS ([LPW93]). Although the Brauer tables form a library of their own, they are described here since all conventions for ATLAS tables stated here hold for Brauer tables, too. Additionally some conventions are necessary about follower characters! These tables have the information

    origin: ATLAS of finite groups

resp.

    origin: modular ATLAS of finite groups

in their text component, further on they are simply called ATLAS tables. In addition to the information given in Chapters 6–8 of the ATLAS which tell how to read the printed tables, there are some rules relating these to the corresponding GAP tables. Improvements Note that for the GAP library not the printed ATLAS is relevant but the revised version given by the list of Improvements to the ATLAS which can be got from Cambridge. Also some tables are regarded as ATLAS tables which are not printed in the ATLAS but available in ATLAS format from Cambridge; at the moment, these are the tables related to L_{2}(49), L_{2}(81), L_{6}(2), O_{8}^{−}(3), O_{8}^{+}(3) and S_{10}(2). Powermaps In a few cases (namely the tables of 3.McL, 3_{2}.U_{4}(3) and its covers, 3_{2}.U_{4}(3).2_{3} and its covers) the powermaps are not uniquely determined by the given information but determined up to matrix automorphisms (see 42.38) of the characters; then the first possible map according to lexicographical ordering was chosen, and the automorphisms are listed in the text component of the concerned table. Projective Characters For any nontrivial multiplier of a simple group or of an automorphic extension of a simple group, there is a component projectives in the table of G that is a list of records with the names of the covering group (e.g. "12 1.U4(3)") and the list of those faithful characters which are printed in the ATLAS(so–called proxy characters).

Projections

ATLAS tables contain the component projections: For any covering group of G for which the character table is available in ATLAS format a record is stored there containing components name (the name of the cover table) and map (the projection map); the projection maps any class of G to that preimage in the cover for that the column is printed in the ATLAS; it is called g_{0} in Chapter 7, Section 14 there.

(In a sense, a projection map is an inverse of the factor fusion from the cover table to the actual table (see 45.4).)

Tables of Isoclinic Groups

As described in Chapter 6, Section 7 and Chapter 7, Section 18 of the ATLAS, there exist two different groups of structure 2.G.2 for a simple group G which are isoclinic. The ATLAS table in the library is that which is printed in the ATLAS, the isoclinic variant can be got

using 42.17 CharTableIsoclinic.

Succession of characters and classes

(Throughout this paragraph, G always means the involved simple group.)

1. For G itself, the succession of classes and characters in the GAP table is as printed in the ATLAS.

2. For an automorphic extension G.a, there are three types of characters:

• If a character χ of G extends to G.a, the different extensions χ^{0}, χ^{1}, …, χ^{a−1} are consecutive (see ATLAS, Chapter 7, Section 16).

• If some characters of G fuse to give a single character of G.a, the position of that character is the position of the first involved character of G.

• If both extension and fusion occurs, the result characters are consecutive, and each replaces the first involved character.

3. Similarly, there are different types of classes for an automorphic extension G.a:

• If some classes collapse, the result class replaces the first involved class.

• For a > 2, any proxy class and its followers are consecutive; if there are more than one followers for a proxy class (the only case that occurs is for a = 5), the succession of followers is the natural one of corresponding galois automorphisms (see ATLAS, Chapter 7, Section 19).

The classes of G.a_{1} always precede the outer classes of G.a_{2} for a_{1}, a_{2} dividing a and a_{1} < a_{2}. This succession is like in the ATLAS, with the only exception U_{3}(8).6.

4. For a central extension M.G, there are different types of characters:

• Every character can be regarded as a faithful character of the factor group m.G, where m divides M. Characters faithful for the same factor group are consecutive like in the ATLAS, the succession of these sets of characters is given by the order of precedence 1, 2, 4, 3, 6, 12 for the different values of m.

• If m > 2, a faithful character of m.G that is printed in the ATLAS (a so-called proxy) represents one or more followers, this means galois conjugates of the proxy; in any GAP table, the proxy precedes its followers; the case m = 12 is the only one that occurs with more than one follower for a proxy, then the three followers are ordered according to the corresponding galois automorphisms 5, 7, 11 (in that succession).

5. For the classes of a central extension we have:

• The preimages of a G-class in M.G are subsequent, the succession is the same as that of the lifting order rows in the ATLAS.

• The primitive roots of unity chosen to represent the generating central element

(class 2) are E(3), E(4), E(6)^5 (= E(2) *E(3)) and E(12)^7 (= E(3) *E(4))

for m = 3, 4, 6 and 12, respectively.

6. For tables of bicyclic extensions m.G.a, both the rules for automorphic and central extensions hold; additionally we have:

• Whenever classes of the subgroup m.G collapse or characters fuse, the result class resp. character replaces the first involved class resp. character.

• Extensions of a character are subsequent, and the extensions of a proxy character precede the extensions of its followers.

• Preimages of a class are subsequent, and the preimages of a proxy class precede the preimages of its followers.

46.3 Examples of the ATLAS format for GAP tables

We give three little examples for the conventions stated in 46.2, listing up the ATLAS format and the table displayed by GAP. First, let G be the trivial group. The cyclic group C_{6} of order 6 can be viewed in several ways:

1. As a downward extension of the factor group C_{2} which contains G as a subgroup; equivalently, as an upward extension of the subgroup C_{3} which has a factor group G:

3.G  3.G.2 G  G.2

                      ;   @   ;   ;   @
                          1           1
                    p power           A
                    p' part           A
                    ind  1A fus ind  2A
                  χ_{1} +   1   :  ++   1
                    ind   1 fus ind   2
                          3           6
                          3           6
                  χ_{2} o2   1   : oo2   1

                                                 2   1   1   1   1   1   1
                                                 3   1   1   1   1   1   1
                                                    1a  3a  3b  2a  6a  6b
                                                2P  1a  3b  3a  1a  3b  3a
                                                3P  1a  1a  1a  2a  2a  2a
                                              X.1    1   1   1   1   1   1
                                              X.2    1   1   1  -1  -1  -1
                                              X.3    1   A  /A   1   A  /A
                                              X.4    1   A  /A  -1  -A -/A
                                              X.5    1  /A   A   1  /A   A
                                              X.6    1  /A   A  -1 -/A  -A
                                              A = E(3)
                                                = (-1+ER(-3))/2 = b3

X.1, X.2 extend χ_{1}. X.3, X.4 extend the proxy character χ_{2}. X.5, X.6 extend its follower. 1a, 3a, 3b are preimages of 1A, and 2a, 6a, 6b are preimages of 2A.

2. As a downward extension of the factor group C_{3} which contains G as a subgroup; equivalently, as an upward extension of the subgroup C_{2} which has a factor group G:

2.G  2.G.3 G  G.3

                           ;   @   ;   ;   @
                               1           1
                         p power           A
                         p' part           A
                         ind  1A fus ind  3A
                       χ_{1} +   1   : +oo   1
                         ind   1 fus ind   3
                               2           6
                       χ_{2} +   1   : +oo   1

                                                      2   1   1   1   1   1   1
                                                      3   1   1   1   1   1   1
                                                         1a  2a  3a  6a  3b  6b
                                                     2P  1a  1a  3b  3b  3a  3a
                                                     3P  1a  2a  1a  2a  1a  2a
                                                   X.1    1   1   1   1   1   1
                                                   X.2    1   1   A   A  /A  /A
                                                   X.3    1   1  /A  /A   A   A
                                                   X.4    1  -1   1  -1   1  -1
                                                   X.5    1  -1   A  -A  /A -/A
                                                   X.6    1  -1  /A -/A   A  -A
                                                   A = E(3)
                                                     = (-1+ER(-3))/2 = b3

X.1-X.3 extend χ_{1}, X.4-X.6 extend χ_{2}. 1a, 2a are preimages of 1A. 3a, 6a are preimages of the proxy class 3A, and 3b, 6b are preimages of its follower class.

3. As a downward extension of the factor groups C_{3} and C_{2} which have G as a factor group:

6.G 3.G 2.G G

                           ;   @
                               1
                         p power
                         p' part
                         ind  1A
                       χ_{1} +   1
                         ind   1
                               2
                       χ_{2} +   1
                         ind   1
                               3
                               3
                       χ_{3} o2   1
                         ind   1
                               6
                               3
                               2
                               3
                               6
                       χ_{4} o2   1

                                                      2   1   1   1   1   1   1
                                                      3   1   1   1   1   1   1
                                                         1a  6a  3a  2a  3b  6b
                                                     2P  1a  3a  3b  1a  3a  3b
                                                     3P  1a  2a  1a  2a  1a  2a
                                                   X.1    1   1   1   1   1   1
                                                   X.2    1  -1   1  -1   1  -1
                                                   X.3    1   A  /A   1   A  /A
                                                   X.4    1  /A   A   1  /A   A
                                                   X.5    1  -A  /A  -1   A -/A
                                                   X.6    1 -/A   A  -1  /A  -A
                                                   A = E(3)
                                                     = (-1+ER(-3))/2 = b3

X.1, X.2 correspond to χ_{1}, χ_{2}, respectively; X.3, X.5 correspond to the proxies χ_{3}, χ_{4}, and X.4, X.6 to their followers. The factor fusion onto 3.G is [ 1, 2, 3, 1, 2, 3 ], that onto G.2 is [ 1, 2, 1, 2, 1, 2 ].

4. As an upward extension of the subgroups C_{3} or C_{2} which both contain a subgroup G:

G  G.2  G.3  G.6

         ;   @   ;   ;   @   ;   ;   @   ;     ;   @
             1           1           1             1
       p power           A           A            AA
       p' part           A           A            AA
       ind  1A fus ind  2A fus ind  3A fus   ind  6A
     χ_{1} +   1   :  ++   1   : +oo   1   :+oo+oo   1

                                                           2   1   1   1   1   1   1
                                                           3   1   1   1   1   1   1
                                                              1a  2a  3a  3b  6a  6b
                                                          2P  1a  1a  3b  3a  3b  3a
                                                          3P  1a  2a  1a  1a  2a  2a
                                                        X.1    1   1   1   1   1   1
                                                        X.2    1  -1   A  /A  -A -/A
                                                        X.3    1   1  /A   A  /A   A
                                                        X.4    1  -1   1   1  -1  -1
                                                        X.5    1   1   A  /A   A  /A
                                                        X.6    1  -1  /A   A -/A  -A
                                                        A = E(3)
                                                          = (-1+ER(-3))/2 = b3

1a, 2a correspond to 1A, 2A, respectively; 3a, 6a correspond to the proxies 3A, 6A, and 3b, 6b to their followers.

The second example explains the fusion case; again, G is the trivial group.

6.G 3.G 2.G G 6.G.2 3.G.2 2.G.2 G.2

                            ;   @   ;   ;  @
                                1          1
                          p power          A
                          p' part          A
                          ind  1A fus ind 2A
                        χ_{1} +   1   :  ++  1
                          ind   1 fus ind  2
                                2          2
                        χ_{2} +   1   :  ++  1
                          ind   1 fus ind  2
                                3
                                3
                        χ_{3} o2   1   *   +
                          ind   1 fus ind  2
                                6          2
                                3
                                2
                                3
                                6
                        χ_{4} o2   1   *   +

3.G.2

                                                       2   1   .   1
                                                       3   1   1   .
                                                          1a  3a  2a
                                                      2P  1a  3a  1a
                                                      3P  1a  1a  2a
                                                    X.1    1   1   1
                                                    X.2    1   1  -1
                                                    X.3    2  -1   .

6.G.2

                                                       2   2   1   1   2   2   2
                                                       3   1   1   1   1   .   .
                                                          1a  6a  3a  2a  2b  2c
                                                      2P  1a  3a  3a  1a  1a  1a
                                                      3P  1a  2a  1a  2a  2b  2c
                                                    Y.1    1   1   1   1   1   1
                                                    Y.2    1   1   1   1  -1  -1
                                                    Y.3    1  -1   1  -1   1  -1
                                                    Y.4    1  -1   1  -1  -1   1
                                                    Y.5    2  -1  -1   2   .   .
                                                    Y.6    2   1  -1  -2   .   .

The tables of G, 2.G, 3.G, 6.G and G.2 are known from the first example, that of 2.G.2 ≅ V_{4} will be given in the next one. So here we only print the GAP tables of 3.G.2 ≅ D_{6} and 6.G.2 ≅ D_{12}: In 3.G.2, X.1, X.2 extend χ_{1}; χ_{3} and its follower fuse to give X.3, and two of the preimages of 1A collapse. In 6.G.2, Y.1-Y.4 are extensions of χ_{1}, χ_{2}, so these characters are the inflated characters from 2.G.2 (with respect to the factor fusion [ 1, 2, 1, 2, 3, 4 ]). Y.5 is inflated from 3.G.2 (with respect to the factor fusion [ 1, 2, 2, 1, 3, 3 ]), and Y.6 is the result of the fusion of χ_{4} and its follower.

For the last example, let G be the group 2^{2}. Consider the following tables:

2.G  2.G.3 G  G.3

                            ;   @   @   @   @   ;   ;   @
                                4   4   4   4           1
                          p power   A   A   A           A
                          p' part   A   A   A           A
                          ind  1A  2A  2B  2C fus ind  3A
                        χ_{1} +   1   1   1   1   : +oo   1
                        χ_{2} +   1   1  -1  -1   .   +   0
                        χ_{3} +   1  -1   1  -1   .
                        χ_{4} +   1  -1  -1   1   .
                          ind   1   4   4   4 fus ind   3
                                2                       6
                        χ_{5} -   2   0   0   0   : -oo   1

G.3

                                                                 2   2   2   .   .
                                                                 3   1   .   1   1
                                                                    1a  2a  3a  3b
                                                                2P  1a  1a  3b  3a
                                                                3P  1a  2a  1a  1a
                                                              X.1    1   1   1   1
                                                              X.2    1   1   A  /A
                                                              X.3    1   1  /A   A
                                                              X.4    3  -1   .   .
                                                              A = E(3)
                                                                = (-1+ER(-3))/2 = b3

2.G

    2   3   3   2   2   2
       1a  2a  4a  4b  4c
   2P  1a  1a  2a  1a  1a
   3P  1a  2a  4a  4b  4c
 X.1    1   1   1   1   1
 X.2    1   1   1  -1  -1
 X.3    1   1  -1   1  -1
 X.4    1   1  -1  -1   1
 X.5    2  -2   .   .   .

2.G.3

                                  2   3   3   2   1   1   1   1
                                  3   1   1   .   1   1   1   1
                                     1a  2a  4a  3a  6a  3b  6b
                                 2P  1a  1a  2a  3b  3b  3a  3a
                                 3P  1a  2a  4a  1a  2a  1a  2a
                               X.1    1   1   1   1   1   1   1
                               X.2    1   1   1   A   A  /A  /A
                               X.3    1   1   1  /A  /A   A   A
                               X.4    3   3  -1   .   .   .   .
                               X.5    2  -2   .   1   1   1   1
                               X.6    2  -2   .   A  -A  /A -/A
                               X.7    2  -2   .  /A -/A   A  -A
                               A = E(3)
                                 = (-1+ER(-3))/2 = b3

In the table of G.3 ≅ A_{4}, the characters χ_{2}, χ_{3} and χ_{4} fuse, and the classes 2A, 2B and 2C collapse. To get the table of 2.G ≅ Q_{8} one just has to split the class 2A and adjust the representative orders. Finally, the table of 2.G.3 ≅ SL_{2}(3) is given; the subgroup fusion corresponding to the injection 2.G ↪ 2.G.3 is [ 1, 2, 3, 3, 3 ], and the factor fusion corresponding to the epimorphism 2.G.3 → G.3 is [ 1, 1, 2, 3, 3, 4, 4 ].

46.4 CAS Tables

All tables of the CAS table library are available in GAP, too. This sublibrary has been completely revised, i.e., errors have been corrected and powermaps have been completed.

Any CAS table is accessible by each of its CAS names, that is, the table name or the filename (see 42.9):

    gap> t:= CharTable( "m10" );; t.name;
    "A6.2_3"

One does, however, not always get the original CAS table: In many cases (mostly ATLAS tables, see 46.2) not only the name but also the succession of classes and characters has changed; the records in the component CAS of the table (see 42.2) contain the permutations which must be applied to classes and characters to get the original CAS table:

    gap> t.CAS;
    [ rec(
          name := "m10",
          permchars := (3,5)(4,8,7,6),
          permclasses := (),
          text :=
           "names:     m10\norder:     2^4.3^2.5 = 720\nnumber of classes:\
     8\nsource:    cambridge atlas\ncomments:  point stabilizer of mathieu\
    -group m11\ntest:      orth, min, sym[3]                            \n\
    " ) ]

The subgroup fusions were computed anew; their record component text tells if the fusion is equal to that in the CAS library –of course modulo the permutation of classes.

Note that the fusions are neither tested to be consistent for any two subgroups of a group and their intersection, nor tested to be consistent with respect to composition of maps.

46.5 Organization of the Table Libraries

The primary file is TBLNAME/ctprimar.tbl. It contains the evaluation function CharTable (see 42.9) and some utilities.

The global variable LIBLIST which contains the information about all tables of the libraries is in the file TBLNAME/ctlist.tbl; LIBLIST is a record with components ORDINARY and GENERIC, both lists; their entries are records with components firstname (the value of the name component of the table), filename (the name of the file that contains the table, with respect to TBLNAME, for ordinary tables also othernames (a list of admissible other names) and possibly CASnames (the list of names that table had in CAS).

LIBLIST can be computed from the libinfo components of the tables using MakeLIBLIST which reads all tables and returns LIBLIST.

Also the secondary files are all stored in the directory TBLNAME; they are ctlist.tbl and

    ctbalter.tbl  ctbatres.tbl  ctbconja.tbl  ctbfisc1.tbl  ctbfisc2.tbl
    ctbline1.tbl  ctbline3.tbl  ctbline4.tbl  ctbline5.tbl  ctbmathi.tbl
    ctbmonst.tbl  ctborth1.tbl  ctborth2.tbl  ctborth3.tbl  ctbspora.tbl
    ctbsympl.tbl  ctbtwist.tbl  ctbunit1.tbl  ctbunit2.tbl  ctbunit3.tbl
    ctbunit4.tbl  ctgeneri.tbl  ctoalter.tbl  ctoatres.tbl  ctoconja.tbl
    ctofisc1.tbl  ctofisc2.tbl  ctoline1.tbl  ctoline2.tbl  ctoline3.tbl
    ctoline4.tbl  ctoline5.tbl  ctomathi.tbl  ctomaxi1.tbl  ctomaxi2.tbl
    ctomaxi3.tbl  ctomaxi4.tbl  ctomisc1.tbl  ctomisc2.tbl  ctomisc3.tbl
    ctomisc4.tbl  ctomisc5.tbl  ctomisc6.tbl  ctomonst.tbl  ctonews.tbl
    ctoorth1.tbl  ctoorth2.tbl  ctoorth3.tbl  ctoorth4.tbl  ctoorth5.tbl
    ctospora.tbl  ctosylno.tbl  ctosympl.tbl  ctotwist.tbl  ctounit1.tbl

    ctounit2.tbl  ctounit3.tbl  ctounit4.tbl

The names begin with ct for “character table”, followed by o for “ordinary”, b for “Brauer” or g for “generic”, then an up to 5 letter description of the contents, e.g., alter for the alternating groups, and the extension .tbl. The file ctbdescr.tbl contains (at most) the Brauer tables corresponding to the ordinary tables in ctodescr.tbl. The format of library tables is always like this:

    LIBTABLE.filename.(tblname):=rec(
          ...

# here the record components are stored

          ... );

Here filename is the name of the file containing the table, relative to TBLNAME, e.g. ctoalter, and tblname is the value of the name component of the table, e.g. "A5". For the contents of the table record, there are three different ways how tables are stored: Full tables (like that of A_{5}) are stored similar to the internal format (see 42.2). Lists of characters, however, will be abbreviated in the following way: For each subset of characters which differ just by multiplication with a linear character or by Galois conjugacy, only one is given by its values, the others are replaced by [TENSOR,[i,j ]] (which means that the character is the tensor product of the i-th and the j -th character) or [GALOIS,[i,j ]] (which means that the character is the j -th Galois conjugate of the i-th character. Brauer tables (like that of A_{5} mod 2) are stored relative to the corresponding ordinary table; instead of irreducible characters the files contain decomposition matrices or Brauer trees for the blocks of nonzero defect (see 42.3), and components which can be got by restriction to p–regular classes are not stored at all. Construction tables (like that of O_{8}^{−}(3)M7) have a component construction that is a function of one variable. This function is called by CharTable (see 42.9) when the table is constructed, i.e. not when the file containing the table is read. The aim of this rather complicated way to store a character table is that big tables with a simple structure (e.g. direct products) can be stored in a very compact way. Another special case where construction tables are useful is that of projective tables: In their component irreducibles they do not contain irreducible characters but a list with information about the factor groups: Any entry is a list of length 2 that contains at position 1 the name of the table of the factor group, at the second position a list of integers representing the Galois automorphisms to get follower characters. E.g., for 12.M_{22}, the

value of irreducibles is
    [["M22",[]],["2.M22",[]],
     ["3.M22",[-1,-13,-13,-1,23,23,-1,-1,-1,-1,-1]],
     ["4.M22",[-1,-1,15,15,23,23,-1,-1]],,
     ["6.M22",[-13,-13,-1,23,23,-1,-7,-7,-1,-1]],,,,,,
     ["12.M22",[[17,-17,-1],[17,-17,-1],[-55,-377,-433],[-55,-377,-433],
     [89,991,1079],[89,991,1079],[-7,7,-1]]]]

Using this and the projectives component of the table of the smallest nontrivial factor group, 42.9 CharTable constructs the irreducible characters. The table head, however, need not be constructed.

46.6 How to Extend a Table Library

If you have some ordinary character tables which are not (or not yet) in a GAP table library, but which you want to treat as library tables, e.g., assign them to variables using 42.9 CharTable, you can include these tables. For that, two things must be done:

First you must notify each table, i.e., tell GAP on which file it can be found, and which names are admissible; this can be done using

NotifyCharTable( firstname, filename, othernames ),

with strings firstname (the name component of the table) and filename (the name of the file containing the table, relative to TBLNAME, and without extension .tbl), and a list othernames of strings which are other admissible names of the table (see 42.9).

NotifyCharTable will add a record with these information to LIBLIST.ORDINARY. A warning is printed for each table libtbl that was already accessible by some of the names, and delete these names in the LIBLIST.ORDINARY component of libtbl. Of course this affects only the value of LIBLIST in the current session, not that on the file.

Note that an error is raised if you want to notify a table with firstname or name in othernames which is already the name component of a library table.

    gap> NotifyCharTable( "Private", "../tables/mytables", [ "My" ] );

# tells GAP that the table with names "Private" and "My" # is stored on file mytables.tbl in the given directory

    gap> list:= List( LIBLIST.ORDINARY, x ->x.firstname );;
    gap> LIBLIST.ORDINARY[ Position( list, "Private" ) ];
    rec(
      firstname := "Private",
      filename := "../tables/mytables",
      othernames := [ "My" ] )

The second condition is that each file must contain tables in library format as described in 46.5; in the example, the contents of the file may be this:

    LIBTABLE.'../tables/mytables'.Private:=
    rec(name:="Private",centralizers:=[1,1],irreducibles:=[[1,1],[1,-1]])
    );

Now the private table is a library table:

    gap> CharTable( "My" );
    rec(
      name := "Private", centralizers:= [ 1, 1 ],
      irreducibles := [ [ 1, 1 ], [ 1, -1 ] ] )

To append the table tbl in library format to the file with name file, use

PrintToLib( file, tbl ).

Note that here file is the absolute name of the file, not the name relative to TBLNAME. Thus the filename in the row with the assignment to LIBTABLE must be adjusted to make the file a library file.

46.7 FirstNameCharTable

FirstNameCharTable( name )

returns the value of the name component of the character table with admissible name name, if exists; otherwise false is returned.

For each admissible name, also the lowercase string is admissible.

    gap> FirstNameCharTable( "m22mod3" ); FirstNameCharTable( "s5" );
    "M22mod3"
    "A5.2"
    gap> FirstNameCharTable( "J5" );
    false

46.8 FileNameCharTable

FileNameCharTable( firstname )

returns the value of the filename component of the information record in LIBLIST for the table with name component firstname, if exists; otherwise false is returned.

    gap> FileNameCharTable( "m22mod3" ); FileNameCharTable( "M22mod3" );
    false
    "ctbmathi"

Getting and Installing GAP

GAP runs on a large number of different operating systems. It behaves slightly different on each of those. This chapter describes the behaviour of GAP, the installation, and the options on some of those operating systems. Currently it contains sections for UNIX (see 47.3), which runs on an ever increasing number of machines, for MS-DOS (see 47.6), which is one operating system on IBM PC compatibles, and TOS (see 47.10), which is the operating system on Atari ST. We hope to provide ports of GAP to DEC VAX or AXP systems under VMS and the Apple Macintosh under MPW soon. For other systems the section 47.14 gives hints how to approach such a port.

47.1 Getting GAP

GAP is distributed free of charge. You can obtain it via ftp and give it away to your colleagues. GAP is not in the public domain, however. In particular you are not allowed to incorporate GAP or parts thereof into a commercial product. If you get GAP, we would appreciate it if you could notify us, e.g., by sending a short e-mail message to gap@samson.math.rwth-aachen.de, containing your full name and address, so that we have a rough idea of the number of users. We also hope that this number will be large enough to convince various agencies that GAP is a project worthy of (financial) support. If you publish some result that was partly obtained using GAP, we would appreciate it if you would cite GAP, just as you would cite another paper that you used. Again we would appreciate if you could inform us about such a paper. We distribute the full source for everything, the C code for the kernel, the GAP code for the library, and the LaTEX code for the manual, which has at present about 900 pages. So it should be no problem to get GAP, even if you have a rather uncommon system. Of course, ports to non UNIX systems may require some work. We already have ports for IBM PC compatibles with an Intel 80386 or 80486 under MS-DOS, Windows, or OS/2, and for the Atari ST under TOS. We hope to provide ports to DEC VAX or AXP systems under VMS and the Apple Macintosh under MPW soon. Note that about 4 MByte of main memory and a harddisk are required to run GAP. GAP 3.3 can be obtained by anonymous ftp from the following servers.

samson.math.rwth-aachen.de

Lehrstuhl D fur Mathematik, RWTH Aachen, Germany, internet number 137.226.152.6, directory /pub/gap/.

dimacs.rutgers.edu

DIMACS, Rutgers, New Brunswick, New Jersey, internet number 128.6.75.16, directory /pub/gap/.

math.ucla.edu

Math. Dept., Univ. of California at Los Angeles, internet number 128.97.4.254, directory /pub/gap/.

wuarchive.wustl.edu

Math. Archives, Washington Univ. at St. Louis, internet number 128.252.135.4,

directory /edu/math/source.code/group.theory/gap.
dehn.mth.pdx.edu

PSU Mathematics Department, Portland State Univ., internet number 131.252.40.89,

directory /mirror/gap/
pell.anu.edu.au

Math. Dept., Australian National Univ., Canberra, internet number 150.203.15.5, directory /pub/gap/.

ftp to the server closest to you, login as user ftp and give your full e-mail address as password. Remember when you transmit the files to set the file transfer type to binary image, otherwise you will only receive unusable garbage. Those servers will always have the latest version of GAP available.

For users in the United Kingdom with Janet access ftp may not work. Please contact Derek Holt (e-mail address dfh@maths.warwick.ac.uk). He has kindly offered us to distribute GAP in the United Kingdom.

The ftp directory contains the following files. Please check first which files you need, to avoid transferring those that you don’t need.

README

the file you are currently reading.

gap3r3p0.zoo

This file contains the complete distribution of GAP version 3 release 3 current patchlevel 0. It is a zoo archive approximately 7 MByte large.

unzoo.c

A simple zoo archive extractor, which should be used to unpack the distribution. The utils subdirectory contains ready compiled executables for common systems.

More files are in the following subdirectories

bin

This directory contains executables for systems that dont come with a C compiler or where another C compiler produces a faster executable. The KERNELS file tells you which executables are here.

split

This directory contains the complete distribution of GAP 3r3p0 in several archives. This allows you to get only the parts that you are really interested in. The SPLIT file tells you which archive contains what.

utils

This directory contains several utilities that you may need to get or upgrade GAP, e.g., unzoo and patch. The UTILS file tells you which files are here.

47.2 Upgrading GAP

From time to time we will upgrade GAP. There are generally two types of upgrades. The major upgrades create new releases and the minor are upgrades between releases. New releases usually introduce new features while upgrades between releases usually only fix bugs found in one release.

Each version has a number. The number of a release consists of two parts. The first part is the version number, which is currently 3. The second part is the release number, which is 1 for the first release, 2 for the second, and so on. The current number is version 3 release 3, which we sometimes abbreviate to V3R3. The number of a upgrade between releases consists of three parts. The first and second part are the version and release number of the current release. The third part is called the patchlevel. The first upgrade after a release has patchlevel 1, the second patchlevel 2, and so on. The current number is version 3 release 3 patchlevel 0, which we sometimes abbreviate to V3R3P0.

Because it might be too much work for you to obtain the complete distribution for every upgrade, we also make the differences between one upgrade and the next one available. They are stored in a single file, called an upgrade file. For example the differences between V3R3P0 and V3R3P1 will be stored in a file called upg3r3p1.dif.

Note that upgrade files always take you from one version to the next. So to upgrade to V3R3P2 you must first upgrade to V3R3P1.

To make it easier for you to apply an upgrade, the upgrade files are in a special format, called (unified) diff, which a special program, called patch, can understand. patch reads the upgrade file and automatically makes all the necessary changes to the files. This program is available on most UNIX machines. However, we also make it available on the ftp servers, so you can get it from there if your machine does not have one or if your machine has one that is too old to understand unified diff-s.

To apply an upgrade you need the sources for the parts of GAP that you are interested in. This should at least be the sources, the library, and the documentation. You may also have the groups library, the primitive groups library, the 2-groups library, and the character table library. If an upgrade changes a file in one of the parts that you do not have, patch will ask you about the file it cannot find. Then you can tell patch to ignore this change, and it will continue to change those files it can find.

In the following example, we assume that you (as user you) have all parts of GAP, that GAP is installed in the subdirectory gap/ of your homedirectory, and that you want to upgrade

from V3R1 to V3R1P1.

The first thing of course is to get the upgrade file. The upgrade files are available at the same places as the rest of GAP (see 47.1). If you are subscribed to the GAP-forum, you will also get the upgrade files as e-mail messages. In this example we will assume that you have obtained the upgrade file upg3r1p1.dif from one of the ftp servers.

you@ernie:~/gap > ls -l
drwxr-xr-x  2 you          1536 May 25 18:03 doc
drwxr-xr-x  2 you           512 May 25 18:03 grp
drwxr-xr-x  2 you          1536 May 25 18:02 lib
drwxr-xr-x  2 you          1536 May 25 18:01 src
drwxr-xr-x  2 you          1536 May 25 18:04 tbl
drwxr-xr-x  2 you          1024 May 25 18:04 two
-rw-r--r--  1 you        123695 May 25 18:04 upg3r1p1.dif
you@ernie:~/gap >

The first thing that you should do is always to read the beginning of the upgrade file. It contains instructions on how to apply the upgrade. You should always read this to see if there are special things that must be done before this upgrade can be applied. For example, before you can apply the upgrade from V3R1P1 to V3R1P2 you must first make a copy of the file src/system.st. In our example, i.e., the upgrade from V3R1 to V3R1P1 no special tasks must be performed. Now you apply the upgrade with the following command.

you@ernie:~/gap > patch -p0 < upg3r1p1.dif
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
!What is this file?
!==================

# patch prints the introduction text, about 200 lines long

!Now for the upgrade itself
!==========================
!
!diff -u doc/aboutgap.tex doc/aboutgap.tex
!--- doc/aboutgap.tex   Thu Apr  9 14:06:31 1992
!+++ doc/aboutgap.tex   Thu Apr 30 14:38:06 1992
--------------------------
Patching file doc/aboutgap.tex using Plan A...
Hunk #1 succeeded at 6.
Hunk #2 succeeded at 13.
Hunk #3 succeeded at 493.
Hunk #4 succeeded at 529.
Hunk #5 succeeded at 1074.
Hunk #6 succeeded at 5168.
Hmm...  The next patch looks like a unified diff to me...
The text leading up to this was:
--------------------------
!diff -u doc/aggroup.tex doc/aggroup.tex

!--- doc/aggroup.tex    Thu Apr  9 14:06:33 1992
!+++ doc/aggroup.tex    Thu Apr 30 14:20:05 1992
--------------------------
Patching file doc/aggroup.tex using Plan A...
Hunk #1 succeeded at 2.
Hunk #2 succeeded at 10.
Hunk #3 succeeded at 336.
Hunk #4 succeeded at 2708.

# similar output for 70 more files, about 700 lines

Hmm...  The next patch looks like a unified diff to me...
The text leading up to this was:
--------------------------
!diff -u src/word.h src/word.h
!--- src/word.h Fri Apr  3 20:56:34 1992
!+++ src/word.h Wed Apr 29 13:28:43 1992
--------------------------
Patching file src/word.h using Plan A...
Hunk #1 succeeded at 3.
done
you@ernie:~/gap >

See below about the things that could go wrong. For the moment we will assume that everything worked, i.e., that patch did not complain.

Next compile the source of the kernel again. How this is done is described in the installation sections (see 47.4). Note that it may not be necessary to do this if the source of the kernel did not change, or if the changes were only cosmetic. The introduction of the upgrade file will tell you if it is really necessary or not.

Then LaTEX the documentation again. How this is done is also described in the installation sections (see 47.4, 47.8, 47.12). Again this may not be necessary, if the changes to the manual were only minor. Again the introduction of the upgrade file will tell you if it is really necessary or not.

Now you may want to throw away the original files. patch saves them by appending the suffix .orig to the original file name. So to remove all original files issue the following command.

you@ernie:~/gap > rm -f */*.orig
you@ernie:~/gap >

There are several things that can go wrong.

The first possibility is that you don’t have patch. In this case get patch, for example by anonymous ftp from samson.math.rwth-aachen.de (Internet number 137.226.152.6).

Another possibility is that you have a version of patch that is too old. Versions of patch before 2.0.2.0 (patchlevel 12u4) don’t understand the unified diff format of this upgrade file. In this case you will see the following message.

you@ernie:~/gap > patch -p0 < upg3r1.dif
Hmm...  I can't seem to find a patch in there anywhere.

you@ernie:~/gap >

To solve this problem, get a newer version of patch. For example by anonymous ftp from

samson.math.rwth-aachen.de.

Another possibility is that you forgot the -p0 option for patch. In this case patch assumes that the files to patch are all in the current directory (not in the subdirectories doc, grp, etc.). It can’t find them there of course. Therefore it asks you for the names of the files to patch interactively.

you@ernie:~/gap > patch -p0 < upg3r1p1.dif
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
!What is this file?
!==================

# patch prints the introduction text, about 200 lines long

!Now for the upgrade itself
!==========================
!
!diff -u doc/aboutgap.tex doc/aboutgap.tex
!--- doc/aboutgap.tex   Thu Apr  9 14:06:31 1992
!+++ doc/aboutgap.tex   Thu Apr 30 14:38:06 1992
--------------------------
File to patch:

Stop patch by hitting ctr-C, and start it anew, this time with the option -p0.

Another possibility is that you already changed a file. For example if you already changed word.c, which could not be compiled with GNU cc on the NeXT, you will get the following message (note the message that hunk #3 failed).

you@ernie:~/gap > patch -p0 < upg3r1p1.dif

# the usual output from patch

Hmm...  The next patch looks like a unified diff to me...
The text leading up to this was:
--------------------------
!diff -u src/word.c src/word.c
!--- src/word.c Sun Apr  5 23:29:44 1992
!+++ src/word.c Wed Apr 29 13:28:43 1992
--------------------------
Patching file src/word.c using Plan A...
Hunk #1 succeeded at 3.
Hunk #2 succeeded at 10.
Hunk #3 failed at 2094.
Hunk #4 succeeded at 2218.
Hunk #5 succeeded at 2469.
1 out of 5 hunks failed--saving rejects to src/word.c.rej

# the rest of the output from patch

you@ernie:~/gap >

In this case examine the *.rej files carefully after running patch. They will tell you what patch expected to find in the file, and what it really found. Then examine the source file itself to see if you can ignore the rejection, or if you should apply the patch manually.

Another possibility is that patch cannot find the files it should patch. For example, if you don’t have the groups library installed patch will give the following output.

you@ernie:~/gap > patch -p0 < upg3r1p1.dif

# the usual output from patch

Hmm...  The next patch looks like a unified diff to me...
The text leading up to this was:
--------------------------
!diff -u grp/permgrp.grp grp/permgrp.grp
!--- grp/permgrp.grp  Tue Apr  7 22:39:12 1992
!+++ grp/permgrp.grp  Thu Apr 30 13:14:12 1992
--------------------------
File to patch:

Simply type return to this question. patch will ask you then.

No file found--skip this patch? [n]

Answer with yreturn, i.e., tell patch to ignore this file. patch will then show the following output.

Skipping patch...
Hunk #1 ignored at 2.
Hunk #2 ignored at 9.
Hunk #3 ignored at 83.
3 out of 3 hunks ignored--saving rejects to grp/permgrp.grp.rej
patch: can't create grp/permgrp.grp.rej,
       output is in /tmp/patchra22700.

Then it will continue to apply the other patches.

47.3 GAP for UNIX

GAP runs very well under UNIX. In fact it is being developed on UNIX workstations. GAP running on any UNIX machine should behave exactly as described in the manual.

GAP has successfully been compiled and installed on the following UNIX machines.

Vendor,  Model,            Processor,  System,      Compiler
DEC,     DECstation 3100,  MIPS R2000, Ultrix 4.0,  cc and GNU gcc
DEC,     DECstation 5120,  MIPS R3000, Ultrix 4.2,  cc and GNU gcc
HP,      HP 9000/825,      HP-PA 1.0,  HP-UX 7.0,   cc
HP,      HP 9000/720,      HP-PA 1.1,  HP-UX 8.0.5, cc

    IBM,     POWERstation 530, RS/6000,    AIX,         cc
    IBM,     PC,               i386/i486,  386BSD,      GNU gcc
    IBM,     PC,               i386/i486,  Linux,       GNU gcc
    MIPS,    M120/5,           MIPS R2000, RiscOS,      cc
    NeXT,    NeXTstation,      MC68040,    Mach,        GNU gcc
    Sequent, Symmetry,         ---,        Dynix,       cc,
    SGI,     Iris,             MIPS R3000, Irix,        cc and GNU gcc
    Sun,     Sun 3/60,         MC68020,    SunOS 4.0.1, cc and GNU gcc
    Sun,     Sun 4/280,        Sparc,      SunOS 4.0.1, cc and GNU gcc

We hope that compiling and installing GAP on another UNIX machine does not pose any problem. If it does, please inform us of your problems, and, if possible of your solution.

The section 47.4 describes how you install GAP on a UNIX machine, and the section 47.5 describe the options that GAP accepts undex UNIX.

47.4 Installation of GAP for UNIX

Installation of GAP for UNIX is fairly easy.

First go to the directory where you want to install GAP. If you will be the only user using GAP, you probably should install it in you homedirectory. If other users will be using GAP also, you should install it in a public place, such as /usr/local/lib/. GAP will be installed in a subdirectory gap3r3p0 of this directory. You can later move GAP to a different location. For example you can first install it in your homedirectory and when it works move it to /usr/local/lib/. In the following example we will assume that you, as user you, want to install GAP for your own use in your homedirectory on a DECstation called ernie. Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

Get the distribution gap3r3p0.zoo and the source for the zoo archive extractor unzoo.c. How you can get those files is described in the section 47.1. The usual way would be to get it with ftp onto your machine. Remember that the distribution consists of binary files and that you must transmit them in binary mode.

Compile the zoo archive extractor unzoo with the command

    you@ernie:~ > cc  -o unzoo  -DSYS_IS_UNIX  -O unzoo.c
    you@ernie:~ > ls -l unzoo
    -rwxr-xr-x     you       45056 Nov  3 10:11 unzoo
    you@ernie:~ >

Unpack the distribution with the command

    you@ernie:~ > unzoo -x gap3r3p0.zoo
    gap3r3p0/bin/gap.sh      -- extracted as text
    gap3r3p0/doc/aboutgap.tex        -- extracted as text
    gap3r3p0/doc/aggroup.tex         -- extracted as text

# about 500 more lines

    gap3r3p0/two/twogpd8.grp         -- extracted as text
    you@ernie:~ >

Change into the source directory gap3r3p0/src/ and execute make to see which compilation targets are predefined.

you@ernie:~ > cd gap3r3p0/src
you@ernie:~/gap3r3p0/src > make
usage: 'make <target>' where target is one of
'atari-m68k-tos-gcc2'   for Atari ST under TOS with GNU cc 2
'dec-mips-ultrix-gcc2'  for DECstation under Ultrix with GNU cc 2
'dec-mips-ultrix-cc'    for DECstation under Ultrix with cc
'hp-hppa1.0-hpux-cc'    for HP 9000/800 under HP-UX with cc
'hp-hppa1.1-hpux-cc'    for HP 9000/700 under HP-UX with cc
'ibm-power-aix-cc'      for IBM RS/6000 under AIX with cc
'ibm-i386-386bsd-gcc2'  for IBM PC under 386BSD with GNU cc 2
'ibm-i386-386bsd-cc'    for IBM PC under 386BSD with cc (GNU)
'ibm-i386-linux-gcc2'   for IBM PC under Linux with GNU cc 2
'ibm-i386-msdos-djgpp'  for IBM PC under MSDOS with DJ GNU cc
'ibm-i386-os2-emx'      for IBM PC under OS/2 with EMX GNU cc
'mips-mips-bsd-cc'      for MIPS under RISC/os Berkeley with cc
'mips-mips-sysv-cc'     for MIPS under RISC/os System V with cc
'next-m68k-mach-gcc2'   for NeXT under Mach with GNU cc 2
'next-m68k-mach-cc'     for NeXT under Mach with cc (GNU)
'sequent-i386-dynix-cc' for Sequent Symmetry under Dynix with cc
'sgi-mips-irix-gcc2'    for SGI under Irix with GNU cc 2
'sgi-mips-irix-cc'      for SGI under Irix with cc
'sun-m68k-sunos-gcc2'   for SUN 3 under SunOs with GNU cc 2
'sun-m68k-sunos-cc'     for SUN 3 under SunOS with cc
'sun-sparc-sunos-gcc2'  for SUN 4 under SunOs with GNU cc 2
'sun-sparc-sunos-cc'    for SUN 4 under SunOS with cc
'bsd'                   for others under Berkeley UNIX with cc
'usg'                   for others under System V UNIX with cc
   targets are listed according to preference, i.e.,
   'sun-sparc-sunos-gcc2' is better than 'sun-sparc-sunos-cc'.
   additional C compiler and linker flags can be passed with
   'make <target> COPTS=<compiler-opts> LOPTS=<linker-opts>',
   i.e., 'make sun-sparc-sunos-gcc2 COPTS=-g LOPTS=-g.'
you@ernie:~/gap3r3p0/src >

Choose the best matching target. If nothing matches precisely, use bsd (if your UNIX is more Berkeley) or usg (if your UNIX is more System V). If compilation of the file system.c fails or the command line editing does not work, remove the file system.o and try the other target. If system.c cannot be compiled neither with target bsd nor with target usg, look at this file and try to modify it so that compilation works. If you can compile but command line editing does not work with either target, we suggest that you always start GAP with option -n to disable command line editing. In any case we would like to hear about such problems.

In our example the right target is obviously dec-mips-ultrix-cc (remember ernie was assumed to be a DECstation). Compile GAP.

you@ernie:~/gap3r3p0/src > make dec-mips-ultrix-cc
cc -DSYS_IS_BSD -DSYS_HAS_ANSI=1 -c system.c
cc -O2 -c gap.c

# about 30 more lines

you@ernie:~/gap3r3p0/src > ls -l gap
-rwxr-xr-x     you      711620 Nov 13 12:47 gap
you@ernie:~/gap3r3p0/src >

You may want to keep the source and object files around until you are certain that everything works. However if space is a tight resource on your system you may remove the source and object files now. All you need in the src/ subdirectory from now on is (are) the executable(s).

Copy or move the executable to the gap3r3p0/bin/ directory.

you@ernie:~/gap3r3p0/src > mv gap ../bin
you@ernie:~/gap3r3p0/src > cd ../..
you@ernie:~ >

Then edit the shell script gap.sh in the gap3r3p0/bin/ directory according to the instructions in this file. This script will start GAP and is the place for all neccessary configurations. Then copy this script to a directory in your search path, i.e., /bin/. (If you are using the C-shell, you will also have to rehash, so that the C-shell adds gap to its internal tables).

you@ernie:~ > cd gap3r3p0/bin
you@ernie:~/gap3r3p0/bin > cp gap.sh ~/bin/gap
you@ernie:~/gap3r3p0/bin > cd ../..
you@ernie:~ >

When you later move GAP to another location you must only edit this script.

Now start GAP and try a few things. The -b option suppresses the banner. Note that GAP has to read most of the library for the fourth statement below, so this takes quite a while. Subsequent definitions of groups will be much faster.

you@ernie:~ > gap -b
gap> 2 * 3 + 4;
10
gap> Factorial( 30 );
265252859812191058636308480000000
gap> Factors( 10^42 + 1 );
[ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]
gap> m11 := Group((1,2,3,4,5,6,7,8,9,10,11),(3,7,11,8)(4,10,5,6));;
gap> Size( m11 );
7920
gap> Factors( 7920 );
[ 2, 2, 2, 2, 3, 3, 5, 11 ]
gap> Number( ConjugacyClasses( m11 ) );
10

Especially try the command line editing and history facilities, because the are probably the most machine dependent feature of GAP. Enter a few commands and then make sure that ctr-P redisplays the last command, that ctr-E moves the cursor to the end of the line, that ctr-B moves the cursor back one character, and that ctr-D deletes single characters. So, after entering the above commands, typing ctr-P ctr-P ctr-E ctr-B ctr-B ctr-B ctr-B ctr-D 1 should give the following line.

gap> Factors( 7921 );

    [ 89, 89 ]

If command line editing does not work, remove the file system.o and try to compile with a different target, i.e., bsd instead of usg or vice versa. If neither works, we suggest that you disable command line editing by calling GAP always with the -n option. In any case we would like to hear about such problems.

If your operating system has job control, make sure that you can still stop GAP, which is usually done by pressing ctr-Z.

If you have a big version of LaTEX available, you may now want to make a printed copy of the manual. Change into the directory gap3r3p0/doc/ and run LaTEX twice on the source. The first pass with LaTEX produces the .aux files, which resolve all the cross references. The second pass produces the final formatted dvi file manual.dvi. Then print the dvi file. How this is done depends on your local TEX installation, if in doubt ask your local computer guru.

    you@ernie:~ > cd gap3r3p0/doc
    you@ernie:~/gap3r3p0/doc > latex manual

# about 2400 messages about undefined references

    you@ernie:~/gap3r3p0/doc > latex manual

# there should be no warnings this time

    you@ernie:~/gap3r3p0/doc > ls -l manual.dvi
    -rw-r--r--     you    2806136 Nov  3 23:28 manual.dvi
    you@ernie:~/gap3r3p0/doc > lp -dvi manual.dvi
    you@ernie:~/gap3r3p0/doc > cd ../..
    you@ernie:~ >

Note that because of the large number of cross references in the manual you need a big LaTEX to format the GAP manual. If you see the error message TeX capacity exceeded, you do not have a big LaTEX; again ask your local computer guru to provide a big version of LaTEX. If this is not possible, you may also obtain the already formatted dvi file manual.dvi from the same place where you obtained the rest of the GAP distribution.

Note that, apart from the *.tex files and the file manual.bib (bibliography database), which you absolutely need, we supply also the files manual.toc (table of contents), manual.ind (unsorted index), manual.idx (sorted index), and manual.bbl (bibliography). If those files are missing, or if you prefer to do everything yourself, here is what you will have to do. After the first pass with LaTEX, you will have preliminary manual.toc and manual.ind files. All the page numbers are still incorrect, because the do not account for the pages used by the table of contents itself. Now bibtex manual will create manual.bbl from manual.bib. After the second pass with LaTEX you will have a correct manual.toc and manual.ind. makeindex now produces the sorted index manual.idx from manual.ind. The third pass with LaTEX incorporates this index into the manual.

    you@ernie:~ > cd gap3r3p0/doc
    you@ernie:~/gap3r3p0/doc > latex manual

# about 2000 messages about undefined references

    you@ernie:~/gap3r3p0/doc > bibtex manual

# bibtex prints the name of each file it is scanning

    you@ernie:~/gap3r3p0/doc > latex manual

# still some messages about undefined citations

    you@ernie:~/gap3r3p0/doc > makeindex manual

# makeindex prints some diagnostic output

    you@ernie:~/gap3r3p0/doc > latex manual

# there should be no warnings this time

    you@ernie:~/gap3r3p0/doc > lp -dvi manual.dvi
    you@ernie:~/gap3r3p0/doc > cd ../..
    you@ernie:~ >

Thats all, finally you are done. We hope that you will enjoy using GAP. If you have problems, do not hesitate to contact us.

47.5 Features of GAP for UNIX

When you start GAP for UNIX, you may specify a number of options on the command-line to change the default behaviour of GAP. All these options start with a hyphen -, followed by a single letter. Options must not be grouped, e.g., gap -gq is illegal, use gap -g -q instead. Some options require an argument, this must follow the option and must be separated by a space, e.g., gap -m 256k, it is not correct to say gap -m256k instead.

GAP for UNIX will only accept lower case options.

As is described in the previous section (see 47.4) usually you will not execute GAP directly. Instead you will call a shell script, with the name gap, which in turn executes GAP. This shell script sets some options as neccessary to make GAP work on your system. This means that the default settings mentioned below may not be what you experience when you execute GAP on your system.

-g

The option -g tells GAP to print a information message every time a garbage collection is performed.

# G collect garbage, 1931 used, 5012 dead, 912 KB free, 4096 KB total

For example, this tells you that there are 1931 live objects that surived a garbage collection, that 5012 unused objects were reclaimed by it, and that 912 KByte of totally allocated 4096 KBytes are available afterwards.

-l libname

The option -l tells GAP that the library of GAP functions is in the directory libname. Per default libname is lib/, i.e., the library is normally expected in the subdirectory lib/ of the current directory. GAP searches for the library files, whose filenames end in .g, and which contain the functions initially known to GAP, in this directory. libname should end with a pathname separator, i.e., /, but GAP will silently add one if it is missing. GAP will read the file libname/init.g during startup. If GAP cannot find this file it will print the following warning

    gap: hmm, I cannot find 'lib/init.g', maybe use option '-l <lib>'?

If you want a bare bones GAP, i.e., if you do not need any library functions, you may ignore this warning, otherwise you should leave GAP and start it again, specifying the correct library path using the -l option.

It is also possible to specify several alternative library pathes by separating them with semicolons ;. Note that in this case all path names must end with the pathname separator /. GAP will then search for its library files in all those directories in turn, reading the first it finds. E.g., if you specify -l "lib/;/usr/local/lib/gap3r3p0/lib/" GAP will search for a library file first in the subdirectory lib/ of the current directory, and if it does not find it there in the directory /usr/local/lib/gap3r3p0/lib/. This way you can built your own directory of GAP library files that override the standard ones.

GAP searches for the group files, whose filenames end in .grp, and which contain the groups initially known to GAP, in the directory one gets by replacing the string lib in libname with the string grp. If you do not want to put the group directory grp/ in the same directory as the lib/ directory, for example if you want to put the groups onto another hard disk partition, you have to edit the assignment in libname/init.g that reads

    GRPNAME := ReplacedString( LIBNAME, "lib", "grp" );

This path can also consist of several alternative pathes, just as the library path. If the library path consists of several alternative pathes the default value for this path will consist of the same pathes, where in each component the last occurrence of lib/ is replaced by

grp/.

Similar considerations apply to the character table files. Those filenames end in .tbl. GAP looks for those files in the directory given by TBLNAME. The default value for TBLNAME is obtained by replacing lib in libname with tbl.

-h docname

The option -h tells GAP that the on-line documenation for GAP is in the directory docname. Per default docname is obtained by replacing lib in libname with doc. docname should end with a pathname separator, i.e., /, but GAP will silently add one if it is missing. GAP will read the file docname/manual.toc when you first use the help system. If GAP cannot find this file it will print the following warning

    help: hmm, I cannot open the table of contents file 'doc/manual.toc'
    maybe you should use the option '-h <docname>'?

-m memory

The option -m tells GAP to allocate memory bytes at startup time. If the last character of memory is k or K it is taken in KBytes and if the last character is m or M memory is taken in MBytes.

Under UNIX the default amount of memory allocated by GAP is 4 MByte. The amount of memory should be large enough so that computations do not require too many garbage collections. On the other hand if GAP allocates more virtual memory than is physically available it will spend most of the time paging. 4 MByte is reasonable for single user workstations with 8 MByte or for multi user systems with more.

-n

The option -n tells GAP to disable the line editing and history (see 3.4).

You may want to do this if the command line editing is incompatible with another program that is used to run GAP. For example if GAP is run from inside a GNU Emacs shell window, -n should be used since otherwise every input line will be echoed twice, once by Emacs and once by GAP.

-b

The option -b tells GAP to surpress the banner. That means that GAP immediately prints the prompt. This is useful when you get tired of the banner after a while.

-q

The option -q tells GAP to be quiet. This means that GAP does not display the banner and the prompts gap>. This is useful if you want to run GAP as a filter with input and output redirection and want to avoid the the banner and the prompts clobber the output file.

-x length

With this option you can tell GAP how long lines are. GAP uses this value to decide when to split long lines.

The default value is 80, which is the right value if you have a standard ASCII terminal. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

-y length

With this option you can tell GAP how many lines your screen has. GAP uses this value to decide after how many lines of on-line help it should display -- <space> for more --.

The default value is 24, which is the right value if you have a standard ASCII terminal. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

Further arguments are taken as filenames of files that are read by GAP during startup, after libname/init.g is read, but before the first prompt is printed. The files are read in the order in that they appear on the command line. GAP only accepts 14 filenames on the command line. If a file cannot be opened GAP will print an error message and will abort.

When you start GAP, it looks for the file with the name .gaprc in your homedirectory. If such a file is found it is read after libname/init.g, but before any of the files mentioned on the command line are read. You can use this file for your private customizations. For example, if you have a file containing functions or data that you always need, you could read this from .gaprc. Or if you find some of the names in the library too long, you could define abbreviations for those names in .gaprc. The following sample .gaprc file does both.

    Read("/usr/you/dat/mygroups.grp");
    Op := Operation;
    OpHom := OperationHomomorphism;
    RepOp := RepresentativeOperation;
    RepsOp := RepresentativesOperation;

47.6 GAP for MS-DOS

This sections contain information about GAP that is specific to the port of GAP for IBM PC compatibles under MS-DOS or Windows (simply called GAP for MS-DOS below).

To run GAP for MS-DOS you need an IBM PC compatible with an Intel 80386, Intel 80486, or Intel Pentium processor, it will not run on IBM PC compatibles with an Intel 80186 or Intel 80286 processor. The system must have at least 4 MByte of main memory and a harddisk. The operating system must be MS-DOS version 5.0 or later or Windows 3.1 or later (earlier versions may work, but this has not been tested).

The section 47.7 describes the copyright as it applies to the executable version that we distribute. The section 47.8 describes how you install GAP for MS-DOS, and the section 47.9 describes the special features of GAP for MS-DOS.

47.7 Copyright of GAP for MS-DOS

In addition to the general copyright for GAP set forth in the Copyright the following terms apply to GAP for MS-DOS.

The system dependent part for GAP for MS-DOS was written by Steve Linton (111 Ross St., Cambridge, CB1 3BS, UK, +44 223 411661, sl25@cus.cam.ac.uk). He assignes the copyright to the Lehrstuhl D fuer Mathematik. Many thanks to Steve Linton for his work.

The executable of GAP for MS-DOS that we distribute was compiled with DJ Delorie’s port of the Free Software Foundation’s GNU C compiler version 2.4. The compiler can be obtained by anonymous ftp from grape.ecs.clarkson.edu where it is in the directory pub/msdos/djgpp. Many thanks to the Free Software Foundation and DJ Delorie for this amazing piece of work.

The GNU C compiler is

Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA

under the terms of the GNU General Public License (GPL). Note that the GNU GPL states that the mere act of compiling does not affect the copyright status of GAP.

The modifications to the compiler to make it operating under MS-DOS, the functions from the standard library libpc.a, the modifications of the functions from the standard library libc.a to make them operate under MS-DOS, and the DOS extender go32 (which is prepended to gapexe.386) are

Copyright (C) 1991 DJ Delorie, 24 Kirsten Ave, Rochester NH 03867-2954, USA

also under the terms of the GNU GPL. The terms of the GPL require that we make the source code for libpc.a available. They can be obtained by writing to Steve Linton (however, it may be easier for you to ftp them from grape.ecs.clarkson.edu yourself). They also require that GAP falls under the GPL too, i.e., is distributed freely, which it basically does anyhow.

The functions in libc.a that GAP for the 386 uses are

Copyright (c) 1988 Regents of the University of California

under the following terms

All rights reserved.

Redistribution and use in source and binary forms are permitted provided that the above copyright notice and this paragraph are duplicated in all such forms and that any documentation, advertising materials, and other materials related to such distribution and use acknowledge that the software was developed by the University of California, Berkeley. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

47.8 Installation of GAP for MS-DOS

Installation of GAP on IBM PC compatibles is fairly easy. As already mentioned above, GAP must be installed on a harddisk, because it is too large to be run from floppy disks.

First go to a directory where you want to install GAP, e.g., c:\. GAP will be installed in a subdirectory gap3r3p0\ of this directory. You can later move GAP to another location, for example you can first install it in d:\tmp\ and once it works move it to c:\. In the following example we assume that you want to install GAP in c:\. Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

Get the GAP distribution onto your IBM PC compatible. One usual way would be to get the distribution with ftp onto some UNIX workstation and to download it from there onto your IBM PC compatible, for example with kermit. Remember that the distribution consists of binary files and that you must transmit them in binary mode. Another possibility is that you got a set of floppy disks.

If you get the distribution via ftp, you must get the distribution gap3r3p0.zoo, the zoo archive extractor unzoo-ibm-i386-msdos.exe, which is in the subdirectory util and which

you should rename to unzoo.exe, and the executable bin3r3p0-ibm-i386-msdos.zoo,

which is in the subdirectory bin and which you should rename to bin3r3p0.zoo. You may have to get the latter 2 files from samson.math.rwth-aachen.de, because some ftp servers may not keep it. We recommend that you use unzoo even if you already have zoo on your system, because unzoo automatically translates text files to the appropriate local format.

If you got the distribution via ftp unpack the executable and the distribution with the following commands

    C: > unzoo -x bin3r3p0.zoo
    gap3r3p0\bin\README.DOS  -- extracted as text
    gap3r3p0\bin\gap.bat     -- extracted as text
    gap3r3p0\bin\gapdjg.exe  -- extracted as binary
    C: > unzoo -x gap3r3p0.zoo
    gap3r3p0\bin\gap.sh      -- extracted as text
    gap3r3p0\doc\aboutgap.tex        -- extracted as text
    gap3r3p0\doc\aggroup.tex         -- extracted as text

# about 500 more lines

    gap3r3p0\two\twogpd8.grp         -- extracted as text
    C: >

If you got the executable and the distribution on disks, you must unpack them with the following comands (assuming that your disk drive is A:)

   C: > rem Insert the 1. disk.
    C: > copy a:\unzoo.exe .

C: > unzoo -x a:\bin3r3p0.zoo
gap3r3p0\bin\README.DOS  -- extracted as text
gap3r3p0\bin\gap.bat     -- extracted as text
gap3r3p0\bin\gapdjg.exe  -- extracted as binary
C: > unzoo -x a:\etc3r3p0.zoo
gap3r3p0\etc\README      -- extracted as text

# about 20 more messages

C: > unzoo -x a:\grp3r3p0.zoo
gap3r3p0\grp\basic.grp   -- extracted as text

# about 10 more messages

C: > unzoo -x a:\lib3r3p0.zoo
gap3r3p0\lib\abattoir.g  -- extracted as text

# about 77 more messages

C: > rem Insert the 2. disk.
C: > unzoo -x a:\doc3r3p0.zoo
gap3r3p0\doc\about.tex   -- extracted as text

# about 65 more messages

C: > unzoo -x a:\two3r3p0.zoo
gap3r3p0\two\twogp.grp

# about 25 more messages

C: > unzoo -x a:\thr3r3p0.zoo
gap3r3p0\thr\thrgp.grp

# about 10 more messages

C: > rem Insert the 3. disk.
C: > unzoo -x a:\tbl3r3x1.zoo
gap3r3p0\tbl\ctbalter.tbl

# about 50 more messages

C: > rem Insert the 4. disk.
C: > unzoo -x a:\tbl3r3x2.zoo
gap3r3p0\tbl\ctomonst.tbl

# about 10 more messages

C: > unzoo -x a:\tom3r3p0.zoo
gap3r3p0\tom\tmaltern.tom

# about 10 more messages

C: > unzoo -x a:\src3r3p0.zoo
gap3r3p0\src\Makefile    -- extracted as text

# about 60 more messages

C: > rem Insert the 5. disk.
C: > unzoo -x a:\pkg3r3p0.zoo
gap3r3p0\pkg\anupq\MakeLibrary\MakeLibray -- extracted as text

# about 150 more messages

C: >

Instead of using the executable that we provide in bin3r3p0.zoo you can compile GAP yourself if you have the DJGPP compiler installed. To do this change into the source directory gap3r3p0\src and compile GAP with the commands

C: > chdir gap3r3p0\src
C:\GAP3R3P0\SRC > make ibm-i386-msdos-djgpp

gcc -DSYS_IS_MSDOS_DJGPP -DSYS_HAS_STRING_PROTO -DSYS_HAS_STDIO_PROTO\
    -DSYS_HAS_MISC_PROTO -c system.c
gcc -O2 -c gap.c

# about 30 more messages

C:\GAP3R3P0\SRC > copy gapdjg.exe ..\bin
C:\GAP3R3P0\SRC > chdir ..\..
C: >

Change into the directory gap3r3p0\bin\ and edit the script gap.bat, which starts GAP, according to the instructions in this file. Then copy this script to a directory in your search path, e.g., c:\bin\, with the commands

C: > chdir gap3r3p0\bin
C:\GAP3R3P0\BIN > edit gap.bat

# edit the script gap.bat

C:\GAP3R3P0\BIN > copy gap.bat c:\bin\gap.bat
C:\GAP3R3P0\BIN > chdir ..\..
C: >

When you later move GAP to another location you must only edit this script.

GAP must sometimes open more than 8 files at once. MS-DOS disallows this, unless you add the following line to the file config.sys on your boot drive. You must then reboot for this change to take effect.

files=32

Start GAP and try a few things. Note that GAP has to read most of the library for the fourth statement below, so this takes quite a while. Subsequent definitions of groups will be much faster.

C: > gap -b
gap> 2 * 3 + 4;
10
gap> Factorial( 30 );
265252859812191058636308480000000
gap> Factors( 10^42 + 1 );
[ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]
gap> m11 := Group((1,2,3,4,5,6,7,8,9,10,11),(3,7,11,8)(4,10,5,6));;
gap> Size( m11 );
7920
gap> Factors( 7920 );
[ 2, 2, 2, 2, 3, 3, 5, 11 ]
gap> Number( ConjugacyClasses( m11 ) );
10

Especially try the command line editing and history facilities, because the are probably the most machine dependent feature of GAP. Enter a few commands and then make sure that ctr-P redisplays the last command, that ctr-E moves the cursor to the end of the line, that ctr-B moves the cursor back one character, and that ctr-D deletes single characters. So after entering the above three commands typing ctr-P ctr-P ctr-E ctr-B ctr-B ctr-B ctr-B ctr-D 1 should give the following line.

    gap> Factors( 7921 );
    [ 89, 89 ]

If you have a big version of LaTEX available you may now want to make a printed copy of the manual. Change into the directory gap3r3p0\doc\ and run LaTEX twice on the source. The first pass with LaTEX produces the .aux files, which resolve all the cross references. The second pass produces the final formatted dvi file manual.dvi. This will take quite a while, since the manual is large. Then print the dvi file. How you actually print the dvi file produced by LaTEX depends on the printer you have, the version of LaTEX you have, and whether you use a TEX-shell or not, so we will not attempt to describe it here.

    C: > chdir gap3r3p0\doc
    C:\GAP3R3P0\DOC > latex manual

# about 2000 messages about undefined references

    C:\GAP3R3P0\DOC > latex manual

# there should be no warnings this time

    C:\GAP3R3P0\DOC > dir manual.dvi
    -a---   2591132 Nov 13 23:29 manual.dvi
    C:\GAP3R3P0\DOC > chdir ..\..
    C: >

Note that because of the large number of cross references in the manual you need a big LaTEX to format the GAP manual. If you see the error message TeX capacity exceeded, you do not have a big LaTEX. In this case you may also obtain the already formatted dvi file manual.dvi from the same place where you obtained the rest of the GAP distribution.

Note that, apart from the *.tex files and the file manual.bib (bibliography database), which you absolutely need, we supply also the files manual.toc (table of contents), manual.ind (unsorted index), manual.idx (sorted index), and manual.bbl (bibliography). If those files are missing, or if you prefer to do everything yourself, here is what you will have to do. After the first pass with LaTEX, you will have preliminary manual.toc and manual.ind files. All the page numbers are still incorrect, because the do not account for the pages used by the table of contents itself. Now bibtex manual will create manual.bbl from manual.bib. After the second pass with LaTEX you will have a correct manual.toc and manual.ind. makeindex now produces the sorted index manual.idx from manual.ind. The third pass with LaTEX incorporates this index into the manual.

    C: > chdir gap3r3p0\doc

# about 2000 messages about undefined references

    C:\GAP3R3P0\DOC > bibtex manual

# bibtex prints the name of each file it is scanning

    C:\GAP3R3P0\DOC > latex manual

# still some messages about undefined citations

    C:\GAP3R3P0\DOC > makeindex manual

# makeindex prints some diagnostic output

    C:\GAP3R3P0\DOC > latex manual

# there should be no warnings this time

    C:\GAP3R3P0\DOC > chdir ..\..
    C: >

Thats all, finally you are done. We hope that you will enjoy using GAP. If you have problems, do not hesitate to contact us.

47.9 Features of GAP for MS-DOS

Note that GAP for MS-DOS will use up to 128 MByte of extended memory (using XMS, VDISK memory allocation strategies) or up to 128 MByte of expanded memory (using VCPI programs, such as QEMM and 386MAX) and up to 128 MByte of disk space for swapping.

If you hit ctr-C the DOS extender (go32) catches it and aborts GAP immediately. The keys ctr-Z and alt-C can be used instead to interrupt GAP.

The arrow keys left, right, up, down, home, end, and delete can be used for command line editing with their intuitive meaning.

Pathnames may be given inside GAP using either slash (/) or backslash (\) as a separator (though \ must be escaped in strings of course).

When you start GAP you may specify a number of options on the command-line to change the default behaviour of GAP. All these options start with a hyphen -, followed by a single letter. Options must not be grouped, e.g., gap -gq is illegal, use gap -g -q instead. Some options require an argument, this must follow the option and must be separated by a space, e.g., gap -m 256k, it is not correct to say gap -m256k instead.

GAP for MS-DOS accepts the following (lowercase) options.

-g

The options -g tells GAP to print a information message every time a garbage collection is performed.

# G collect garbage, 1931 used, 5012 dead, 912 KB free, 3072 KB total

For example, this tells you that there are 1931 live objects that surived a garbage collection, that 5012 unused objects were reclaimed by it, and that 912 KByte of totally allocated 3072 KBytes are available afterwards.

-l libname

The option -l tells GAP that the library of GAP functions is in the directory libname. Per default libname is lib/, i.e., the library is normally expected in the subdirectory lib/ of the current directory. GAP searches for the library files, whose filenames end in .g, and which contain the functions initially known to GAP, in this directory. libname should end with a pathname separator, i.e., \, but GAP will silently add one if it is missing. GAP will read the file libname\init.g during startup. If GAP cannot find this file it will print the following warning

    gap: hmm, I cannot find 'lib\init.g', maybe use option '-l <lib>'?

If you want a bare bones GAP, i.e., if you do not need any library functions, you may ignore this warning, otherwise you should leave GAP and start it again, specifying the correct library path using the -l option.

It is also possible to specify several alternative library pathes by separating them with semicolons ;. Note that in this case all path names must end with the pathname separator \. GAP will then search for its library files in all those directories in turn, reading the first it finds. E.g., if you specify -l "lib\;\usr\local\lib\gap3r3p0\lib\" GAP will search for a library file first in the subdirectory lib\ of the current directory, and if it does not find it there in the directory \usr\local\lib\gap3r3p0\lib\. This way you can built your own directory of GAP library files that override the standard ones.

GAP searches for the group files, whose filenames end in .grp, and which contain the groups initially known to GAP, in the directory one gets by replacing the string lib in libname by the string grp. If you do not want to put the group directory grp\ in the same directory as the lib\ directory, for example if you want to put the groups onto another hard disk partition, you have to edit the assignment in libname\init.g that reads

    GRPNAME := ReplacedString( LIBNAME, "lib", "grp" );

This path can also consist of several alternative pathes, just as the library path. If the library path consists of several alternative pathes the default value for this path will consist of the same pathes, where in each component the last occurrence of lib\ is replaced by

grp\.

Similar considerations apply to the character table files. Those filenames end in .tbl. GAP looks for those files in the directory given by TBLNAME. The default value for TBLNAME is obtained by replacing lib in libname with tbl.

-h docname

The option -h tells GAP that the on-line documenation for GAP is in the directory docname. Per default docname is obtained by replacing lib in libname with doc. docname should end with a pathname separator, i.e., \, but GAP will silently add one if it is missing. GAP will read the file docname\manual.toc when you first use the help system. If GAP cannot find this file it will print the following warning

    help: hmm, I cannot open the table of contents file 'doc\manual.toc'
    maybe you should use the option '-h <docname>'?

-m memory

The option -m tells GAP to allocate memory bytes at startup time. If the last character of memory is k or K it is taken in KBytes and if the last character is m or M memory is taken in MBytes.

GAP for MS-DOS will by default allocate 4 MBytes of memory. If you specify -m memory GAP will only allocate that much memory. The amount of memory should be large enough so that computations do not require too many garbage collections. On the other hand if GAP allocates more virtual memory than is physically available it will spend most of the time paging.

-n

The options -n tells GAP to disable the line editing and history (see 3.4).

There does not seem to be a good reason to do this on IBM PC compatibles.

-b

The option -b tells GAP to surpress the banner. That means that GAP immediately prints the prompt. This is useful when you get tired of the banner after a while.

-q

The option -q tells GAP to be quiet. This means that GAP does not display the banner and the prompts gap>. This is useful if you want to run GAP as a filter with input and output redirection and want to avoid the the banner and the prompts clobber the output file.

-x length With this option you can tell GAP how long lines are. GAP uses this value to decide when to split long lines.

The default value is 80, which is correct if you start GAP from the desktop or one of the usual shells. However, if you start GAP from a window shell such as gemini, you may want to decrease this value. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

-y length

With this option you can tell GAP how many lines your screen has. GAP uses this value to decide after how many lines of on-line help it should display -- <space> for more --.

The default value is 24, which is the right value if you start GAP from the desktop or one of the usual shells. However, if you start GAP from a window shell such as gemini, you may want to decrease this value. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

-z freq

GAP for MS-DOS checks in regular intervals whether the user has entered ctr-Z or alt-C to interrupt an ongoing computation. Under MS-DOS this requires reading the keyboard status (UNIX on the other hand will deliver a signal to GAP when the user entered ctr-C), which is rather expensive. Therefor GAP only reads the keyboard status every freq-th time. The default is 20. With the option -z this value can be changed. Lower values make GAP more responsive to interrupts, higher values make GAP a little bit faster.

Further arguments are taken as filenames of files that are read by GAP during startup, after libname\init.g is read, but before the first prompt is printed. The files are read in the order in that they appear on the command line. GAP only accepts 14 filenames on the command line. If a file cannot be opened GAP will print an error message and will abort.

When you start GAP, it looks for the file with the name gap.rc in your homedirectory (i.e., the directory defined by the environment variable HOME). If such a file is found it is read after libname\init.g, but before any of the files mentioned on the command line are read. You can use this file for your private customizations. For example, if you have a file containing functions or data that you always need, you could read this from gap.rc. Or if you find some of the names in the library too long, you could define abbreviations for those names in gap.rc. The following sample gap.rc file does both.

    Read("c:\\gap\\dat\\mygroups.grp");
    Op := Operation;
    OpHom := OperationHomomorphism;
    RepOp := RepresentativeOperation;
    RepsOp := RepresentativesOperation;

47.10 GAP for TOS

This sections contain information about GAP that is specific to the port of GAP for Atari ST systems under TOS (simply called GAP for TOS below).

To run GAP for TOS you need an Atari ST or Atari TT. The system must have at least 4 MByte of main memory and a harddisk. The operation system must be TOS 1.4 or later. Since GAP is a plain text application a monochrom monitor is probably best, but you may also use a color monitor.

The section 47.11 describes the copyright as it applies to the executable version that we distribute. The section 47.12 describes how you install GAP for TOS, and the section 47.13 describes the special features of GAP for TOS.

47.11 Copyright of GAP for TOS

In addition to the general copyright for GAP set forth in the copyright the following terms apply to the executable of GAP for TOS.

GAP for TOS was compiled with J. Bammi’s port of the Free Software Foundation’s GNU C compiler version 2.5. The compiler is available by anonymous ftp from the ftp server

atari.archive.umich.edu where it is found in the directory atari/Gnustuff/Tos. Many

thanks to the Free Software Foundation and J. Bammi for this amazing piece of work.

The GNU C compiler is

Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA

under the terms of the GNU General Public License (GPL). Note that the GNU GPL states that the mere act of compiling does not affect the copyright status of GAP.

The executable is linked with the GNU library gnu.obl, which is

Copyright (C) 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA

under the terms of the GNU Library General Public License (LGPL). The terms of the LGPL require that we make the source code for the gnu.olb available. They can be obtained by writing to Martin Schoenert (however, it may be easier for you to ftp them from atari.archive.umich.edu yourself). They also require that we make GAP available in a form that allows you to modify it, which we basically to anyhow.

47.12 Installation of GAP for TOS

Installation of GAP on an Atari ST is fairly easy. As already mentioned above, GAP must be installed on a harddisk, because it is too large to be run from floppy disks.

First go to a directory where you want to install GAP, e.g., c:\. GAP will be installed in a subdirectory gap3r3p0\ of this directory. You can later move GAP to another location, for example you can first install it in d:\tmp\ and once it works move it to c:\. In the following example we assume that you want to install GAP in c:\. We will also assume in the examples that you install GAP using some shell, e.g. tcsh, ksh, bash, or gulam. Installation from the desktop is also possible, but it would be much more difficult to give examples, because you have to use the mouse in this case. Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

Get the GAP distribution onto your Atari ST. One usual way would be to get the distribution with ftp onto some UNIX workstation and to download it from there onto your Atari ST, for example with kermit. Remember that the distribution consists of binary files and that you must transmit them in binary mode. Another possibility is that you got a set of floppy disks.

If you get the distribution via ftp, you must get the distribution gap3r3p0.zoo, the zoo archive extractor unzoo-atari-m68k-tos.ttp, which is in the subdirectory util and which

you should rename to unzoo.ttp, and the executable bin3r3p0-atari-m68k-tos.zoo,

which is in the subdirectory bin and which you should rename to bin3r3p0.zoo. You may have to get the latter 2 files from samson.math.rwth-aachen.de, because some ftp servers may not keep it. We recommend that you use unzoo even if you already have zoo on your system, because unzoo automatically translates text files to the appropriate local format. If you got the distribution via ftp unpack the executable and the distribution with the following commands

C: > unzoo -x bin3r3p0.zoo
gap3r3p0\bin\README.TOS  -- extracted as text
gap3r3p0\bin\gap.ttp     -- extracted as binary
C: > unzoo -x gap3r3p0.zoo
gap3r3p0\bin\gap.sh      -- extracted as text
gap3r3p0\doc\aboutgap.tex        -- extracted as text
gap3r3p0\doc\aggroup.tex         -- extracted as text

# about 500 more lines

gap3r3p0\two\twogpd8.grp         -- extracted as text
C: >

If you got the executable and the distribution on disks, you must unpack them with the following comands (assuming that your disk drive is A:)

C: > rem Insert the 1. disk.
C: > copy a:\unzoo.exe .
C: > unzoo -x a:\bin3r3p0.zoo
gap3r3p0\bin\README.TOS  -- extracted as text
gap3r3p0\bin\gap.ttp     -- extracted as binary
C: > unzoo -x a:\etc3r3p0.zoo
gap3r3p0\etc\README      -- extracted as text

# about 20 more messages

C: > unzoo -x a:\grp3r3p0.zoo
gap3r3p0\grp\basic.grp   -- extracted as text

# about 10 more messages

C: > unzoo -x a:\lic3r3p0.zoo
gap3r3p0\lib\ctautoms.g  -- extracted as text

# about 10 more messages

C: > rem Insert the 2. disk.
C: > unzoo -x a:\lib3r3p0.zoo
gap3r3p0\lib\abattoir.g  -- extracted as text

# about 70 more messages

C: > rem Insert the 3. disk.
C: > unzoo -x a:\doc3r3p0.zoo
gap3r3p0\doc\about.tex   -- extracted as text

# about 65 more messages

C: > rem Insert the 4. disk.
C: > unzoo -x a:\two3r3p0.zoo
gap3r3p0\two\twogp.grp

# about 25 more messages

C: > unzoo -x a:\thr3r3p0.zoo
gap3r3p0\thr\thrgp.grp

# about 10 more messages

C: > rem Insert the 5. disk.
C: > unzoo -x a:\tom3r3p0.zoo
gap3r3p0\tom\tmaltern.tom

# about 10 more messages

C: > rem Insert the 6. disk.
C: > unzoo -x a:\src3r3p0.zoo
gap3r3p0\src\Makefile    -- extracted as text

# about 60 more messages

C: >

Instead of using the executable that we provide in bin3r3p0.zoo you can compile GAP yourself if you have the GNU C compiler installed. To do this change into the source directory gap3r3p0\src\ and compile GAP with the commands

C: > chdir gap3r3p0\src
C:\GAP3R3P0\SRC > make atari-m68k-tos-gcc2
gcc -DSYS_IS_TOS_GCC2 -DSYS_HAS_MISC_PROTO -c system.c
gcc -O2 -c gap.c

# about 30 more messages

C:\GAP3R3P0\SRC > copy gap.ttp ..\bin
C:\GAP3R3P0\SRC > chdir ..\..
C: >

In either case now move the executable gap.ttp from the gap3r3p0\bin\ subdirectory to the gap3r3p0\ directory. Then you can either start GAP from the desktop by double clicking or from a shell such as Gulam or bash (in which case you must use -b installdir\gap3r3p0\lib\ option to tell GAP where to find the library).

Start GAP and try a few things. Note that GAP has to read most of the library for the fourth statement below, so this takes quite a while. Subsequent definitions of groups will be much faster.

C: > gap -b
gap> 2 * 3 + 4;
10
gap> Factorial( 30 );
265252859812191058636308480000000
gap> Factors( 10^42 + 1 );
[ 29, 101, 281, 9901, 226549, 121499449, 4458192223320340849 ]
gap> m11 := Group((1,2,3,4,5,6,7,8,9,10,11),(3,7,11,8)(4,10,5,6));;
gap> Size( m11 );
7920
gap> Factors( 7920 );
[ 2, 2, 2, 2, 3, 3, 5, 11 ]
gap> Number( ConjugacyClasses( m11 ) );
10

Especially try the command line editing and history facilities, because the are probably the most machine dependent feature of GAP. Enter a few commands and then make sure that ctr-P redisplays the last command, that ctr-E moves the cursor to the end of the line, that ctr-B moves the cursor back one character, and that ctr-D deletes single characters. So after entering the above three commands typing ctr-P ctr-P ctr-E ctr-B ctr-B ctr-B ctr-B ctr-D 1 should give the following line.

    gap> Factors( 7921 );
    [ 89, 89 ]

If you have a big version of LaTEX available you may now want to make a printed copy of the manual. Change into the directory gap3r3p0\doc\ and run LaTEX twice on the source. The first pass with LaTEX produces the .aux files, which resolve all the cross references. The second pass produces the final formatted dvi file manual.dvi. This will take quite a while, since the manual is large. Then print the dvi file. How you actually print the dvi file produced by LaTEX depends on the printer you have, the version of LaTEX you have, and whether you use a TEX-shell or not, so we will not attempt to describe it here.

    C: > chdir gap3r3p0\doc
    C:\GAP3R3P0\DOC > latex manual

# about 2000 messages about undefined references

    C:\GAP3R3P0\DOC > latex manual

# there should be no warnings this time

    C:\GAP3R3P0\DOC > dir manual.dvi
    -a---   2591132 Nov 13 23:29 manual.dvi
    C:\GAP3R3P0\DOC > chdir ..\..
    C: >

Note that because of the large number of cross references in the manual you need a big LaTEX to format the GAP manual. If you see the error message TeX capacity exceeded, you do not have a big LaTEX. In this case you may also obtain the already formatted dvi file manual.dvi from the same place where you obtained the rest of the GAP distribution.

Note that, apart from the *.tex files and the file manual.bib (bibliography database), which you absolutely need, we supply also the files manual.toc (table of contents), manual.ind (unsorted index), manual.idx (sorted index), and manual.bbl (bibliography). If those files are missing, or if you prefer to do everything yourself, here is what you will have to do. After the first pass with LaTEX, you will have preliminary manual.toc and manual.ind files. All the page numbers are still incorrect, because the do not account for the pages used by the table of contents itself. Now bibtex manual will create manual.bbl from manual.bib. After the second pass with LaTEX you will have a correct manual.toc and manual.ind. makeindex now produces the sorted index manual.idx from manual.ind. The third pass with LaTEX incorporates this index into the manual.

    C: > chdir gap3r3p0\doc

# about 2000 messages about undefined references

    C:\GAP3R3P0\DOC > bibtex manual

# bibtex prints the name of each file it is scanning

    C:\GAP3R3P0\DOC > latex manual

# still some messages about undefined citations

    C:\GAP3R3P0\DOC > makeindex manual

# makeindex prints some diagnostic output

    C:\GAP3R3P0\DOC > latex manual

# there should be no warnings this time

    C:\GAP3R3P0\DOC > chdir ..\..
    C: >

Thats all, finally you are done. We hope that you will enjoy using GAP. If you have problems, do not hesitate to contact us.

47.13 Features of GAP for TOS

GAP for TOS will usually allocate all available memory. Note that GAP for TOS does not support virtual memory, i.e., you can never use more memory than this default gives you. If you specify -m memory, GAP will only allocate that much memory. If you specify a negative amount, GAP leaves this much memory unallocated. You may want to do this, so that that it is possible to run other programs from inside GAP with the Exec command.

The arrow keys left, right, up, and down can be used for command line editing with their intuitive meaning.

GAP for TOS supports UNIXMODE, i.e., if the environment variable UNIXMODE is set to an appropriate value such as /.,rCLAHbd, GAP will emulate a UNIX style file system on top of the TOS filesystem. That is GAP will accept pathnames with the / as separator, which it will automatically map to the TOS separator \. It will also allow long filenames (with more than 8+3 characters), which it will automatically map to TOS filenames and remember the true filenames in a special file .dir. .dir is used to symbolic links, which can be resolved by GAP (to create then you need the GNU ln program).

GAP for TOS runs as an ordinary TOS program. That means that it does not open windows or display a menu bar etc. The advantage of this is that no valuable memory space is wasted for the windows. The disadvantage is that GEM programs with windows are certainly more sexy. A GEM version is being developed, but don’t hold your breath.

Apart from this everything works as described in this manual and you should not have any problems using GAP.

When you start GAP on the Atari ST, you may specify a number of options on the commandline to change the default behaviour of GAP. All these options start with a hyphen -, followed by a single letter. Options must not be grouped, e.g., gap -gq is illegal, use gap -g -q instead. Some options require an argument, this must follow the option and must be separated by a space, e.g., gap -m 256k, it is not correct to say gap -m256k instead.

You must declare GAP as TOS-takes-parameter program if you want to specify commandline options when you start GAP by double clicking. This means that the name of the GAP program file must be gap.ttp. When you start GAP from a shell, such as gulam.tos, the name of the program file does not matter. GAP for TOS, unlike the UNIX versions, does not distuingish upper and lower case for options.

GAP for TOS accepts the following (lowercase) options.

-g

The options -g tells GAP to print a information message every time a garbage collection is performed.

# G collect garbage, 1931 used, 5012 dead, 912 KB free, 3072 KB total For example, this tells you that there are 1931 live objects that surived a garbage collection, that 5012 unused objects were reclaimed by it, and that 912 KByte of totally allocated 3072 KBytes are available afterwards.

-l libname

The option -l tells GAP that the library of GAP functions is in the directory libname. Per default libname is lib/, i.e., the library is normally expected in the subdirectory lib/ of the current directory. GAP searches for the library files, whose filenames end in .g, and which contain the functions initially known to GAP, in this directory. libname should end with a pathname separator, i.e., \, but GAP will silently add one if it is missing. GAP will read the file libname\init.g during startup. If GAP cannot find this file it will print the following warning

    gap: hmm, I cannot find 'lib\init.g', maybe use option '-l <lib>'?

If you want a bare bones GAP, i.e., if you do not need any library functions, you may ignore this warning, otherwise you should leave GAP and start it again, specifying the correct library path using the -l option.

It is also possible to specify several alternative library pathes by separating them with semicolons ;. Note that in this case all path names must end with the pathname separator \. GAP will then search for its library files in all those directories in turn, reading the first it finds. E.g., if you specify -l "lib\;\usr\local\lib\gap3r3p0\lib\" GAP will search for a library file first in the subdirectory lib\ of the current directory, and if it does not find it there in the directory \usr\local\lib\gap3r3p0\lib\. This way you can built your own directory of GAP library files that override the standard ones.

GAP searches for the group files, whose filenames end in .grp, and which contain the groups initially known to GAP, in the directory one gets by replacing the string lib in libname by the string grp. If you do not want to put the group directory grp\ in the same directory as the lib\ directory, for example if you want to put the groups onto another hard disk partition, you have to edit the assignment in libname\init.g that reads

    GRPNAME := ReplacedString( LIBNAME, "lib", "grp" );

This path can also consist of several alternative pathes, just as the library path. If the library path consists of several alternative pathes the default value for this path will consist of the same pathes, where in each component the last occurrence of lib\ is replaced by

grp\.

Similar considerations apply to the character table files. Those filenames end in .tbl. GAP looks for those files in the directory given by TBLNAME. The default value for TBLNAME is obtained by replacing lib in libname with tbl.

-h docname

The option -h tells GAP that the on-line documenation for GAP is in the directory docname. Per default docname is obtained by replacing lib in libname with doc. docname should end with a pathname separator, i.e., \, but GAP will silently add one if it is missing. GAP will read the file docname\manual.toc when you first use the help system. If GAP cannot find this file it will print the following warning

    help: hmm, I cannot open the table of contents file 'doc\manual.toc'
    maybe you should use the option '-h <docname>'?

-m memory The option -m tells GAP to allocate memory bytes at startup time. If the last character of memory is k or K it is taken in KBytes and if the last character is m or M memory is taken in MBytes.

GAP for TOS will by default allocate all available memory. If you specify -m memory GAP will only allocate that much memory, unless memory is larger than the default, in that case GAP will fail. If you speficy a negative amount GAP leaves that much memory unallocated. You may want to do this so that it is possible to run other programs from inside GAP with the Exec command, for example gap -m -256k will leave 256 KByte unallocated, enough to run MicroEMACS from inside GAP.

-n

The options -n tells GAP to disable the line editing and history (see 3.4).

There does not seem to be a good reason to do this on an Atari ST.

-b

The option -b tells GAP to surpress the banner. That means that GAP immediately prints the prompt. This is useful when you get tired of the banner after a while.

-q

The option -q tells GAP to be quiet. This means that GAP does not display the banner and the prompts gap>. This is useful if you want to run GAP as a filter with input and output redirection and want to avoid the the banner and the prompts clobber the output file.

-x length

With this option you can tell GAP how long lines are. GAP uses this value to decide when to split long lines.

The default value is 80, which is correct if you start GAP from the desktop or one of the usual shells. However, if you start GAP from a window shell such as gemini, you may want to decrease this value. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

-y length

With this option you can tell GAP how many lines your screen has. GAP uses this value to decide after how many lines of on-line help it should display -- <space> for more --.

The default value is 24, which is the right value if you start GAP from the desktop or one of the usual shells. However, if you start GAP from a window shell such as gemini, you may want to decrease this value. If you have a larger monitor, or use a smaller font, or redirect the output to a printer, you may want to increase this value.

-z freq

GAP for TOS checks in regular intervals whether the user has entered ctr-Z or alt-C to interrupt an ongoing computation. Under TOS this requires reading the keyboard status (UNIX on the other hand will deliver a signal to GAP when the user entered ctr-C), which is rather expensive. Therefor GAP only reads the keyboard status every freq-th time. The default is 20. With the option -z this value can be changed. Lower values make GAP more responsive to interrupts, higher values make GAP a little bit faster.

Further arguments are taken as filenames of files that are read by GAP during startup, after libname\init.g is read, but before the first prompt is printed. The files are read in the order in that they appear on the command line. GAP only accepts 14 filenames on the command line. If a file cannot be opened GAP will print an error message and will abort.

When you start GAP, it looks for the file with the name gap.rc in your homedirectory (i.e., the directory defined by the environment variable HOME). If such a file is found it is read after libname\init.g, but before any of the files mentioned on the command line are read. You can use this file for your private customizations. For example, if you have a file containing functions or data that you always need, you could read this from gap.rc. Or if you find some of the names in the library too long, you could define abbreviations for those names in gap.rc. The following sample gap.rc file does both.

    Read("c:\\gap\\dat\\mygroups.grp");
    Op := Operation;
    OpHom := OperationHomomorphism;
    RepOp := RepresentativeOperation;
    RepsOp := RepresentativesOperation;

47.14 Porting GAP

Porting GAP to a new operating system should not be very difficult. However, GAP expects some features from the operating system and the compiler and porting GAP to a system or with a compiler that do not have those features may prove very difficult.

The design of GAP makes it quite portable. GAP consists of a small kernel written in the programming language C and a large library written in the programming language provided by the GAP kernel, which is also called GAP.

Once the kernel has been ported, the library poses no additional problem, because all those functions only need the kernel to work, they need no additional support from the environment.

The kernel itself is separated into a large part that is largely operating system and compiler independent, and one file that contains all the operating system and compiler dependent functions. Usually only this file must be modified to port GAP to a new operating system.

Now lets take a look at the minimal support that GAP needs from the operating system and the machine.

First of all you need enough filespace. The kernel sources and the object files need between 3.5 MByte and 4 MByte, depending on the size of object files produced by your compiler. The library takes up an additional 3.5 MBytes, and the online documentation also needs 2.5 MByte. So you need about 10 MByte of available filespace, for example on a harddisk.

Next you need enough main memory in your computer. The size of the GAP kernel varies between different machine, with as little as 300 KByte (compiled with GNU C on an Atari ST) and as much as 600 KByte (compiled with UNIX cc on a HP 9000/800). Add to that the fact the library of functions that GAP loads takes up another 1.5 MByte. So it is clear that at least 4 MByte of main memory are required to do any serious work with GAP.

Note that this implies that there is no point in trying to port GAP to plain MS-DOS running on IBM PCs and compatibles. The version of GAP for IBM PC compatibles that we provide runs on machines with the Intel 80386 or Intel 80486 processor under extended DOS in protected 32 bit mode. (This is also necessary, because, as mentioned below, GAP wants to view its memory as a large flat address space.)

Next lets turn to the requirements for the C compiler and its library.

As was already mentioned, the GAP kernel is written in the C language. We have tried to use as few features of the C language as possible. GAP has been compiled without problems with compilers that adhere to the old definition from Kernighan and Ritchie, and with compilers that adhere to the new definition from the ANSI-C standard.

However, it is probably necessary that the compiler has a default integer size of 32 bits, i.e., sizeof(int) should be 4 not 2. It may be possible to fix all the places where 32 bit ints are assumed, but we assume that it will be easier to get a 32 bit compiler.

The most critical aspect probably is that the GAP kernel needs a flat address space. There are especially two systems where this is a problem.

The first is MS-DOS with its segments. As was already mentioned above, we circumvent this by using an extended DOS in protected 32 bit mode on PC compatibles with an Intel 386 or Intel 486 processor.

The other system is the Macintosh. On the Macintosh the operating system wants to deal out the memory only in small chunks, and also wants programs to allow it to move those chunks to the disk temporarily. This is because Apple wanted to support programs that used quite some memory on its initial Macs, which had only 512 KByte of memory, without true virtual memory support. Thus they forced the application programs to simulate virtual memory. GAP does not do this. Probably the best way to deal with that is to allocate as much memory as one can get in one large chunk, while leaving enough memory free for the operating system, and to lock this chunk in memory. Such behaviour is considered uncooperative, but it is the best GAP can do without a major rewrite.

The two points mentioned above are necessary, because most parts of GAP depend on those features. All other dependencies on the operating system or compiler should be separated in one special file which is called the system file. When you port GAP to a new operating system, you probably have to create a new system file. You should however look through the system.c file that we supply and take as much code from them as possible. Currently system.c supports Berkeley UNIX, System V UNIX, IBM PC compatibles under MS-DOS with the DJGPP compiler, and the Atari ST under TOS with the GNU C compiler.

The system file contains the following functions.

First file input and output. The functions used by the three system files mentioned above are fopen, fclose, fgets, and fputs. They are pretty standard, and in fact are in the ANSI C standard library. The only thing that may be necessary is to make sure that files are opened in text mode. However, the most important transformation applied in text mode seems to be to replace the end of line sequence newline-return, used in some operating systems, with a single newline, used in C. However, since GAP treats newline and return both as whitespaces even this is not absolutely necessary.

Second you need character oriented input from the keyboard and to the screen. This is not absolutely necessary, you can use the line oriented input and output described above. However, if you want the history and the command line editing, GAP must be able to read every single character as the user types it without echo, and also must be able to put single characters to the screen. Reading characters unblocked and without echo is very system dependent.

Third you need a way to detect if the user typed ctr-C to interrupt an ongoing computation in GAP. Again this is not absolutely necessary, you can do without. However if you want to be able to interrupt computations, GAP must be able to receive the interrupt. This can be done in two ways. Under UNIX you can catch the signal that is generated if the user types ctr-C (SIGINT). Under other operating systems that do not support such signals you can poll the input stream at regular intervals and simply look for ctr-C.

Fourth you need a way to find out how long GAP has been running. Again this is not absolutely necessary. You can simply always return 0, fooling GAP into thinking that it is extremely fast. However if you want timing statistics, GAP must be able to find out how much CPU time it has spent.

The last and most important function is the function to allocate memory for GAP. GAP assumes that it can allocate the initial workspace with the function SyGetmem and expand this workspace on demand with further calls to SyGetmem. The memory allocated by consequitive calls to SyGetmem must be adjacent, because GAP wants to manage a single large block of memory. Usually this can be done with the C library function sbrk. If this does not work, you can define a large static array in the system file and return this on the first call to SyGetmem and return 0 on subsequent calls to indicate that this array cannot dynamically be expanded.

Share Libraries

Contributions from people working at Lehrstuhl D, RWTH Aachen, or any other place can become available in GAP in two different ways:

1. They can become parts of the main GAP library of functions. Their origin will then be rather carefully documented in the respective program files, but will not occur in the description of these functions in the manual. This is e.g. the case – to mention just one of many such contributions – with programs for finding composition factors of permutation groups, written by Akos Seress. The reason for this decision about keeping track of the origin of such contributions is that quite often such functions in the main GAP library have a complicated history with changes and contributions from various people.

2. On the other hand there are packages written by one or several persons for specific purposes either in the GAP language or even in C which are made available en block in GAP. Such packages will constitute the share library. A package in the share library will stay under the full responsibility of its author(s), which will be named in the respective chapter in the manual, they will in particular keep the copyright for this package, and they will also have to provide the documentation for it. However provisions will be made to call the functions of such a package like any other GAP functions, and to call the documentation via help functions like any other part of the GAP documentation. Also these packages will automatically be made available with the main body of GAP through ftp and will be sent together with the main body of GAP in case we have to fulfill a request to send GAP to institutions that cannot obtain GAP via electronic networks.

The inclusion of packages into the GAP share library should be negotiated with Lehrstuhl D für Mathematik, RWTH Aachen, for certain standards of the documentation and program organisation that should be met in order to facilitate the use of the packages in the context of GAP without problems. A necessary condition for any package to become part of the GAP share library is that it is made available under the conditions formulated in the GAP copyright statement, in particular free of any charge, except for refund of expenses for sending, if such occur.

The first section describes how to load a share library package (see 48.1).

The next sections describe the ANU pq package and how to install it (see 48.2 and 48.3).

The next sections describe the GRAPE package and how to install it (see 48.4 and 48.5).

The next sections describe the NQ package and how to install it (see 48.6 and 48.7).

The next sections describe the SISYPHOS package and how to install it (see 48.8 and 48.9).

The next sections describe the Weyl package and how to install it (see 48.10 and 48.11).

The last sections describe the experimental X-Windows interface and how to install it (see 48.12 and 48.13).

48.1 RequirePackage

RequirePackage( name )

RequirePackage will try to initialize the share library name. If the package name is not installed at your site RequirePackage will signal an error. If the package name is already initialized RequirePackage simply returns without any further actions.

    gap> CartanMat( "A", 4 );
    Error, Variable: 'CartanMat' must have a value
    gap> ?Cartan
        'CartanMat( <type>, <n> )'
        returns the Cartan matrix of Dynkin type <type> and rank <n>.
            gap> CartanMat( "F", 4 );
            [ [  2, -1,  0,  0 ],
              [ -1,  2, -1,  0 ],
              [  0, -2,  2, -1 ],
              [  0,  0, -1,  2 ] ]
        This function requires the package  "weyl"  (see "RequirePackage").

# on some system name is case-sensitive, so watch out for typos

    gap> RequirePackage( "Weyl" );
    Error, share library "Weyl" is not installed
    gap> RequirePackage( "weyl" );
    gap> CartanMat( "A", 4 );;
    gap> PrintArray( last );
    [ [   2,  -1,   0,   0 ],
      [  -1,   2,  -1,   0 ],
      [   0,  -1,   2,  -1 ],
      [   0,   0,  -1,   2 ] ]

48.2 ANU pq Package

The ANU pq provides access to implementations of the following algorithms:

1. A p-quotient algorithm to compute a power-commutator presentation for a group of prime power order. The algorithm implemented here is based on that described in Havas and Newman (1980) and papers referred to there. Another description of the algorithm appears in Vaughan-Lee (1990). A FORTRAN implementation of this algorithm was programmed by Alford and Havas. The basic data structures of that implementation are retained.

2. A p-group generation algorithm to generate descriptions of groups of prime power order. The algorithm implemented here is based on the algorithms described in Newman (1977) and O’Brien (1990). A FORTRAN implementation of this algorithm was earlier developed by Newman and O’Brien.

3. A standard presentation algorithm used to compute a canonical power-commutator presentation of a p-group. The algorithm implemented here is described in O’Brien (1992).

4. An algorithm which can be used to compute the automorphism group of a p-group. The algorithm implemented here is described in O’Brien (1993).

The following section describes the installation of the ANU pq package, a description of the functions available in the ANU pq package is given in chapter 49.

A reader interested in details of the algorithms and explanations of terms used is referred to [HN80], [O´ Br90], [O´ Br92], [O´ Br93], [New77], [VL84], [VL90b], and [VL90a].

For details about the implementation and the standalone version see the README. This implementation was developed in C by

Eamonn O’Brien School of Mathematical Sciences Australian National University Canberra, ACT 0200

e-mail obrien@pell.anu.edu.au Telephone +61-(0)6-249 2963 (office) FAX +61-(0)6-249 5549

48.3 Installing the ANU pq Package

The ANU pq is written in C and the package can only be installed under UNIX. It has been tested on DECstation running Ultrix, a HP 9000/700 and HP 9000/800 running HP-UX, a MIPS running RISC/os Berkeley, a NeXTstation running NeXTSTEP 3.0, and SUNs running SunOS.

If you got a complete binary and source distribution for your machine, nothing has to be done if you want to use the ANU pq for a single architecture. If you want to use the ANU pq for machines with different architectures skip the extraction and compilation part of this section and proceed with the creation of shell scripts described below.

If you got a complete source distribution, skip the extraction part of this section and proceed with the compilation part below.

In the example we will assume that you, as user gap, are installing the ANU pq package for use by several users on a network of two DECstations, called bert and tiffy, and a NeXTstation, called bjerun. We assume that GAP is also installed on these machines following the instructions given in 47.4.

Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file anupq.zoo (see 47.1). Then you must locate the GAP directory containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be replaced by the current patch level.

    gap@tiffy:~ > ls -l

drwxr-xr-x  11 gap     1024 Nov  8  1991 gap3r3p0
-rw-r--r--   1 gap   360891 Dec 27 15:16 anupq.zoo
gap@tiffy:~ > ls -l gap3r3p0
drwxr-xr-x   2 gap     3072 Nov 26 11:53 doc
drwxr-xr-x   2 gap     1024 Nov  8  1991 grp
drwxr-xr-x   2 gap     2048 Nov 26 09:42 lib
drwxr-xr-x   2 gap     2048 Nov 26 09:42 pkg
drwxr-xr-x   2 gap     2048 Nov 26 09:42 src
drwxr-xr-x   2 gap     1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

gap@tiffy:~ > unzoo x anupq
gap@tiffy:~ > ls -l gap3r3p0/pkg/anupq
drwxr-xr-x  5 gap       512 Feb 24 11:17 MakeLibrary
-rw-r--r--  1 gap     28926 Jun  8 14:21 Makefile
-rw-r--r--  1 gap      8818 Jun  8 14:21 README
-rw-r--r--  1 gap       753 Jun 23 18:59 StandardPres
drwxr-xr-x  2 gap      1024 Jun  8 14:15 TEST
drwxr-xr-x  2 gap       512 Jun 16 16:03 bin
drwxr-xr-x  2 gap       512 May 16 06:58 cayley
drwxr-xr-x  2 gap       512 Jun  8 08:48 doc
drwxr-xr-x  2 gap      1024 Mar  5 04:01 examples
drwxr-xr-x  2 gap       512 Jun 23 16:37 gap
drwxr-xr-x  2 gap       512 Jun 24 10:51 include
-rw-rw-rw-  1 gap       867 Jun  9 16:12 init.g
drwxr-xr-x  2 gap      1024 May 21 02:28 isom
drwxr-xr-x  2 gap      6656 Jun 24 11:10 srce

Switch into the directory gap3r3p0/pkg/anupq/srce/. The makefile for the gap version of the ANU pq is in the subdirectory above so do not type make.

gap@tiffy:~ > cd gap3r3p0/pkg/anupq
gap@tiffy:../srce > make -f ../Makefile
usage: 'make <target>'  where target is one of
'dec-mips-ultrix-gcc2-gmp'  for DECstations running Ultrix with gcc2/gmp
'dec-mips-ultrix-cc-gmp'    for DECstations running Ultrix with cc/gmp
'dec-mips-ultrix-gcc2'      for DECstations running Ultrix with gcc2
'dec-mips-ultrix-cc'        for DECstations running Ultrix with cc
'hp-hppa1.1-hpux-cc-gmp'    for HP 9000/700 running HP-UX with cc/gmp
'hp-hppa1.1-hpux-cc'        for HP 9000/700 running HP-UX with cc
'hp-hppa1.0-hpux-cc-gmp'    for HP 9000/800 running HP-UX with cc/gmp
'hp-hppa1.0-hpux-cc'        for HP 9000/800 running HP-UX with cc
'ibm-i386-386bsd-gcc2-gmp'  for IBM PCs running 386BSD with gcc2/gmp
'ibm-i386-386bsd-cc-gmp'    for IBM PCs running 386BSD with cc/gmp
'ibm-i386-386bsd-gcc2'      for IBM PCs running 386BSD with gcc2
'ibm-i386-386bsd-cc'        for IBM PCs running 386BSD with cc (GNU)
'mips-mips-bsd-cc-gmp'      for MIPS running RISC/os Berkeley with cc/gmp
'mips-mips-bsd-cc'          for MIPS running RISC/os Berkeley with cc

'next-m68kmach-gcc2-gmp'    for NeXT running Mach with gcc2
'next-m68k-mach-cc-gmp'     for NeXT running Mach with cc/gmp
'next-m68k-mach-gcc2'       for NeXT running Mach with gcc2
'next-m68k-mach-cc'         for NeXT running Mach with cc (GNU)
'sun-sparc-sunos-gcc2-gmp'  for SUN 4 running SunOs with gcc2/gmp
'sun-sparc-sunos-cc-gmp'    for SUN 4 running SunOs with cc/gmp
'sun-sparc-sunos-gcc2'      for SUN 4 running SunOs with gcc2
'sun-sparc-sunos-cc'        for SUN 4 running SunOs with cc
'unix-gmp'                  for a generic unix system with cc/gmp
'unix'                      for a generic unix system with cc
'clean'                     remove all created files
   targets are listed according to preference,
   i.e., 'sun-sparc-sunos-gcc2' is better than 'sun-sparc-sunos-cc'.
   additional C compiler and linker flags can be passed with
   'make <target> COPTS=<compiler-opts> LOPTS=<linker-opts>',
   i.e., 'make sun4-sunos-cc COPTS=-g LOPTS=-g'.
   set GAP if gap 3.3 is not started with the command 'gap',
   i.e., 'make sun-sparc-sunos-cc GAP=/home/gap/bin/gap-3.3'.
   in order to use the GNU multiple precision (gmp) set
   'GNUINC' (default '/usr/local/include') and
   'GNULIB' (default '/usr/local/lib')

Select the target you need. If you have the GNU multiple precision arithmetic (gmp) installed on your system, select the target ending in -gmp. Note that the gmp is not required. In our case we first compile the DECstation version. We assume that the command to start

GAP is /usr/local/bin/gap for tiffy and bjerun and /rem/tiffy/usr/local/bin/gap
for bert.
    gap@tiffy:../srce > make -f ../gap/Makefile dec-mips-ultric-cc \
                              GAP=/usr/local/bin/gap

# you will see a lot of messages and a few warnings

    gap@tiffy:../srce > mv pq ../bin/pq-dec-mips-ultrix

Now repeat the compilation for the NeXTstation. Do not forget to clean up.

    gap@tiffy:../srce > rlogin bjerun
    gap@bjerun:~ > cd gap3r3p0/pkg/anupq/srce
    gap@bjerun:../srce > make -f ../gap/Makefile clean
    gap@bjerun:../srce > make -f ../gap/Makefile next-m68k-mach-cc \
                              GAP=/usr/local/bin/gap

# you will see a lot of messages and a few warnings

    gap@bjerun:../srce > mv pq ../bin/pq-next-m68k-mach
    gap@bjerun:../srce > exit
    gap@tiffy:../srce >

Switch into the subdirectory bin/ and create a script which will call the correct binary for each machine. A skeleton shell script is provided in bin/pq.sh.

    gap@tiffy:../srce > cd ..
    gap@tiffy:../anupq > cat > bin/pq

#!/bin/csh
switch ( `hostname` )
  case 'tiffy':
    exec $0-dec-mips-ultrix $* ;
    breaksw ;
  case 'bert':
    setenv ANUPQ_GAP_EXEC /rem/tiffy/usr/local/bin/gap ;
    exec $0-dec-mips-ultrix $* ;
    breaksw ;
  case 'bjerun':
    limit stacksize 2048 ;
    exec $0-next-m68k-mach $* ;
    breaksw ;
  default:
    echo "pq: sorry, no executable exists for this machine" ;
    breaksw ;
endsw

ctr-D

gap@tiffy:../anupq > chmod 755 bin/pq

Note that the NeXTstation requires you to raise the stacksize. If your default limit on any other machine for the stack size is less than 1024 you might need to add the limit

stacksize 2048 line.

If the documentation is not already installed or an older version is installed, copy the file gap/anupq.tex into the doc/ directory and run latex again (see 47.4). In general the documentation will already be installed so you can just skip the following step.

gap@tiffy:../anupq > cp gap/anupq.tex ../../doc
gap@tiffy:../anupq > cd ../../doc
gap@tiffy:../doc > latex manual

# a few messages about undefined references

gap@tiffy:../doc > latex manual

# a few messages about undefined references

gap@tiffy:../doc > makeindex manual

# makeindex prints some diagnostic output

gap@tiffy:../doc > latex manual

# there should be no warnings this time

gap@tiffy:../doc cd ../pkg/anupq

Now it is time to test the installation. The first test will only test the ANU pq.

gap@tiffy:../anupq > bin/pq < gap/test1.pga

# a lot of messages ending in

**************************************************
Starting group: c3c3 #2;2 #4;3
Order: 3^7
Nuclear rank: 3
3-multiplicator rank: 4
# of immediate descendants of order 3^8 is 7
# of capable immediate descendants is 5

**************************************************
34 capable groups saved on file c3c3_class4
Construction of descendants took 1.92 seconds
Select option: 0
Exiting from p-group generation
Select option: 0
Exiting from ANU p-Quotient Program
Total user time in seconds is 1.97
gap@tiffy:../anupq > ls -l c3c3*
total 89
-rw-r--r--    1 gap    3320 Jun 24 11:24 c3c3_class2
-rw-r--r--    1 gap    5912 Jun 24 11:24 c3c3_class3
-rw-r--r--    1 gap   56184 Jun 24 11:24 c3c3_class4
gap:../anupq > rm c3c3_class*

The second test will test the stacksize. If it is too small you will get a memory fault, try to raise the stacksize as described above.

gap@tiffy:../anupq > bin/pq < gap/test2.pga

# a lot of messages ending in

**************************************************
Starting group: c2c2 #1;1 #1;1 #1;1
Order: 2^5
Nuclear rank: 1
2-multiplicator rank: 3
Group c2c2 #1;1 #1;1 #1;1 is an invalid starting group
**************************************************
Starting group: c2c2 #2;1 #1;1 #1;1
Order: 2^5
Nuclear rank: 1
2-multiplicator rank: 3
Group c2c2 #2;1 #1;1 #1;1 is an invalid starting group
Construction of descendants took 0.47 seconds
Select option: 0
Exiting from p-group generation
Select option: 0
Exiting from ANU p-Quotient Program
Total user time in seconds is 0.50
gap@tiffy:../anupq > ls -l c2c2*
total 45
-rw-r--r--    1 gap   6228 Jun 24 11:25 c2c2_class2
-rw-r--r--    1 gap  11156 Jun 24 11:25 c2c2_class3
-rw-r--r--    1 gap   2248 Jun 24 11:25 c2c2_class4

-rw-r--r--    1 gap      0 Jun 24 11:25 c2c2_class5
gap:../anupq > rm c2c2_class*

The third example tests the link between the ANU pq and GAP. If there is a problem you will get a error message saying Error in system call to GAP; if this happens, check the environment variable ANUPQ GAP EXEC.

gap@tiffy:../anupq > bin/pq < gap/test3.pga

# a lot of messages ending in

**************************************************
Starting group: c5c5 #1;1 #1;1
Order: 5^4
Nuclear rank: 1
5-multiplicator rank: 2
# of immediate descendants of order 5^5 is 2
**************************************************
Starting group: c5c5 #1;1 #2;2
Order: 5^5
Nuclear rank: 3
5-multiplicator rank: 3
# of immediate descendants of order 5^6 is 3
# of immediate descendants of order 5^7 is 3
# of capable immediate descendants is 1
# of immediate descendants of order 5^8 is 1
# of capable immediate descendants is 1
**************************************************
2 capable groups saved on file c5c5_class4
**************************************************
Starting group: c5c5 #1;1 #2;2 #4;2
Order: 5^7
Nuclear rank: 1
5-multiplicator rank: 2
# of immediate descendants of order 5^8 is 2
# of capable immediate descendants is 2
**************************************************
Starting group: c5c5 #1;1 #2;2 #7;3
Order: 5^8
Nuclear rank: 2
# of immediate descendants of order 5^9 is 1
# of capable immediate descendants is 1
# of immediate descendants of order 5^10 is 1
# of capable immediate descendants is 1
**************************************************
4 capable groups saved on file c5c5_class5

Construction of descendants took 0.62 seconds
Select option: 0
Exiting from p-group generation
Select option: 0
Exiting from ANU p-Quotient Program
Total user time in seconds is 0.68
gap@tiffy:../anupq > ls -l c5c5*
total 41
-rw-r--r--    1 gap     924 Jun 24 11:27 c5c5_class2
-rw-r--r--    1 gap    2220 Jun 24 11:28 c5c5_class3
-rw-r--r--    1 gap    3192 Jun 24 11:30 c5c5_class4
-rw-r--r--    1 gap    7476 Jun 24 11:32 c5c5_class5
gap:../anupq > rm c5c5_class*

The fourth test will test the standard presentation part of the pq.

gap@tiffy:../anupq > bin/pq -i -k < gap/test4.sp

# a lot of messages ending in

Computing standard presentation for class 9 took 0.43 seconds
The largest 5-quotient of the group has class 9
Select option: 0
Exiting from ANU p-Quotient Program
Total user time in seconds is 2.17
gap@tiffy:../anupq > ls -l SPRES
-rw-r--r--  1 gap     768 Jun 24 11:33 SPRES
gap@tiffy:../anupq > diff SPRES gap/out4.sp

# there should be no difference if compiled with -gmp

156250000
gap@tiffy:../anupq > rm SPRES

The last test will test the link between GAP and the ANU pq. If everything goes well you should not see any message.

gap@tiffy:../anupq > gap -b
gap> RequirePackage( "anupq" );
gap> ReadTest( "gap/anupga.tst" );
gap>

You may now repeat the tests for the other machines.

48.4 GRAPE Package

GRAPE (Version 2.1) is a system for computing with graphs, and is primarily designed for constructing and analysing graphs related to groups and finite geometries.

The vast majority of GRAPE functions are written entirely in the GAP language, except for the automorphism group and isomorphism testing functions, which use Brendan McKay’s nauty (Version 1.7) package [McK90].

Except for the nauty 1.7 package included with GRAPE, the GRAPE system was designed and written by Leonard H. Soicher, School of Mathematical Sciences, Queen Mary and Westfield College, Mile End Road, London E1 4NS, U.K., email:L.H.Soicher@qmw.ac.uk.

Please tell Leonard Soicher if you install GRAPE. Also, if you use GRAPE to solve a problem then also tell him about it, and reference

L.H.Soicher, GRAPE: a system for computing with graphs and groups, in Proc. 1991 DI-MACS Workshop on Groups and Computation, DIMACS Series in Discrete Mathematics and Theoretical Computer Science, to appear.

This document is in doc/grape1.tex.

If you use the automorphism group and graph isomorphism testing functions of GRAPE then you are also using Brendan McKay’s nauty package, and should also reference

B.D.McKay, nauty users guide (version 1.5), Technical Report TR-CS-90-02, Computer Science Department, Australian National University, 1990.

This document is in nauty17/nug.alw in postscript form. There is also a readme for nauty

in nauty17/read.me.

Warning A canonical labelling given by nauty can depend on the version of nauty (previously Version 1.4, and presently Version 1.7, in GRAPE), certain parameters of nauty (always set the same by GRAPE), and the compiler and computer used. If you use a canonical labelling (say by using the IsIsomorphicGraph function) of a graph stored on a file, then you must be sure that this field was created in the same environment in which you are presently computing. If in doubt, unbind the canonicalLabelling field of the graph.

GRAPE is provided ”as is”, with no warranty whatsoever. Changes from previous versions of GRAPE, other than additional functions and function parameters which are described in chapter 51, can be found in the file README. Please read the copyright notice in the file

COPYING.

Please send comments on GRAPE, bug reports, etc. to L.H.Soicher@qmw.ac.uk.

48.5 Installing the GRAPE Package

GRAPE consists of two parts. The first part is a set of GAP functions for constructing and analysing graphs, which will run on any machine that supports GAP. The second part is based on the nauty package written in C and computes automorphism groups of graphs, and tests for graph isomorphisms. This part of the package can only be installed under UNIX.

If you got a complete binary and source distribution for your machine, nothing has to be done if you want to use GRAPE for a single architecture. If you want to use GRAPE for machines with different architectures skip the extraction and compilation part of this section and proceed with the creation of shell scripts described below.

If you got a complete source distribution, skip the extraction part of this section and proceed with the compilation part below.

In the example we will assume that you, as user gap, are installing the GRAPE package for use by several users on a network of two DECstations, called bert and tiffy, and a PC running 386BSD, called waldorf. We assume that GAP is also installed on these machines following the instructions given in 47.4.

Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file grape.zoo (see 47.1). Then you must locate the GAP directories containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be replaced by current the patch level.

gap@tiffy:~ > ls -l
drwxr-xr-x  11 gap      gap          1024 Nov  8  1991 gap3r3p0
-rw-r--r--   1 gap      gap        342865 May 27 15:16 grape.zoo
gap@tiffy:~ > ls -l gap3r3p0
drwxr-xr-x   2 gap      gap          3072 Nov 26 11:53 doc
drwxr-xr-x   2 gap      gap          1024 Nov  8  1991 grp
drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 lib
drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 src
drwxr-xr-x   2 gap      gap          1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

gap@tiffy:~ > unzoo x grape.zoo
gap@tiffy:~ > ls -l gap3r3p0/pkg/grape
-rw-r--r--    1 gap   1063 May 22 14:40 COPYING
-rw-r--r--    1 gap   2636 May 28 09:58 Makefile
-rw-r--r--    1 gap   4100 May 24 14:57 README
drwxr-xr-x    2 gap    512 May 28 11:36 bin
drwxr-xr-x    2 gap    512 May 25 14:52 doc
drwxr-xr-x    2 gap    512 May 22 16:59 grh
-rw-r--r--    1 gap  82053 May 27 12:19 init.g
drwxr-xr-x    2 gap    512 May 27 14:18 lib
drwxr-xr-x    2 gap    512 May 28 11:36 nauty17
drwxr-xr-x    2 gap    512 May 22 12:32 prs
drwxr-xr-x    2 gap    512 May 28 11:36 src

You are now able to use the all functions described in chapter 51 except AutGroupGraph and IsIsomorphicGraph which use the nauty package.

gap> RequirePackage("grape");
Loading  GRAPE 2.1  (GRaph Algorithms using PErmutation groups),
by L.H.Soicher@qmw.ac.uk.
gap> gamma := JohnsonGraph( 4, 2 );
rec(
  isGraph := true,
  order := 6,
  group := Group( (1,5)(2,6), (1,3)(4,6), (2,3)(4,5) ),
  schreierVector := [ -1, 3, 2, 3, 1, 2 ],
  adjacencies := [ [ 2, 3, 4, 5 ] ],
  representatives := [ 1 ],
  names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ],

      [ 3, 4 ] ],
  isSimple := true )

If the documentation is not already installed or an older version is installed, copy the file doc/grape.tex into the doc/ directory and run latex again (see 47.4). In general the documentation will already be installed so you can just skip the following step. The dvi-file doc/grapedoc.dvi contains this chapter and chapter 51.

gap@tiffy:~ > cd gap3r3p0/pkg/grape
gap@tiffy:../grape > cp doc/grape.tex ../../doc
gap@tiffy:../grape > cd ../../doc
gap@tiffy:../doc > latex manual

# a few messages about undefined references

gap@tiffy:../doc > latex manual

# a few messages about undefined references

gap@tiffy:../doc > makeindex manual

# makeindex prints some diagnostic output

gap@tiffy:../doc > latex manual

# there should be no warnings this time

gap@tiffy:../doc cd ../pkg/grape

In order to compile nauty and the filters used by GRAPE to interact with nauty type make to get a list of support machines.

gap@tiffy:../grape > make
usage: 'make <target>' EXT=<ext> where target is one of
'dec-mips-ultrix-cc'   for DECstations running Ultrix with cc
'hp-hppa1.1-hpux-cc'   for HP 9000/700 under HP-UX with cc
'hp-hppa1.0-hpux-cc'   for HP 9000/800 under HP-UX with cc
'ibm-i386-386bsd-gcc2' for IBM PCs under 386BSD with GNU cc 2
'ibm-i386-386bsd-cc'   for IBM PCs under 386BSD with cc (GNU)
'sun-sparc-sunos-cc'   for SUN 4 under SunOS with cc
'bsd'                  for others under Berkeley UNIX with cc
'usg'                  for others under System V UNIX with cc
where <ext> should be a sensible extension, i.e.,
'EXT=.sun' for SUN or 'EXT=' if GRAPE only runs
on a single architecture

Select the target you need. In your case we first compile the DECstation version. We use the extension -dec-mips-ultrix, which creates the binaries

dreadnaut-dec-mips-ultrix, drcanon3-dec-mips-ultrix,
gap3todr-dec-mips-ultrix and drtogap3-dec-mips-ultrix

in the bin/ directory.

gap@tiffy:../grape > make dec-mips-ultric-cc EXT=-dec-mips-ultrix

# you will see a lot of messages

Now repeat the compilation for the PC. Do not forget to clean up.

gap@tiffy:../grape > rlogin waldorf
gap@waldorf:~ > cd gap3r3p0/pkg/grape
gap@waldorf:../grape > make clean

    gap@waldorf:../grape > make ibm-i386-386bsd-gcc2 EXT=-ibm-i386-386bsd

# you will see a lot of messages

    gap@waldorf:../grape > exit
    gap@tiffy:../grape >

Switch into the subdirectory bin/ and create four shell scripts which will call the correct binary for each machine. Skeleton shell scripts are provided in bin/dreadbaut.sh,

bin/drcanon3, etc.
    gap@tiffy:../grape > cat > bin/dreadnaut
    #!/bin/csh
    switch ( `hostname` )
      case 'tiffy':
      case 'bert':
        exec $0-dec-mips-ultrix $* ;
        breaksw ;
      case 'waldorf':
        exec $0-ibm-i386-386bsd $* ;
        breaksw ;
      default:
        echo "dreadnaut: sorry, no executable exists for this machine" ;
        breaksw ;
    endsw

ctr-D

    gap@tiffy:../grape > chmod 755 bin/dreadnaut

You must also create similar shell scripts for drcanon3, drtogap3, and gap3todr. Note that if you are using grape only on a single architecture you can specify an empty extension using EXT= as a parameter to make. In this case do not create the above shell scripts. The following example will test the interface between GRAPE and nauty.

    gap> IsIsomorphicGraph( JohnsonGraph(7,3), JohnsonGraph(7,4) );
    true
    gap> AutGroupGraph( JohnsonGraph(4,2) );
    Group( (3,4), (2,3)(4,5), (1,2)(5,6) )

48.6 NQ Package

NilpotentQuotient( F )
NilpotentQuotient( F , c )

NilpotentQuotient computes the quotient groups of the finitely presented group F successively modulo the terms of the lower central series of F . If it terminates, it returns a list L. The i-th entry of L contains the non-trivial abelian invariants of the i-th factor of the lower central series of F (the largest abelian quotient being the first factor).

NilpotentQuotient accepts a positive integer c as an optional second argument. If the second argument is present, the function computes the quotient group of F modulo the c-th term of the lower central series of F (the commutator subgroup is the first term).

    gap> RequirePackage("nq");
    gap> a := AbstractGenerator( "a" );;

    gap> b := AbstractGenerator( "b" );;
    gap>
    gap> G := rec( generators := [a, b],
    >     relators   := [ LeftNormedComm( b,a,a,a,a ),
    >                     LeftNormedComm( b,a,b,b,b ),
    >                     LeftNormedComm( b,a,a*b,a*b,a*b ),
    >                     LeftNormedComm( b,a,a*b^2,a*b^2,a*b^2 ),
    >                     LeftNormedComm( b,a,b,a,a,a ),
    >                     LeftNormedComm( b,a,a,b,b,b ) ]
    >    );;
    gap>
    gap> NilpotentQuotient( G, 6 );
    [ [ 0, 0 ], [ 0 ], [ 0, 0 ], [ 0, 0, 0 ], [ 2, 0, 0 ], [ 2, 10, 0 ] ]

This implementation was developed in C by

Werner Nickel School of Mathematical Sciences Australian National University Canberra, ACT 0200

e-mail werner@pell.anu.edu.au

48.7 Installing the NQ Package

The NQ is written in C and the package can only be installed under UNIX. It has been tested on DECstation running Ultrix, a NeXTstation running NeXT-Step 3.0, and SUNs running SunOS. It requires the GNU multiple precision arithmetic. Make sure that this library is installed before trying to install the NQ.

If you got a complete binary and source distribution for your machine, nothing has to be done if you want to use the NQ for a single architecture. If you want to use the NQ for machines with different architectures skip the extraction and compilation part of this section and proceed with the creation of shell scripts described below.

If you got a complete source distribution, skip the extraction part of this section and proceed with the compilation part below.

In the example we will assume that you, as user gap, are installing the NQ package for use by several users on a network of two DECstations, called bert and tiffy, and a NeXTstation, called bjerun. We assume that GAP is also installed on these machines following the instructions given in 47.4.

Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file nq.zoo (see 47.1). Then you must locate the GAP directories containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be be replaced by the patch level.

    gap@tiffy:~ > ls -l
    drwxr-xr-x  11 gap      gap          1024 Nov  8  1991 gap3r3p0
    -rw-r--r--   1 gap      gap        106307 Jan 24 15:16 nq.zoo
    gap@tiffy:~ > ls -l

drwxr-xr-x   2 gap      gap          3072 Nov 26 11:53 doc
drwxr-xr-x   2 gap      gap          1024 Nov  8  1991 grp
drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 lib
drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 src
drwxr-xr-x   2 gap      gap          1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

gap@tiffy:~ > unzoo x nq.zoo
gap@tiffy:~ > ls -l gap3r3p0/pkg/nq
drwxr-xr-x   2 gap    gap      1024 Jan 24 21:00 bin
drwxr-xr-x   2 gap    gap      1024 Jan 19 11:33 examples
drwxr-xr-x   2 gap    gap      1024 Jan 24 21:03 gap
lrwxrwxrwx   1 gap    gap         8 Jan 19 11:33 init.g
drwxr-xr-x   2 gap    gap      1024 Jan 24 21:04 src
-rwxr--r--   1 gap    gap       144 Dec 28 15:08 testNq

Switch into the directory src/ and type make to compile the NQ. If the header files for the GNU multiple precision arithmetic are not in /usr/local/include you must set GNUINC to the correct directory. If the library for the GNU multiple precision arithmetic is not /usr/local/lib/libmp.a you must set GNULIB. In your case we first compile the DECstation version. If your operating system does not provide a function getrusage start make

with COPTS=-DNO GETRUSAGE.
gap@tiffy:~ > cd gap3r3p0/pkg/nq/src
gap@tiffy:../src > make GNUINC=/usr/gnu/include \
                        GNULIB=/usr/gnu/lib/libmp.a
# you will see a lot of messages

Now it is possible to test the standalone.

gap@tiffy:../src > cd ..
gap@tiffy:../nq > testNq

If testNq reports a difference others then machine name, runtime or size, check the GNU multiple precision arithmetic and warnings generated by make. If testNq succeeded , move the executable to the bin/ directory.

 gap@tiffy:../nq > mv src/nq bin/nq-dec-mips-ultrix

Now repeat the compilation for the NeXTstation. Do not forget to clean up.

gap@tiffy:../nq > rlogin bjerun
gap@bjerun:~ > cd gap3r3p0/pkg/nq/src
gap@bjerun:../src > make clean
gap@bjerun:../src > make
# you will see a lot of messages
gap@bjerun:../src > mv nq ../bin/nq-next-m68k-mach
gap@bjerun:../src > exit
gap@tiffy:../src >

Switch into the subdirectory bin/ and create a script which will call the correct binary for each machine. A skeleton shell script is provided in bin/nq.sh.

    gap@tiffy:../src > cd ..
    gap@tiffy:../nq > cat > bin/nq
    #!/bin/csh
    switch ( `hostname` )
      case 'bert':
      case 'tiffy':
        exec $0-dec-mips-ultrix $* ;
        breaksw ;
      case 'bjerun':
        exec $0-next-m68k-mach $* ;
        breaksw ;
      default:
        echo "nq: sorry, no executable exists for this machine" ;
        breaksw ;
    endsw

ctr-D

    gap@tiffy:../nq > chmod 755 bin/nq

Now it is time to test the package. Assuming that testNq worked the following will test the link to GAP.

    gap@tiffy:../nq > gap -b
    gap> RequirePackage( "nq" );
    gap> ReadTest( "gap/nq.tst" );
    gap>

48.8 SISYPHOS Package

SISYPHOS provides access to implementations of algorithms for dealing with p-groups and their modular group algebras. At the moment only the programs for p-groups are accessible via GAP. They can be used to compute isomorphisms between p-groups, and automorphism groups of p-groups.

The description of the functions available in the SISYPHOS package is given in chapter 52.

For details about the implementation and the standalone version see the README. This implementation was developed in C by

Martin Wursthorn Math. Inst. B, 3. Lehrstuhl Universität Stuttgart

e-mail pluto@machnix.mathematik.uni-stuttgart.de Tel. +49 (0)711 685 5517 Fax. +49 (0)711 685 5322

48.9 Installing the SISYPHOS Package

SISYPHOS is written in ANSI-C and should run on every UNIX system (and some non-UNIX systems) that provides an ANSI-C Compiler, e.g., the GNU C compiler. SISYPHOS has been ported to IBM RS6000 running AIX 3.2, HP9000 7xx running HP-UX 8.0/9.0, PC 386/486 running Linux, PC 386/486 running DOS or OS/2 with emx and ATARI ST/TT running TOS.

In the example we will assume that you, as user gap, are installing the SISYPHOS package for use by several users on a network of two DECstations, called bert and tiffy, and a 486 PC, called waldorf. We assume that GAP is also installed on these machines following the instructions given in 47.4.

Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file sisyphos.zoo (see 47.1). Then you must locate the GAP directories containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be be replaced by the patch level.

    gap@tiffy:~ > ls -l
    drwxr-xr-x  11 gap     1024 Nov  8  1991 gap3r3p0
    -rw-r--r--   1 gap   245957 Dec 27 15:16 sisyphos.zoo
    gap@tiffy:~ > ls -l gap3r3p0
    drwxr-xr-x   2 gap     3072 Nov 26 11:53 doc
    drwxr-xr-x   2 gap     1024 Nov  8  1991 grp
    drwxr-xr-x   2 gap     2048 Nov 26 09:42 lib
    drwxr-xr-x   2 gap     2048 Nov 26 09:42 src
    drwxr-xr-x   2 gap     1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

    gap@tiffy:~ > unzoo x sisyphos.zoo
    gap@tiffy:~ > ls -l gap3r3p0/pkg/sisyphos
    -rw-r--r--  1 gap          9496 Feb 11  1993 README
    drwxr-xr-x  3 gap           512 Oct 19 10:24 doc
    drwxr-xr-x  2 gap           512 Oct 15 14:30 groups
    drwxr-xr-x  2 gap           512 Apr  1  1993 ideal
    -rw-r--r--  1 gap         22072 Oct 19 10:23 init.g
    drwxr-xr-x  2 gap          1536 Oct 15 14:49 src

Switch into the directory src/. It contains the makefile for SISYPHOS.

gap@tiffy:../src > make
usage: 'make <target>'  where target is one of
'hp700-hpux-gcc2'    for HP 9000/7xx under HP-UX with GNU cc 2
'hp700-hpux-cc'      for HP 9000/7xx under HP-UX with cc
'hp700-hpux-cci'     for HP 9000/7xx under HP-UX with cc -
                       generate version for profile dependent optimization
'hp700-hpux-ccp'     for HP 9000/7xx under HP-UX with cc -
                       relink with profile dependent optimization
'ibm6000-aix-cc'     for IBM RS/6000 under AIX with cc
'ibmpc-linux-gcc2'   for IBM PCs under Linux with GNU cc 2
'ibmpc-emx-gcc2'     for IBM PCs under DOS or OS/2 2.0 with emx
'generic-unix-gcc2'  for other UNIX machines with GNU cc 2
                     this should work on most machines

Select the target you need. In our case we first compile the DECstation version. We assume that the command to start GAP is /usr/local/bin/gap for tiffy and waldorf

and /rem/tiffy/usr/local/bin/gap for bert.
gap@tiffy:../src > make generic-unix-gcc2

# you will see a lot of messages and maybe a few warnings You should test the standalone now. The following command should run without any comment. This will work, however, only for UNIX machines.

gap@tiffy:../src > testsis

The executables will be collected in the /bin directory, so we move that for the DECstation there.

gap@tiffy:../src > mv sis ../bin/sis.ds

Now repeat the compilation for the PC. Do not forget to clean up.

gap@tiffy:../src > rlogin waldorf
gap@waldorf:~ > cd gap3r3p0/pkg/sisyphos/src
gap@waldorf:../src > make clean
gap@waldorf:../src > make generic-unix-gcc2

# you will see a lot of messages and maybe a few warnings Test the executable (under UNIX only), and move it to the right place.

gap@waldorf:../src > testsis
gap@waldorf:../src > mv sis ../bin/sis.386bsd
gap@waldorf:../src > exit
gap@tiffy:../src >

Switch into the subdirectory bin/ and create a script which will call the correct binary for each machine.

gap@tiffy:../src > cd ..
gap@tiffy:../sisyphos > cat > bin/sis
#!/bin/csh
switch ( `hostname` )
  case 'bert':
  case 'tiffy':
    exec ~gap/3.2/pkg/sisyphos/bin/sis.ds $* ;
    breaksw ;
  case 'waldorf':
    exec ~gap/3.2/pkg/sisyphos/bin/sis.386bsd $* ;
    breaksw ;
  default:
    echo "sis: sorry, no executable exists for this machine" ;
    breaksw ;
endsw

ctr-D

gap@tiffy:../sisyphos > chmod 755 bin/sis

48.10 Weyl Package

There is a collection of programs for dealing with finite Weyl groups, associated (Iwahori-) Hecke algebras, and their representations. These programs were written by Meinolf Geck (Lehrstuhl D für Mathematik, RWTH Aachen, Templergraben 64, 5100 Aachen, Germany, e-mail:geck@tiffy.math.rwth-aachen.de).

On a low level, these programs provide the basic data about a fixed finite Weyl group W: having specified a Cartan matrix, they compute the root system, the reflection representation, and the permutation representation on the root vectors. Elements of W can be given either as permutations or as reduced words in the standard generators (i.e., simply as a list of integers labelling the generators), and there are functions to convert these expressions into each other. In particular, all functions of GAP for working with permutation groups can be applied.

On a higher level, it is possible to compute distinguished coset representatives and representatives of the conjugacy classes of W of minimal length. Furthermore, there are functions for calculating Kazhdan-Lusztig polynomials, left cells, and the corresponding representations of the associated Hecke algebra H (for groups of rank ≤ 5, say). Also, computations inside H are possible, such as multiplying two arbitrary elements of H and expressing the result as a linear combination in the basis elements T_{w}, w ∈ W.

The following section describes the installation of the Weyl package, a description of the functions available in the Weyl package is given in chapter 50.

48.11 Installing the Weyl Package

The Weyl package is completely written in the GAP language, it does not require any additional programms and/or compilations. It will run on any computer that runs GAP. In the following we will describe the installation under UNIX. The installation on the Atari ST, TT or IBM PC is similar.

If you got a complete source distribution, nothing has to be done.

In the example we give we will assume that GAP is installed in the home directory of a pseudo user gap and that you, as user gap, want to install the Weyl package. Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file weyl.zoo (see 47.1). Then you must locate the GAP directories containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be replaced by the current the patch level.

    gap:~ > ls -l
    drwxr-xr-x  11 gap      gap          1024 Nov  8  1991 gap3r3p0
    -rw-r--r--   1 gap      gap         35870 Nov 26 12:33 weyl.zoo
    gap:~ > ls -l gap3r3p0
    drwxr-xr-x   2 gap      gap          3072 Nov 26 11:53 doc
    drwxr-xr-x   2 gap      gap          1024 Nov  8  1991 grp
    drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 lib
    drwxr-xr-x   2 gap      gap          2048 Nov 26 09:42 src
    drwxr-xr-x   2 gap      gap          1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

    gap:~ > unzoo x weyl.zoo

gap:~ > ls -l gap3r3p0/pkg/weyl
-rw-r--r--   1 gap      gap          1536 Nov 22 04:16 README
-rw-r--r--   1 gap      gap         57058 Nov 22 04:02 init.g
-rw-r--r--   1 gap      gap         40304 Nov 22 04:18 weyl.tex
-rw-r--r--   1 gap      gap         57058 Nov 22 03:41 weylgrp.g

If the documentation is not already installed or an older version is installed, copy the file weyl.tex into the doc/ directory and run latex again (see 47.4). In general the documentation will already be installed so you can just skip the following step.

gap:~ > cd gap3r3p0/pkg/weyl
gap:../weyl > cp weyl.tex ../../doc
gap:../weyl > cd ../../doc
gap:../doc > latex manual

# a few messages about undefined references

gap:../doc > latex manual

# a few messages about undefined references

gap:../doc > makeindex manual

# makeindex prints some diagnostic output

gap:../doc > latex manual

# there should be no warnings this time Now it is time to test the installation. Let us assume that the executable of GAP lives in src/ and is called gap.

gap:~/gap3r3p0 > src/gap -b
gap> ?CartanMat
CartanMat ____________________________Weyl Groups and Hecke Algebras
'CartanMat( <type>, <n> )'
returns the Cartan matrix of Dynkin type <type> and rank <n>.
    gap> CartanMat( "F", 4 );
    [ [  2, -1,  0,  0 ],
      [ -1,  2, -1,  0 ],
      [  0, -2,  2, -1 ],
      [  0,  0, -1,  2 ] ]
This function requires the package  "weyl"  (see "RequirePackage").
gap> RequirePackage( "weyl" );
gap> CartanMat( "F", 4 );
[ [ 2, -1, 0, 0 ], [ -1, 2, -1, 0 ], [ 0, -2, 2, -1 ],
  [ 0, 0, -1, 2 ] ]

If RequirePackage signals an error check the permissions of the subdirectories pkg/ and

weyl/.

48.12 The XGap Package

XGap is an experimental graphical user interface for GAP. The only supported window system at the moment is X-Windows 11R5, however, programs written for XGap will run on other platforms as soon as an user interface is available on these platforms.

Right now, no documentation is available, manual entries are delivered as soon as the system is stable. If you want to play with XGap try

    gap> GrahpicLattice( Group( (1,2,3,4), (1,3) ), 400, 600 );;
    gap> s := GrahpicSheet( "Hallo World", 300, 300 );;
    gap> c := Circle( s, 100, 100, 50 );;
    gap> c.operations.MoveDelta( c, 10, 10 );
    gap> for i  in [ 1 .. 10 ]  do
    > c.operations.MoveDelta( c, i, -i );
    > od;
    gap> Delete( s, c );
    gap> Close(s);

Please send comments, bug reports, etc. to

    fceller@math.rwth-achen.de or  gap-trouble@math.rwth-aachen.de

48.13 Installing the XGap Package

XGap is written in C and the package can only be installed under UNIX running X-Windows 11R5. It has been tested on a DECstation running Ultrix, a SUN running SunOS, a PC running 386bsd, and a NeXTstation running NeXTSTEP 3.0 and MouseX.

If you got a complete binary and source distribution for your machine, no compilation is necessary, you must however adapted the shell script gap3r3p0/pkg/xgap/bin/xgap.

If you got a complete source distribution, skip the extraction part of this section and proceed with the compilation part below.

In the example we will assume that you, as user gap, are installing the XGap package for use by several users on a network of two DECstations, called bert and tiffy. We assume that GAP is also installed on these machines following the instructions given in 47.4.

Note that certain parts of the output in the examples should only be taken as rough outline, especially file sizes and file dates are not to be taken literally.

First of all you have to get the file xgap.zoo (see 47.1). Then you must locate the GAP directory containing lib/ and doc/, this is usually gap3r3p0 where 0 is to be replaced by the current patch level.

    gap@tiffy:~ > ls -l
    drwxr-xr-x  11 gap     1024 Nov  8  1991 gap3r3p0
    -rw-r--r--   1 gap   360891 Dec 27 15:16 anupq.zoo
    gap@tiffy:~ > ls -l gap3r3p0
    drwxr-xr-x   2 gap     3072 Nov 26 11:53 doc
    drwxr-xr-x   2 gap     1024 Nov  8  1991 grp
    drwxr-xr-x   2 gap     2048 Nov 26 09:42 lib
    drwxr-xr-x   2 gap     2048 Nov 26 09:42 pkg
    drwxr-xr-x   2 gap     2048 Nov 26 09:42 src
    drwxr-xr-x   2 gap     1024 Nov 26 09:42 tst

Unpack the package using unzoo (see 47.4). Note that you must be in the directory containing gap3r3p0 to unpack the files. After you have unpacked the source you may remove the archive-file.

    gap@tiffy:~ > unzoo x xgap
    gap@tiffy:~ > ls -l gap3r3p0/pkg/xgap
    -rw-rw-rw-   1 gap  120  4401 Oct 18 12:12 Makefile
    drwxr-xr-x   2 gap  120   512 Oct 21 11:28 bin
    drwxr-xr-x   3 gap  120   512 Oct 18 12:06 lib
    drwxr-xr-x   3 gap  120   512 Oct 21 11:29 src
    drwxr-xr-x   2 gap  120   512 Apr 19  1993 tst

Switch into the directory gap3r3p0/pkg/xgap/src/ and type make. Note that we do not

supply an Imakefile.
gap@tiffy:~ > cd gap3r3p0/pkg/xgap/src
gap@tiffy:../src > make
usage: 'make <target>' where target is one of
'next-m68k-mach-gcc2'  for NeXT under Mach with X11R5 and GNU cc 2
'ibm-i386-386bsd-gcc2' for IBM PCs under 386BSD with X386 and GNU cc 2
'dec-mips-ultrix-gcc2' for DECstations under Ultrix with X11R5 and GNU cc 2
'dec-mips-ultrix-cc'   for DECstations under Ultrix with X11R5 and cc
'sun-sparc-sunos-cc'   for SUN SPARC under SunOS with X11R5 and cc
'sun-sparc-sunos-gcc'  for SUN SPARC under SunOS with X11R5 and GNU cc 2
    if the X11 include files directory does not live in
    "/usr/include",  you must set 'X11INC' to point to
    the correct directory
    if the X11 library files do not live in "/usr/lib",
    you must set 'X11LIB' to point to the correct directory

Select the target you need. In our case we use dec-mips-ultrix-gcc2 in order to compile the DECstation version.

    gap@tiffy:../src > make dec-mips-ultrix-gcc2

# you will see a lot of messages This create a executable xgap in the current directory. If GAP lives in the directory

     gap/gap3r3p0/src/gap try the following
    gap@tiffy:../src > ./xgap -G ~gap/gap3r3p0/src/gap

This command should create a new window in which GAP is awaiting your input, type quit; inside this window. Strip the executable and move it into the directory bin/, but do not call it xgap.

    gap@tiffy:../src > strip xgap
    gap@tiffy:../src > mv xgap ../bin/xgap-dec-mips-ultrix

Switch into the directory bin/ and adapted the shell script xgap, you must at least set the environment variables EXEC, LIB, DOC, and GAP, where EXEC should point to your newly created executable of XGap, LIB should (after the first semicolon) point to the directory where GAP’s libraries files live (it must end with a /), DOC should point to the directory containing the TeX files, and GAP should point to the executable of GAP.

ANU Pq

The ANU p-quotient program (pq) may be called from GAP. Using this program, GAP provides access to the following: the p-quotient algorithm; the p-group generation algorithm; a standard presentation algorithm; an algorithm to compute the automorphism group of a p-group.

The following section describes the function Pq, which gives access to the p-quotient algorithm.

The next section describes the function PqDescendants, which gives access to the p-group generation algorithm.

The next sections describe functions for saving results to file (see 49.3 and 49.4).

The next section describes the function StandardPresentation which gives access to the standard presentation algorithm and to the algorithm used to compute the automorphism group of a p-group.

The last sections describes the function IsIsomorphicPGroup which implements an isomorphism test for p-groups using the standard presentation algorithm.

49.1 Pq

Pq( F , ...  )

Let F be a finitely presented group. Then Pq returns the desired p-quotient of F as an ag group.

The following parameters or parameter pairs are supported.

”Prime”, p Compute the p-quotient for the prime p.

”ClassBound”, n The p-quotient computed has lower exponent-p class at most n.

”Exponent”, n The p-quotient computed has exponent n. By default, no exponent law is enforced.

”Metabelian”

The largest metabelian p-quotient is constructed.

”Verbose”

The runtime-information generated by the ANU pq is displayed. By default, pq works silently.

”OutputLevel”, n The runtime-information generated by the ANU pq is displayed at output level n, which must be a integer from 0 to 3. This parameter implies ”Verbose”.

”SetupFile”, name Do not run the ANU pq, just construct the input file and store it in the file name. In this case true is returned.

Alternatively, you can pass Pq a record as a parameter, which contains as entries some (or all) of the above mentioned. Those parameters which do not occur in the record are set to their default values.

    gap> RequirePackage("anupq");
    gap> f2 := FreeGroup( 2, "f2" );
    Group( f2.1, f2.2 )
    gap> Pq( f2, rec( Prime := 2,  ClassBound := 3 ) );
    Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7, G.8, G.9, G.10 )
    gap> g := f2 / [ f2.1^4, f2.2^4 ];;
    gap> Pq( g, rec( Prime := 2, ClassBound := 3 ) );
    Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7, G.8 )
    gap> Pq( g, "Prime", 2, "ClassBound", 3, "Exponent", 4 );
    Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7 )
    gap> g := f2 / [ f2.1^25, Comm(Comm(f2.2,f2.1),f2.1), f2.2^5 ];;
    gap> Pq( g, "Prime", 5, "Metabelian", "ClassBound", 5 );
    Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7 )

This function requires the package ”anupq” (see 48.1).

49.2 PqDescendants

PqDescendants( G, ...  )

Let G be an ag group of prime power order with a consistent power-commutator presentation (see 24.29). A list of automorphisms of G must be bound to the record component G.automorphisms such that G.automorphisms together with the inner automorphisms of G generate the automorphism group of G. PqDescendants returns a list of descendants of G.

The following optional parameters or parameter pairs are supported.

”ClassBound”, n PqDescendants generates only descendants with lower exponent-p class at most n. The default value is the exponent-p class of G plus one.

”OrderBound”, n PqDescendants generates only descendants of size at most p^{n}. Note that you cannot set both ”OrderBound” and ”StepSize”.

”StepSize”, n Let n be a positive integer. PqDescendants generates only those immediate descendants which are p^{n} bigger than their parent group.

”StepSize”, l Let l be a list of positive integers such that the sum of the length of l and the exponent-p class of G is equal to the class bound ”ClassBound”. Then l describes the step size for each additional class.

”AgAutomorphisms”

The automorphisms stored in G.automorphisms are a PAG-generating sequence for the automorphism group of G supplied in reverse order.

”RankInitialSegmentSubgroups”, n Set the rank of the initial segment subgroup chosen to be n. By default, this has value 0.

”SpaceEfficient”

The ANU pq performs calculations more slowly but with greater space efficiency. This flag is frequently necessary for groups of large Frattini quotient rank. The space saving occurs because only one permutation is stored at any one time. This option is only available in conjunction with the ”AgAutomorphisms” flag.

”AllDescendants”

By default, only capable descendants are constructed. If this flag is set, compute all descendants.

”Exponent”, n Construct only descendants with exponent n. Default is no exponent law.

”Metabelian”

Construct only metabelian descendants.

”Verbose”

The runtime-information generated by the ANU pq is displayed. By default, pq works silently.

”SetupFile”, name Do not run the ANU pq, just construct the input file and store it in the file name. In this case true is returned.

”TmpDir”, dir PqDescendants stores intermediate results in temporary files; the location of these files is determined by the value selected by TmpName. If your default temporary directory does not have enough free disk space, you can supply an alternative path dir. In this case PqDescendants stores its intermediate results in a temporary subdirectory of dir. Alternatively, you can globally set the variable ANUPQtmpDir, for instance in your ”.gaprc” file, to point to a suitable location.

Alternatively, you can pass PqDescendants a record as a parameter, which contains as entries some (or all) of the above mentioned. Those parameters which do not occur in the record are set to their default values.

Note that you cannot set both ”OrderBound” and ”StepSize”.

In the first example we compute all descendants of the Klein four group which have exponent-2 class at most 5 and order at most 2^{6}.

gap> f2 := FreeGroup( 2, "g" );;
gap> g := AgGroupFpGroup(f2 / [f2.1^2, f2.2^2, Comm(f2.2,f2.1)]);

Group( g.1, g.2 )
gap> g.name := "g";;
gap> g.automorphisms := [];;
gap> GroupHomomorphismByImages( g, g, [g.1, g.2], [g.2, g.1*g.2] );
GroupHomomorphismByImages( g, g, [ g.1, g.2 ], [ g.2, g.1*g.2 ] )
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages( g, g, [g.1, g.2], [g.2, g.1] );
GroupHomomorphismByImages( g, g, [ g.1, g.2 ], [ g.2, g.1 ] )
gap> Add( g.automorphisms, last );
gap> l := PqDescendants( g, "OrderBound", 6, "ClassBound", 5,
>                        "AllDescendants", "AgAutomorphisms" );;
gap> Length(l);
83
gap> Number( l, x -> x.isCapable );
47
gap> List( l, x -> Size(x) );
[ 8, 8, 8, 16, 16, 16, 32, 16, 16, 16, 16, 16, 32, 32, 64, 64, 32,
  32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 32, 32,
  32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
  64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 ]
gap> List( l, x -> Length( PCentralSeries( x, 2 ) ) - 1 );
[ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
  3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5 ]

In the second example we compute all capable descendants of order 27 of the elementary abelian group of order 9.

gap> f2 := FreeGroup( 2, "g" );;
gap> g := AgGroupFpGroup(f2 / [ f2.1^3, f2.2^3, Comm(f2.1,f2.2) ]);
Group( g.1, g.2 )
gap> g.name := "g";;
gap> g.automorphisms := [];;
gap> GroupHomomorphismByImages(g, g, [g.1, g.2], [g.1^2, g.2^2]);;
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages(g, g, [g.1, g.2], [g.2^2, g.1]);;
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages(g,g,[g.1,g.2],[g.1*g.2^2,g.1^2*g.2^2]);;
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages(g, g, [g.1,g.2], [g.1,g.1^2*g.2]);;
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages(g, g, [g.1, g.2], [g.1^2, g.2]);;
gap> Add( g.automorphisms, last );
gap> l := PqDescendants( g, "OrderBound", 3,
>                           "ClassBound", 2,
>                           "AgAutomorphisms" );;
gap> Length(l);

2
gap> List( l, x -> Size(x) );
[ 27, 27 ]
gap> List( l, x -> Length( PCentralSeries( x, 3 ) ) - 1 );
[ 2, 2 ]

In the third example, we compute all capable descendants of the elementary abelian group of order 5^{2} which have exponent-5 class at most 3, exponent 5, and are metabelian.

gap> f2 := FreeGroup( 2, "g" );;
gap> g := AgGroupFpGroup( f2 / [f2.1^5, f2.2^5, Comm(f2.2,f2.1)] );
Group( g.1, g.2 )
gap> g.name := "g";;
gap> a1 := GroupHomomorphismByImages(g, g, [g.1, g.2], [g.1^2, g.2]);;
gap> a2 := GroupHomomorphismByImages(g,g,[g.1,g.2],[g.1^4*g.2,g.1^4]);;
gap> g.automorphisms := [ a1, a2 ];;
gap> l := PqDescendants(g,"Metabelian","ClassBound",3,"Exponent",5);;
gap> List( l, x -> Length( PCentralSeries( x, 5 ) ) - 1 );
[ 2, 3, 3 ]
gap> List( l, x -> Length( DerivedSeries( x ) ) );
[ 3, 3, 3 ]
gap> List( l, x -> Maximum( List( Elements(x), y -> Order(x,y) ) ) );
[ 5, 5, 5 ]

This function requires the package ”anupq” (see 48.1).

49.3 PqList

PqList( file )
PqList( file, sub )
PqList( file, n )

The function PqList reads a file file and returns the list L of ag groups defined in this file.

If list sub is supplied as a parameter, the function returns Sublist( L, sub ). If an integer n is supplied, PqList returns L[n].

This function and SavePqList (see 49.4) can be used to save and restore a list of descendants (see 49.2).

This function requires the package ”anupq” (see 48.1).

49.4 SavePqList

SavePqList( name, list )

The function SavePqList writes a list of descendants list to a file name.

This function and PqList (see 49.3) can be used to save and restore results of PqDescendants (see 49.2).

This function requires the package ”anupq” (see 48.1).

49.5 StandardPresentation

StandardPresentation( F , p, ...  )
StandardPresentation( F , G, ...  )

Let F be a finitely presented group. Then StandardPresentation returns the standard presentation for the desired p-quotient of F as an ag group.

Let H be the p-quotient whose standard presentation is computed. A generating set for a supplement to the inner automorphism group of H is also returned, stored as the component H .automorphisms. Each generator is described by its action on each of the generators of the standard presentation of H .

A finitely-presented group F must be supplied as input. Usually, the user will also supply a prime p and the program will compute the standard presentation for the desired p-quotient of F .

Alternatively, a user may supply an ag group G which is the class 1 p-quotient of F . If this is so, a list of automorphisms of G must be bound to the record component G.automorphisms such that G.automorphisms together with the inner automorphisms of G generate the automorphism group of G. The presentation for G can be constructed by an initial call to Pq (see 49.1).

Of course, G need not be the class 1 p-quotient of F . However, G.automorphisms must contain a description of the automorphism group of G and this is most readily available when G is an elementary abelian group. Where the necessary information is available for a p-quotient of higher class, one can apply the standard presentation algorithm from that class onwards.

The following parameters or parameter pairs are supported.

”ClassBound”, n The standard presentation is computed for the largest p-quotient of F having lower exponent-p class at most n.

”Exponent”, n The p-quotient computed has exponent n. By default, no exponent law is enforced.

”Metabelian”

The p-quotient constructed is metabelian.

”AgAutomorphisms”

The automorphisms stored in G.automorphisms are a PAG-generating sequence for the automorphism group of G supplied in reverse order.

”Verbose”

The runtime-information generated by the ANU pq is displayed. By default, pq works silently.

”OutputLevel”, n The runtime-information generated by the ANU pq is displayed at output level n, which must be a integer from 0 to 3. This parameter implies ”Verbose”.

”SetupFile”, name Do not run the ANU pq, just construct the input file and store it in the file name. In this case true is returned.

”TmpDir”, dir StandardPresentation stores intermediate results in temporary files; the location of these files is determined by the value selected by TmpName. If your default temporary directory does not have enough free disk space, you can supply an alternative path dir. In this case StandardPresentation stores its intermediate results in a temporary subdirectory of dir. Alternatively, you can globally set the variable ANUPQtmpDir, for instance in your ”.gaprc” file, to point to a suitable location.

Alternatively, you can pass StandardPresentation a record as a parameter, which contains as entries some (or all) of the above mentioned. Those parameters which do not occur in the record are set to their default values.

We illustrate the method with the following examples.

gap> f2 := FreeGroup( "a", "b" );;
gap> g := f2 / [f2.1^25, Comm(Comm(f2.2,f2.1), f2.1), f2.2^5];
Group( a, b )
gap> StandardPresentation( g, 5, "ClassBound", 10 );
Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7, G.8, G.9, G.10, G.11,
G.12, G.13, G.14, G.15, G.16, G.17, G.18, G.19, G.20, G.21, G.22,
G.23, G.24, G.25, G.26 )
gap> f2 := FreeGroup( "a", "b" );;
gap> g := f2 / [ f2.1^625,
>  Comm(Comm(Comm(Comm(f2.2,f2.1),f2.1),f2.1),f2.1)/Comm(f2.2,f2.1)^5,
>  Comm(Comm(f2.2,f2.1),f2.2), f2.2^625 ];;
gap> StandardPresentation( g, 5, "ClassBound", 15, "Metabelian" );
Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7, G.8, G.9, G.10, G.11,
G.12, G.13, G.14, G.15, G.16, G.17, G.18, G.19, G.20 )
gap> f4 := FreeGroup( "a", "b", "c", "d" );;
gap> g4 := f4 / [ f4.2^4, f4.2^2 / Comm(Comm (f4.2, f4.1), f4.1),
>                f4.4^16, f4.1^16 / (f4.3 * f4.4),
>                f4.2^8 / (f4.4 * f4.3^4) ];
Group( a, b, c, d )
gap> g := Pq( g4, "Prime", 2, "ClassBound", 1 );
Group( G.1, G.2 )
gap> g.automorphisms := [];;
gap> GroupHomomorphismByImages(g,g,[g.1,g.2],[g.2,g.1*g.2]);;
gap> Add( g.automorphisms, last );
gap> GroupHomomorphismByImages(g,g,[g.1,g.2],[g.2,g.1]);;
gap> Add( g.automorphisms, last );
gap> StandardPresentation(g4,g,"ClassBound",14,"AgAutomorphisms");
Group( G.1, G.2, G.3, G.4, G.5, G.6, G.7, G.8, G.9, G.10, G.11,
G.12, G.13, G.14, G.15, G.16, G.17, G.18, G.19, G.20, G.21, G.22,
G.23, G.24, G.25, G.26, G.27, G.28, G.29, G.30, G.31, G.32, G.33,
G.34, G.35, G.36, G.37, G.38, G.39, G.40, G.41, G.42, G.43, G.44,
G.45, G.46, G.47, G.48, G.49, G.50, G.51, G.52, G.53 )

This function requires the package ”anupq” (see 48.1).

49.6 IsIsomorphicPGroup

IsIsomorphicPGroup( G, H )

The functions returns true if G is isomorphic to H . Both groups must be ag groups of prime power order.

gap> p1 := Group( (1,2,3,4), (1,3) );
Group( (1,2,3,4), (1,3) )
gap> p2 := SolvableGroup( 8, 5 );
Q8
gap> p3 := SolvableGroup( 8, 4 );
D8
gap> IsIsomorphicPGroup( AgGroup(p1), p2 );
false
gap> IsIsomorphicPGroup( AgGroup(p1), p3 );
true

The function computes and compares the standard presentations for G and H (see 49.5).

Weyl Groups and Hecke Algebras

In this chapter we describe functions for dealing with finite Weyl groups, associated Hecke algebras over a ring of Laurent polynomials, and their representations.

A finite Weyl group is a group with a presentation of the form

⟨s_{1}, ..., s_{n} | (s_{i}s_{j})^{m(i,j)} = 1 for all i, j⟩

where the m(i, j) are integers contained in {2, 3, 4, 6} and where m(i, i) = 1 for all i. These groups play a significant role in various areas of mathematics such as reflection groups, Lie algebras and linear algebraic groups. Each Weyl group is a direct product of indecomposable Weyl groups which are in turn classified by means of the well–known Dynkin diagrams.

The Hecke algebra H corresponding to a finite Weyl group as above is defined as follows. Let A be a commutative ring and q_{1}, ..., q_{n} ∈ A such that q_{i} = q_{j} whenever s_{i} and s_{j} are conjugate in W. Then H is the associative A–algebra with 1 with generators a_{1}, ..., a_{n} and defining relations of the form

a_{i}^{2} = q_{i}1 + (q_{i} − 1)a_{i} for all i

and

a_{i}a_{j}a_{i}... = a_{j}a_{i}a_{j}... m(i, j) factors on each side.

Usually, A is chosen to be the ring of Laurent polynomials in an indeterminate v, and all q_{i} are chosen to be v^{2}. For each w ∈ W we define an element T_{w} ∈ H as follows. If w = s_{i_{1}} ⋯ s_{i_{k}} is a reduced expression, for some for some i_{j} ∈ {1, …, n}, then we let T_{w} := T_{a_{i_{1}}} ⋯ T_{a_{i_{k}}}. It can be shown that T_{w} is indeed independent of the choice of the reduced expression. The elements T_{w}, w ∈ W, form an A–basis of the algebra H.

A suitable reference for the general theory is, for example, the book by J.E.Humpreys on ‘Reflection Groups and Coxeter Groups’ (Cambridge Studies in advanced Mathematics 29). A good reference for applications of Weyl groups and Hecke algebras are Chapters 1,2 in the book by Shi Jian-Yi on ‘The Kazhdan-Lusztig Cells in Certain Affine Weyl Groups’ (Springer Lecture Notes in Mathematics 1179). At first, let us describe in an informal way some examples of how these programs can be used. The starting point for our programs is to specify a Cartan matrix. This is a square matrix corresponding to a (finite) root system R in some Euclidean space V with (i, j)-entry given by 2(r_{i}, r_{j})/(r_{i}, r_{i}) where r_{i} and r_{j} are fundamental roots in R. The Cartan matrices for the indecomposable root system of type A_{n}, B_{n}, C_{n}, D_{n}, G_{2}, F_{4}, E_{6}, E_{7}, E_{8} are accessible through the function CartanMat.

    gap> RequirePackage( "weyl" );
    gap> C := CartanMat( "D", 4 );;
    gap> PrintArray( last );
    [ [   2,   0,  -1,   0 ],
      [   0,   2,  -1,   0 ],
      [  -1,  -1,   2,  -1 ],
      [   0,   0,  -1,   2 ] ]

The Cartan matrices corresponding to the non-cristallographic groups are now also available under the names H_{3}, H_{4} and I_{2}(m). Given two Cartan matrices, their matrix direct sum (corresponding to the orthogonal direct sum of the root systems) is produced by using the function DirectSumCartanMat. This Cartan matrix conversely determines the root system and hence also the corresponding Weyl group W, i.e., the finite subgroup of the full orthogonal group of V generated by the reflections along the vectors in R. We choose a basis of V consisting of the fundamental root vectors, and express every root vector as a linear combination of and every reflection matrix with respect to this basis. This is performed by

the functions RootSystem and SimpleReflectionMatrices.
    gap> S:=SimpleReflectionMatrices(C);
    [ [ [ -1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 1, 0, 1, 0 ], [ 0, 0, 0, 1 ] ],
      [ [ 1, 0, 0, 0 ], [ 0, -1, 0, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, 0, 1 ] ],
      [ [ 1, 0, 1, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, -1, 0 ], [ 0, 0, 1, 1 ] ],
      [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 1 ], [ 0, 0, 0, -1 ] ] ]
    gap> R := Rootsystem(C);
    [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ],
      [ 1, 0, 1, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, 1, 1 ], [ 1, 1, 1, 0 ],
      [ 1, 0, 1, 1 ], [ 0, 1, 1, 1 ], [ 1, 1, 1, 1 ], [ 1, 1, 2, 1 ] ]

We notice that the root vectors in R are ordered by increasing height (and that only the positive root vectors are returned; whenever the negative roots are needed, they can be computed by simply typing -R). If we label the (positive) roots by their position in the above list, and the negative roots by the next consecutive numbers, then we can also represent each fundamental reflection by the permutation which it induces on the root vectors.

    gap> P := PermRepresentationRoots( S, R );
    [ ( 1,13)( 3, 5)( 6, 8)( 7, 9)(10,11)(15,17)(18,20)(19,21)(22,23),
      ( 2,14)( 3, 6)( 5, 8)( 7,10)( 9,11)(15,18)(17,20)(19,22)(21,23),
      ( 1, 5)( 2, 6)( 3,15)( 4, 7)(11,12)(13,17)(14,18)(16,19)(23,24),
      ( 3, 7)( 4,16)( 5, 9)( 6,10)( 8,11)(15,19)(17,21)(18,22)(20,23) ]

This is maybe the most fundamental command, and in fact, every function which has to perform computations with group elements in W, does so by working with this (faithful) permutation representation of W. The procedure up to now can be abbreviated by one command, namely

    gap> W := Weyl(C);;

which produces a record with entries:

isWeylGroup

is set to true.

cartan

contains the Cartan matrix C.

dim

contains the length of C.

degree

number of positive and negative roots.

N

number of positive roots.

roots

contains the root system R.

matgens

contains the matrix generators S.

permgens

contains the permutation generators P.

This record is all that the following programs and commands need. For a given element w ∈ W, the length l(w) is defined to be smallest integer k such that w can be written as a product of k fundamental reflections. Such an expression of shortest possible length will be called a reduced word for w. A user might be interested to think of the elements of W as such words in the generating fundamental reflections. For these programs, we represent a word simply as a list of integers corresponding to the fundamental roots, e.g., [] is the identity element, and [1], [2], etc. represent the reflection along the first, the second etc. fundamental root vector. For other purposes, it might be better to see the permutation of an element w on the root vectors. The functions WeylWordPerm and PermWeylWord will do the conversion of one form into the other.

    gap> PermWeylWord( W, [ 1, 3, 2, 1, 3 ] );
    ( 1,14,13, 2)( 3,17, 8,18)( 4,12)( 5,20, 6,15)( 7,10,11, 9)(16,24)
    (19,22,23,21)
    gap> WeylWordPerm( W, last );
    [ 1, 3, 1, 2, 3 ]

We notice that the word we started with and the one that we ended up with, are not the same. But of course, they represent the same element of W. The reason for this difference is that the function WeylWordPerm always computes a reduced word which is the lexicographically smallest among all possible expressions of an element of W as a word in the fundamental reflections! The function ReducedWeylWord does the same but with an arbitrary word as input (and not with a permutation). In particular, the element used in the above example has length 5. Sometimes, it is not necessary to compute a reduced word for an element w and one is only interested in the length l(w). The crucial point of working with the permutation representation of W on R is that l(w) can be computed very fast and effectively from the permutation, since it also given by the number of positive roots mapped to negative roots by w, i.e., by the number of i ∈ {1, ..., N} such that i^{w} > N.

gap> LongestWeylWord( W );  # the (unique) longest element in W
[ 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 4 ]
gap> PermWeylWord( W, last );
( 1,13)( 2,14)( 3,15)( 4,16)( 5,17)( 6,18)( 7,19)( 8,20)( 9,21)(10,22)
(11,23)(12,24)
gap> WeylLengthPerm( W, last );
12

These are the most basic functions available. We now give an example of how the other commands do work.

gap> WeylReflections( W );  # all reflections in W
[ [ 1 ], [ 2 ], [ 3 ], [ 4 ], [ 1, 3, 1 ], [ 2, 3, 2 ], [ 3, 4, 3 ],
  [ 1, 2, 3, 1, 2 ], [ 1, 3, 4, 3, 1 ], [ 2, 3, 4, 3, 2 ],
  [ 1, 2, 3, 4, 3, 1, 2 ], [ 3, 1, 2, 3, 4, 3, 1, 2, 3 ] ]
gap> WeylElements( W );;
#I  Order = 192
gap> List( last, Length );
[ 1, 4, 9, 16, 23, 28, 30, 28, 23, 16, 9, 4, 1 ]

The last line tells us that there is 1 element of length 0, there are 4 elements of length 4, etc.

gap> WeylConjugacyClasses( W );
#I  Number of cosets = 8
#I  Number of cosets = 6
#I  Number of cosets = 2
#I  30 elements to consider
#I  Still 18 elements to consider
#I  13 conjugacy classes found
[ [  ], [ 1 ], [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 4 ], [ 1, 2, 3 ],
  [ 1, 2, 4 ], [ 1, 3, 4 ], [ 2, 3, 4 ], [ 1, 2, 3, 4 ],
  [ 1, 3, 1, 2, 3, 4 ], [ 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 4 ] ]

This are the representatives of the conjugacy classes of minimal length.

gap> LeftCells( W );;
#I  Order = 192
#I  R(w) = [  ] :  Size = 1,  1 new cell
#I  R(w) = [ 1 ] :  Size = 7,  2 new cells
#I  R(w) = [ 1, 2 ] :  Size = 17,  2 new cells
#I  R(w) = [ 1, 2, 3 ] :  Size = 7,  2 new cells
#I  R(w) = [ 1, 2, 3, 4 ] :  Size = 1,  1 new cell
#I  R(w) = [ 1, 2, 4 ] :  Size = 23,  5 new cells
#I  R(w) = [ 1, 3 ] :  Size = 17,  2 new cells
#I  R(w) = [ 1, 3, 4 ] :  Size = 7,  2 new cells
#I  R(w) = [ 1, 4 ] :  Size = 17,  2 new cells
#I  R(w) = [ 2 ] :  Size = 7,  2 new cells

#I  R(w) = [ 2, 3 ] :  Size = 17,  2 new cells
#I  R(w) = [ 2, 3, 4 ] :  Size = 7,  2 new cells
#I  R(w) = [ 2, 4 ] :  Size = 17,  2 new cells
#I  R(w) = [ 3 ] :  Size = 23,  5 new cells
#I  R(w) = [ 3, 4 ] :  Size = 17,  2 new cells
#I  R(w) = [ 4 ] :  Size = 7,  2 new cells
gap> last[3];
[ [ [ 2, 4, 3, 1 ], [ 3, 2, 4, 3, 1 ], [ 1, 3, 2, 4, 3, 1 ] ],
  [ [ 0 ], [ 1, 0 ], [ 0, 1, 0 ] ] ]

This is a left cell consisting of three elements with the corresponding matrix of μ_{x,y}, the coefficient of v^{(l(y)−l(x)−1)} in the Kazhdan-Lusztig polynomial P_{x,y}(v^{2}).

gap> LeftCellRepresentation( W, last, E(4) );;
gap> PrintArray( last );
[ [        [ -1, 0, 0 ],        [ 0, -1, 0 ],     [ 0, E(4), -1 ] ],
  [     [ -1, E(4), 0 ],        [ 0, -1, 0 ],        [ 0, 0, -1 ] ],
  [        [ -1, 0, 0 ],  [ E(4), -1, E(4) ],        [ 0, 0, -1 ] ],
  [     [ -1, E(4), 0 ],        [ 0, -1, 0 ],        [ 0, 0, -1 ] ] ]

The corresponding left cell representation with the indeterminate v replaced by E(4).

Of course, the user should observe limitations on storage for working with these programs, e.g., the command WeylElements applied to a Weyl group of type E_{8} will try to compute all elements as words in the fundamental reflections. Every computer will run out of memory!

Some good examples to see how long the programs will take for computations in big Weyl groups are the following.

LeftCells( Weyl( CartanMat( "F", 4 ) ) );

(which takes roughly 5 hours cpu time on a HP computer)

or

WeylConjugacyClasses( Weyl( CartanMat( "E", 8 ) ) ) ;

(which takes a few days...)

Finally, we give an example of some computations with Hecke algebras. We start with the Weyl group of type A_{4}.

gap> W := Weyl( CartanMat("A",4) );;

We wish to work over the ring of Laurent polynomials in an indeterminate v, and we want to have u = v^{2} as a parameter for the generic Hecke algebra H of W.

gap> v := Indeterminate( Rationals );;  v.name := "v";;
gap> Hecke( W, v^2 );

From v we can build arbitrary Laurent polynomials by using the operations +, -, *, and ^.

gap> 2*v+3*v^-2-7*(v+v^-1)*(3*v^2-1);
-21*v^3 - 12*v + 7*v^(-1) + 3*v^(-2)

The command Hecke produces an additional component T in the record of W which gives, for an element w ∈ W, the corresponding basis element T_{w} in H. Computations like the following are possible.

gap> W.T([1]);

v^0*T([ 1 ])
gap> last^2;  # the quadratic relation for a standard base element
v^2*T([  ])+(v^2 - 1)*T([ 1 ])
gap> W.T([1,2,3,4]);
v^0*T([ 1, 2, 3, 4 ])
gap> last^-1;
(1 - 4*v^(-2) + 6*v^(-4) - 4*v^(-6) + v^(-8))*T([  ])
+(-v^(-2) + 3*v^(-4) - 3*v^(-6) + v^(-8))*T([ 1 ])
+(-v^(-2) + 3*v^(-4) - 3*v^(-6) + v^(-8))*T([ 2 ])
+(-v^(-2) + 3*v^(-4) - 3*v^(-6) + v^(-8))*T([ 3 ])
+(-v^(-2) + 3*v^(-4) - 3*v^(-6) + v^(-8))*T([ 4 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 1, 3 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 1, 4 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 2, 1 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 2, 4 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 3, 2 ])
+(v^(-4) - 2*v^(-6) + v^(-8))*T([ 4, 3 ])
+(-v^(-6) + v^(-8))*T([ 1, 4, 3 ])
+(-v^(-6) + v^(-8))*T([ 2, 1, 4 ])
+(-v^(-6) + v^(-8))*T([ 3, 2, 1 ])
+(-v^(-6) + v^(-8))*T([ 4, 3, 2 ])
+v^(-8)*T([ 4, 3, 2, 1 ])
gap> last2*last;
v^0*T([  ])

Now we turn to some aspects about the representation theory of Hecke algebras. At first we compute the left cells of W.

gap> ce := LeftCells(W);;
#I  Order = 120
#I  R(w) = [  ] :  Size = 1,  1 new cell
#I  R(w) = [ 1 ] :  Size = 4,  1 new cell
#I  R(w) = [ 1, 2 ] :  Size = 6,  1 new cell
#I  R(w) = [ 1, 2, 3 ] :  Size = 4,  1 new cell
#I  R(w) = [ 1, 2, 3, 4 ] :  Size = 1,  1 new cell
#I  R(w) = [ 1, 2, 4 ] :  Size = 9,  2 new cells
#I  R(w) = [ 1, 3 ] :  Size = 16,  3 new cells
#I  R(w) = [ 1, 3, 4 ] :  Size = 9,  2 new cells
#I  R(w) = [ 1, 4 ] :  Size = 11,  2 new cells
#I  R(w) = [ 2 ] :  Size = 9,  2 new cells
#I  R(w) = [ 2, 3 ] :  Size = 11,  2 new cells
#I  R(w) = [ 2, 3, 4 ] :  Size = 4,  1 new cell
#I  R(w) = [ 2, 4 ] :  Size = 16,  3 new cells
#I  R(w) = [ 3 ] :  Size = 9,  2 new cells
#I  R(w) = [ 3, 4 ] :  Size = 6,  1 new cell
#I  R(w) = [ 4 ] :  Size = 4,  1 new cell

We use LeftCellRepresentation to get the corresponding left cell representations.

gap> l := List( last, i -> LeftCellRepresentation(W,i,v) );;
gap> c := WeylConjugacyClasses(W);

[ [  ], [ 1 ], [ 1, 3 ], [ 1, 2 ], [ 1, 2, 4 ], [ 1, 2, 3 ],
  [ 1, 2, 3, 4 ] ]
gap> ch := List( l, i -> CharHeckeRepresentation(i,c) );;

The last command computes the character values on representatives of minimal length in the conjugacy classes.

gap> Set(last);
[ [ v^0, -v^0, v^0, v^0, -v^0, -v^0, v^0 ],
  [ v^0, v^2, v^4, v^4, v^6, v^6, v^8 ],
  [ 4*v^0, v^2 - 3, -2*v^2 + 2, -v^2 + 2, 2*v^2 - 1, v^2 - 1, -v^2 ],
  [ 4*v^0, 3*v^2 - 1, 2*v^4 - 2*v^2, 2*v^4 - v^2, v^6 - 2*v^4,
      v^6 - v^4, -v^6 ],
  [ 5*v^0, 2*v^2 - 3, v^4 - 2*v^2 + 2, -2*v^2 + 1, -v^4 + v^2 - 1,
      v^2, 0*v^0 ],
  [ 5*v^0, 3*v^2 - 2, 2*v^4 - 2*v^2 + 1, v^4 - 2*v^2, v^6 - v^4 + v^2,
      -v^4, 0*v^0 ],
  [ 6*v^0, 3*v^2 - 3, v^4 - 4*v^2 + 1, v^4 - 2*v^2 + 1,
      -2*v^4 + 2*v^2, -v^4 + v^2, v^4 ] ]

HeckeCharTable constructs a record of a character table, which can be displayed using

DisplayCharTable.
gap> t := HeckeCharTable( W, c, last );;
gap> DisplayCharTable(t);
H()
   2  3      2           3          1           1        2    .
   3  1      1           .          1           1        .    .
   5  1      .           .          .           .        .    1
     1a     2a          2b         3a          6a       4a   5a
X.1   1     -1           1          1          -1       -1    1
X.2   1    v^2         v^4        v^4         v^6      v^6  v^8
X.3   4  v^2-3     -2v^2+2     -v^2+2      2v^2-1    v^2-1 -v^2
X.4   4 3v^2-1   2v^4-2v^2   2v^4-v^2    v^6-2v^4  v^6-v^4 -v^6
X.5   5 2v^2-3  v^4-2v^2+2    -2v^2+1  -v^4+v^2-1      v^2    0
X.6   5 3v^2-2 2v^4-2v^2+1   v^4-2v^2 v^6-v^4+v^2     -v^4    0
X.7   6 3v^2-3  v^4-4v^2+1 v^4-2v^2+1  -2v^4+2v^2 -v^4+v^2  v^4

To compute character values on other elements, we proceed as follows.

gap> w0 := LongestWeylWord(W);
[ 1, 2, 1, 3, 2, 1, 4, 3, 2, 1 ]
gap> wcl := WeylClassPolynomials( W, c, last );
#I  1 2 3 4 4 6 5 4 2
[ 0, 0, v^8, v^10 - 2*v^8 + v^6, v^10 - v^8 + v^6 - v^4,
  v^12 - v^10 + v^4 - v^2, v^12 - v^10 - v^2 + 1 ]

In order to get the values of the irreducible characters on T_{w_{0}}, we have to postmultiply the character table of H by wcl regarded as a column vector

gap> TransposedMat([wcl]);;
gap> MultMat( t.irreducibles, last );
[ [ v^0 ], [ v^20 ], [ 0*v^0 ], [ 0*v^0 ], [ v^8 ], [ v^12 ],
  [ -2*v^10 ] ]

For any comments, suggestions of improvements I would be very grateful,

Aachen, November 1992, Meinolf Geck

50.1 CartanMat

CartanMat( type, n )

returns the Cartan matrix of Dynkin type type and rank n. Admissable types are the strings

"A", "B", "C", "D", "E", "F", "G", "H", "I2".
gap> CartanMat( "F", 4 );;
gap> PrintArray( last );
[ [   2,  -1,   0,   0 ],
[  -1,   2,  -1,   0 ],
[   0,  -2,   2,  -1 ],
[   0,   0,  -1,   2 ] ]
gap> CartanMat( "I2", 5 );
[ [ 2, E(5)^2+E(5)^3 ], [ E(5)^2+E(5)^3, 2 ] ]

(this gives the dihedral group of order 10, so the integer 5 is not the rank in this case.)

This function requires the package ”weyl”(see 48.1).

50.2 DirectSumCartanMat

DirectSumCartanMat( mat1 , mat2 )

returns the block diagonal direct sum of the Cartan matrices mat1 and mat2 .

gap> C1 := CartanMat( "A", 4 );;
gap> PrintArray( last );
[ [   2,  -1,   0,   0 ],
[  -1,   2,  -1,   0 ],
[   0,  -1,   2,  -1 ],
[   0,   0,  -1,   2 ] ]
gap> C2 := CartanMat( "D", 4 );;
gap> PrintArray( last );
[ [   2,   0,  -1,   0 ],
[   0,   2,  -1,   0 ],
[  -1,  -1,   2,  -1 ],
[   0,   0,  -1,   2 ] ]
gap> DirectSumCartanMat( C1, C2 );;
gap> PrintArray( last );
[ [   2,  -1,   0,   0,   0,   0,   0,   0 ],
[  -1,   2,  -1,   0,   0,   0,   0,   0 ],
[   0,  -1,   2,  -1,   0,   0,   0,   0 ],
[   0,   0,  -1,   2,   0,   0,   0,   0 ],
[   0,   0,   0,   0,   2,   0,  -1,   0 ],
[   0,   0,   0,   0,   0,   2,  -1,   0 ],
[   0,   0,   0,   0,  -1,  -1,   2,  -1 ],
[   0,   0,   0,   0,   0,   0,  -1,   2 ] ]

This function requires the package ”weyl”(see 48.1).

50.3 SimpleReflectionMatrices

SimpleReflectionMatrices( mat )

returns the matrices (in row convention) of the simple reflections of the Weyl group determined by the Cartan matrix mat with respect to the basis consisting of the fundamental root vectors corresponding to the rows of mat. (This function is only used in the function

Weyl.)
    gap> C := CartanMat( "A", 4 );;
    gap> PrintArray( last );
    [ [   2,  -1,   0,   0 ],
      [  -1,   2,  -1,   0 ],
      [   0,  -1,   2,  -1 ],
      [   0,   0,  -1,   2 ] ]
    gap> SimpleReflectionMatrices( C );
    [ [ [ -1, 0, 0, 0 ], [ 1, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ] ],
      [ [ 1, 1, 0, 0 ], [ 0, -1, 0, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, 0, 1 ] ],
      [ [ 1, 0, 0, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, -1, 0 ], [ 0, 0, 1, 1 ] ],
      [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 1 ], [ 0, 0, 0, -1 ] ] ]

This function requires the package ”weyl”(see 48.1).

50.4 Rootsystem

Rootsystem( mat )

returns the positive roots of the root system determined by the Cartan matrix mat, given by their coefficients when expressed as linear combinations of fundamental roots. Thus the fundamental roots are the standard basis vectors e_{i}, corresponding to the rows i = 1, ..., n of mat. The roots are ordered by increasing height. Note that we use the convention that mat[i,j] equals 2(e_{i}, e_{j})/(e_{i}, e_{i}). (This function is only used in the function Weyl.)

    gap> C := CartanMat( "A", 4 );;
    gap> PrintArray( last );
    [ [   2,  -1,   0,   0 ],
      [  -1,   2,  -1,   0 ],
      [   0,  -1,   2,  -1 ],
      [   0,   0,  -1,   2 ] ]
    gap> Rootsystem( C );
    [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ],
      [ 1, 1, 0, 0 ], [ 0, 1, 1, 0 ], [ 0, 0, 1, 1 ], [ 1, 1, 1, 0 ],
      [ 0, 1, 1, 1 ], [ 1, 1, 1, 1 ] ]

This function requires the package ”weyl”(see 48.1).

50.5 PermRepresentationRoots

PermRepresentationRoots( mats , roots )

returns the permutations induced by the fundamental reflections mats of a given Weyl group on the corresponding root system roots. If there are 2N roots in total, then 1, ..., N correspond to the positive roots, and N + 1, ..., 2N to the negative roots. (This function is only used in the function Weyl.)

    gap> C := CartanMat( "A", 4 );;
    gap> S := SimpleReflectionMatrices( C );;
    gap> R := Rootsystem( C );;
    gap> PermRepresentationRoots( S, R );
    [ ( 1,11)( 2, 5)( 6, 8)( 9,10)(12,15)(16,18)(19,20),
      ( 1, 5)( 2,12)( 3, 6)( 7, 9)(11,15)(13,16)(17,19),
      ( 2, 6)( 3,13)( 4, 7)( 5, 8)(12,16)(14,17)(15,18),
      ( 3, 7)( 4,14)( 6, 9)( 8,10)(13,17)(16,19)(18,20) ]

This function requires the package ”weyl”(see 48.1).

50.6 Weyl

Weyl( mat )

returns a record containing the basic information about the Weyl group determined by the Cartan matrix mat. This record has the following entries.

cartan

the Cartan matrix mat

dim

the size of mat

degree

the number of positive roots

N

the total number of roots

roots

the root vectors

matgens

the matrices of the simple reflections

permgens

the permutations on the root vectors

    gap> W := Weyl( CartanMat("G",2) );
    rec(
      cartan := [ [ 2, -1 ], [ -3, 2 ] ],
      dim := 2,
      degree := 12,
      N := 6,
      roots := [ [ 1, 0 ], [ 0, 1 ], [ 1, 1 ], [ 1, 2 ], [ 1, 3 ],
          [ 2, 3 ] ],
      matgens := [ [ [ -1, 0 ], [ 1, 1 ] ], [ [ 1, 3 ], [ 0, -1 ] ] ],
      permgens :=
       [ ( 1, 7)( 2, 3)( 5, 6)( 8, 9)(11,12), ( 1, 5)( 2, 8)( 3, 4)( 7,11)
            ( 9,10) ],
      parameter := [ 1, 1 ] )

This function requires the package ”weyl”(see 48.1).

50.7 PermWeylWord

PermWeylWord( W , w )

returns the permutation on the root vectors determined by an element which is given as a list w of integers representing the standard generators of the Weyl group W .

gap> W := Weyl( CartanMat("G",2) );;
gap> PermWeylWord( W, [1,2,1] );
( 1,12)( 2, 4)( 3, 9)( 6, 7)( 8,10)

This function requires the package ”weyl”(see 48.1).

50.8 WeylWordPerm

WeylWordPerm( W , w )

returns a reduced word in the standard generators of the Weyl group W determined by the permutation w on the root vectors.

gap> W := Weyl( CartanMat("G",2) );;
gap> WeylWordPerm( W, ( 1,12)( 2, 4)( 3, 9)( 6, 7)( 8,10) );
[ 1, 2, 1 ]

This function requires the package ”weyl”(see 48.1).

50.9 WeylLengthPerm

WeylLengthPerm( W , w )

returns the length of the permutation w, which corresponds to an element in the Weyl group W , as a reduced expression in the standard generators.

gap> W := Weyl( CartanMat("F",4) );;
gap> p := PermWeylWord( W, [1,2,3,4,2] );
( 1,44,38,25,20,14)( 2, 5,40,47,48,35)( 3, 7,13,21,19,15)
( 4, 6,12,28,30,36)( 8,34,41,32,10,17)( 9,18)(11,26,29,16,23,24)
(27,31,37,45,43,39)(33,42)
gap> WeylLengthPerm( W, p );
5
gap> WeylWordPerm( W, p );
[ 1, 2, 3, 2, 4 ]

This function requires the package ”weyl”(see 48.1).

50.10 ReducedWeylWord

ReducedWeylWord( W , w )

returns a reduced expression for an element of the Weyl group W , which is given as a list w of integers where each entry i in this list represents the i-th standard generator of W .

gap> W := Weyl( CartanMat("E",6) );;
gap> ReducedWeylWord( W, [1,1,1,1,1,2,2,2,3] );
[ 1, 2, 3 ]

This function requires the package ”weyl”(see 48.1).

50.11 LongestWeylWord

LongestWeylWord( W )

returns a reduced expression in the standard generators of the unique element of maximal length of the Weyl group W .

    gap> W := Weyl( CartanMat("E",6) );;
    gap> w0 := LongestWeylWord(W);
    [ 1, 2, 3, 1, 4, 2, 3, 1, 4, 3, 5, 4, 2, 3, 1, 4, 3, 5, 4, 2, 6, 5,
      4, 2, 3, 1, 4, 3, 5, 4, 2, 6, 5, 4, 3, 1 ]

This function requires the package ”weyl”(see 48.1).

50.12 WeylReflections

WeylReflections( W )

returns a list of reduced expressions in the standard generators for the set of reflections in the Weyl group W . The i-th entry in this list is the reflection along the i-th root in

W .roots.
    gap> W := Weyl( CartanMat("B",2) );;
    gap> W.roots;
    [ [ 1, 0 ], [ 0, 1 ], [ 1, 1 ], [ 2, 1 ] ]
    gap> WeylReflections(W);
    [ [ 1 ], [ 2 ], [ 2, 1, 2 ], [ 1, 2, 1 ] ]

This function requires the package ”weyl”(see 48.1).

50.13 WeylRightCosetRepresentatives

WeylRightCosetRepresentatives( W , I , J )

returns a list of reduced words in the Weyl group W which are distinguished representatives for the right cosets of W (J) in W (I) where, for each sublist K of [1..W.dim], W (K) is the parabolic subgroup generated by the simple reflections corresponding to elements in K.

    gap> W := Weyl( CartanMat("F",4) );;
    gap> WeylRightCosetRepresentatives( W, [1,2,3], [1,2] );
    #I  Number of cosets = 8
    [ [  ], [ 3 ], [ 3, 2 ], [ 3, 2, 1 ], [ 3, 2, 3 ], [ 3, 2, 1, 3 ],
      [ 3, 2, 1, 3, 2 ], [ 3, 2, 1, 3, 2, 3 ] ]

This function requires the package ”weyl”(see 48.1).

50.14 WeylCosetPermRepresentation

WeylCosetPermRepresentation( W , J )

returns the list of permutations induced by the standard generators of the Weyl group W on the cosets of the parabolic subgroup generated by the elements in the set J . The cosets are in the order determined by the result of the function WeylRightCosetRepresentatives(

W , [1..W .dim], J )

gap> W := Weyl( CartanMat("F",4) );;
gap> WeylCosetPermRepresentation( W, [1,2,3] );
#I  Number of cosets = 24
[ ( 4, 5)( 6, 7)( 8,10)(16,18)(17,19)(20,21),
  ( 3, 4)( 7, 9)(10,12)(14,16)(15,17)(21,22),
  ( 2, 3)( 4, 6)( 5, 7)( 9,11)(12,14)(13,15)(17,20)(19,21)(22,23),
  ( 1, 2)( 6, 8)( 7,10)( 9,12)(11,13)(14,15)(16,17)(18,19)(23,24) ]

This function requires the package ”weyl”(see 48.1).

50.15 WeylElements

WeylElements( W )

returns a list of which the i - th entry is the list of reduced words of length i-1 in the Weyl group W .

gap> W := Weyl( CartanMat("G",2) );;
gap> WeylElements(W);
#I  Order = 12
[ [ [  ] ], [ [ 1 ], [ 2 ] ], [ [ 1, 2 ], [ 2, 1 ] ],
  [ [ 1, 2, 1 ], [ 2, 1, 2 ] ], [ [ 1, 2, 1, 2 ], [ 2, 1, 2, 1 ] ],
  [ [ 1, 2, 1, 2, 1 ], [ 2, 1, 2, 1, 2 ] ], [ [ 1, 2, 1, 2, 1, 2 ] ] ]

This function requires the package ”weyl”(see 48.1).

50.16 WeylConjugacyClasses

WeylConjugacyClasses( W )

returns a list of representatives of the conjugacy classes of the Weyl group W . Each element in this list is given as a word in the standard generators, and it has the property that it is of minimal length in its conjugacy class.

gap> W := Weyl( CartanMat("F",4) );;
gap> WeylConjugacyClasses(W);
#I  Number of cosets = 24
#I  Number of cosets = 8
#I  Number of cosets = 3
#I  108 elements to consider
#I  Still 34 elements to consider
#I  25 conjugacy classes found
[ [  ], [ 1 ], [ 3 ], [ 1, 2 ], [ 1, 3 ], [ 2, 3 ], [ 3, 4 ],
  [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 3, 4 ], [ 2, 3, 4 ], [ 1, 2, 3, 4 ],
  [ 2, 3, 2, 3 ], [ 1, 2, 3, 2, 3 ], [ 2, 3, 2, 3, 4 ],
  [ 1, 2, 3, 2, 3, 4 ], [ 1, 3, 2, 1, 3, 2, 3, 4 ],
  [ 1, 2, 1, 3, 2, 1, 3, 2, 3 ], [ 2, 3, 2, 3, 4, 3, 2, 3, 4 ],
  [ 1, 2, 1, 3, 2, 1, 3, 2, 3, 4 ], [ 1, 2, 3, 2, 3, 4, 3, 2, 3, 4 ],
  [ 1, 2, 1, 3, 2, 1, 3, 4, 3, 2, 3, 4 ],
  [ 1, 2, 1, 3, 2, 1, 3, 2, 3, 4, 3, 2, 3, 4 ],
  [ 1, 2, 3, 2, 1, 3, 2, 3, 4, 3, 2, 1, 3, 2, 3, 4 ],
  [ 1, 2, 1, 3, 2, 1, 3, 2, 3, 4, 3, 2, 1, 3, 2, 3, 4, 3, 2, 1, 3, 2,

          3, 4 ] ]

This function requires the package ”weyl”(see 48.1).

50.17 ParametersCentralizers

ParametersCentralizers( W )

returns a list of pairs which parametrizes the conjugacy classes of centralizers of semisimple elements in a Chevalley group with Weyl group W . Each pair consists of a subset which contains fundamental roots or the highest root in the root system of W , and an element in the normalizer in W of the subgroup generated by the reflections along the roots in the given subset.

    gap> W := Weyl( CartanMat( "A", 4 ) );;
    gap> ParametersCentralizers( W );
    #I  Number of Psi's = 7
    #I  7 conjugacy classes
    #I  3 conjugacy classes
    #I  2 conjugacy classes
    #I  2 conjugacy classes
    #I  1 conjugacy classes
    #I  1 conjugacy classes
    #I  1 conjugacy classes
    #I  Number of Pairs = 17
    [ [ [  ], [  ] ], [ [  ], [ 4 ] ], [ [  ], [ 4, 3 ] ],
      [ [  ], [ 4, 3, 2, 1 ] ], [ [  ], [ 3, 2, 1 ] ],
      [ [  ], [ 2, 1, 4 ] ], [ [  ], [ 2, 1, 3, 2 ] ], [ [ 1 ], [  ] ],
      [ [ 1 ], [ 4 ] ], [ [ 1 ], [ 4, 3 ] ], [ [ 1, 2 ], [  ] ],
      [ [ 1, 2 ], [ 4 ] ], [ [ 1, 3 ], [  ] ],
      [ [ 1, 3 ], [ 2, 1, 3, 2 ] ], [ [ 1, 2, 3 ], [  ] ],
      [ [ 1, 2, 4 ], [  ] ], [ [ 1, 2, 3, 4 ], [  ] ] ]

This function requires the package ”weyl”(see 48.1).

50.18 Bruhat

Bruhat( W , y, w )

returns true, if the element y is less than or equal to the element w of the Weyl group W , and false otherwise. Both y and w must be given as permutations on the root vectors of W .

This function requires the package ”weyl”(see 48.1).

50.19 KazhdanLusztigPolynomial

KazhdanLusztigPolynomial( W , y, w, u )

returns the Kazhdan - Lusztig polynomial in the indeterminate u corresponding to the elments y and w (given as reduced expressions in the standard generators) of the Weyl group W .

gap> W := Weyl( CartanMat("B",4) );;
gap> y := [1,2,3,4,3,2,1];;
gap> x := [1];;
gap> u := Indeterminate(Rationals);;  u.name := "u";;
gap> KazhdanLusztigPolynomial( W, x, y, u );
#I  Order = 384
u^2 + 2*u + 1

This function requires the package ”weyl”(see 48.1).

50.20 KLCoefficient

KLCoefficient( W , y, w, k )

returns the k-th coefficient of the Kazhdan - Lusztig polynomial corresponding to the elments y and w, which must be given as permutations on the root vectors, of the Weyl group W . (This function does not use the polynomial arithmetic and is slightly more efficient than the

function KazhdanLusztigPolynomial.)
gap> W := Weyl( CartanMat("B",4) );;
gap> y := [1,2,3,4,3,2,1];;
gap> py := PermWeylWord( W, y );
( 1,28)( 2,15)( 4,27)( 6,16)( 7,24)( 8,23)(11,20)(12,17)(14,30)(18,31)
(22,32)
gap> x := [1];;
gap> px := PermWeylWord( W, x );
( 1,17)( 2, 8)( 6,11)(10,14)(18,24)(22,27)(26,30)
gap> Bruhat( W, px, py );
true
gap> List( [0..3], i -> KLCoefficient( W, px, py, i ) );
#I  Order = 384
[ 1, 2, 1, 0 ]

So the Kazhdan-Lusztig polynomial corresponding to x and y is 1 + 2u + u^{2}.

This function requires the package ”weyl”(see 48.1).

50.21 WeylMueMat

WeylMueMat( W , list )

returns the list of highest coefficients μ_{y,w} of the Kazhdan- Lusztig polynomials corresponding to pairs of elements of the Weyl group W from a list list. The elements in list must be reduced expressions in the standard generators, ordered by increasing length. E.g., list is

the result of Iterated(WeylElements(W ),Concatenation). (See the next section for an

example.)

This function requires the package ”weyl”(see 48.1).

50.22 DecomposedLeftCells

DecomposedLeftCells( W , c, mue )

given a list c of reduced words in the Weyl group W with corresponding matrix mue of highest coefficients of the corresponding Kazhdan-Lusztig polynomials, it returns a list of pairs. The first component of each pair consists of the reduced words in the list c which lie in one left cell C, the second component consists of the corresponding matrix of highest coefficients μ_{y,w}, where y, w are in C.

gap> W := Weyl( CartanMat("G",2) );;
gap> WeylElements(W);;
#I  Order = 12
gap> c := Iterated( last, Concatenation );
[ [  ], [ 1 ], [ 2 ], [ 1, 2 ], [ 2, 1 ], [ 1, 2, 1 ], [ 2, 1, 2 ],
  [ 1, 2, 1, 2 ], [ 2, 1, 2, 1 ], [ 1, 2, 1, 2, 1 ],
  [ 2, 1, 2, 1, 2 ], [ 1, 2, 1, 2, 1, 2 ] ]
gap> mue := WeylMueMat( W, c );
#I  Order = 12
[ [ 0 ], [ 1, 0 ], [ 1, 0, 0 ], [ 0, 1, 1, 0 ], [ 0, 1, 1, 0, 0 ],
  [ 0, 0, 0, 1, 1, 0 ], [ 0, 0, 0, 1, 1, 0, 0 ],
  [ 0, 0, 0, 0, 0, 1, 1, 0 ], [ 0, 0, 0, 0, 0, 1, 1, 0, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 ],
  [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 ] ]
gap> DecomposedLeftCells( W, c, mue );
[ [ [ [  ] ], [ [ 0 ] ] ],
  [ [ [ 1 ], [ 2, 1 ], [ 1, 2, 1 ], [ 2, 1, 2, 1 ], [ 1, 2, 1, 2, 1 ]
         ],
      [ [ 0 ], [ 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1, 0
             ] ] ],
  [ [ [ 2 ], [ 1, 2 ], [ 2, 1, 2 ], [ 1, 2, 1, 2 ], [ 2, 1, 2, 1, 2 ]
         ],
      [ [ 0 ], [ 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1, 0
             ] ] ], [ [ [ 1, 2, 1, 2, 1, 2 ] ], [ [ 0 ] ] ] ]

This function requires the package ”weyl”(see 48.1).

50.23 LeftCells

LeftCells( W )

returns a list of pairs. The first component of each pair consists of the reduced words in the Weyl group W which lie in one left cell C, the second component consists of the corresponding matrix of highest coefficients μ_{y,w}, where y, w are in C.

gap> W := Weyl( CartanMat("G",2) );;
gap> LeftCells(W);
#I  Order = 12
#I  R(w) = [  ] :  Size = 1,  1 new cell
#I  R(w) = [ 1 ] :  Size = 5,  1 new cell
#I  R(w) = [ 1, 2 ] :  Size = 1,  1 new cell
#I  R(w) = [ 2 ] :  Size = 5,  1 new cell
[ [ [ [  ] ], [ [ 0 ] ] ],
  [ [ [ 1 ], [ 2, 1 ], [ 1, 2, 1 ], [ 2, 1, 2, 1 ], [ 1, 2, 1, 2, 1 ]

             ],
          [ [ 0 ], [ 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1, 0
                 ] ] ], [ [ [ 1, 2, 1, 2, 1, 2 ] ], [ [ 0 ] ] ],
      [ [ [ 2 ], [ 1, 2 ], [ 2, 1, 2 ], [ 1, 2, 1, 2 ], [ 2, 1, 2, 1, 2 ]
             ],
          [ [ 0 ], [ 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1, 0
                 ] ] ] ]

This function requires the package ”weyl”(see 48.1).

50.24 LeftCellRepresentation

LeftCellRepresentation( W , cell, v )

returns a list of matrices giving the left cell representation of the Hecke algebra with parameter v^{2} associated with the Weyl group W . The argument cell is a pair with first component a list of reduced words which form a left cell, and second component the corresponding matrix of highest coefficients of the corresponding Kazhdan-Lusztig polynomials. Typically, cell is the result of the function WeylMueMat.

This function requires the package ”weyl”(see 48.1).

50.25 Hecke

Hecke( W , arg )

adds to the record of the Weyl group W a component T which is a function that produces, for each element w ∈ W, the corresponding basis element T_{w} in the generic Hecke algebra. arg specifies the index parameters corresponding to the standard generators. If arg is a single field element or a polynomial, then all parameters are assumed to be equal to this element. Alternatively, arg may be a list of such numbers or polynomials. Accordingly, a component parameter is added to the record of W .

    gap> W := Weyl( CartanMat("B",3) );;
    gap> q := Indeterminate(Rationals);;  q.name := "q";;
    gap> Hecke( W, 1 );

or

    gap> Hecke( W, [q,q^2,q^2] );

This function requires the package ”weyl”(see 48.1).

50.26 WeylClassPolynomials

WeylClassPolynomials( W , reps, w )

returns the class polynomials of the element w with respect to representatives reps of minimal lengths in the conjugacy classes of the Weyl group W . Typically, reps is the result of the WeylConjugacyClasses and w is a reduced expression in the standard generators of W . (For the definition of these polynomials, see the article by G.Pfeiffer and M.Geck in Advances in Mathematics (to appear).)

This function requires the package ”weyl”(see 48.1).

50.27 HeckeReflectionRepresentation

HeckeReflectionRepresentation( W )

returns a list of matrices which give the reflection representation of the Hecke algebra corresponding to the Weyl group W . The function Hecke must have been applied to the record W .

gap> W := Weyl( CartanMat("F",4) );;
gap> Hecke(W,1);   # the parameters are set to 1, so that the
gap>               # Hecke algebra is isomorphic to the
gap>               # group algebra of the Weyl group.
gap> HeckeReflectionRepresentation(W);
[ [ [ -1, 0, 0, 0 ], [ -1, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ] ],
  [ [ 1, -1, 0, 0 ], [ 0, -1, 0, 0 ], [ 0, -1, 1, 0 ], [ 0, 0, 0, 1 ]
     ],
  [ [ 1, 0, 0, 0 ], [ 0, 1, -2, 0 ], [ 0, 0, -1, 0 ], [ 0, 0, -1, 1 ]
     ],
  [ [ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, -1 ], [ 0, 0, 0, -1 ]
     ] ]

This function requires the package ”weyl”(see 48.1).

50.28 CheckHeckeDefiningRelations

CheckHeckeDefiningRelations( W , t )

returns true or false, according to whether a given set t of matrices corresponding to the standard generators of the Weyl group W defines a representation of the associated Hecke algebra or not.

gap> W := Weyl( CartanMat("F",4) );;
gap> Hecke( W, 1 );
gap> HeckeReflectionRepresentation(W);;
gap> CheckHeckeDefiningRelations( W, last );
true

This function requires the package ”weyl”(see 48.1).

50.29 CharHeckeRepresentation

CharHeckeRepresentation( rep , elts )

given a list rep of matrices corresponding to the standard generators of a finite Weyl group and a list elts of words in the standard generators it returns the list of traces of the corresponding representation on the elements in elts.

gap> W := Weyl( CartanMat("F",4) );;
gap> Hecke( W, 1 );
gap> r := WeylConjugacyClasses(W);;
#I  Number of cosets = 24
#I  Number of cosets = 8
#I  Number of cosets = 3

    #I  108 elements to consider
    #I  Still 34 elements to consider
    #I  25 conjugacy classes found
    gap> t := HeckeReflectionRepresentation(W);;
    gap> CharHeckeRepresentation( t, r );
    [ 4, 2, 2, 1, 0, 2, 1, 1, -1, -1, 1, 0, 0, 0, 0, 0, 2, -2, -2, -1,
      -1, 0, -2, -2, -4 ]

This function requires the package ”weyl”(see 48.1).

50.30 HeckeCharTable

HeckeCharTable( W , rep, elts)

given a Weyl group W , a list rep of characters and a list elts of representatives of minimal lengths in the conjugacy classes it returns a character table record.

This function requires the package ”weyl”(see 48.1).

Grape

This chapter describes the main functions of the GRAPE (Version 2.1) share library package for computing with graphs and groups. All functions described here are written entirely in the GAP language, except for the automorphism group and isomorphism testing functions, which make use of B. McKay’s nauty (Version 1.7) package [McK90].

GRAPE is primarily designed for the construction and analysis of graphs related to permutation groups and finite geometries. Special emphasis is placed on the determination of regularity properties and subgraph structure. The GRAPE philosophy is that a graph Γ always comes together with a known subgroup G of Aut (Γ), and that G is used to reduce the storage and CPU-time requirements for calculations with Γ (see [Soi93]). Of course G may be the trivial group, and in this case GRAPE algorithms will perform more slowly than strictly combinatorial algorithms (although this degradation in performance is hopefully never more than a fixed constant factor).

In general GRAPE deals with directed graphs which may have loops but have no multiple edges. However, many GRAPE functions only work for simple graphs (i.e. no loops, and whenever [x, y] is an edge then so is [y, x]), but these functions will check if an input graph is simple.

In GRAPE, a graph gamma is stored as a record, with mandatory components isGraph,

order, group, schreierVector, representatives, and adjacencies. Usually, the user

need not be aware of this record structure, and is strongly advised only to use GRAPE functions to construct and modify graphs.

The order component contains the number of vertices of gamma. The vertices of gamma are always 1, .., gamma.order, but they may also be given names, either by a user or by a

function constructing a graph (e.g. InducedSubgraph, BipartiteDouble, QuotientGraph).

The names component, if present, records these names. If the names component is not present (the user may, for example, choose to unbind it), then the names are taken to be 1, ..., gamma.order. The group component records the the GAP permutation group associated with gamma (this group must be a subgroup of Aut (gamma)). The representatives component records a set of orbit representatives for gamma.group on the vertices of gamma, with gamma.adjacencies[i] being the set of vertices adjacent to gamma.representatives[i]. The only mandatory component which may change once a graph is initially constructed is adjacencies (when an edge orbit of gamma.group is added to, or removed from, gamma).

A graph record may also have some of the optional components isSimple, autGroup, and canonicalLabelling, which record information about that graph.

GRAPE has the ability to make use of certain random group theoretical algorithms, which can result in time and store savings. The use of these random methods may be switched on and off by the global boolean variable GRAPE RANDOM, whose default value is false (random methods not used). Even if random methods are used, no function described below depends on the correctness of such a randomly computed result. For these functions the use of these random methods only influences the time and store taken. All global variables used by GRAPE start with GRAPE .

The user who is interested in knowing more about the GRAPE system, and its advanced use, should consult [Soi93] and the GRAPE source code.

51.1 Functions to construct and modify graphs

The following sections describe the functions used to construct and modify graphs.

51.2 Graph

Graph( G, L, act, rel )

Graph( G, L, act, rel, invt )

This is the most general and useful way of constructing a graph in GRAPE.

First suppose that the optional boolean parameter invt is unbound or has value false. Then L should be a list of elements of a set S on which the group G acts (operates in GAP language), with the action given by the function act. The parameter rel should be a boolean function defining a G-invariant relation on S (so that for g in G, x, y in S, rel(x, y) if and only if rel(act(x, g), act(y, g))). Then function Graph returns a graph gamma which has as vertex names

                    Concatenation( Orbits( G, L, act ) )

(the concatenation of the distinct orbits of the elements in L under G), and for vertices v, w of gamma, [v, w] is an edge if and only if

          rel( VertexName( gamma, v ), VertexName( gamma, w ) )

Now if the parameter invt exists and has value true, then it is assumed that L is invariant under G with respect to action act. Then the function Graph behaves as above, except that the vertex names of gamma become (a copy of) L.

The group associated with the graph gamma returned is the image of G acting via act on gamma.names.

For example, suppose you have an n × n adjacency matrix A for a graph X, so that the vertices of X are {1, …, n}, and [i, j] is an edge of X if and only if A[i][j] = 1. Suppose also that G ≤ Aut (X) (G may be trivial). Then you can make a GRAPE graph isomorphic to X

via Graph( G, [1..n], OnPoints, function(x,y) return A[x][y]=1; end, true );
    gap> A := [[0,1,0],[1,0,0],[0,0,1]];
    [ [ 0, 1, 0 ], [ 1, 0, 0 ], [ 0, 0, 1 ] ]

gap> G := Group( (1,2) );
Group( (1,2) )
gap> Graph( G, [1..3], OnPoints,
>           function(x,y) return A[x][y]=1; end,
>           true );
rec(
isGraph := true,
order := 3,
group := Group( (1,2) ),
schreierVector := [ -1, 1, -2 ],
adjacencies := [ [ 2 ], [ 3 ] ],
representatives := [ 1, 3 ],
names := [ 1 .. 3 ] )

We now construct the Petersen graph.

gap> Petersen := Graph( SymmetricGroup(5), [[1,2]], OnSets,
>                 function(x,y) return Intersection(x,y)=[]; end );
rec(
isGraph := true,
order := 10,
group := Group( ( 1, 2)( 6, 8)( 7, 9), ( 1, 3)( 4, 8)( 5, 9),
                ( 2, 4)( 3, 6)( 9,10), ( 2, 5)( 3, 7)( 8,10) ),
schreierVector := [ -1, 1, 2, 3, 4, 3, 4, 2, 2, 4 ],
adjacencies := [ [ 8, 9, 10 ] ],
representatives := [ 1 ],
names := [ [ 1, 2 ], [ 2, 5 ], [ 1, 5 ], [ 2, 3 ], [ 2, 4 ],
    [ 1, 3 ], [ 1, 4 ], [ 3, 5 ], [ 4, 5 ], [ 3, 4 ] ] )

51.3 EdgeOrbitsGraph

EdgeOrbitsGraph( G, E )
EdgeOrbitsGraph( G, E, n )

This is a common way of constructing a graph in GRAPE.

This function returns the (directed) graph with vertex set {1, ..., n}, edge set ∪_{e∈E} e^{G}, and associated (permutation) group G, which must act naturally on {1, ..., n}. The parameter E should be a list of edges (lists of length 2 of vertices), although a singleton edge will be understood as an edge list of length 1. The parameter n may be omitted, in which case the number of vertices is the largest point moved by a generator of G.

Note that G may be the trivial permutation group (Group( () ) in GAP notation), in which case the (directed) edges of gamma are simply those in the list E.

gap> EdgeOrbitsGraph( Group((1,3),(1,2)(3,4)), [[1,2],[4,5]], 5 );
rec(
isGraph := true,
order := 5,
group := Group( (1,3), (1,2)(3,4) ),
schreierVector := [ -1, 2, 1, 2, -2 ],

adjacencies := [ [ 2, 4, 5 ], [  ] ],
representatives := [ 1, 5 ],
isSimple := false )

51.4 NullGraph

NullGraph( G )
NullGraph( G, n )

This function returns the null graph on n vertices, with associated (permutation) group G. The parameter n may be omitted, in which case the number of vertices is the largest point moved by a generator of G.

See also 51.29.

gap> NullGraph( Group( (1,2,3) ), 4 );
rec(
isGraph := true,
order := 4,
group := Group( (1,2,3) ),
schreierVector := [ -1, 1, 1, -2 ],
adjacencies := [ [  ], [  ] ],
representatives := [ 1, 4 ],
isSimple := true )

51.5 CompleteGraph

CompleteGraph( G )
CompleteGraph( G, n )
CompleteGraph( G, n, mustloops )

This function returns a complete graph on n vertices with associated (permutation) group G. The parameter n may be omitted, in which case the number of vertices is the largest point moved by a generator of G. The optional boolean parameter mustloops determines whether the complete graph has all loops present or no loops (default: false (no loops)).

See also 51.30.

gap> CompleteGraph( Group( (1,2,3), (1,2) ) );
rec(
isGraph := true,
order := 3,
group := Group( (1,2,3), (1,2) ),
schreierVector := [ -1, 1, 1 ],
adjacencies := [ [ 2, 3 ] ],
representatives := [ 1 ],
isSimple := true )

51.6 JohnsonGraph

JohnsonGraph( n, e )

This function returns a graph gamma isomorphic to the Johnson graph, whose vertices are the e-subsets of {1, ..., n}, with x joined to y if and only if |x ∩ y| = e − 1. The group associated with gamma is image of the the symmetric group S_{n} acting on the e-subsets of {1, …, n}.

gap> JohnsonGraph(5,3);
rec(
isGraph := true,
order := 10,
group := Group( ( 1, 8)( 2, 9)( 4,10), ( 1, 5)( 2, 6)( 7,10),
                ( 1, 3)( 4, 6) ( 7, 9), ( 2, 3)( 4, 5)( 7, 8) ),
schreierVector := [ -1, 4, 3, 4, 2, 3, 4, 1, 3, 2 ],
adjacencies := [ [ 2, 3, 4, 5, 7, 8 ] ],
representatives := [ 1 ],
names := [ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ], [ 1, 3, 4 ],
    [ 1, 3, 5 ], [ 1, 4, 5 ], [ 2, 3, 4 ], [ 2, 3, 5 ], [ 2, 4, 5 ],
    [ 3, 4, 5 ] ],
isSimple := true )

51.7 AddEdgeOrbit

AddEdgeOrbit( gamma, e )
AddEdgeOrbit( gamma, e, H )

This procedure adds the edge orbit e^{gamma.group} to the edge set of graph gamma. The parameter e must be a sequence of length 2 of vertices of gamma. If the optional third parameter H is given then it is assumed that e[2] has the same orbit under H as it does under the stabilizer in gamma.group of e[1], and this knowledge can greatly speed up the procedure. Note that if gamma.group is trivial then this procedure simply adds the single edge e to gamma.

gap> gamma := NullGraph( Group( (1,3), (1,2)(3,4) ) );
rec(
isGraph := true,
order := 4,
group := Group( (1,3), (1,2)(3,4) ),
schreierVector := [ -1, 2, 1, 2 ],
adjacencies := [ [  ] ],
representatives := [ 1 ],
isSimple := true )
gap> AddEdgeOrbit( gamma, [4,3] );
gap> gamma;
rec(
isGraph := true,
order := 4,
group := Group( (1,3), (1,2)(3,4) ),
schreierVector := [ -1, 2, 1, 2 ],
adjacencies := [ [ 2, 4 ] ],
representatives := [ 1 ],

isSimple := true )

51.8 RemoveEdgeOrbit

RemoveEdgeOrbit( gamma, e )
RemoveEdgeOrbit( gamma, e, H )

This procedure removes the edge orbit e^{gamma.group} from the edge set of the graph gamma. The parameter e must be a sequence of length 2 of vertices of gamma, but if e is not an edge of gamma then this procedure has no effect. If the optional third parameter H is given then it is assumed that e[2] has the same orbit under H as it does under the stabilizer in gamma.group of e[1], and this knowledge can greatly speed up the procedure.

gap> gamma := CompleteGraph( Group( (1,3), (1,2)(3,4) ) );
rec(
isGraph := true,
order := 4,
group := Group( (1,3), (1,2)(3,4) ),
schreierVector := [ -1, 2, 1, 2 ],
adjacencies := [ [ 2, 3, 4 ] ],
representatives := [ 1 ],
isSimple := true )
gap> RemoveEdgeOrbit( gamma, [4,3] );
gap> gamma;
rec(
isGraph := true,
order := 4,
group := Group( (1,3), (1,2)(3,4) ),
schreierVector := [ -1, 2, 1, 2 ],
adjacencies := [ [ 3 ] ],
representatives := [ 1 ],
isSimple := true )

51.9 AssignVertexNames

AssignVertexNames( gamma, names )

This function allows the user to give new names to the vertices of gamma, by specifying a list names of vertex names for the vertices of gamma, such that names[i] contains the user’s name for the i-th vertex of gamma.

A copy of names is assigned to gamma.names. See also 51.14.

gap> gamma := NullGraph( Group(()), 3 );
rec(
isGraph := true,
order := 3,
group := Group( () ),
schreierVector := [ -1, -2, -3 ],
adjacencies := [ [  ], [  ], [  ] ],
representatives := [ 1, 2, 3 ],

  isSimple := true )
gap> AssignVertexNames( gamma, ["a","b","c"] );
gap> gamma;
rec(
  isGraph := true,
  order := 3,
  group := Group( () ),
  schreierVector := [ -1, -2, -3 ],
  adjacencies := [ [  ], [  ], [  ] ],
  representatives := [ 1, 2, 3 ],
  isSimple := true,
  names := [ "a", "b", "c" ] )

51.10 Functions to inspect graphs, vertices and edges

The next sections describe functions to inspect graphs, vertices and edges.

51.11 IsGraph

IsGraph( obj )

This boolean function returns true if and only if obj , which can be an object of arbitrary type, is a graph.

gap> IsGraph( 1 );
false
gap> IsGraph( JohnsonGraph( 3, 2 ) );
true

51.12 OrderGraph

OrderGraph( gamma )

This function returns the number of vertices (order) of the graph gamma.

gap> OrderGraph( JohnsonGraph( 4, 2 ) );
6

51.13 IsVertex

IsVertex( gamma, v )

This boolean function returns true if and only if v is vertex of gamma.

gap> gamma := JohnsonGraph( 3, 2 );;
gap> IsVertex( gamma, 1 );
true
gap> IsVertex( gamma, 4 );
false

51.14 VertexName

VertexName( gamma, v )

This function returns (a copy of) the name of the vertex v of gamma. See also 51.9.

gap> VertexName( JohnsonGraph(4,2), 6 );
[ 3, 4 ]

51.15 Vertices

Vertices( gamma )

This function returns the vertex set {1, ..., gamma.order} of the graph gamma.

gap> Vertices( JohnsonGraph( 4, 2 ) );
[ 1 .. 6 ]

51.16 VertexDegree

VertexDegree( gamma, v )

This function returns the (out)degree of the vertex v of the graph gamma.

gap> VertexDegree( JohnsonGraph( 3, 2 ), 1 );
2

51.17 VertexDegrees

VertexDegrees( gamma )

This function returns the set of vertex (out)degrees for the graph gamma.

gap> VertexDegrees( JohnsonGraph( 4, 2 ) );
[ 4 ]

51.18 IsLoopy

IsLoopy( gamma )

This boolean function returns true if and only if the graph gamma has a loop, that is, an edge of the form [x, x].

gap> IsLoopy( JohnsonGraph( 4, 2 ) );
false
gap> IsLoopy( CompleteGraph( Group( (1,2,3), (1,2) ), 3 ) );
false
gap> IsLoopy( CompleteGraph( Group( (1,2,3), (1,2) ), 3, true ) );
true

51.19 IsSimpleGraph

IsSimpleGraph( gamma )

This boolean function returns true if and only if the graph gamma is simple, that is, has no loops and whenever [x, y] is an edge then so is [y, x].

gap> IsSimpleGraph( CompleteGraph( Group( (1,2,3) ), 3 ) );
true
gap> IsSimpleGraph( CompleteGraph( Group( (1,2,3) ), 3, true ) );
false

51.20 Adjacency

Adjacency( gamma, v )

This function returns (a copy of) the set of vertices of gamma adjacent to vertex v. A vertex w is adjacent to v if and only if [v, w] is an edge.

gap> Adjacency( JohnsonGraph( 4, 2 ), 1 );
[ 2, 3, 4, 5 ]
gap> Adjacency( JohnsonGraph( 4, 2 ), 6 );
[ 2, 3, 4, 5 ]

51.21 IsEdge

IsEdge( gamma, e )

This boolean function returns true if and only if e is an edge of gamma.

gap> IsEdge( JohnsonGraph( 4, 2 ), [ 1, 2 ] );
true
gap> IsEdge( JohnsonGraph( 4, 2 ), [ 1, 6 ] );
false

51.22 DirectedEdges

DirectedEdges( gamma )

This function returns the set of directed (ordered) edges of the graph gamma.

See also 51.23.

gap> gamma := JohnsonGraph( 3, 2 );
rec(
  isGraph := true,
  order := 3,
  group := Group( (1,3), (1,2) ),
  schreierVector := [ -1, 2, 1 ],
  adjacencies := [ [ 2, 3 ] ],
  representatives := [ 1 ],
  names := [ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ],
  isSimple := true )

    gap> DirectedEdges( gamma );
    [ [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 3 ], [ 3, 1 ], [ 3, 2 ] ]
    gap> UndirectedEdges( gamma );
    [ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ]

51.23 UndirectedEdges

UndirectedEdges( gamma )

This function returns the set of undirected (unordered) edges of gamma, which must be a simple graph.

See also 51.22.

    gap> gamma := JohnsonGraph( 3, 2 );
    rec(
      isGraph := true,
      order := 3,
      group := Group( (1,3), (1,2) ),
      schreierVector := [ -1, 2, 1 ],
      adjacencies := [ [ 2, 3 ] ],
      representatives := [ 1 ],
      names := [ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ],
      isSimple := true )
    gap> DirectedEdges( gamma );
    [ [ 1, 2 ], [ 1, 3 ], [ 2, 1 ], [ 2, 3 ], [ 3, 1 ], [ 3, 2 ] ]
    gap> UndirectedEdges( gamma );
    [ [ 1, 2 ], [ 1, 3 ], [ 2, 3 ] ]

51.24 Distance

Distance( gamma, X , Y )
Distance( gamma, X , Y , G )

This function returns the distance from X to Y in gamma. The parameters X and Y may be vertices or vertex sets. We define the distance d(X , Y ) from X to Y to be the minimum length of a (directed) path joining a vertex of X to a vertex of Y if such a path exists, and −1 otherwise.

The optional parameter G, if present, is assumed to be a subgroup of Aut (gamma) fixing X setwise. Including such a G can speed up the function.

    gap> Distance( JohnsonGraph(4,2), 1, 6 );
    2
    gap> Distance( JohnsonGraph(4,2), 1, 5 );
    1

51.25 Diameter

Diameter( gamma )

This function returns the diameter of gamma. A diameter of −1 is returned if gamma is not (strongly) connected.

gap> Diameter( JohnsonGraph( 5, 3 ) );
2
gap> Diameter( JohnsonGraph( 5, 4 ) );
1

51.26 Girth

Girth( gamma )

This function returns the girth of gamma, which must be a simple graph. A girth of −1 is returned if gamma is a forest.

gap> Girth( JohnsonGraph( 4, 2 ) );
3

51.27 IsConnectedGraph

IsConnectedGraph( gamma )

This boolean function returns true if and only if gamma is (strongly) connected, i.e. if there is a (directed) path from x to y for every pair of vertices x, y of gamma.

gap> IsConnectedGraph( JohnsonGraph(4,2) );
true
gap> IsConnectedGraph( NullGraph(SymmetricGroup(4)) );
false

51.28 IsBipartite

IsBipartite( gamma )

This boolean function returns true if and only if the graph gamma, which must be simple, is bipartite, i.e. if the vertex set can be partitioned into two null graphs (which are called bicomponents or parts of gamma).

See also 51.41, 51.51, and 51.54.

gap> gamma := JohnsonGraph(4,2);
rec(
  isGraph := true,
  order := 6,
  group := Group( (1,5)(2,6), (1,3)(4,6), (2,3)(4,5) ),
  schreierVector := [ -1, 3, 2, 3, 1, 2 ],
  adjacencies := [ [ 2, 3, 4, 5 ] ],
  representatives := [ 1 ],
  names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ],
      [ 3, 4 ] ],
  isSimple := true )
gap> IsBipartite(gamma);
false
gap> delta := BipartiteDouble(gamma);
rec(

  isGraph := true,
  order := 12,
  group := Group( ( 1, 5)( 2, 6)( 7,11)( 8,12), ( 1, 3)( 4, 6)
    ( 7, 9)(10,12), ( 2, 3)( 4, 5)( 8, 9)(10,11), ( 1, 7)( 2, 8)
    ( 3, 9)( 4,10)( 5,11)( 6,12) ),
  schreierVector := [ -1, 3, 2, 3, 1, 2, 4, 4, 4, 4, 4, 4 ],
  adjacencies := [ [ 8, 9, 10, 11 ] ],
  representatives := [ 1 ],
  isSimple := true,
  names :=
   [ [ [ 1, 2 ], "+" ], [ [ 1, 3 ], "+" ], [ [ 1, 4 ], "+" ],
      [ [ 2, 3 ], "+" ], [ [ 2, 4 ], "+" ], [ [ 3, 4 ], "+" ],
      [ [ 1, 2 ], "-" ], [ [ 1, 3 ], "-" ], [ [ 1, 4 ], "-" ],
      [ [ 2, 3 ], "-" ], [ [ 2, 4 ], "-" ], [ [ 3, 4 ], "-" ] ] )
gap> IsBipartite(delta);
true

51.29 IsNullGraph

IsNullGraph( gamma )

This boolean function returns true if and only if the graph gamma has no edges. See also 51.4.

gap> IsNullGraph( CompleteGraph( Group(()), 3 ) );
false
gap> IsNullGraph( CompleteGraph( Group(()), 1 ) );
true

51.30 IsCompleteGraph

IsCompleteGraph( gamma )
IsCompleteGraph( gamma, mustloops )

This boolean function returns true if and only if the graph gamma is a complete graph. The optional boolean parameter mustloops determines whether all loops must be present for gamma to be considered a complete graph (default: false (loops are ignored)). See also 51.5.

gap> IsCompleteGraph( NullGraph( Group(()), 3 ) );
false
gap> IsCompleteGraph( NullGraph( Group(()), 1 ) );
true
gap> IsCompleteGraph( CompleteGraph(SymmetricGroup(3)), true );
false

51.31 Functions to determine regularity properties of graphs

The following sections describe functions to determine regularity properties of graphs.

51.32 IsRegularGraph

IsRegularGraph( gamma )

This boolean function returns true if and only if the graph gamma is (out)regular.

    gap> IsRegularGraph( JohnsonGraph(4,2) );
    true
    gap> IsRegularGraph( EdgeOrbitsGraph(Group(()),[[1,2]],2) );
   false

51.33 LocalParameters

LocalParameters( gamma, V )
LocalParameters( gamma, V , G )

This function determines any local parameters c_{i}(V ), a_{i}(V ), or b_{i}(V ) that simple, connected gamma may have, with respect to the singleton vertex or vertex set V (see [BCN89]). The function returns a list of triples, whose i-th element is [c_{i−1}(V ), a_{i−1}(V ), b_{i−1}(V )], except that if some local parameter does not exist then a −1 is put in its place. This function can be used to determine whether a given subset of the vertices of a graph is a distanceregular code in that graph.

The optional parameter G, if present, is assumed to be a subgroup of Aut (gamma) fixing V (setwise). Including such a G can speed up the function.

    gap> LocalParameters( JohnsonGraph(4,2), 1 );
    [ [ 0, 0, 4 ], [ 1, 2, 1 ], [ 4, 0, 0 ] ]
    gap> LocalParameters( JohnsonGraph(4,2), [1,6] );
    [ [ 0, 0, 4 ], [ 2, 2, 0 ] ]

51.34 GlobalParameters

GlobalParameters( gamma )

In a similar way to LocalParameters (see 51.33), this function determines the global parameters c_{i}, a_{i}, b_{i} of simple, connected gamma (see [BCN89]). The nonexistence of a global parameter is denoted by −1.

    gap> gamma := JohnsonGraph(4,2);;
    gap> GlobalParameters( gamma );
    [ [ 0, 0, 4 ], [ 1, 2, 1 ], [ 4, 0, 0 ] ]
    gap> GlobalParameters( BipartiteDouble(gamma) );
    [ [ 0, 0, 4 ], [ 1, 0, 3 ], [ -1, 0, -1 ], [ 4, 0, 0 ] ]

51.35 IsDistanceRegular

IsDistanceRegular( gamma )

This boolean function returns true if and only if gamma is distance-regular, i.e. gamma is simple, connected, and all possible global parameters exist.

    gap> gamma := JohnsonGraph(4,2);;

    gap> IsDistanceRegular( gamma );
    true
    gap> IsDistanceRegular( BipartiteDouble(gamma) );
    false

51.36 CollapsedAdjacencyMat

CollapsedAdjacencyMat( G, gamma )

This function returns the collapsed adjacency matrix for gamma, where the collapsing group is G. It is assumed that G is a subgroup of Aut (gamma). The (i, j)-entry of the collapsed adjacency matrix equals the number of edges in {[x, y]|y ∈ jth G-orbit }, where x is a fixed vertex in the i-th G-orbit. See also 51.37.

    gap> gamma := JohnsonGraph(4,2);;
    gap> G := Stabilizer( gamma.group, 1 );;
    gap> CollapsedAdjacencyMat( G, gamma );
    [ [ 0, 4, 0 ], [ 1, 2, 1 ], [ 0, 4, 0 ] ]

51.37 OrbitalGraphIntersectionMatrices

OrbitalGraphIntersectionMatrices( G )
OrbitalGraphIntersectionMatrices( G, H )

This function returns a sequence of intersection matrices corresponding to the orbital graphs for the transitive permutation group G. An intersection matrix for an orbital graph gamma for G is a collapsed adjacency matrix of gamma, collapsed with respect to the stabilizer in G of a point. If the optional parameter H is given then it is assumed to be the stabilizer in G of the point 1, and this information can speed up the function. See also 51.36.

    gap> OrbitalGraphIntersectionMatrices( SymmetricGroup(7) );
    [ [ [ 1, 0 ], [ 0, 1 ] ], [ [ 0, 6 ], [ 1, 5 ] ] ]

51.38 Some special vertex subsets of a graph

The following sections describe functions for special vertex subsets of a graph.

51.39 Component

Component( gamma, v )

This function returns the set of all vertices in gamma which may be reached by a (directed) path starting at the vertex v. See also 51.40.

    gap> Component( NullGraph( Group((1,2)) ), 2 );
    [ 2 ]
    gap> Component( JohnsonGraph(4,2), 2 );
    [ 1, 2, 3, 4, 5, 6 ]

51.40 Components

Components( gamma )

This function returns a list of the vertex sets of the connected components of gamma, which must be a simple graph.

See also 51.39.

    gap> Components( NullGraph( Group((1,2,3,4)) ) );
    [ [ 1 ], [ 2 ], [ 3 ], [ 4 ] ]
    gap> Components( JohnsonGraph(4,2) );
    [ [ 1, 2, 3, 4, 5, 6 ] ]

51.41 Bicomponents

Bicomponents( gamma )

If the graph gamma, which must be simple, is bipartite, this function returns a length 2 list of bicomponents or parts of gamma, otherwise the empty list is returned.

Note: if gamma is not connected then its bicomponents are not necessarily uniquely determined. See also 51.28.

    gap> Bicomponents( NullGraph(SymmetricGroup(4)) );
    [ [ 1, 2, 3 ], [ 4 ] ]
    gap> Bicomponents( JohnsonGraph(4,2) );
    [  ]

51.42 DistanceSet

DistanceSet( gamma, distances, V )
DistanceSet( gamma, distances, V , G )

This function returns the set of vertices w of gamma, such that d(V , w) is in distances (a list or singleton distance).

The optional parameter G, if present, is assumed to be a subgroup of Aut (gamma) fixing V setwise. Including such a G can speed up the function.

    gap> DistanceSet( JohnsonGraph(4,2), 1, [1,6] );
    [ 2, 3, 4, 5 ]

51.43 Layers

Layers( gamma, V )
Layers( gamma, V , G )

This function returns a list whose i-th element is the set of vertices of gamma at distance i − 1 from V , which may be a vertex set or a singleton vertex.

The optional parameter G, if present, is assumed to be a subgroup of Aut (gamma) which fixes V setwise. Including such a G can speed up the function.

    gap> Layers( JohnsonGraph(4,2), 6 );
    [ [ 6 ], [ 2, 3, 4, 5 ], [ 1 ] ]

51.44 IndependentSet

IndependentSet( gamma )
IndependentSet( gamma, indset )

IndependentSet( gamma, indset, forbidden )

Returns a (hopefully large) independent set (coclique) of the graph gamma, which must be simple. At present, a greedy algorithm is used. The returned independent set will contain the (assumed) independent set indset (default: []), and not contain any element of forbidden (default: [], in which case the returned independent set is maximal). An error is signalled if indset and forbidden have non-trivial intersection.

    gap> IndependentSet( JohnsonGraph(4,2), [3] );
    [ 3, 4 ]

51.45 Functions to construct new graphs from old

The following sections describe functions to construct new graphs from old ones.

51.46 InducedSubgraph

InducedSubgraph( gamma, V )
InducedSubgraph( gamma, V , G )

This function returns the subgraph of gamma induced on the vertex list V (which must not contain repeated elements). If the optional third parameter G is given, then it is assumed that G fixes V setwise, and is a group of automorphisms of the induced subgraph when restriced to V . This knowledge is then used to give an associated group to the induced subgraph. If no such G is given then the associated group is trivial.

    gap> gamma := JohnsonGraph(4,2);;
    gap> S := [2,3,4,5];;
    gap> InducedSubgraph( gamma, S, Stabilizer(gamma.group,S,OnSets) );
    rec(
      isGraph := true,
      order := 4,
      group := Group( (2,3), (1,2)(3,4) ),
      schreierVector := [ -1, 2, 1, 2 ],
      adjacencies := [ [ 2, 3 ] ],
      representatives := [ 1 ],
      isSimple := true,
      names := [ [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ] ] )

51.47 DistanceSetInduced

DistanceSetInduced( gamma, distances, V )
DistanceSetInduced( gamma, distances, V , G )

This function returns the subgraph of gamma induced on the set of vertices w of gamma such that d(V , w) is in distances (a list or singleton distance).

The optional parameter G, if present, is assumed to be a subgroup of Aut (gamma) fixing V setwise. Including such a G can speed up the function.

gap> DistanceSetInduced( JohnsonGraph(4,2), [0,1], [1] );
rec(
isGraph := true,
order := 5,
group := Group( (2,3)(4,5), (2,5)(3,4) ),
schreierVector := [ -1, -2, 1, 2, 2 ],
adjacencies := [ [ 2, 3, 4, 5 ], [ 1, 3, 4 ] ],
representatives := [ 1, 2 ],
isSimple := true,
names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ] ] )

51.48 DistanceGraph

DistanceGraph( gamma, distances )

This function returns the graph delta, with the same vertex set as gamma, such that [x, y] is an edge of delta if and only if d(x, y) (in gamma) is in the list distances.

gap> DistanceGraph( JohnsonGraph(4,2), [2] );
rec(
isGraph := true,
order := 6,
group := Group( (1,5)(2,6), (1,3)(4,6), (2,3)(4,5) ),
schreierVector := [ -1, 3, 2, 3, 1, 2 ],
adjacencies := [ [ 6 ] ],
representatives := [ 1 ],
names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ],
  [ 3, 4 ] ],
isSimple := true )
gap> Components(last);
[ [ 1, 6 ], [ 2, 5 ], [ 3, 4 ] ]

51.49 ComplementGraph

ComplementGraph( gamma )
ComplementGraph( gamma, comploops )

This function returns the complement of the graph gamma. The optional boolean parameter comploops determines whether or not loops/nonloops are complemented (default: false (loops/nonloops are not complemented)).

gap> ComplementGraph( NullGraph(SymmetricGroup(3)) );
rec(
isGraph := true,
order := 3,
group := Group( (1,3), (2,3) ),
schreierVector := [ -1, 2, 1 ],
adjacencies := [ [ 2, 3 ] ],

representatives := [ 1 ],
isSimple := true )
gap> IsLoopy(last);
false
gap> IsLoopy(ComplementGraph(NullGraph(SymmetricGroup(3)),true));
true

51.50 PointGraph

PointGraph( gamma )
PointGraph( gamma, v )

Assuming that gamma is simple, connected, and bipartite, this function returns the induced subgraph on the (connected) component of DistanceGraph(gamma,2) containing the vertex v (default: v = 1).

Thus, if gamma is the incidence graph of a connected geometry, and v represents a point, then the point graph of the geometry is returned.

gap> BipartiteDouble( CompleteGraph(SymmetricGroup(4)) );;
gap> PointGraph(last);
rec(
isGraph := true,
order := 4,
group := Group( (3,4), (2,4), (1,4) ),
schreierVector := [ -1, 2, 1, 3 ],
adjacencies := [ [ 2, 3, 4 ] ],
representatives := [ 1 ],
isSimple := true,
names := [ [ 1, "+" ], [ 2, "+" ], [ 3, "+" ], [ 4, "+" ] ] )
gap> IsCompleteGraph(last);
true

51.51 EdgeGraph

EdgeGraph( gamma )

This function returns the edge graph, also called the line graph, of the simple graph gamma.

This edge graph delta has the unordered edges of gamma as vertices, and e is joined to f in delta precisely when |e ∩ f| = 1.

gap> EdgeGraph( CompleteGraph(SymmetricGroup(5)) );
rec(
isGraph := true,
order := 10,
group := Group( ( 1, 7)( 2, 9)( 3,10), ( 1, 4)( 5, 9)( 6,10),
  ( 2, 4)( 5, 7)( 8,10), ( 3, 4)( 6, 7)( 8, 9) ),
schreierVector := [ -1, 3, 4, 2, 3, 4, 1, 4, 2, 2 ],
adjacencies := [ [ 2, 3, 4, 5, 6, 7 ] ],
representatives := [ 1 ],
isSimple := true,

names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 1, 5 ], [ 2, 3 ],
  [ 2, 4 ], [ 2, 5 ], [ 3, 4 ], [ 3, 5 ], [ 4, 5 ] ] )

51.52 UnderlyingGraph

UnderlyingGraph( gamma )

This function returns the underlying graph delta of gamma. The graph delta has the same vertex set as gamma, and has an edge [x, y] precisely when gamma has an edge [x, y] or an edge [y, x]. This function also sets the isSimple components of gamma and delta.

gap> gamma := EdgeOrbitsGraph( Group((1,2,3,4)), [1,2] );
rec(
isGraph := true,
order := 4,
group := Group( (1,2,3,4) ),
schreierVector := [ -1, 1, 1, 1 ],
adjacencies := [ [ 2 ] ],
representatives := [ 1 ],
isSimple := false )
gap> UnderlyingGraph(gamma);
rec(
isGraph := true,
order := 4,
group := Group( (1,2,3,4) ),
schreierVector := [ -1, 1, 1, 1 ],
adjacencies := [ [ 2, 4 ] ],
representatives := [ 1 ],
isSimple := true )

51.53 QuotientGraph

QuotientGraph( gamma, R )

Let S be the smallest gamma.group-invariant equivalence relation on the vertices of gamma, such that S contains the relation R (which should be a list of ordered pairs (length 2 lists) of vertices of gamma). Then this function returns a graph isomorphic to the quotient delta of the graph gamma, defined as follows. The vertices of delta are the equivalence classes of S, and [X, Y] is an edge of delta if and only if [x, y] is an edge of gamma for some x ∈ X, y ∈ Y.

gap> gamma := JohnsonGraph(4,2);;
gap> QuotientGraph( gamma, [[1,6]] );
rec(
isGraph := true,
order := 3,
group := Group( (1,2), (1,3), (2,3) ),
schreierVector := [ -1, 1, 2 ],
adjacencies := [ [ 2, 3 ] ],
representatives := [ 1 ],

isSimple := true,
names := [ [ [ 1, 2 ], [ 3, 4 ] ], [ [ 1, 3 ], [ 2, 4 ] ],
    [ [ 1, 4 ], [ 2, 3 ] ] ] )

51.54 BipartiteDouble

BipartiteDouble( gamma )

This function returns the bipartite double of the graph gamma, as defined in [BCN89].

gap> gamma := JohnsonGraph(4,2);
rec(
isGraph := true,
order := 6,
group := Group( (1,5)(2,6), (1,3)(4,6), (2,3)(4,5) ),
schreierVector := [ -1, 3, 2, 3, 1, 2 ],
adjacencies := [ [ 2, 3, 4, 5 ] ],
representatives := [ 1 ],
names := [ [ 1, 2 ], [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ],
    [ 3, 4 ] ],
isSimple := true )
gap> IsBipartite(gamma);
false
gap> delta := BipartiteDouble(gamma);
rec(
isGraph := true,
order := 12,
group := Group( ( 1, 5)( 2, 6)( 7,11)( 8,12), ( 1, 3)( 4, 6)
  ( 7, 9)(10,12), ( 2, 3)( 4, 5)( 8, 9)(10,11), ( 1, 7)( 2, 8)
  ( 3, 9)( 4,10)( 5,11)( 6,12) ),
schreierVector := [ -1, 3, 2, 3, 1, 2, 4, 4, 4, 4, 4, 4 ],
adjacencies := [ [ 8, 9, 10, 11 ] ],
representatives := [ 1 ],
isSimple := true,
names :=
 [ [ [ 1, 2 ], "+" ], [ [ 1, 3 ], "+" ], [ [ 1, 4 ], "+" ],
    [ [ 2, 3 ], "+" ], [ [ 2, 4 ], "+" ], [ [ 3, 4 ], "+" ],
    [ [ 1, 2 ], "-" ], [ [ 1, 3 ], "-" ], [ [ 1, 4 ], "-" ],
    [ [ 2, 3 ], "-" ], [ [ 2, 4 ], "-" ], [ [ 3, 4 ], "-" ] ] )
gap> IsBipartite(delta);
true

51.55 GeodesicsGraph

GeodesicsGraph( gamma, x, y )

This function returns the the graph induced on the set of geodesics between vertices x and y, but not including x or y. This function is only for a simple graph gamma.

gap> GeodesicsGraph( JohnsonGraph(4,2), 1, 6 );

    rec(
      isGraph := true,
      order := 4,
      group := Group( (1,3)(2,4), (1,4)(2,3), (1,3,4,2) ),
      schreierVector := [ -1, 2, 1, 2 ],
      adjacencies := [ [ 2, 3 ] ],
      representatives := [ 1 ],
      isSimple := true,
      names := [ [ 1, 3 ], [ 1, 4 ], [ 2, 3 ], [ 2, 4 ] ] )
    gap> GlobalParameters(last);
    [ [ 0, 0, 2 ], [ 1, 0, 1 ], [ 2, 0, 0 ] ]

51.56 CollapsedIndependentOrbitsGraph

CollapsedIndependentOrbitsGraph( G, gamma )
CollapsedIndependentOrbitsGraph( G, gamma, N )

Given a subgroup G of the automorphism group of the graph gamma, this function returns a graph isomorphic to delta, defined as follows. The vertices of delta are those G-orbits of the vertices of gamma that are independent sets, and x is not joined to y in delta if and only if x ∪ y is an independent set in gamma.

If the optional parameter N is given, then it is assumed to be a subgroup of Aut (gamma) preserving the set of G-orbits of the vertices of gamma (for example, the normalizer in gamma.group of G). This information can make the function more efficient.

    gap> G := Group( (1,2) );;
    gap> gamma := NullGraph( SymmetricGroup(3) );;
    gap> CollapsedIndependentOrbitsGraph( G, gamma );
    rec(
      isGraph := true,
      order := 2,
      group := Group( () ),
      schreierVector := [ -1, -2 ],
      adjacencies := [ [  ], [  ] ],
      representatives := [ 1, 2 ],
      isSimple := true,
      names := [ [ 1, 2 ], [ 3 ] ] )

51.57 CollapsedCompleteOrbitsGraph

CollapsedCompleteOrbitsGraph( G, gamma )
CollapsedCompleteOrbitsGraph( G, gamma, N )

Given a subgroup G of the automorphism group of the simple graph gamma, this function returns a graph isomorphic to delta, defined as follows. The vertices of delta are those Gorbits of the vertices of gamma on which complete subgraphs are induced in gamma, and x is joined to y in delta if and only if x ≠ y and the subgraph of gamma induced on x ∪ y is a complete graph.

If the optional parameter N is given, then it is assumed to be a subgroup of Aut (gamma) preserving the set of G-orbits of the vertices of gamma (for example, the normalizer in gamma.group of G). This information can make the function more efficient.

gap> G := Group( (1,2) );;
gap> gamma := NullGraph( SymmetricGroup(3) );;
gap> CollapsedCompleteOrbitsGraph( G, gamma );
rec(
  isGraph := true,
  order := 1,
  group := Group( () ),
  schreierVector := [ -1 ],
  adjacencies := [ [  ] ],
  representatives := [ 1 ],
  names := [ [ 3 ] ],
  isSimple := true )
gap> gamma := CompleteGraph( SymmetricGroup(3) );;
gap> CollapsedCompleteOrbitsGraph( G, gamma );
rec(
  isGraph := true,
  order := 2,
  group := Group( () ),
  schreierVector := [ -1, -2 ],
  adjacencies := [ [ 2 ], [ 1 ] ],
  representatives := [ 1, 2 ],
  names := [ [ 1, 2 ], [ 3 ] ],
  isSimple := true )

51.58 NewGroupGraph

NewGroupGraph( G, gamma )

This function returns a copy delta of gamma, except that the group associated with delta is G, which is a assumed to be a subgroup of Aut (delta).

Note that the result of some functions of a graph depend on the group associated with that graph (which must always be a subgroup of the automorphism group of the graph).

gap> gamma := JohnsonGraph(4,2);;
gap> aut := AutGroupGraph(gamma);
Group( (3,4), (2,3)(4,5), (1,2)(5,6) )
gap> Size(gamma.group);
24
gap> Size(aut);
48
gap> delta := NewGroupGraph( aut, gamma );;
gap> Size(delta.group);
48
gap> IsIsomorphicGraph( gamma, delta );
true

51.59 Vertex-Colouring and Complete Subgraphs

The following sections describe functions for vertex-colouring or constructing complete subgraphs of given graphs.

51.60 VertexColouring

VertexColouring( gamma )

This function returns a proper vertex-colouring C for the graph gamma, which must be simple.

This proper vertex-colouring C is a list of natural numbers, indexed by the vertices of gamma, and has the property that C[i] ≠ C[j] whenever [i, j] is an edge of gamma. At present a greedy algorithm is used.

    gap> VertexColouring( JohnsonGraph(4,2) );
    [ 1, 2, 3, 3, 2, 1 ]

51.61 CompleteSubgraphs

CompleteSubgraphs( gamma )
CompleteSubgraphs( gamma, k )
CompleteSubgraphs( gamma, k, alls )

This function returns a set K of complete subgraphs of gamma, which must be a simple graph. A complete subgraph is represented by its vertex set. If k > −1 then the elements of K each have size k, otherwise the elements of K represent maximal complete subgraphs of gamma. The default for k is −1, i.e. maximal complete subgraphs.

The optional boolean parameter alls controls how many complete subgraphs are returned. If alls is true (the default), then K will contain (perhaps properly) a set of gamma.group orbit-representatives of the size k (if k > −1) or maximal (if k < 0) complete subgraphs of gamma.

If alls is false then K will contain at most one element. In this case, if k < 0 then K will contain just one maximal complete subgraph, and if k > −1 then K will contain a complete subgraph of size k if and only if such a subgraph is contained in gamma.

    gap> gamma := JohnsonGraph(5,2);;
    gap> CompleteSubgraphs(gamma);
    [ [ 1, 2, 3, 4 ], [ 1, 2, 5 ] ]
    gap> CompleteSubgraphs(gamma,2,false);
    [ [ 1, 2 ] ]

51.62 CompleteSubgraphsOfGivenSize

CompleteSubgraphsOfGivenSize( gamma, k )
CompleteSubgraphsOfGivenSize( gamma, k, alls )
CompleteSubgraphsOfGivenSize( gamma, k, alls, maxi )
CompleteSubgraphsOfGivenSize( gamma, k, alls, maxi, colnum )

Let gamma be a simple graph and k > 0. This function returns a set K of complete subgraphs of size k of gamma, if such subgraphs exist (else the empty set is returned). A complete subgraph is represented by its vertex set. This function is more efficient for its purpose than the more general function CompleteSubgraphs. The boolean parameter alls is used to control how many complete subgraphs are returned. If alls is true (the default), then K will contain (perhaps properly) a set of gamma.group orbit-representatives of the size k complete subgraphs of gamma. If alls is false then K will contain at most one element, and will contain one element if and only if gamma contains a complete subgraph of size k. If the boolean parameter maxi is bound and has value true, then it is assumed that all complete subgraphs of size k of gamma are maximal. If the optional rational parameter colnum is given, then a sensible value is

          OrderGraph(gamma)/Length(Set(VertexColouring(gamma))).

The use of this parameter may speed up the function.

    gap> gamma := JohnsonGraph(5,2);;
    gap> CompleteSubgraphsOfGivenSize(gamma,5);
    [  ]
    gap> CompleteSubgraphsOfGivenSize(gamma,4,true,true);
    [ [ 1, 2, 3, 4 ] ]
    gap> gamma := NewGroupGraph( Group(()), gamma );;
    gap> CompleteSubgraphsOfGivenSize(gamma,4,true,true);
    [ [ 1, 2, 3, 4 ], [ 1, 5, 6, 7 ], [ 2, 5, 8, 9 ], [ 3, 6, 8, 10 ],
      [ 4, 7, 9, 10 ] ]

51.63 Functions depending on nauty

For convenience, GRAPE provides a (somewhat primitive) interface to Brendan McKay’s nauty (Version 1.7) package (see [McK90]) for calculating automorphism groups of vertexcoloured graphs, and for testing graph isomorphism.

51.64 AutGroupGraph

AutGroupGraph( gamma )
AutGroupGraph( gamma, colouring )

The first version of this function returns the automorphism group of the (directed) graph gamma, using nauty. In the second version, colouring is a vertex-colouring of gamma, and the subgroup of Aut (gamma) preserving this colouring is returned. Here, a colouring should be given as a list of sets, forming a partion of the vertices. The set for the last colour may be omitted. Note that we do not require that adjacent vertices have different colours.

    gap> gamma := JohnsonGraph(4,2);;
    gap> Size(AutGroupGraph(gamma));
    48
    gap> Size(AutGroupGraph(gamma,[[1,6]]));

51.65 IsIsomorphicGraph

IsIsomorphicGraph( gamma1 , gamma2 )

This boolean function uses the nauty program to test the isomorphism of gamma1 with gamma2 . The value true is returned if and only if the graphs are isomorphic (as directed, uncoloured graphs).

This function creates or uses the record component canonicalLabelling of a graph. As noted in [McK90], a canonical labelling given by nauty can depend on the version of nauty (Version 1.7 in GRAPE 2.1; this is a change from previous versions of GRAPE), certain parameters of nauty (always set the same by GRAPE 2.1), and the compiler and computer used. If you use the canonicalLabelling component (say by using IsIsomorphicGraph) of a graph stored on a file, then you must be sure that this field was created in the same environment in which you are presently computing. If in doubt, unbind the canonicalLabelling component of the graph before testing isomorphism.

    gap> gamma := JohnsonGraph(7,4);;
    gap> delta := JohnsonGraph(7,3);;
    gap> IsIsomorphicGraph( gamma, delta );
    true

51.66 An example

We conclude this chapter with a simple example to illustrate further the use of GRAPE.

In this example we construct the Petersen graph P, and its edge graph (often called line graph) EP. We compute the (global) parameters of EP, and so verify that EP is distanceregular (see [BCN89]). We also show that there is just one orbit of 1-factors of P under the automorphism group of P (but you should read the documentation of the function CompleteSubgraphsOfGivenSize to see exactly what that function does).

    gap> P := Graph( SymmetricGroup(5), [[1,2]], OnSets,
    >          function(x,y) return Intersection(x,y)=[]; end );
    rec(
      isGraph := true,
      order := 10,
      group := Group( ( 1, 2)( 6, 8)( 7, 9), ( 1, 3)( 4, 8)( 5, 9),
                      ( 2, 4)( 3, 6)( 9,10), ( 2, 5)( 3, 7)( 8,10) ),
      schreierVector := [ -1, 1, 2, 3, 4, 3, 4, 2, 2, 4 ],
      adjacencies := [ [ 8, 9, 10 ] ],
      representatives := [ 1 ],
      names := [ [ 1, 2 ], [ 2, 5 ], [ 1, 5 ], [ 2, 3 ], [ 2, 4 ],
          [ 1, 3 ], [ 1, 4 ], [ 3, 5 ], [ 4, 5 ], [ 3, 4 ] ] )
    gap> Diameter(P);
    2
    gap> Girth(P);
    5
    gap> EP := EdgeGraph(P);
    rec(
      isGraph := true,

order := 15,
group := Group( ( 1, 4)( 2, 5)( 3, 6)(10,11)(12,13)(14,15),
                ( 1, 7)( 2, 8)( 3, 9)(10,15)(11,13)(12,14),
                ( 2, 3)( 4, 7)( 5,10)( 6,11)( 8,12)( 9,14),
                ( 1, 3)( 4,12)( 5, 8)( 6,13)( 7,10)( 9,15) ),
schreierVector := [ -1, 3, 4, 1, 3, 1, 2, 3, 2, 4, 1, 4, 1, 2, 2 ],
adjacencies := [ [ 2, 3, 13, 15 ] ],
representatives := [ 1 ],
isSimple := true,
names := [ [ [ 1, 2 ], [ 3, 5 ] ], [ [ 1, 2 ], [ 4, 5 ] ],
    [ [ 1, 2 ], [ 3, 4 ] ], [ [ 1, 3 ], [ 2, 5 ] ],
    [ [ 1, 4 ], [ 2, 5 ] ], [ [ 2, 5 ], [ 3, 4 ] ],
    [ [ 1, 5 ], [ 2, 3 ] ], [ [ 1, 5 ], [ 2, 4 ] ],
    [ [ 1, 5 ], [ 3, 4 ] ], [ [ 1, 4 ], [ 2, 3 ] ],
    [ [ 2, 3 ], [ 4, 5 ] ], [ [ 1, 3 ], [ 2, 4 ] ],
    [ [ 2, 4 ], [ 3, 5 ] ], [ [ 1, 3 ], [ 4, 5 ] ],
    [ [ 1, 4 ], [ 3, 5 ] ] ] )
gap> GlobalParameters(EP);
[ [ 0, 0, 4 ], [ 1, 1, 2 ], [ 1, 2, 1 ], [ 4, 0, 0 ] ]
gap> CompleteSubgraphsOfGivenSize(ComplementGraph(EP),5);
[ [ 1, 5, 9, 11, 12 ] ]

Sisyphos

This chapter describes the GAP accessible functions of the Sisyphos (Version 0.6) share library package for computing with modular group algebras of p-groups, namely a function to convert a p-group into Sisyphos readable format (see 52.2), several functions that compute automorphism groups of p-groups (see 52.4), functions that compute normalized automorphism groups as polycyclically presented groups (see 52.5, 52.6), functions that test two p-groups for isomorphism (see 52.7) and compute isomorphisms between p-groups (see 52.8), and a function to compute the element list of an automorphism group that is given by generators (see 52.10).

The algorithms require presentations that are compatible with a characteristic series of the group with elementary abelian factors, e.g. the p-central series. If necessary such a presentation is computed secretly using the p-central series, the computations are done using this presentation, and then the results are carried back to the original presentation. The check of compatibility is done by the function IsCompatiblePCentralSeries (see 52.3). The component isCompatiblePCentralSeries of the group will be either true or false then. If you know in advance that your group is compatible with a series of the kind required, e.g. the Jennings-series, you can avoid the check by setting this flag to true by hand.

52.1 PrintSISYPHOSWord

PrintSISYPHOSWord( P, a )

For a polycyclically presented group P and an element a of P, PrintSISYPHOSWord( P ,a ) prints a string that encodes a in the input format of the Sisyphos system.

The string "1" means the identity element, the other elements are products of powers of generators, the i-th generator is given the name gi.

     gap> g := SolvableGroup ( "D8" );;
     gap> PrintSISYPHOSWord ( g, g.2*g.1 );
     g1*g2*g3

52.2 PrintSisyphosInputPGroup

PrintSisyphosInputPGroup( P, name, type )

prints the presentation of the finite p-group P in a format readable by the Sisyphos system. P must be a polycyclically or freely presented group. In Sisyphos, the group will be named name. If P is polycyclically presented the i-th generator gets the name gi. In the case of a free presentation the names of the generators are not changed; note that Sisyphos accepts only generators names beginning with a letter followed by a sequence of letters, digits,underscores and dots. type must be either "pcgroup" or the prime dividing the order of P. In the former case the Sisyphos object has type pcgroup, P must be polycyclically presented for that. In the latter case a Sisyphos object of type group is created. For avoiding computations in freely presented groups, is neither checked that the presentation describes a p-group, nor that the given prime really divides the group order. See the Sisyphos manual [Wur93] for details.

gap> g:= SolvableGroup( "D8" );;
gap> PrintSisyphosInputPGroup( g, "d8", "pcgroup" );
d8 = pcgroup(2,
gens(
g1,
g2,
g3),
rels(
g1^2 = 1,
g2^2 = 1,
g3^2 = 1,
[g2,g1] = g3));
gap> q8 := FreeGroup ( 2 );;
gap> q8.relators := [q8.1^2,q8.2^2/q8.1^2,Comm(q8.2,q8.1)/q8.1^2];;
gap> PrintSisyphosInputPGroup ( q8, "q8", 2 );
#I  PQuotient: class 1 : 2
#I  PQuotient: Runtime : 0
q8 = group (minimal,
2,
gens(
f.1,
f.2),
rels(
f.1^4,
f.2^2*f.1^-2,
f.2^-1*f.1^-1*f.2*f.1^-1));

52.3 IsCompatiblePCentralSeries

IsCompatiblePCentralSeries( G )

If the component G.isCompatiblePCentralSeries of the polycyclically presented p-group G is bound, its value is returned, otherwise the exponent-p-central series of G is computed and compared to the given presentation. If the generators of each term of this series form a subset of the generators of G the component G.isCompatiblePCentralSeries is set to true, otherwise to false. This value is then returned by the function.

gap> g:= SolvableGroup( "D8" );;
gap> IsCompatiblePCentralSeries ( g );
true
gap> a := AbstractGenerators ( "a", 5 );;
gap> h := AgGroupFpGroup ( rec (
> generators := a,
> relators :=
[a[1]^2/(a[3]*a[5]),a[2]^2/a[3],a[3]^2/(a[4]*a[5]),a[4]^2,a[5]^2]));;
gap> h.name := "H";;
gap> IsCompatiblePCentralSeries ( h );
false
gap> PCentralSeries ( h, 2 );
[ H, Subgroup( H, [ a3, a4, a5 ] ), Subgroup( H, [ a4*a5 ] ),
  Subgroup( H, [  ] ) ]

52.4 Automorphisms

Automorphisms( P )
OuterAutomorphisms( P )
NormalizedAutomorphisms( P )
NormalizedOuterAutomorphisms( P )

all return a record with components

sizeOutG

the size of the group of outer automorphisms of P,

sizeInnG

the size of the group of inner automorphisms of P,

sizeAutG

the size of the full automorphism group of P,

generators

a list of group automorphisms that generate the group of all, outer, normalized or normalized outer automorphisms of the polycyclically presented p-group P, respectively. In the case of outer or normalized outer automorphisms, this list consists of preimages in Aut(P) of a generating set for Aut(P)/Inn(P) or Aut_{n}(P)/Inn(P), respectively.

gap> g:= SolvableGroup( "Q8" );;
gap> Automorphisms( g );
rec(
sizeAutG := 24,
sizeInnG := 4,
sizeOutG := 6,
generators :=
[ GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ b, a, c ] ),
 GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b, b, c ] ),
 GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a, b*c, c ] ),
 GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*c, b, c ] ) ] )
gap> OuterAutomorphisms( g );

    rec(
    sizeAutG := 24,
    sizeInnG := 4,
    sizeOutG := 6,
    generators :=
    [ GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ b, a, c ] ),
      GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b, b, c ] ) ] )

Note: If the component P.isCompatiblePCentralSeries is not bound it is computed

using IsCompatiblePCentralSeries.

52.5 AgNormalizedAutomorphisms

AgNormalizedAutomorphisms( P )

returns a polycyclically presented group isomorphic to the group of all normalized automorphisms of the polycyclically presented p-group P.

     gap> g:= SolvableGroup( "D8" );;
     gap> aut:= AgNormalizedAutomorphisms( g );
     Group( g0, g1 )
     gap> Size( aut );
     4

Note: If the component P.isCompatiblePCentralSeries is not bound it is computed

using IsCompatiblePCentralSeries.

52.6 AgNormalizedOuterAutomorphisms

AgNormalizedOuterAutomorphisms( P )

returns a polycyclically presented group isomorphic to the group of normalized outer automorphisms of the polycyclically presented p-group P.

     gap> g:= SolvableGroup( "D8" );;
     gap> aut:= AgNormalizedOuterAutomorphisms( g );
     Group( IdAgWord )

Note: If the component P.isCompatiblePCentralSeries is not bound it is computed

using IsCompatiblePCentralSeries.

52.7 IsIsomorphic

IsIsomorphic( P1 , P2 )

returns true if the polycyclically or freely presented p-group P1 and the polycyclically presented p-group P2 are isomorphic, false otherwise.

     gap> g:= SolvableGroup( "D8" );;
     gap> nonab:= AllTwoGroups( Size, 8, IsAbelian, false );
     [ Group( a1, a2, a3 ), Group( a1, a2, a3 ) ]
     gap> List( nonab, x -> IsIsomorphic( g, x ) );
     [ true, false ]

(The function Isomorphisms returns isomorphisms in case the groups are isomorphic.)

Note: If the component P2 .isCompatiblePCentralSeries is not bound it is computed

using IsCompatiblePCentralSeries.

52.8 Isomorphisms

Isomorphisms( P1 , P2 )

If the polycyclically or freely presented p-groups P1 and the polycyclically presented p-group P2 are not isomorphic, Isomorphisms returns false. Otherwise a record is returned that encodes the isomorphisms from P1 to P2 ; its components are

epimorphism

a list of images of P1 .generators that defines an isomorphism from P1 to P2 ,

generators

a list of image lists which encode automorphisms that together with the inner automorphisms generate the full automorphism group of P2

sizeOutG

size of the group of outer automorphisms of P2 ,

sizeInnG

size of the group of inner automorphisms of P2 ,

sizeOutG

size of the full automorphism group of P2 .

    gap> g:= SolvableGroup( "Q8" );;
    gap> nonab:= AllTwoGroups( Size, 8, IsAbelian, false );
    [ Group( a1, a2, a3 ), Group( a1, a2, a3 ) ]
    gap> nonab[2].name:= "im";;
    gap> Isomorphisms( g, nonab[2] );
    rec(
    sizeAutG := 24,
    sizeInnG := 4,
    sizeOutG := 6,
    epimorphism := [ a1, a2, a3 ],
    generators :=
    [ GroupHomomorphismByImages( im, im, [ a1, a2, a3 ],
       [ a2, a1, a3 ] ),
      GroupHomomorphismByImages( im, im, [ a1, a2, a3 ],
       [ a1*a2, a2, a3 ] ) ] )

(The function IsIsomorphic tests for isomorphism of p-groups.)

Note: If the component P2 .isCompatiblePCentralSeries is not bound it is computed

using IsCompatiblePCentralSeries.

52.9 CorrespondingAutomorphism

CorrespondingAutomorphism( G, w )

If G is a polycyclically presented group of automorphisms of a group P as returned by

AgNormalizedAutomorphisms (see 52.5) or AgNormalizedOuterAutomorphisms (see 52.6),

and w is an element of G then the automorphism of P corresponding to w is returned.

gap> g:= TwoGroup( 64, 173 );;
gap> g.name := "G173";;
gap> autg := AgNormalizedAutomorphisms ( g );
Group( g0, g1, g2, g3, g4, g5, g6, g7, g8 )
gap> CorrespondingAutomorphism ( autg, autg.2*autg.1^2 );
GroupHomomorphismByImages ( G173, G173, [ a1, a2, a3, a4, a5, a6 ],
[ a1, a2*a4, a3*a6, a4*a6, a5, a6 ] )

52.10 AutomorphismGroupElements

AutomorphismGroupElements( A )

A must be an automorphism record as returned by one of the automorphism routines or a list consisting of automorphisms of a p-group P.

In the first case a list of all elements of Aut(P) or Aut_{n}(P) is returned, if A has been created by Automorphisms or NormalizedAutomorphisms (see 52.4), respectively, or a list of coset representatives of Aut(P) or Aut_{n}(P) modulo Inn(P), if A has been created by

OuterAutomorphisms or NormalizedOuterAutomorphisms (see 52.4), respectively.

In the second case the list of all elements of the subgroup of Aut(P) generated by A is returned.

gap> g:= SolvableGroup( "Q8" );;
gap> outg:= OuterAutomorphisms( g );;
gap> AutomorphismGroupElements( outg );
[ GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a, b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ b, a, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b, b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b*c, a, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ b, a*b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a, a*b*c, c ] ) ]
gap> l:= [ outg.generators[2] ];
[ GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b, b, c ] ) ]
gap> AutomorphismGroupElements( l );
[ GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a, b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b, b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*c, b, c ] ),
  GroupHomomorphismByImages( Q8, Q8, [ a, b, c ], [ a*b*c, b, c ] ) ]

Bibliography

[AMW82] D[avid] G. Arrell, S[anjiv] Manrai, and M[ichael] F. Worboys. A procedure for obtaining simplified defining relations for a subgroup. In Campbell and Robertson [CR82], pages 155–159.

[AR84] D[avid] G. Arrell and E[dmund] F. Robertson. A modified Todd-Coxeter algorithm. In Atkinson [Atk84], pages 27–32.

[Art68] E[mil] Artin. Galoissche Theorie. Verlag Harri Deutsch, Frankfurt/Main, 1968.

[Atk84] Michael D. Atkinson, editor. Computational Group Theory, Proceedings LMS Symposium on Computational Group Theory, Durham 1982. Academic Press, 1984.

[Bau91] Ulrich Baum. Existenz und effiziente Konstruktion schneller Fouriertransformationen überauflösbarer Gruppen. Dissertation, Rheinische Friedrich Wilhelm Universität Bonn, Bonn, Germany, 1991.

[BC76] M[ichael] J. Beetham and C[olin] M. Campbell. A note on the Todd-Coxeter coset enumeration algorithm. Proc. Edinburgh Math. Soc. (2), 20:73–79, 1976.

[BC89] Richard P. Brent and Graeme L. Cohen. A new lower bound for odd perfect numbers. Math. Comput., 53:431–437, 1989.

[BC92] Wieb Bosma and John [J.] Cannon. Handbook of Cayley functions. Technical report, Department of Pure Mathematics, University of Sydney, Sydney, Australia, 1992.

[BCN89] A.E. Brouwer, A.M. Cohen, and A. Neumaier. Distance-Regular Graphs. Spinger, Berlin and New York, 1989.

[Bis89] Thomas Bischops. Collectoren im Programmsystem GAP. Diplomarbeit, Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany, 1989.

[Bur55] W[illiam S.] Burnside. Theory of Groups of Finite Order. Dover Publications, New York, 1955. Unabridged republication of the second edition, published in 1911.

[But82] Gregory Butler. Computing in permutation and matrix groups II: Backtrack algorithm. Math. Comput., 39:671–680, 1982.

[But85] Gregory Butler. Effective computation with group homomorphisms. J. Symbolic Computation, 1:143–157, 1985.

[Cam71] Harvey A. Campbell. An extension of coset enumeration. M. Sc. thesis, McGill University, Montreal, Canada, 1971.

[CCN^{+}85] J[ohn] H. Conway, R[obert] T. Curtis, S[imon] P. Norton, R[ichard] A. Parker, and R[obert] A. Wilson. Atlas of finite groups. Oxford University Press, 1985.

[Cel92] Frank Celler. Kohomologie und Normalisatoren in GAP. Diplomarbeit, Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany, 1992.

[CNW90] Frank Celler, Joachim Neubüser, and Charles R. B. Wright. Some remarks on the computation of complements and normalizers in soluble groups. Acta Applicandae Mathematicae, 21:57–76, 1990.

[Coh93] Henri Cohen. A Course in Computational Algebraic Number Theory, volume 138 of Graduate Texts in Mathematics. Spinger, Berlin and New York, 1993.

[Con90a] S[am] B. Conlon. Calculating characters of p-groups. J. Symbolic Computation, 9(5 & 6):535–550, 1990.

[Con90b] S[am] B. Conlon. Computing modular and projective character degrees of soluble groups. J. Symbolic Computation, 9(5 & 6):551–570, 1990.

[CR82] Colin M. Campbell and Edmund F. Robertson, editors. Groups-St. Andrews 1981, Proceedings of a conference, St. Andrews 1981, LMS Lecture Note Series, vol. 71. Cambridge University Press, 1982.

[Dix67] J[ohn] D. Dixon. High speed computations of group characters. Num. Math., 10:446–450, 1967.

[Dre69] Andreas [W. M.] Dress. A characterization of solvable groups. Math. Z., 110:213– 217, 1969.

[FJNT] V[olkmar] Felsch, D[avid] L. Johnson, J[oachim] Neubüser, and S[ergey] V. Tsaranov. The structure of certain Coxeter groups. To appear.

[Fra82] J[ames] S. Frame. Recursive computation of tensor power components. Bayreuther Math. Schr., 10:153–159, 1982.

[FS84] Volkmar Felsch and Günter Sandlöbes. An interactive program for computing subgroups. In Atkinson [Atk84], pages 137–143.

[GKP90] Ronald L. Graham, Donald E. Knuth, and Oren Patashnik. Concrete Mathematics. Addison-Wesley, 1990.

[Gla87] S[tephan] P. Glasby. Computational Approaches to the Theory of Finite Soluble Groups. Phd thesis, Department of Pure Mathematics, University of Sydney, Sydney, Australia, 1987.

[GS90] S[tephan] P. Glasby and M[ichael] C. Slattery. Computing intersections and normalizers in soluble groups. J. Symbolic Computation, 9:637–651, 1990.

[Hav69] George Havas. Symbolic and algebraic calculation. Basser Computing Dept., Technical Report 89, Basser Department of Computer Science, University of Sydney, Sydney, Australia, 1969.

[Hav74] George Havas. A Reidemeister-Schreier program. In M[ichael] F. Newman, editor, Proceedings of the Second International Conference on the Theory of Groups, Canberra, 1973, Lecture Notes in Mathematics, vol. 372, pages 347–356. Springer, Berlin, 1974.

[HIÖ89] Trevor [O.] Hawkes, I. M[artin] Isaacs, and M. Özaydin. On the Möbius function of a finite group. Rocky Mountain J. Math., 19:1003–1034, 1989.

[HKRR84] George Havas, P[eter] E. Kenne, J[ames] S. Richardson, and E[dmund] F. Robertson. A Tietze transformation program. In Atkinson [Atk84], pages 67–71.

[HN80] George Havas and M[ichael] F. Newman. Application of computers to questions like those of Burnside. In J[ens] L. Mennicke, editor, Burnside groups, Proceedings of a workshop, Bielefeld, Germany, 1977, Lecture notes in Mathematics, vol. 806, pages 211–230. Springer, Berlin, 1980.

[HS64] Marshall Hall, Jr. and James K. Senior. The Groups of Order 2^{n} (n ≤ 6). The Macmillan Company, New York, 1964.

[Hup67] B[ertram] Huppert. Endliche Gruppen I, volume 134 of Grundlehren Math. Wiss. Springer, Berlin, 1967.

[Ker91] Adalbert Kerber. Algebraic Combinatorics Via Finite Group Actions. BI-Wissen-schaftsverlag, Mannheim, 1991.

[Lau82] Reinhard Laue. Zur Konstruktion und Klassifikation endlicher auflösbarer Gruppen, volume 9 of Bayreuther Math. Schr. Universität Bayreuth, Bayreuth, Germany, 1982.

[Leh89a] Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany. SOGOS - A Program System for Handling Subgroups of Finite Soluble Groups, version 5.0, User’s reference manual, 1989.

[Leh89b] Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany. SPAS - Subgroup Presentation Algorithms System, version 2.5, User’s reference manual, 1989.

[Leo80] Jeffrey S. Leon. On an algorithm for finding a base and a strong generating set for a group given by generating permutations. Math. Comput., 35:941–974, 1980.

[LGS90] C[harles] R. Leedham-Green and L[eonard] H. Soicher. Collection from the left and other strategies. J. Symbolic Computation, 9(5 & 6):665–675, 1990.

[LLL82] A. K. Lenstra, H. W. Lenstra, and L. Lovász. Factoring polynomials with rational coefficients. Math. Ann., 261:513–534, 1982.

[LNS84] R[einhard] Laue, J[oachim] Neubüser, and U[lrich] Schoenwaelder. Algorithms for finite soluble groups and the SOGOS system. In Atkinson [Atk84], pages 105–135.

[LP91] Klaus Lux and Herbert Pahlings. Computational aspects of representation theory of finite groups. In G. O. Michler and C. R. Ringel, editors, Representation theory of finite groups and finite–dimensional algebras, volume 95 of Progress in Mathematics, pages 37–64. Birkhäuser, Basel, 1991.

[LPW93] Klaus Lux, R[ichard] A. Parker, and R[obert] A. Wilson. Modular Atlas of Finite Groups. To appear, 1993.

[McK90] B.D. McKay. nauty user’s guide (version 1.5), Technical report TR-CS-90-02. Australian National University, Computer Science Department, ANU, 1990.

[McL77] D. H. McLain. An algorithm for determining defining relations of a subgroup. Glasgow Math. J., 18:51–56, 1977.

[Mni92] Jürgen Mnich. Untergruppenverbände und auflösbare Gruppen in GAP. Diplomarbeit, Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany, 1992.

[Mon85] Peter L. Montgomery. Modular multiplication without trial division. Math. Comput., 44:519–521, 1985.

[Mur58] F[rancis] D. Murnaghan. The orthogonal and symplectic groups. Communications Series A 13, Dublin Inst. Adv. Studies, 1958.

[Neu67] Joachim Neubüser. Die Untergruppenverbände der Gruppen der Ordnungen ≤ 100 mit Ausnahme der Ordnungen 64 und 96. Habilitationsschrift, Universität Kiel, Kiel, Germany, 1967.

[Neu82] Joachim Neubüser. An elementary introduction to coset table methods in computational group theory. In Campbell and Robertson [CR82], pages 1–45.

[New77] M[ichael] F. Newman. Determination of groups of prime-power order. In R. A. Bryce, J. Cossey, and M[ichael] F. Newman, editors, Group theory, Proc. Miniconf., Austral. Nat. Univ., Canberra, 1975, Lecture Notes in Math., vol. 573, pages 73–84. Springer, Berlin, 1977.

[NO89] M[ichael] F. Newman and E[amonn] A. O´ Brien. A CAYLEY library for the groups of order dividing 128. In Group Theory, Proceedings of the 1987 Singapore Conference, Singapore 1987, pages 437–442. Walter de Gruyter, Berlin, New York, 1989.

[NPP84] J[oachim] Neubüser, H[erbert] Pahlings, and W[ilhelm] Plesken. CAS; design and use of a system for the handling of characters of finite groups. In Atkinson [Atk84], pages 195–247.

[O´ Br90] E[amonn] A. O´ Brien. The p-group generation algorithm. J. Symbolic Computation, 9:677–698, 1990.

[O´ Br91] E[amonn] A. O´ Brien. The groups of order 256. J. Algebra, 142, 1991.

[O´ Br92] E[amonn] A. O´ Brien. Isomorphism testing for p-groups. J. Symbolic Computation, 1992. To appear.

[O´ Br93] E[amonn] A. O´ Brien. Computing automorphism groups of p-groups. CANT ’92, 1993. submitted to Proceeding of CANT ’92 (Sydney).

[Ost86] Th[omas] Ostermann. Charaktertafeln von Sylownormalisatoren sporadischer einfacher Gruppen. Vorlesungen aus dem Fachbereich Mathematik 14, Universität Essen, Essen, Germany, 1986.

[Pah93] Herbert Pahlings. On the Möbius function of a finite group. Arch. Math., 1993. To appear.

[Ple90] W[ilhelm] Plesken. Additive decompositions of positive integral quadratic forms. The paper is available at Lehrstuhl B für Mathematik, Rheinisch Westfälische Technische Hochschule Aachen, may be it will be published in the near future, 1990.

[Poh87] M[ichael] Pohst. A modification of the lll reduction algorithm. J. Symbolic Computation, 4:123–127, 1987.

[Rob88] E[dmund] F. Robertson. Tietze transformations with weighted substring search. J. Symbolic Computation, 6:59–64, 1988.

[Sch90] Gerhard J. A. Schneider. Dixon’s character table algorithm revisited. J. Symbolic Computation, 9:601–606, 1990.

[Sho92] Mark W. Short. The Primitive Soluble Permutation Groups of Degree less than 256, volume 1519 of Lecture Notes in Mathematics. Springer, Berlin and Heidelberg, 1992.

[Sim70] Charles C. Sims. Computational methods in the study of permutation groups. In John Leech, editor, Computational Problems in Abstract Algebra, Proc. Conf. Oxford, 1967, pages 169–183. Pergamon Press, Oxford, 1970.

[Soi93] L[eonard] H. Soicher. GRAPE: a system for computing with graphs and groups. In L. Finkelstein and B. Kantor, editors, Proceedings of the 1991 DIMACS Workshop on Groups and Computation, DIMACS Series in Discrete Mathematics and Theoretical Computer Science. American Mathematical Society, 1993. To appear.

[Thi87] Peter Thiemann. SOGOS III - Charaktere und Effizienzuntersuchung. Diplomarbeit, Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany, 1987.

[VL84] M[ichael] R. Vaughan-Lee. An aspect of the nilpotent quotient algorithm. In Atkinson [Atk84], pages 75–84.

[VL90a] M[ichael] R. Vaughan-Lee. Collection from the left. J. Symbolic Computation, 9:725–733, 1990.

[VL90b] M[ichael] R. Vaughan-Lee. The restricted Burnside problem, volume 5 of London Mathematical Society monographs. Clarendon, Oxford, 1990.

[Wur93] Martin Wursthorn. SISYPHOS Computing in modular group algebras, Version 0.5. Math. Inst. B, 3. Lehrstuhl, Universität Stuttgart, 1993.

[Zum89] Matthias Zumbroich. Grundlagen einer Arithmetik in Kreisteilungskörpern und ihre Implementation in CAS. Dimplomarbeit, Lehrstuhl D für Mathematik, Rheinisch Westfälische Technische Hochschule, Aachen, Germany, 1989.

Index

887

Index

D_{n} lattices, 729 b_{N}, 350 c_{N}, 350 d_{N}, 350 e_{N}, 350 f_{N}, 350 g_{N}, 350 h_{N}, 350 i_{N}, 350 j_{N}, 350 k_{N}, 350 l_{N}, 350 m_{N}, 350 n_{k}, 350 r_{N}, 350 s_{N}, 350 t_{N}, 350 u_{N}, 350 v_{N}, 350 w_{N}, 350 x_{N}, 350 y_{N}, 350 .gaprc, 778, 786, 793 4.2BSD, 773 4.3BSD, 773

Abbreviating Section Names, 189 AbelianGroup, 579

for ag groups, 471 AbelianInvariants, 254

for ag groups, 471

for finitely presented groups, 432 AbelianInvariants for Ag Groups, 475 AbelianInvariantsNormalClosureFpGroup,

433 AbelianInvariantsNormalClosureFpGroupRrs,

433 AbelianInvariantsSubgroupFpGroup, 432 AbelianInvariantsSubgroupFpGroupMtc,

432 AbelianInvariantsSubgroupFpGroupRrs,

432 About Character Tables, 108 About Constants and Operators, 48 About Conventions, 46 About Defining New Domains, 136 About Defining New Group Elements,

148 About Defining New Parametrized Domains, 144 About Domains and Categories, 95 About Fields, 90 About Finitely Presented Groups and Presentations, 85

About First Steps, 47 About Functions, 52 About Further List Operations, 64 About GAP, 45 About Group Libraries, 122 About Groups, 69 About Help, 48 About Identical Lists, 55 About Lists, 53 About Loops, 62 About Mappings and Homomorphisms,

103 About Matrix Groups, 94 About Operations of Groups, 77 About Ranges, 61 About Records, 60 About Sets, 57 About Starting and Leaving GAP, 46 About Syntax Errors, 48 About the Implementation of Domains,

127 About Variables and Assignments, 50 About Vectors and Matrices, 58 About Writing Functions, 65 AbsInt, 326 absolute value of an integer, 326 AbstractGenerator, 422 AbstractGenerators, 422 accessing

list elements, 515 record elements, 622

Accessing Record Elements, 622 Add, 518 add

an element to a set, 537 elements to a list, 518 the elements of a list, 531

AddBase, 320

for vector space, 320 AddEdgeOrbit, 853 AddGenerator, 439 AddRelator, 439 AddSet, 537 Adjacency, 857 Advanced Methods for Dixon Schneider

Calculations, 674 AffineOperation, 498 Ag Group Functions, 479 Ag Group Operations, 468 Ag Group Records, 469 Ag Groups, 467 Ag Word Comparisons, 460 Ag Words, 459 Agemo, 242 AgGroup, 479

for Permutation Groups, 412 AgGroupFpGroup, 480 AgNormalizedAutomorphisms, 878 AgNormalizedOuterAutomorphisms, 878 AgOrbitStabilizer, 498 AgSubgroup, 488 AlternatingGroup, 579 An example, 873 An Example of a Computation in a

Domain, 198 An Example of Advanced Dixon

Schneider Calculations, 675 and, 618 AntiSymmetricParts, 714 ANU Pq, 821 ANU pq Package, 800 Append, 518 append

elements to a list, 518 one string to another, 549 to a file, 192

AppendTo, 192 Arrangements, 638 AsGroup, 240

for ag groups, 471 AsGroup for Ag Groups, 474 assignment

to a list, 516 to a record, 622 variable, 173 Assignments, 173 AssignVertexNames, 854 AssociatedPartition, 645 Associates, 215

for gaussians, 358 associativity, 172 AsSubgroup, 242 Atari ST, 788 ATLAS irrationalities, 350 ATLAS Tables, 757 atomic irrationalities, 350 augmented coset table, 440, 441 AutGroupGraph, 872 automorphism group

of a character table, 696 of a field, 228 of a matrix, 695 of number fields, 366 AutomorphismGroupElements, 880 Automorphisms, 877 automorphisms

of p groups, 826 Automorphisms of p-groups, 877

backslash, 547 Base, 319

of vector space, 319 base

of a number field, 368 of vector space, 557

Base for Permutation Groups, 408 Base for Row Spaces, 562 BaseMat, 571 Bell, 636 Bernoulli, 647 Bicomponents, 863 Binomial, 636 BipartiteDouble, 868 blank, 167 blist, 541 BlistList, 541 Blocks, 311

for Permutation Groups, 416 BNF, 179 body, 177 Boolean Lists, 541 Booleans, 617 bound, 169 Brauer Table Records, 667 Brauer tables

format, 667 BrauerTableOps, 669 Break Loops, 185 Browsing through the Sections, 188 Bruhat, 843 BSD, 773

candidates

for permutation characters, 719 CanonicalAgWord, 463 Carmichaels lambda function, 336 Cartan matrices, 837

direct sum of, 837 CartanMat, 837 Cartesian, 526 Cartesian product, 679 CAS Tables, 762 CAS tables,CAS format,CAS, 701 CentralCompositionSeriesPPermGroup,

411 Centralizer, 243

for ag groups, 471 for matrix groups, 576 for Permutation Groups, 412

CentralWeight, 461 Centre, 243 CF, 363 Cgs, 488 ChangeCollector, 483 Changing Presentations, 439 character

permutation, 261 character table

display, 690 Character Table Libraries, 755 Character Table Records, 663 Character Tables, 661 character tables, 670, 755 access to, 671 ATLAS, 757 calculate, 671 CAS, 759, 762 computation, 674 conventions, 669 format, 663 generic, 703, 704, 706 libraries of, 755 of groups, 671 sort, 692–694

characteristic

of a finite field element, 378 characteristic polynom

of a field element, 230 Characters, 707 characters

induce from cyclic subgroups, 718 induced, 717 inflate, 717 irreducible differences of, 716 permutation, 719 reduced, 712 restricted, 716 scalar product of, 707, 708 sort, 692 symmetrisations of, 713, 714 tensor products of, 713

Characters of Finite Polycyclic Groups,

686 CharDegAgGroup, 684 CharFFE, 378 CharHeckeRepresentation, 847 CharPol, 230 CharTable, 671 CharTableCollapsedClasses, 683 CharTableDirectProduct, 679 CharTableFactorGroup, 677 CharTableIsoclinic, 682 CharTableNormalSubgroup, 678 CharTableOps, 669 CharTablePGroup, 686 CharTableRegular, 681 CharTableSpecialized, 706 CharTableSplitClasses, 682 CharTableSSGroup, 684 CharTableWreathSymmetric, 679 CheckFixedPoints, 746 CheckHeckeDefiningRelations, 847 CheckPermChar, 745 chinese remainder, 327 ChineseRem, 327 class functions, 735 class multiplication coefficient, 687, 688 class names, 687 classes

collapse, 718

of cyclic subgroup, 689 real, 689 roots of, 689

ClassMultCoeffCharTable, 687 ClassNamesCharTable, 687 ClassNamesTom, 657 ClassOrbitCharTable, 689 ClassRootsCharTable, 689 ClassStructureCharTable, 688 ClassTypesTom, 657 clone

an object, 631, 632 Closure, 244 coefficient

binomial, 636 Coefficients, 322

in vector space, 322 coefficients

for cyclotomics, 348 Coefficients for Number Fields, 368 CoeffsCyc, 348 CollapsedAdjacencyMat, 862 CollapsedCompleteOrbitsGraph, 869 CollapsedIndependentOrbitsGraph, 869 CollapsedMat, 718 Collected, 527 CollectorlessFactorGroup, 491 Combinations, 638 Combinatorics, 635 CombinatoricSplit, 675 Comm

for group elements, 236 for words, 423 comments, 167 CommutativeDiagram, 738 CommutatorFactorGroup, 250

for finitely presented groups, 432 CommutatorSubgroup, 244 for ag groups, 471

CommutatorSubgroup for Ag Groups,

475 comparison

of double cosets, 279 of right cosets, 274

Comparisons, 171 comparisons

of booleans, 617

of finite field elements, 376

of integers, 324 of lists, 522 of polynomials, 386 of rationals, 343 of strings, 550

Comparisons of Booleans, 617 Comparisons of Cyclotomics, 348 Comparisons of Domains, 200 Comparisons of Field Elements, 227 Comparisons of Finite Field Elements,

376 Comparisons of Gaussians, 355 Comparisons of Group Elements, 236 Comparisons of Integers, 324 Comparisons of Lists, 522 Comparisons of Mappings, 597 Comparisons of Permutations, 398 Comparisons of Polynomials, 386 Comparisons of Rationals, 343 Comparisons of Records, 625 Comparisons of Ring Elements, 211 Comparisons of Strings, 550 Comparisons of Unknowns, 373 Comparisons of Words, 423 Complement, 505 Complementclasses, 506 ComplementConjugatingAgWord, 507 ComplementGraph, 865 Complements, 505 CompleteGraph, 852 CompleteSubgraphs, 871 CompleteSubgraphsOfGivenSize, 871 Component, 862 Components, 863 CompositionLength, 461 CompositionMapping, 606

for Frobenius automorphisms, 381 for GroupHomomorphismByImages,

290 for GroupHomomorphismByImages

for permutation groups, 417 CompositionMaps, 736 CompositionSeries, 251

for Permutation Groups, 412 CompositionSubgroup, 492 Concatenation, 525 concatenation

of lists, 525 of strings, 549

ConcatenationString, 549 conductor, 364 Congruences, 743 Conjugacy Class Records, 263 Conjugacy Classes, 261 ConjugacyClass, 262 ConjugacyClasses, 261

for ag groups, 471 for matrix groups, 576

ConjugacyClassesSubgroups, 263 ConjugacyClassSubgroups, 270 conjugate

of a group, 293 of a group element, 236 of a word, 423

Conjugate Subgroup

of Permutation Group, 412 Conjugates, 229

for finite fields, 382 for gaussians, 358

conjugates

of a field element, Galois, 229 ConjugateSubgroup, 245

for ag groups, 471 ConjugateSubgroups, 272 ConjugationGroupHomomorphism, 289 ConsiderKernels, 744 ConsiderSmallerPowermaps, 744 ConsiderTableAutomorphisms, 747 Construction of Ag Groups, 468 ContainedCharacters, 731 ContainedDecomposables, 730 ContainedMaps, 737 ContainedPossibleCharacters, 732 ContainedPossibleVirtualCharacters, 733 ContainedSpecialVectors, 731 Contents of the Table Libraries, 755 Conventions for Character Tables, 669 convert

a finite field element to an integer,

379 to a list, 514 to a set, 536 to a string, 549 to an integer, 326

CoprimeComplement, 507 Copy, 631 copy

an object, 631, 632 Copyright of GAP for MS-DOS, 781 Copyright of GAP for TOS, 789 Core, 245 CorrespondingAutomorphism, 879 Coset, 274

for Permutation Groups, 412 coset

double, 278 left, 277 right, 274

Cosets

for Permutation Groups, 412 cosets

double, 278 left, 276 right, 273

Cosets of Subgroups, 273 CosetTableFpGroup, 434 cross product of lists, 526 Cycle, 299 CycleLength, 299 CycleLengths, 300 Cycles, 300 CyclicExtensionsTom, 657 CyclicGroup, 579

for ag groups, 471 CyclicGroup for Ag Groups, 476 cyclotomic field elements, 345 Cyclotomic Field Records, 363 Cyclotomic Integers, 346 Cyclotomics, 345 data type

unknown, 371 DEC, 708 DecodeTree, 453 DecomposedFixedPointVector, 653 DecomposedLeftCells, 844 Decomposition, 708 decomposition matrix, 708 Decreased, 728 default field

for cyclotomics, 364 default functions, 196 default ring

for cyclotomic integers, 365 DefaultField, 226 DefaultField and Field for Cyclotomics,

364 DefaultRing, 210 DefaultRing and Ring for Cyclotomic

Integers, 365 Degree, 389 degree

of a finite field element, 379 DegreeFFE, 379 DegreeOperation, 303 Denominator, 342 denominator

of a rational, 342 Depth, 461 Derangements, 641 Derivative, 390 DerivedSeries, 250

for ag groups, 471 DerivedSubgroup, 246 DeterminantMat, 569 DiagonalizeMat, 572 Diameter, 858 Difference, 205 difference

of boolean lists, 544 of gaussians, 356 of records, 627

DifferenceAgWord, 463 DifferenceBlist, 544 DihedralGroup, 579 Dimension, 321

of vector space, 321 dimension

of vector space, 557 DimensionsLoewyFactors, 254 DimensionsMat, 569 direct product, 679 DirectedEdges, 857 DirectProduct, 281

for ag groups, 471 for groups, 281

DirectProduct for Ag Groups, 477 DirectProduct for Groups, 281 DirectSumCartanMat, 837 discrete logarithm

of a finite field element, 380 dispatcher functions, 196 Dispatchers, 196 DisplayCharTable, 690 DisplayTom, 654 Distance, 858 DistanceGraph, 865 DistanceSet, 863 DistanceSetInduced, 864 divisors

of an integer, 332 DivisorsInt, 332 Dixon Schneider, 671 DixonInit, 674 DixonSplit, 675 DixontinI, 674 DnLattice, 729 DnLatticeIterative, 729 do, 176 Domain, 199 Domain Functions for Number Fields,

369 domain record

for right cosets, 276 Domain Records, 196 Domains, 195 Double Coset Records, 280 DoubleCoset, 278 DoubleCosetGroupOps, 279 DoubleCosets, 278 doublequotes, 547

E, 345 EB, 350 EC, 350 ED, 350 EdgeGraph, 866 EdgeOrbitsGraph, 851 Edit, 194 EE, 350 EF, 350 EG, 350 EH, 350 EI, 350 Eigenvalues, 711 EJ, 350 EK, 350 EL, 350 ElementaryAbelianGroup, 579

for ag groups, 471 ElementaryAbelianGroup for Ag Groups,

477 ElementaryAbelianSeries, 251

for ag groups, 471 for Permutation Groups, 412

ElementaryDivisorsMat, 572 ElementOrdersPowermap, 752 Elements, 200

for ag groups, 469 for Conjugacy Classes, 262 for conjugacy classes of subgroups,

271 for double cosets, 279 for finite fields, 381 for finitely presented groups, 430 for groups, 292 for Permutation Groups, 412 for right cosets, 274 for vector spaces, 319

elements

of a domain, 200 Elements for Ag Groups, 470 Elements for Groups, 292 elif, 174 EliminatedWord, 426 else, 174 EM, 350 Embedding

into direct products, 281 into semidirect products, 282

embeddings of lattices, 725 end, 177 Enlarging Lists, 521 Environment, 183 environment, 177 equality

for field homomorphisms, 233 of ag words, 460 of gaussians, 355 of group elements, 236 of group homomorphisms, 287 of records, 625

ER, 350 Error, 185 errors

syntax, 183 ES, 350 ET, 350 EU, 350 EuclideanDegree, 217

for gaussians, 358

for polynomials, 392, 394 EuclideanQuotient, 218

for gaussians, 358 for polynomials, 392

EuclideanRemainder, 217 for gaussians, 358 for polynomials, 392

Eulers totient function, 336 EV, 350 evaluation, 168 EW, 350 EX, 350 Example Functions, 583 Example, normal closure, 508 Examples of Generic Character Tables,

704 Examples of the ATLAS format for GAP

tables, 759 Exec, 194 execution, 172 Exponent, 255 exponent

of the prime residue group, 336 ExponentAgWord, 465 Exponents, 496 ExponentsAgWord, 465 ExponentsPermSolvablePermGroup, 412 ExponentSumWord, 425 Expressions, 168 ExtendedIntersectionSumAgGroup, 500 ExtendStabChain, 407 Extract, 727 Extraction Functions, 584 EY, 350

factor group

table of, 677 Factor Groups of Ag Groups, 490 FactorArg, 491 FactorGroup, 249

for ag groups, 490 FactorGroup for AgGroups, 490 FactorGroupElement, 249 Factorial, 635 Factorization, 255 factorization

of an integer, 332 Factorization for PQp, 487 Factors, 216

for gaussians, 358 for polynomials, 392, 394

FactorsAgGroup, 497 FactorsInt, 332 Faithful Permutation Characters, 723 features

under MS-DOS, 786 under TOS, 793 under UNIX, 778

Features of GAP for MS-DOS, 786 Features of GAP for TOS, 793 Features of GAP for UNIX, 778 fi, 174 Fibonacci, 646 Field, 226 field

cyclotomic, 363 finite, 375 for cyclotomics, 364 galois, 375 number, 362

Field Functions for Finite Fields, 382 Field Functions for Gaussian Rationals,

358 Field Functions for Rationals, 344 Field Homomorphisms, 232 field homomorphisms

Frobenius, 381 Field Records, 234 Fields, 225 Fields over Subfields, 227 file

append to a, 192

load a, 190

load a library, 191 log input to a, 192 log to a, 192 print to a, 191 read a, 190 read a library, 191

FileNameCharTable, 766 Filtered, 528 find

an element in a list, 523

an element in a sorted list, 524 Finite Field Elements, 375 Finite Fields, 375 Finite Polycyclic Groups, 467 Finitely Presented Groups, 429 finiteness test

for domains, 202 First, 529 FirstClassPQp, 487 FirstNameCharTable, 766 FittingSubgroup, 246 Flat, 525 For, 176 for, 176 for loop, 176 ForAll, 528 ForAny, 529 Format of Sections, 187 FpGroup

for ag groups, 471 FpGroup for Ag Groups, 479 FpGroupPresentation, 436 Frame, 714 FreeGroup, 430 Frobenius Schur indicator, 711 FrobeniusAutomorphism, 381 ftp, 767 function, 177 Function Calls, 170 Functions, 177 Functions depending on nauty, 872 Functions to construct and modify

graphs, 850 Functions to construct new graphs from

old, 864 Functions to determine regularity

properties of graphs, 860 Functions to inspect graphs, vertices and

edges, 855 fusion

store, 698 fusion map

get, 697 fusion of classes, 683 FusionCharTableTom, 656 FusionConjugacyClasses, 698 fusions, 741 FusionsAllowedByRestrictions, 748

galois automorphism, 349 galois conjugate

unique, 351 galois conjugate characters, 352 Galois conjugates

of a field element, 229 galois conjugation, 349 galois field, 375 Galois group

of a field, 228 GaloisCyc, 349 GaloisField, 380 GaloisGroup, 228

for finite fields, 382 GaloisGroup for Number Fields, 366 GaloisMat, 352 GAP for MS-DOS, 780 GAP for TOS, 788 GAP for UNIX, 773 gap.rc, 786, 793 GAPChars, 699 Gaussians, 355 Gcd, 220

for polynomials, 392, 394 GcdRepresentation, 221 GeneralLinearGroup, 579 GeneralUnitaryGroup, 579 Generating Systems of Ag Groups, 487 generator

of the prime residue group, 337, 338 generators

of Galois group, 365 GeneratorsPrimeResidues, 365 Generic Character Tables, 703 generic character tables, 670, 755 GeodesicsGraph, 868 GetFusionMap, 697 Getting and Installing GAP, 767 Getting Character Tables, 670 Getting GAP, 767 GF, 380 Girth, 859 GlobalParameters, 861 Grape, 849 GRAPE Package, 807 Graph, 850 Group, 239

for ag groups, 471 Group Constructions, 281 Group Elements, 236 Group for Ag Groups, 475 Group Functions for Ag Groups, 471 Group Functions for Finitely Presented

Groups, 431 Group Functions for Matrix Groups, 576 Group Functions for Permutation Groups, 412 Group Homomorphisms, 285 group homomorphisms by images, 290 conjugation, 289 inner, 289 natural, 288 operation, 310 Group Libraries, 579 Group Records, 293 GroupHomomorphismByImages, 290 GroupHomomorphismsByImages for permutation groups, 417

Groups, 235

HallConjugatingWordAgGroup, 508 HallSubgroup, 492 Hecke, 846 HeckeCharTable, 848 HeckeReflectionRepresentation, 847 Help, 187 help

abbreviating, 189 browsing, 188 format, 187 index, 189 redisplaying, 189 scrolling, 187

Help Index, 189 Homomorphisms, 611 homomorphisms

by images, group, 290 conjugation, group, 289 Frobenius, field, 381 inner, group, 289 natural, group, 288 of fields, 232 of groups, 285 operation, group, 310

Homomorphisms for Permutation

Groups, 417 How to Extend a Table Library, 765 HP-UX, 773

IdempotentsTom, 657 Identical Lists, 519 Identical Records, 623 Identifiers, 168 IdentityMapping, 608 IdentityMat, 568 IdWord, 421 If, 174 if, 174 if statement, 174 Igs, 488 Image, 600

for blocks homomorphisms, 417 for field homomorphisms, 233 for Frobenius automorphisms, 381 for group homomorphisms, 287 for GroupHomomorphismByImages,

290 for GroupHomomorphismByImages

for permutation groups, 417 for OperationHomomorphism, 310 for transitive constituent

homomorphisms, 417 Images, 602

for blocks homomorphisms, 417 for field homomorphisms, 233 for group homomorphisms, 287 for GroupHomomorphismByImages,

290 for transitive constituent homomorphisms, 417

ImagesRepresentative, 603 In, 523 in

for ag groups, 469

for Conjugacy Classes, 262

for conjugacy classes of subgroups,

271 for domains, 202 for finite fields, 381 for finitely presented groups, 430 for gaussians, 357 for matrix groups, 575 for records, 628 for row spaces, 562

In for Records, 628 IncludeIrreducibles, 675 IndependentSet, 864 Indeterminate, 385 Indeterminateness, 740 Index, 255

for finitely presented groups, 432 Indicator, 711 Indirected, 751 Induced, 717 InducedCyclic, 718 InducedSubgraph, 864 Inequalities, 721 Inflated, 717 InfoLattice1, 270 InitClassesCharTable, 686 InitFusion, 745 initialize classlengths, 686 InitPowermap, 742 InitPQp, 486 InnerAutomorphism, 289 installation, 767

under MS-DOS, 782 under TOS, 789 under UNIX, 774 under Windows, 782

Installation of GAP for MS-DOS, 782 Installation of GAP for TOS, 789 Installation of GAP for UNIX, 774 Installing the ANU pq Package, 801 Installing the GRAPE Package, 808 Installing the NQ Package, 812 Installing the SISYPHOS Package, 814 Installing the Weyl Package, 817 Installing the XGap Package, 819 Int, 326 IntCyc, 347 integer part of a quotient, 325 Integers, 323 Integral Bases for Number Fields, 367 IntegralizedMat, 709 InterpolatedPolynomial, 391 IntersectBlist, 544 Intersection, 203

for ag groups, 469 for double cosets, 279 for finite fields, 381 for finitely presented groups, 430 for groups, 292 for matrix groups, 575 for Permutation Groups, 412 for right cosets, 274 for row spaces, 562 of vector spaces, 319

intersection of boolean lists, 543, 544 of domains, 203 of sets, 538

Intersection for Ag Groups, 470 Intersection for Groups, 292 IntersectionBlist, 543 Intersections of Ag Groups, 499 IntersectionsTom, 655 IntersectionSumAgGroup, 501 IntersectSet, 538 IntFFE, 379 InverseClassesCharTable, 687 InverseMap, 736 InverseMapping, 608

for GroupHomomorphismByImages,

290 InverseMatMod, 709 irrationalities, 345 IrreducibleDifferences, 716 IsAbelian, 256 IsAgGroup, 480 IsAgWord, 462 IsAssociated, 214

for gaussians, 358 IsAutomorphism, 614 IsBijection, 596 IsBipartite, 859 IsBlist, 542 IsBool, 619 IsBound, 630 IsCentral, 256 IsCharTable, 668 IsCommutativeRing, 212 IsCompatiblePCentralSeries, 876 IsCompleteGraph, 860 IsConjugacyClass, 262 IsConjugacyClassSubgroups, 271 IsConjugate, 256 IsConnectedGraph, 859 IsConsistent, 481 IsCoset, 274 IsCyc, 347 IsCycInt, 347 IsCyclic, 257

for ag groups, 471 IsCyclic for Ag Groups, 476 IsCyclicTom, 656 IsCyclotomicField, 362 IsDistanceRegular, 861 IsDoubleCoset, 278 IsEdge, 857 IsElementAgSeries, 495 IsElementaryAbelian, 257 IsElementaryAbelianAgSeries, 481 IsEndomorphism, 614 IsEpimorphism, 613 IsEqualSet, 536 IsEquivalentOperation, 315 IsEuclideanRing, 213 IsFFE, 378 IsField, 225 IsFieldHomomorphism, 232 IsFinite, 202

for ag groups, 471 for double cosets, 279 for right cosets, 274 for vector spaces, 319

IsFixpoint, 302 IsFixpointFree, 302 IsGaussInt, 357 IsGaussRat, 357 IsGeneralMapping, 594 IsGraph, 855 IsGroup, 241 IsGroupElement, 237 IsGroupHomomorphism, 286 IsHomomorphism, 611

for fields, 232 for groups, 286

IsIdentical, 520 IsInjective, 595

for field homomorphisms, 233 for group homomorphisms, 287

IsInt, 326 IsIntegralRing, 212 IsIrreducible, 216

for gaussians, 358 IsIsomorphic, 878 IsIsomorphicGraph, 873 IsIsomorphicPGroup, 828 IsIsomorphism, 613 IsLaurentPolynomialRing, 392 IsLeftCoset, 277 IsList, 514 IsLoopy, 856 IsMapping, 594

for GroupHomomorphismByImages,

290 for GroupHomomorphismByImages for permutation groups, 417

IsMat, 567 IsMonomorphism, 612 IsNilpotent, 258

for ag group, 471 IsNormal, 258

for ag groups, 471 IsNormal for Ag Groups, 476 IsNormalized, 489 IsNullGraph, 860 IsNumberField, 362 isoclinic table, 682 Isomorphisms, 879 IsParent, 239 IsPerfect, 258

for ag group, 471 IsPerm, 399 IsPermChar, 719 IsPermGroup, 403 IsPNilpotent, 495 IsPolynomial, 386 IsPolynomialRing, 391 IsPrime, 216

for gaussians, 358 IsPrimeInt, 330 IsPrimePowerInt, 331 IsPrimitive, 312 IsPrimitiveRootMod, 337 IsRange, 554 IsRat, 341 IsRec, 630 IsRegular, 305 IsRegularGraph, 861 IsRightCoset, 274 IsRing, 209 IsRowSpace, 561 IsSemiRegular, 306

for Permutation Groups, 416 IsSet, 536 IsSimple, 259 IsSimpleGraph, 857 IsSolvable, 259

for Permutation Groups, 412 IsString, 551 IsSubgroup, 260

for ag groups, 471 IsSubgroup for Ag Groups, 476 IsSubnormal, 260 IsSubset, 203

for ag groups, 469 for groups, 292

IsSubsetBlist, 543 IsSubspace, 319 IsSurjective, 595

for field homomorphisms, 233 for group homomorphisms, 287

IsTransitive, 303

IsTrivial

for groups, 260 IsTrivial for Groups, 260 IsUniqueFactorizationRing, 213 IsUnit, 214

for gaussians, 358 for matrix rings, 574

IsUnknown, 372 IsVector, 559 IsVectorSpace, 318 IsVertex, 855 IsWord, 424 iterate a function over a list, 532 Iterated, 532

Jacobi, 338 JenningsSeries, 252 JohnsonGraph, 852

KazhdanLusztigPolynomial, 843 Kernel, 615

for blocks homomorphisms, 417 for fields, 233 for groups, 286 for OperationHomomorphism, 310

KernelChar, 710 KernelFieldHomomorphism, 233 KernelGroupHomomorphism, 286 Keywords, 168 KLCoefficient, 844 Kronecker product, 679 KroneckerProduct, 569

Lambda, 336 larger prime, 331 LargestMovedPointPerm, 399 last, 183 Lattice, 264 lattice base reduction, 723–725 LaurentPolynomialRing, 391 Layers, 863 Lcm, 221 LeadingCoefficient, 390 LeadingExponent, 462 LeftCellRepresentation, 846 LeftCells, 845 LeftCoset, 277 LeftCosets, 276 LeftQuotient

for group elements, 236

for words, 423 Legendre, 338 Length, 516 length

of a list, 516 of a string, 551 of a word, 425

LengthString, 551 LengthWord, 425 LenstraBase, 367 Lexical Structure, 166 libraries of character tables, 755 library of character tables, 757, 759, 762 library tables, 670, 755 add, 765 generic, 703 Line Editing, 185 LinearCombination, 321 in vector space, 321 LinearIndependentColumns, 709 LinearOperation, 499 List, 514 list

of primes, 330 List Assignment, 516 List Elements, 515 ListBlist, 542 ListPerm, 400 Lists, 513 lists

boolean, 541 ListStabChain, 409 LLL, 725 LLL algorithm

for characters, 725 for Gram matrices, 724 for vectors, 723 LLLReducedBasis, 723 LLLReducedGramMat, 724 load

a file, 190

a library file, 191 local, 177 LocalParameters, 861 log

to a file, 192 log input

to a file, 192 logarithm

of a finite field element, discrete, 380 logarithm of an integer, 327 LogFFE, 380 logical, 617 logical operations, 618 LogInputTo, 192 LogInt, 327 LogTo, 192 LongestWeylWord, 841 loop

for, 176

read eval print, 183 repeat, 175 while, 175

LowerCentralSeries, 252

for ag groups, 471 LowIndexSubgroupsFpGroup, 435 Lucas, 646

Main Loop, 183 MAKElb11, 699 MakeStabChain, 406 MakeStabChainStrongGenerators, 408 map, 514

composition, 736

indeterminateness, 740 indirection by a, 751 inverse of a, 736 parametrize, 737 parametrized, 735

MappedWord, 427 Mapping Functions for Field

Homomorphisms, 233 Mapping Functions for Group

Homomorphisms, 287 Mapping Records, 609 MappingByFunction, 609 MappingPermListList, 401 Mappings, 593 maps, 735 Maps and Parametrized Maps, 735 Marks, 652 MatAutomorphisms, 695 MatClassMultCoeffsCharTable, 688 MatGroupAgGroup, 482 MatRepresentationsPGroup, 685 Matrices, 565 matrix, 565 matrix automorphisms, 750, 751 Matrix Group Records, 577 Matrix Groups, 575 Matrix Representations of Finite

Polycyclic Groups, 685 Matrix Rings, 573 MatScalarProducts, 708 MatTom, 653 MaximalElement, 497 Maximum, 531 maximum

of a list, 531 of integers, 531

membership test for ag groups, 469 for domains, 202 for finite fields, 381 for gaussians, 357 for records, 628

Membership Test for Domains, 202 membershiptest

for groups, 293 MergedCgs, 489 MergedIgs, 490 mersenne primes, 333 minimal polynom

of a field element, 231 MinimalGeneratingSet, 495 Minimum, 532 minimum

of a list, 532 of integers, 532

MinPol, 231

MinusCharacter, 714

MOC

interface to, 699, 701 MOCChars, 699 MOCTable, 699 Mod, 219 Modified Todd-Coxeter, 441 modular roots, 339 Moebius inversion function, 333 MoebiusMu, 333 MoebiusTom, 656 Monomial Representations of Finite Polycyclic Groups, 685

More about Ag Groups, 467 More about Ag Words, 459 More about Boolean Lists, 545 More about Cyclotomics, 345 More about Dispatchers, 197 More about Generic Character Tables,

703 More about Groups and Subgroups, 238 More about Maps and Parametrized

Maps, 735 More about Ranges, 554 More about Sets, 539 More about Tables of Marks, 649 More about Vectors, 559 MS-DOS

features, 786 installation, 782 options, 786

MSDOS, 780 multiplicative order of an integer, 337 multiply

the elements of a list, 531 multisets, 535 Multivariate Polynomials, 385 Murnaghan components, 714, 715

NaturalHomomorphism, 288 NewGroupGraph, 870 newline, 167 NextClassPQp, 487 NextPrimeInt, 331 NF, 362 NilpotentQuotient, 811 NK, 350 NofCyc, 348 Norm, 230

for finite fields, 382 for gaussians, 358

norm

of a field element, 230 normal subgroup

table of, 678 NormalBaseNumberField, 368 NormalClosure, 246 NormalIntersection, 247

for ag groups, 471 Normalize, 489 Normalized, 489 NormalizedAutomorphisms, 877 NormalizedOuterAutomorphisms, 877 Normalizer, 247

for ag groups, 471 for finitely presented groups, 432 for matrix groups, 576 for Permutation Groups, 412

Normalizer for Ag Groups, 475 NormalizerTom, 655 NormalSubgroups, 272 NormedVector, 559 not, 618 NotifyCharTable, 765 NQ Package, 811 NrCombinations, 638 NrDerangements, 641 NrOrderedPartitions, 643 NrPartitions, 642 NrPartitionsSet, 642 NrPermutationsList, 640 NrPolyhedralSubgroups, 689 NrRestrictedPartitions, 644 NrSubs, 652 NrTuples, 640 NrUnorderedTuples, 639 NullGraph, 852 NullMat, 568 NullspaceMat, 571 Number, 527 number

Bell, 636 binomial, 636 of divisors of an integer, 333 of elements in a list, 527 Stirling, of the first kind, 637 Stirling, of the second kind, 637

Number Field Records, 362 number fields

Galois group, 366 Number Theory, 335 NumberConjugacyClasses, 496 Numerator, 342 numerator

of a rational, 342

od, 176 One Cohomology Group, 502 OneCoboundaries, 503 OneCocycles, 504 OnLeft, 298 OnLeftCosets, 298 OnLines, 298 OnPairs, 298 OnPoints, 298 OnRight, 298 OnRightCosets, 298 OnSets, 298 OnTuples, 298 Operation, 309 OperationCosetsFpGroup, 435 OperationHomomorphism, 310

for blocks, 417

for transitive constituents, 417 Operations, 172 operations

for booleans, 618 for groups, 293 for integers, 324 for lists, 522 for polynomials, 387 for rationals, 343 for vectors, 558

Operations for Booleans, 618 Operations for Cyclotomics, 349 Operations for Field Elements, 228 Operations for Finite Field Elements, 377 Operations for Gaussians, 356 Operations for Group Elements, 236 Operations for Groups, 293 Operations for Integers, 324 Operations for Lists, 522 Operations for Mappings, 598 Operations for Matrices, 565 Operations for Permutations, 398 Operations for Polynomials, 387 Operations for Rationals, 343 Operations for Records, 627 Operations for Ring Elements, 211 Operations for Unknowns, 373 Operations for Vectors, 558 Operations for Words, 423 Operations of Groups, 297 Operations of Permutation Groups, 416 operations record, 196 Operations Records for Character

Tables, 669 operators

for cyclotomics, 348, 349 options, 767

under MS-DOS, 786 under TOS, 793 under UNIX, 778

or, 618 Orbit, 306

for ag groups, 471 Orbitalgorithms of Ag Groups, 497 OrbitalGraphIntersectionMatrices, 862 OrbitFusions, 749 OrbitLength, 307 OrbitLengths, 309 OrbitPowermaps, 750 Orbits, 308 Order, 237

for finitely presented groups, 432 order

of a finite field element, 379 of a group element, 237 of the prime residue group, 336

OrderedPartitions, 643

OrderFFE, 379 OrderGraph, 855 ordering

of ag words, 460 of gaussians, 355 of group elements, 236 of group homomorphisms, 287 of records, 625

OrderMat, 570 OrderMod, 337 Organization of the Table Libraries, 763 OrthogonalComponents, 714 OrthogonalEmbeddings, 725 OrthogonalEmbeddingsSpecialDimension,

725 Other Operations, 298 OuterAutomorphisms, 877 output

suppressing, 183

p-regular table, 681 PadicCoefficients, 709 paramap, 735 ParametersCentralizers, 843 Parametrized, 737 parametrized maps, 735 Parent, 239 part

of a string, 550 Partitions, 642 partitions, 635

improper, of an integer, 643

of a set, 642 of an integer, 642 ordered, of an integer, 643

restricted, of an integer, 644 PartitionsSet, 642 PartitionTuples, 645 PCentralSeries, 252 PCore, 247 Permanent, 641 PermBounds, 721 PermCharInfo, 720 PermChars, 721 PermCharsTom, 656 PermGroup

for matrix groups, 576 PermGroupAgGroup, 482 PermGroupOps.ElementProperty, 409 PermGroupOps.Indices, 408 PermGroupOps.LargestMovedPoint, 404 PermGroupOps.MovedPoints, 404 PermGroupOps.NrMovedPoints, 404 PermGroupOps.PgGroup, 412 PermGroupOps.SmallestMovedPoint, 404 PermGroupOps.StrongGenerators, 409 PermGroupOps.SubgroupProperty, 411 PermList, 401 PermRepresentationRoots, 838 Permutation, 301 permutation character, 745 Permutation Character Candidates, 719 permutation characters, 721

candidates for, 719

faithful candidates for, 723 Permutation Group Records, 419 Permutation Groups, 403 PermutationCharacter, 261 Permutations, 397 permutations

fixpointfree, 641

list, 640 PermutationsList, 640 Permuted, 530 PermWeylWord, 840 Phi, 336 PointGraph, 866 PolyhedralGroup, 579 Polynomial, 386 PolynomialRing, 391 Polynomials, 383 Porting GAP, 796 Position, 523 PositionProperty, 525 PositionSorted, 524 PositionWord, 426 Power, 719 power

of gaussians, 356

of group elements, 236

of records, 627 of words, 423

Powermap, 741 PowerMapping, 607 powermaps, 741–744, 747, 750, 751 PowermapsAllowedBySymmetrisations,

747 PowerMod, 220 PowerPartition, 645 powerset, 638 Powmap, 752 Pq, 821 PqDescendants, 822 PqList, 825 PQp, 486 PQuotient, 483 precedence, 172 PreImage, 603

for blocks homomorphisms, 417 for field homomorphisms, 233 for group homomorphisms, 287 for GroupHomomorphismByImages,

290 for OperationHomomorphism, 310

PreImages, 605 for blocks homomorphisms, 417 for field homomorphisms, 233 for group homomorphisms, 287 for GroupHomomorphismByImages,

290 for transitive constituent homomorphisms, 417

PreImagesRepresentative, 606 Presentation Records, 436 PresentationFpGroup, 436 PresentationNormalClosure, 443 PresentationNormalClosureRrs, 442 PresentationSubgroup, 442 PresentationSubgroupMtc, 441 PresentationSubgroupRrs, 440 previous result, 183 PrevPrimeInt, 331 primary subgroup generators, 440, 441,

454 prime residue group, 335

exponent, 336 generator, 337, 338 order, 336

PrimeBlocks, 710 PrimeResidues, 335 Primes, 330 primes

mersenne, 333 primitive root modulo an integer, 338 PrimitiveRootMod, 338 Print, 191

for double cosets, 279 for right cosets, 274

print

to a file, 191 PrintAmbiguity, 740 PrintClassSubgroupLattice, 268, 270 Printing of Records, 629 PrintSisyphosInputPGroup, 875 PrintSISYPHOSWord, 875 PrintTo, 191 PrintToCAS, 701 PrintToLib, 765 PrintToMOC, 701 Procedure Calls, 174 Product, 531 product

for double cosets, 279 for right cosets, 274 of a group and a group element, 293 of gaussians, 356 of group elements, 236 of list and group element, 236, 423 of records, 627 of words, 423

Profile, 193 Projection

for subdirect products, 283

onto component of direct products,

281 onto component of semidirect

products, 282 ProjectionMap, 737 prompt, 183

partial, 183 Properties and Property Tests, 253 PRump, 493

Quadratic, 351

quadratic irrationalities, 351 quadratic number fields, 351 quadratic residue, 338, 339 QuoInt, 325 Quotient, 212 quotient

of gaussians, 356

of group elements, 236

of groups, 293

of list and group element, 236 of list and word, 423 of records, 627 of words, 423 QuotientGraph, 867 QuotientMod, 219 QuotientRemainder, 218 for gaussians, 358 for polynomials, 392

Radical, 248 Random, 206

for ag groups, 471

for Conjugacy Classes, 262 for double cosets, 279 for finite fields, 381 for gaussians, 357 for matrix groups, 575 for Permutation Groups, 412 for right cosets, 274

random element

of a domain, 206 RandomList, 532 Ranges, 553 RankMat, 570 rational characters, 353 RationalizedMat, 353 Rationals, 341 Read, 190 read

a file, 190

a library file, 191 read eval print loop, 183 Reading Sections, 187 ReadLib, 191 RealClassesCharTable, 689 RecFields, 633 record

operations, 196 Record Assignment, 622 Records, 621 recursion, 177 recursive functions, 177 Redisplaying a Section, 189 Reduced, 712 Reduced Reidemeister-Schreier, 440 ReducedAgWord, 464 ReducedOrdinary, 712 ReducedWeylWord, 840 ReduceStabChain, 407 RefinedAgSeries, 482 RefinedSubnormalSeries, 493 regular classes, 681 RelativeOrder, 462 remainder of a quotient, 325 RemInt, 325 remove

an element from a set, 537 RemoveEdgeOrbit, 854 RemoveRelator, 439 RemoveSet, 537 Repeat, 175 repeat, 175 repeat loop, 175 representation

as a sum of two squares, 359 Representative, 206 representative

of a domain, 206 RepresentativeOperation, 313

for matrix groups, 576

for Permutation Groups, 416 RepresentativesFusions, 750 RepresentativesOperation, 314 RepresentativesPowermaps, 751 RequirePackage, 800 residue

quadratic, 338, 339 Restricted, 716 RestrictedPartitions, 644 RestrictedPerm, 401 Return, 179 return, 179 reverse the elements of a list, 526 Reversed, 526 Right Cosets Records, 276 RightCoset, 274

for ag groups, 471 RightCoset for Ag Groups, 478 RightCosetGroupOps, 274 RightCosets, 273 Ring, 210 ring

for cyclotomic integers, 365 Ring Functions for Gaussian Integers,

358 Ring Functions for Integers, 329 Ring Functions for Laurent Polynomial

Rings, 394 Ring Functions for Matrix Rings, 574 Ring Functions for Polynomial Rings,

392 Ring Records, 222 Rings, 209 root

of 1 modulo an integer, 339

of an integer, 327

of an integer modulo another, 339 of an integer, smallest, 328

RootInt, 327 RootMod, 339 roots of unity, 345 RootsUnityMod, 339 Rootsystem, 838 RoundCyc, 347 Row Space Operations, 563 Row Space Records, 562 Row Spaces, 561 RowSpace, 561 Runtime, 192

Save, 485

for presentation records, 438 SavePqList, 825 ScalarProduct, 707 scalars, 557 ScanMOC, 699 scope, 169 secondary subgroup generators, 440, 441,

453 Selection Functions, 582 selections, 635 SemidirectProduct, 282

for groups, 283 SemidirectProduct for Groups, 283 sequence

bernoulli, 647 fibonacci, 646 lucas, 646

Series of Subgroups, 250 Set, 536 set difference

of domains, 205 Set Functions for Ag Groups, 469 Set Functions for Conjugacy Classes, 262 Set Functions for Double Cosets, 279 Set Functions for Finite Fields, 381 Set Functions for Finitely Presented

Groups, 430 Set Functions for Gaussians, 357 Set Functions for Groups, 292 Set Functions for Integers, 328 Set Functions for Matrix Groups, 575 Set Functions for Matrix Rings, 573 Set Functions for Permutation Groups,

412 Set Functions for Rationals, 344 Set Functions for Right Cosets, 274 Set Functions for Row Spaces, 562 Set Functions for Sets, 538 Set Functions for Subgroup Conjugacy

Classes, 271 Set Functions for Vector Spaces, 319 SetPrintLevel, 267, 270 Sets, 535 ShallowCopy, 632 Share Libraries, 799 short vectors spanning a lattice, 723, 725 ShortestVectors, 727 SiftedAgWord, 464 SiftedVector, 562 Sigma, 333 sign

of an integer, 326 SignInt, 326 SignPartition, 645 SignPerm, 400 SimpleReflectionMatrices, 838 SimplifiedFpGroup, 443 SimplifyPresentation, 444 Sisyphos, 875 SISYPHOS Package, 814 Size, 203

for ag groups, 469

for Conjugacy Classes, 262

for conjugacy classes of subgroups,

271 for double cosets, 279 for finitely presented groups, 430 for matrix groups, 575 for Permutation Groups, 412 for right cosets, 274 of vector spaces, 319

size

of a domain, 203 Size for Ag Groups, 470 SizeBlist, 542 SizeScreen, 192 smaller prime, 331 SmallestGeneratorPerm, 400 SmallestMovedPointPerm, 400 SmallestRootInt, 328 SolutionMat, 571 Some Notes on Character Theory in

GAP, 661 Some special vertex subsets of a graph,

862 Sort, 529 sort a list, 529 SortCharactersCharTable, 692 SortCharTable, 694 SortClassesCharTable, 693 Sortex, 530 SortParallel, 530 space, 167 special character sequences, 547 SpecialLinearGroup, 579 SpecialUnitaryGroup, 579 split classes, 682 SplitCharacters, 675 square root

of an integer, 327 Stabilizer, 312

for ag groups, 471 for matrix groups, 576 for Permutation Groups, 416 Stabilizer Chains, 405 Stabilizer for Ag Groups, 476 StandardAssociate, 215

for gaussians, 358

for polynomials, 392, 394 StandardPresentation, 826 StarCyc, 351 Statements, 172 Stirling number of the first kind, 637 Stirling number of the second kind, 637 Stirling1, 637 Stirling2, 637 StoreFusion, 698 String, 549 Strings and Characters, 547 structure constant, 687, 688 SubdirectProduct, 283 Subfields of Cyclotomic Fields, 361 Subgroup, 241 subgroup and permutation character, 720 Subgroup Conjugacy Class Records, 271 subgroup fusions, 697, 698, 741, 745–750 subgroup generators tree, 454 Subgroup Presentations, 440 SubgroupFusions, 741 Subgroups, 242 subgroups

polyhedral, 689 Subgroups and Properties of Ag Groups,

492 Sublist, 526 SubnormalSeries, 253 Subroutines of Decomposition, 709 subset test

for domains, 203 subsets, 541, 638 SubstitutedWord, 426 SubString, 550 substring

of a string, 550 subtract

a boolean list from another, 544 a set from another, 538

SubtractBlist, 544 SubtractSet, 538 Subword, 425 Sum, 531 sum of divisors of an integer, 333 of gaussians, 356 of records, 627 SumAgGroup, 501 SumAgWord, 464 SumFactorizationFunctionAgGroup, 502 SunOS, 773 SylowComplements, 494 SylowSubgroup, 248

for ag groups, 471 for matrix groups, 576 for Permutation Groups, 412

SylowSystem, 494 Symbols, 166 symmetric group

powermap, 645 SymmetricGroup, 579 SymmetricParts, 714 Symmetrisations, 713 symmetrizations

orthogonal, 714 symplectic, 715 SymplecticComponents, 715 SymplecticGroup, 579 syntax errors, 183 System V, 773 SysV, 773

table automorphisms, 747, 749, 750 table of factor group, 677 Table of Marks Records, 650 table of normal subgroup, 678 TableAutomorphisms, 696 TableOfMarks, 651 tables, 661, 670, 755

add to a library, 765 format, 663, 667 generic, 703, 704, 706 libraries of, 755 library, 757, 759, 762 sort, 692–694 Tables of Marks, 649 tabulator, 167 Tau, 333 tensor product, 679 Tensored, 713 test

for a boolean, 619 for a cyclotomic, 347 for a cyclotomic field, 362 for a cyclotomic integer, 347 for a finite field element, 378 for a list, 514 for a power of a prime, 331 for a prime, 330 for a primitive root, 337 for a range, 554 for a rational, 341 for a set, 536 for a string, 551 for a vector, 559 for an integer, 326 for gaussian integer, 357 for gaussian rational, 357 for group element, 237 for list elements, 630 for membership, 523 for number field, 362 for record elements, 630 for records, 630 for set equality, 536 for subsets, 538

TestCharTable, 669 TestConsistencyMaps, 746 TestTom, 654 The 2-Groups Library, 587 The 3-Groups Library, 589 The Basic Groups Library, 579 The Irreducible Solvable Linear Groups

Library, 591 The Library of Tables of Marks, 650 The Prime Quotient Algorithm, 483 The Primitive Groups Library, 585 The Programming Language, 165 The Solvable Groups Library, 586 The Syntax in BNF, 179 The XGap Package, 818 then, 174 Tietze options, 451 Tietze record, 436 Tietze Transformations, 444 TomCyclic, 658 TomDihedral, 658 TomFrobenius, 659 TomMat, 653 TOS, 788 features, 793 installation, 789 options, 793

total length of a presentation, 444 Trace, 230

for gaussians, 358 trace

of a field element, 230 TraceMat, 569 TransferDiagram, 739 TransformingPermutations, 696 TransformingPermutationsCharTables,

696 Transitivity, 304 TransposedMat, 568 tree decoding, 453 TriangulizeMat, 570 TrivialSubgroup, 248 Tuples, 640 TwoSquares, 359 type

boolean, 617 cyclotomic, 345 gaussian integers, 355 gaussian rationals, 355 integer, 323 list, 513 matrices, 565 rationals, 341 records, 621 strings, 547 words, 421

TzEliminate, 446 TzFindCyclicJoins, 447 TzGo, 444 TzGoGo, 446 TzPrint, 437 TzPrintGenerators, 437 TzPrintLengths, 450 TzPrintOptions, 451 TzPrintPairs, 450 TzPrintPresentation, 437 TzPrintRelators, 437 TzPrintStatus, 437 TzSearch, 446 TzSearchEqual, 447 TzSubstitute, 448 TzSubstituteCyclicJoins, 450 ULTRIX, 773 Unbind, 631 UnderlyingGraph, 867 UndirectedEdges, 858 Union, 204 union

of boolean lists, 543, 544 of domains, 204 of sets, 537

UnionBlist, 543 UniteBlist, 544 UniteSet, 537 Units, 214

for gaussians, 358 UNIX

features, 778 installation, 774 options, 778

Unknown, 372 Unknowns, 371 UnorderedTuples, 639 until, 175 UpdateMap, 738 Upgrading GAP, 769 UpperCentralSeries, 253

Value, 390 Variables, 169 Vector Space Records, 318 Vector Spaces, 317 Vectors, 557 VectorSpace, 317 VectorSpace Functions for Row Spaces,

562 Vertex-Colouring and Complete

Subgraphs, 871 VertexColouring, 871 VertexDegree, 856 VertexDegrees, 856 VertexName, 856 Vertices, 856

Weight, 487 WeightsTom, 652 Weyl, 839 Weyl Groups and Hecke Algebras, 829 Weyl Package, 816 WeylClassPolynomials, 846 WeylConjugacyClasses, 842 WeylCosetPermRepresentation, 841 WeylElements, 842 WeylLengthPerm, 840 WeylMueMat, 844 WeylReflections, 841 WeylRightCosetRepresentatives, 841 WeylWordPerm, 840 While, 175 while, 175 while loop, 175 Whitespaces, 167 Windows, 780

installation, 782 words, 421 Words in Abstract Generators, 421 Words in Finite Polycyclic Groups, 459 wreath product

character table, 679 WreathProduct, 284

for ag groups, 471 WreathProduct for Ag Groups, 478 WreathProduct for Groups, 285

X, 385

Z, 375

ZumbroichBase, 366