polycyclic-2.16/0000755000076600000240000000000013706672364012641 5ustar mhornstaffpolycyclic-2.16/PackageInfo.g0000644000076600000240000001153113706672341015154 0ustar mhornstaff############################################################################# ## #W PackageInfo.g GAP 4 Package `polycyclic' Bettina Eick #W Werner Nickel #W Max Horn ## SetPackageInfo( rec( PackageName := "Polycyclic", Subtitle := "Computation with polycyclic groups", Version := "2.16", Date := "25/07/2020", # dd/mm/yyyy format License := "GPL-2.0-or-later", Persons := [ rec( LastName := "Eick", FirstNames := "Bettina", IsAuthor := true, IsMaintainer := true, Email := "beick@tu-bs.de", WWWHome := "http://www.iaa.tu-bs.de/beick", PostalAddress := Concatenation( "Institut Analysis und Algebra\n", "TU Braunschweig\n", "Universitätsplatz 2\n", "D-38106 Braunschweig\n", "Germany" ), Place := "Braunschweig", Institution := "TU Braunschweig" ), rec( LastName := "Nickel", FirstNames := "Werner", IsAuthor := true, IsMaintainer := false, # MH: Werner rarely (if at all) replies to emails sent to this # old email address. To discourage users from sending bug reports # there, I have disabled it here. #Email := "nickel@mathematik.tu-darmstadt.de", WWWHome := "http://www.mathematik.tu-darmstadt.de/~nickel/", ), rec( LastName := "Horn", FirstNames := "Max", IsAuthor := true, IsMaintainer := true, Email := "horn@mathematik.uni-kl.de", WWWHome := "https://www.quendi.de/math", PostalAddress := Concatenation( "Fachbereich Mathematik\n", "TU Kaiserslautern\n", "Gottlieb-Daimler-Straße 48\n", "67663 Kaiserslautern\n", "Germany" ), Place := "Kaiserslautern, Germany", Institution := "TU Kaiserslautern" ) ], Status := "accepted", CommunicatedBy := "Charles Wright (Eugene)", AcceptDate := "01/2004", PackageWWWHome := "https://gap-packages.github.io/polycyclic/", README_URL := Concatenation( ~.PackageWWWHome, "README.md" ), PackageInfoURL := Concatenation( ~.PackageWWWHome, "PackageInfo.g" ), SourceRepository := rec( Type := "git", URL := "https://github.com/gap-packages/polycyclic", ), IssueTrackerURL := Concatenation( ~.SourceRepository.URL, "/issues" ), ArchiveURL := Concatenation( ~.SourceRepository.URL, "/releases/download/v", ~.Version, "/polycyclic-", ~.Version ), ArchiveFormats := ".tar.gz", AbstractHTML := Concatenation( "This package provides various algorithms for computations ", "with polycyclic groups defined by polycyclic presentations." ), PackageDoc := rec( BookName := "polycyclic", ArchiveURLSubset := [ "doc" ], HTMLStart := "doc/chap0.html", PDFFile := "doc/manual.pdf", SixFile := "doc/manual.six", LongTitle := "Computation with polycyclic groups", Autoload := true ), Dependencies := rec( GAP := ">= 4.9", NeededOtherPackages := [["alnuth", "3.0"], ["autpgrp","1.6"]], SuggestedOtherPackages := [ ], ExternalConditions := [ ] ), AvailabilityTest := ReturnTrue, TestFile := "tst/testall.g", Keywords := [ "finitely generated nilpotent groups", "metacyclic groups", "collection", "consistency check", "solvable word problem", "normalizers","centralizers", "intersection", "conjugacy problem", "subgroups of finite index", "torsion subgroup", "finite subgroups", "extensions", "complements", "cohomology groups", "orbit-stabilizer algorithms", "fitting subgroup", "center", "infinite groups", "polycyclic generating sequence", "polycyclic presentation", "polycyclic group", "polycyclically presented group", "polycyclic presentation", "maximal subgroups", "Schur cover", "Schur multiplicator", ], AutoDoc := rec( TitlePage := rec( Copyright := "License\ ©right; 2003-2018 by Bettina Eick, Max Horn and Werner Nickel

\ The &Polycyclic; package is free software;\ you can redistribute it and/or modify it under the terms of the\ http://www.fsf.org/licenses/gpl.html\ as published by the Free Software Foundation; either version 2 of the License,\ or (at your option) any later version.", Acknowledgements := "\ We appreciate very much all past and future comments, suggestions and\ contributions to this package and its documentation provided by &GAP;\ users and developers.", ) ), )); polycyclic-2.16/LICENSE0000644000076600000240000003647413706672341013657 0ustar mhornstaffThe polycyclic package is free software; you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your opinion) any later version. The polycyclic package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Version 2 of the GNU General Public License follows. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS polycyclic-2.16/README.md0000644000076600000240000001071213706672341014114 0ustar mhornstaff[![Build Status](https://travis-ci.org/gap-packages/polycyclic.svg?branch=master)](https://travis-ci.org/gap-packages/polycyclic) [![Code Coverage](https://codecov.io/github/gap-packages/polycyclic/coverage.svg?branch=master&token=)](https://codecov.io/gh/gap-packages/polycyclic) The GAP 4 package `Polycyclic' ============================== Introduction ------------ This is the package `Polycyclic` written for GAP 4. It provides a basis for working with polycyclic groups defined by polycyclic presentations. To have the full functionality of the package available you need at least GAP version 4.5 and the GAP 4 package Alnuth and its dependencies must be installed. The features of this package include - creating a polycyclic group from a polycyclic presentation - arithmetic in a polycyclic group - computation with subgroups and factor groups of a polycyclic group - computation of standard subgroup series such as the derived series, the lower central series - computation of the first and second cohomology - computation of group extensions - computation of normalizers and centralizers - solutions to the conjugacy problems for elements and subgroups - computation of torsion and various finite subgroups - computation of various subgroups of finite index - computation of the Schur multiplicator, the non-abelian exterior square and the non-abelian tenor square There is a manual in the subdirectory `doc` which describes the available functions in detail. If you have used `Polycyclic`, and found important features missing or if there is a bug, we would appreciate it very much if you could report this via , or by sending us an email. - Bettina Eick - Max Horn Contents -------- With this version you should have obtained the following files and directories: - `README`: this file - `init.g`: the file that initializes this package - `read.g`: the file that reads in the package - `PackageInfo.g`: the file for the new package loading mechanism - `doc`: directory containing the manual - `gap`: directory containing the GAP code, it contains: - `action`: actions of polycyclic groups and orbit-stabilizer - `basic`: basic stuff for pcp groups - `cohom`: cohomology for pcp groups - `exam`: examples of pcp groups - `matrep`: matrix representations for pcp groups - `matrix`: basic stuff for matrices and lattices - `pcpgrp`: higher level functions for pcp groups Installation ------------ Make sure that the GAP 4 package Alnuth is installed to have the full range of methods available in polycyclic. There are two ways of installing the package. If you have permission to add files to the installation directory of GAP 4 on your system, you may install polycyclic in the `pkg` subdirectory of the GAP installation tree. If you do not, you can also install polycyclic in a directory where you have write permissions. For general advice, see also the GAP 4 manual about the installation of GAP packages. ### Installation in the GAP 4 pkg subdirectory on a Unix system. We assume that the archive file polycyclic.tar.gz or polycyclic.tar is present in pkg and that the current directory is pkg. All that needs to be done is to unpack the archive. tar xzf polycyclic.tar.gz The tar-command has unpacked the code into a directory called `polycyclic` in the current directory. You can check if GAP recognizes the polycyclic package by starting GAP and doing the following: $ gap4 [... startup messages ...] gap> LoadPackage("polycyclic"); true gap> ### Installation in a private directory Let's say you would like to install polycyclic in a directory called mygap. Create a subdirectory `pkg` in mygap and move the polycyclic archive into that subdirectory. cd mygap mkdir pkg mv polycyclic.tar.gz pkg cd pkg tar xzf polycyclic.tar.gz The tar-command has unpacked the code into a directory called `polycyclic` in the current directory. You can check if GAP recognizes the polycyclic package by starting GAP and doing the following. GAP needs to be told that it should scan the directory mygap/pkg for GAP packages. This is achieved by calling gap with the option -l. Note the semicolon and the single quotes. $ gap4 -l ';mygap/' [... startup messages ...] gap> LoadPackage("polycyclic"); true gap> polycyclic-2.16/gap/0000755000076600000240000000000013706672341013403 5ustar mhornstaffpolycyclic-2.16/gap/pcpgrp/0000755000076600000240000000000013706672341014676 5ustar mhornstaffpolycyclic-2.16/gap/pcpgrp/inters.gi0000644000076600000240000001143513706672341016527 0ustar mhornstaff############################################################################# ## #W grpint.gi Polycyc Bettina Eick ## ############################################################################# ## #F NormalIntersection( N, U ) . . . . . . . . . . . . . . . . . . . U \cap N ## ## The core idea here is that the intersection U \cap N equals the kernel of ## the natural homomorphism \phi : U \to UN/N ; see also section 8.8.1 of ## the "Handbook of computational group theory". ## So we can apply the methods for computing kernels of homomorphisms, but we ## need the group UN for this as well, at least implicitly. ## ## The resulting algorithm is quite similar to the Zassenhaus algorithm for ## simultaneously computing the intersection and sum of two vector spaces. InstallMethod( NormalIntersection, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( N, U ) local G, igs, igsN, igsU, n, s, I, id, ls, rs, is, g, d, al, ar, e, tm; # get common overgroup of N and U G := PcpGroupByCollector( Collector( N ) ); igs := Igs(G); igsN := Cgs( N ); igsU := Cgs( U ); n := Length( igs ); # if N or U is trivial if Length( igsN ) = 0 then return N; elif Length( igsU ) = 0 then return U; fi; # if N or U are equal to G if Length( igsN ) = n and ForAll(igsN, x -> LeadingExponent(x) = 1) then return U; elif Length(igsU) = n and ForAll(igsU, x -> LeadingExponent(x) = 1) then return N; fi; # if N is a tail, we can read off the result directly s := Depth( igsN[1] ); if Length( igsN ) = n-s+1 and ForAll( igsN, x -> LeadingExponent(x) = 1 ) then I := Filtered( igsU, x -> Depth(x) >= s ); return SubgroupByIgs( G, I ); fi; # otherwise compute id := One(G); ls := ListWithIdenticalEntries( n, id ); # ls = left side rs := ListWithIdenticalEntries( n, id ); # rs = right side is := ListWithIdenticalEntries( n, id ); # is = intersection for g in igsU do d := Depth( g ); ls[d] := g; rs[d] := g; od; I := []; for g in igsN do d := Depth( g ); if ls[d] = id then ls[d] := g; else Add( I, [ g, id ] ); fi; od; # enter the pairs [ ar, al ] of into [ , ] for tm in I do al := tm[1]; ar := tm[2]; d := Depth( al ); # compute sum and intersection while al <> id and ls[d] <> id do e := Gcdex( LeadingExponent(ls[d]), LeadingExponent(al) ); tm := ls[d]^e.coeff1 * al^e.coeff2; al := ls[d]^e.coeff3 * al^e.coeff4; ls[d] := tm; tm := rs[d]^e.coeff1 * ar^e.coeff2; ar := rs[d]^e.coeff3 * ar^e.coeff4; rs[d] := tm; d := Depth( al ); od; # we have a new sum generator if al <> id then Assert(1, ls[d] = id); ls[d] := al; # new generator of UN rs[d] := ar; tm := RelativeOrder( al ); if tm > 0 then al := al^tm; ar := ar^tm; Add( I, [ al, ar ] ); fi; # we have a new intersection generator elif ar <> id then Assert(1, al = id); # here we have al=id; so ar is in the intersection; # filter it into the polycyclic sequence `is` d := Depth( ar ); while ar <> id and is[d] <> id do e := Gcdex(LeadingExponent( is[d] ), LeadingExponent( ar )); tm := is[d]^e.coeff1 * ar^e.coeff2; ar := is[d]^e.coeff3 * ar^e.coeff4; is[d] := tm; d := Depth( ar ); od; if ar <> id then is[d] := ar; fi; fi; od; # sum := Filtered( ls, x -> x <> id ); I := Filtered( is, x -> x <> id ); return Subgroup( G, I ); end ); ############################################################################# ## #M Intersection( N, U ) ## InstallMethod( Intersection2, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( U, V ) # check for trivial cases if IsInt(Size(U)) and IsInt(Size(V)) then if IsInt(Size(V)/Size(U)) and ForAll(Igs(U), x -> x in V ) then return U; elif Size(V) x in U ) then return V; fi; fi; # test if one the groups is known to be normal if IsNormal( V, U ) then return NormalIntersection( U, V ); elif IsNormal( U, V ) then return NormalIntersection( V, U ); fi; Error("sorry: intersection for non-normal groups not yet installed"); end ); polycyclic-2.16/gap/pcpgrp/pcpattr.gi0000644000076600000240000000321113706672341016671 0ustar mhornstaff############################################################################# ## #W pcpattr.gi Polycyc Bettina Eick ## ## Some general attributes for pcp groups. Some of them are only available ## for nilpotent pcp groups. ## ############################################################################# ## #M MinimalGeneratingSet( G ) ## InstallMethod( MinimalGeneratingSet, "for pcp groups", [IsPcpGroup], function( G ) if IsNilpotentGroup( G ) then return MinimalGeneratingSetNilpotentPcpGroup(G); else Error("sorry: function is not installed"); fi; end ); ############################################################################# ## #M SmallGeneratingSet( G ) ## InstallMethod( SmallGeneratingSet, "for pcp groups", [IsPcpGroup], function( G ) local g, s, U, i, V; if Size(G) = 1 then return []; fi; g := Igs(G); s := [g[1]]; U := SubgroupNC( G, s ); i := 1; while IndexNC(G,U) > 1 do i := i+1; Add( s, g[i] ); V := SubgroupNC( G, s ); if IndexNC(V, U) > 1 then U := V; else Unbind(s[Length(s)]); fi; od; return s; end ); ############################################################################# ## #M SylowSubgroup( G, p ) ## InstallMethod( SylowSubgroupOp, [IsPcpGroup, IsPosInt], function( G, p ) local iso; if not IsFinite(G) then Error("sorry: function is not installed"); fi; # HACK: Until we write a proper native method, use that for pc groups iso := IsomorphismPcGroup(G); return PreImagesSet(iso, SylowSubgroup(Image(iso),p)); end ); polycyclic-2.16/gap/pcpgrp/grpinva.gi0000644000076600000240000001521213706672341016666 0ustar mhornstaff############################################################################# ## #W grpinva.gi Polycyc Bettina Eick ## ## Functions to compute all invariant subgroups in an elementary abelian ## subgroup or in a free abelian group up to a certain index ## ## ## First we consider the elementary abelian situation ## ############################################################################# ## #F AllSubspaces( dim, p ) . . . . . . . . . . . . list all subspaces of p^dim ## AllSubspaces := function( dim, p ) local idm, exp, i, t, e, j, f, c, k; # create all normed bases in p^dim idm := IdentityMat( dim ); exp := [[]]; for i in [1..dim] do t := []; for e in exp do # create subspaces of same dimension for j in [1..p^Length(e)-1] do f := StructuralCopy( e ); c := CoefficientsQadic( j, p ); for k in [1..Length(c)] do f[k][i] := c[k]; od; Add( t, f ); od; # add higher dimensional one f := StructuralCopy( e ); Add( f, idm[i] ); Add( t, f ); od; Append( exp, t ); od; Unbind( exp[Length(exp)] ); return exp * One(GF(p)); end; ############################################################################# ## #F OnBasesCase( base, mat ) ## OnBasesCase := function( base, mat ) local new; if Length(base) = 0 then return base; fi; new := base * mat; if IsFFE( new[1][1] ) then TriangulizeMat( new ); else new := TriangulizedIntegerMat( new ); fi; return new; end; ############################################################################# ## #F InvariantSubspaces( C, d ) ## InvariantSubspaces := function( C, d ) local p, l, invs, modu; # set up p := C.char; l := C.dim; # distinguish two cases if IsBound( C.spaces ) then invs := Filtered( C.spaces, x -> l - Length(x) <= d ); if not IsBound( C.central ) or not C.central then invs := FixedPoints( invs, C.mats, OnBasesCase ); fi; else modu := GModuleByMats( C.mats, C.dim, C.field ); invs := MTX.BasesSubmodules( modu ); invs := Filtered( invs, x -> Length( x ) < l ); invs := Filtered( invs, x -> l - Length( x ) <= d ); fi; return invs; end; ############################################################################# ## #F OrbitsInvariantSubspaces( C, d ) ## OrbitsInvariantSubspaces := function( C, d ) local invs, o, i, n, j; invs := InvariantSubspaces( C, d ); if ForAny( C.smats, x -> x <> C.one ) then o := PcpOrbitsStabilizers( invs, C.super, C.smats, OnBasesCase ); # purify stabilizer for i in [1..Length(o)] do n := []; for j in [1..Length(o[i].stab)] do n[j] := ExponentsByPcp(C.super, o[i].stab[j]); n[j] := MappedVector( n[j], C.super); od; o[i].stab := n; od; return o; else return List( invs, x -> rec( repr := x, stab := C.super ) ); fi; end; ## ## Now we deal with the free abelian case ## ############################################################################# ## #F InsertZeros( d, exp, n ) ## InsertZeros := function( d, exp, n ) local new, b; new := n * IdentityMat( d ); for b in exp do new[PositionNonZero(b)] := b; od; return new; end; ############################################################################# ## #F PcpsBySpaces( A, B, dim, p, bases ) ## PcpsBySpaces := function( A, B, dim, p, bases ) local tmp, base, new, b, i, C, gen, pcp; tmp := []; gen := Igs( B ); for base in bases do new := InsertZeros( dim, base, p ); for i in [1..Length( new )] do new[i] := MappedVector( IntVector( new[i] ), gen ); od; new := Filtered( new, x -> x <> One(A) ); C := SubgroupByIgs( A, new ); pcp := Pcp( B, C ); pcp!.index := IndexNC( B, C ); Add( tmp, pcp ); od; return tmp; end; ############################################################################# ## #F AllSubgroupsAbelian( dim, l ) ## ## The subgroups of the free abelian group of rank dim up to index l given ## as exponent vectors. ## AllSubgroupsAbelian := function( dim, l ) local A, gens, fac, sub, i, p, r, sp, j, q, B, pcps, tmp, L, pcpL, pcpsS, C, grps, U, V, pcpS, new; # create the abelian group A := AbelianPcpGroup( dim, List( [1..dim], x -> l ) ); gens := Cgs(A); # first separate the primes fac := Collected( Factors( l ) ); sub := List( fac, x -> [A] ); for i in [1..Length(fac)] do p := fac[i][1]; r := fac[i][2]; sp := AllSubspaces( dim, p ); for j in [1..r] do # set up q := p^(j-1); B := SubgroupByIgs( A, List( gens, x -> x^q ) ); pcps := PcpsBySpaces( A, B, dim, p, sp ); # loop over all subgroups and spaces tmp := []; for L in sub[i] do pcpL := Pcp( L, B ); for pcpS in pcps do if IndexNC( A, L ) * pcpS!.index <= l then # compute complements in L to R / S C := rec(); C.group := L; C.factor := pcpL; C.normal := pcpS; AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); AddInversesCR( C ); Append( tmp, ComplementsCR( C ) ); fi; od; od; Append( sub[i], tmp ); od; sub[i] := List( sub[i], x -> List( Igs(x), Exponents ) ); od; # intersect `jeder gegen jeden' grps := sub[1]; for i in [2..Length(fac)] do tmp := []; for U in grps do for V in sub[i] do new := AbelianIntersection( U, V ); Append( tmp, new ); od; od; grps := ShallowCopy( tmp ); od; grps := List( grps, x -> InsertZeros( dim, x, l ) ); return grps{[2..Length(grps)]}; end; AllSubgroupsAbelian2 := function( dim, n ) local A, cl; A := AbelianPcpGroup( dim, List( [1..dim], x -> n ) ); cl := FiniteSubgroupClasses( A ); cl := List( cl, Representative ); cl := Filtered( cl, x -> IndexNC(A,x) <= n ); cl := List( cl, x -> List( Cgs(x), Exponents ) ); cl := List( cl, x -> InsertZeros( dim, x, n ) ); return cl{[2..Length(cl)]}; end; polycyclic-2.16/gap/pcpgrp/normcon.gi0000644000076600000240000002420513706672341016675 0ustar mhornstaff############################################################################ ## #W normcon.gi Polycyc Bettina Eick ## ## Computing normalizers of subgroups. ## Solving the conjugacy problem for subgroups. ## ############################################################################# ## #F AffineActionOnH1( CR, cc ) ## AffineActionOnH1 := function( CR, cc ) local aff, l, i, lin, trl, j; aff := OperationOnH1( CR, cc ); l := Length( cc.factor.rels ); for i in [1..Length(aff)] do if aff[i] = 1 then aff[i] := IdentityMat( l+1 ); else lin := List( aff[i].lin, x -> cc.CocToFactor( cc, x ) ); trl := cc.CocToFactor( cc, aff[i].trl ); for j in [1..l] do Add( lin[j], 0 ); od; Add( trl, 1 ); aff[i] := Concatenation( lin, [trl] ); fi; od; if not IsBool(cc.fld) then aff := aff * One(cc.fld); fi; return aff; end; ############################################################################# ## #F VectorByComplement( CR, igs ) ## ## bad hack ... igs and fac have to fit together. ## VectorByComplement := function( CR, U ) local fac, vec, igs; fac := CR.factor; igs := Cgs(U); vec := List( [1..Length(fac)], i -> ExponentsByPcp( CR.normal, fac[i]^-1 * igs[i] ) ); return Flat(vec); end; ############################################################################# ## #F LiftBlockToPointNormalizer( CR, cc, C, H, HN, c ) ## LiftBlockToPointNormalizer := function( CR, cc, C, H, HN, c ) local b, r, t, i, d, igs; # set up b and t b := AddIgsToIgs( Igs(H), DenominatorOfPcp( CR.normal ) ); r := List( cc.rls, x -> MappedVector( x, CR.normal ) ); b := AddIgsToIgs( r, b ); t := ShallowCopy( AsList( Pcp( C, HN ) ) ); # catch a special case if Length(cc.gcb) = 0 then return SubgroupByIgsAndIgs( C, t, b ); fi; # add normalizer to centralizer and complement for i in [1..Length(t)] do d := VectorByComplement( CR, H^t[i] ); if not IsBool( cc.fld ) then d := d * One( cc.fld ); fi; d := cc.CocToCBElement( cc, d-c ) * cc.trf; t[i] := t[i] * MappedVector( d, CR.normal ); od; return SubgroupByIgsAndIgs( C, t, b ); end; ############################################################################# ## #F NormalizerOfIntersection( C, N, I ) ## NormalizerOfIntersection := function( C, N, I ) local pcp, int, fac, act, p, d, F, stb, ind; # catch trivial cases if Size(I) = 1 or IndexNC(N,I) = 1 then return C; fi; # set up pcp := Pcp(N, "snf"); int := List( Igs(I), x -> ExponentsByPcp( pcp, x ) ); fac := Pcp( C, N ); act := LinearActionOnPcp( fac, pcp ); p := RelativeOrdersOfPcp( pcp )[1]; d := Length( pcp ); Info( InfoPcpGrp, 2," normalize intersection in layer of type ",p,"^",d); # the finite case if p > 0 then F := GF(p); act := InducedByField( act, F ); int := VectorspaceBasis( int*One(F) ); stb := PcpOrbitStabilizer( int, fac, act, OnSubspacesByCanonicalBasis ); stb := AddIgsToIgs( stb.stab, AsList(pcp) ); return SubgroupByIgs( C, stb ); # the infinite case else ind := NaturalHomomorphismByPcp( fac ); int := LatticeBasis( int ); C := Image( ind ); C := NormalizerIntegralAction( C, act, int ); return PreImage( ind, C ); fi; end; ############################################################################# ## #F StabilizerOfCocycle( CR, cc, C, elm ) ## StabilizerOfCocycle := function( CR, cc, C, elm ) local aff, s, l, D, nat, act, e, oper, stb; # determine operation and catch trivial case aff := AffineActionOnH1( CR, cc ); if ForAll( aff, x -> x = x^0 ) then return C; fi; # determine stabilizer of free abelian part s := Position( cc.factor.rels, 0 ); l := Length( cc.factor.rels ); D := C; if not IsBool(s) then nat := NaturalHomomorphismByPcp( CR.super ); act := List( aff, x -> x{[s..l+1]}{[s..l+1]} ); e := elm{[s..l]}; Add( e, 1 ); D := Image( nat, D ); D := StabilizerIntegralAction( D, act, e ); D := PreImage( nat, D ); fi; if Size(D) = 1 or s = 1 then return D; fi; # now it remains to do an affine finite os calculation Add( elm, 1 ); # set up action for D if IndexNC(C,D) > 1 then act := Pcp( D, CR.group ); aff := InducedByPcp( CR.super, act, aff ); else act := CR.super; fi; # set up operation if IsBool(cc.fld) then oper := function( pt, aff ) local im, i; im := pt * aff; for i in [1..l] do if cc.factor.rels[i] > 0 then im[i] := im[i] mod cc.factor.rels[i]; fi; od; return im; end; else elm := elm * One(cc.fld); oper := OnRight; fi; # compute stabilizer stb := PcpOrbitStabilizer( elm, act, aff, oper ); return SubgroupByIgsAndIgs( C, stb.stab, Igs(CR.group) ); end; ############################################################################# ## #F PcpsOfAbelianFactor( N, I ) ## PcpsOfAbelianFactor := function( N, I ) local ser, sub, pcp, rel, tor, gen, M, p, T; # set up ser := []; sub := Igs(I); pcp := Pcp(N, I, "snf"); rel := RelativeOrdersOfPcp( pcp ); tor := pcp{Filtered([1..Length(rel)], x -> rel[x] > 0 )}; # the factor mod torsion T := SubgroupByIgsAndIgs( N, tor, sub ); if IndexNC(N,T) > 1 then Add( ser, Pcp(N,T,"snf") ); pcp := Pcp(T, I); rel := RelativeOrdersOfPcp( pcp ); fi; # now the torsion parts while Length(pcp) > 0 do p := Factors(rel[1])[1]; gen := List( pcp, x -> x^p ); gen := Filtered( gen, x -> x <> One(N) ); M := SubgroupByIgsAndIgs( N, gen, sub ); Add( ser, Pcp(T,M,"snf") ); T := M; pcp := Pcp(T, I); rel := RelativeOrdersOfPcp( pcp ); od; return ser; end; ############################################################################# ## #F NormalizerOfComplement( C, H, N, I ) ## NormalizerOfComplement := function( C, H, N, I ) local pcps, pcp, M, L, CR, cc, c, e; # catch the trivial case if IndexNC(H,I) = 1 or IndexNC(N,I) = 1 then return C; fi; Info( InfoPcpGrp, 2, " normalize complement"); # compute efa series through N / I pcps := PcpsOfAbelianFactor( N, I ); # loop over series for pcp in pcps do M := SubgroupByIgs( C, NumeratorOfPcp( pcp ) ); L := SubgroupByIgsAndIgs( C, Igs(H), Igs(M) ); # set up H^1 CR := rec( group := L, super := Pcp( C, L ), factor := Pcp( L, M ), normal := pcp ); AddFieldCR( CR ); AddRelatorsCR( CR ); AddOperationCR( CR ); AddInversesCR( CR ); # determine 1-cohomology cc := OneCohomologyEX( CR ); if IsBool( cc ) then Error("no complement \n"); fi; # stabilize vector if Length( cc.factor.rels ) > 0 then Info( InfoPcpGrp, 2, " H1 is of type ",cc.factor.rels); c := VectorByComplement( CR, H ); if not IsBool( cc.fld ) then c := c * One( cc.fld ); fi; e := cc.CocToFactor( cc, c ); C := StabilizerOfCocycle( CR, cc, C, e ); fi; # lift to point normalizer C := LiftBlockToPointNormalizer( CR, cc, C, H, L, c ); od; return C; end; ############################################################################# ## #F NormalizerBySeries( G, U, efa ) ## NormalizerBySeries := function( G, U, efa ) local C, i, N, M, hom, H, I, nat, k; # do a simple check if Size(U) = 1 or G = U then return G; fi; # loop over series C := G; for i in [2..Length(efa)-1] do Info( InfoPcpGrp, 1, "start layer ",i); # get layer N := efa[i]; M := efa[i+1]; # determine factor C/M hom := NaturalHomomorphismByNormalSubgroup( G, M ); if Size(M) > 1 then N := Image( hom, N ); C := Image( hom, C ); fi; H := Image( hom, U ); # first normalize the intersection I = N cap H I := NormalIntersection( N, H ); C := NormalizerOfIntersection( C, N, I ); # now normalize complement C := NormalizerOfComplement( C, H, N, I ); # add checking if required if CHECK_NORM@ then Info( InfoPcpGrp, 1, " check result "); H := Image( hom, U ); if ForAny( Igs(C), x -> H^x <> H ) then Error("normalizer is not normalizing"); fi; fi; if Size(M) > 1 then C := PreImage( hom, C ); fi; od; return C; end; ############################################################################# ## #F Normalizer ## NormalizerPcpGroup := function( G, U ) local GG, UU, NN; # translate GG := PcpGroupByEfaSeries(G); UU := PreImage(GG!.bijection,U); # compute NN := NormalizerBySeries( GG, UU, EfaSeries(GG) ); # translate back return Image(GG!.bijection, NN ); end; InstallMethod( NormalizerOp, "for a pcp group", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( G, U ) local H; # catch a special case if IsSubgroup( G, U ) then return NormalizerPcpGroup( G, U ); fi; # find a common overgroup of G and U and compute the normalizer in there H := PcpGroupByCollectorNC( Collector( G ) ); H := SubgroupByIgs( H, Igs(G), Igs(U) ); return Intersection( G, NormalizerPcpGroup( H, U ) ); end ); ############################################################################# ## #F ConjugacySubgroupsBySeries( G, U, V, pcps ) ## ConjugacySubgroupsBySeries := function( G, U, V, pcps ) Error("not yet installed"); end; ############################################################################# ## #F IsConjugate( G, U, V ) ## InstallMethod( IsConjugate, "for a pcp group", IsCollsElmsElms, [IsPcpGroup, IsPcpGroup, IsPcpGroup], function( G, U, V ) # compute return ConjugacySubgroupsBySeries( G, U, V, PcpsOfEfaSeries(G) ); end ); polycyclic-2.16/gap/pcpgrp/nilpot.gi0000644000076600000240000001112013706672341016517 0ustar mhornstaff############################################################################ ## #W nilpot.gi Polycyc Bettina Eick #W Werner Nickel ## ## This file defines special functions for nilpotent groups. The ## corresponding methods are usually defined with the general methods ## for pcp groups in other files. ## ############################################################################# ## #F MinimalGeneratingSet( G ) ## MinimalGeneratingSetNilpotentPcpGroup := function( G ) return GeneratorsOfPcp( Pcp( G, DerivedSubgroup(G), "snf" ) ); end; ############################################################################# ## #F PcpNextStepCentralizer( gens, cent, pcp ) ## PcpNextStepCentralizer := function( gens, cent, pcp ) local pcpros, rels, i, g, newgens, matrix, notcentral, h, pcpgens, comm, null, j, elm, r, l; pcpgens := GeneratorsOfPcp( pcp ); pcpros := RelativeOrdersOfPcp( pcp ); ## Get the relations in this factor group. rels := []; for i in [1..Length(pcpgens)] do if pcpros[i] > 0 then r := ExponentsByPcp( pcp, pcpgens[i]^pcpros[i] ); r[i] := -pcpros[i]; Add( rels, r ); fi; od; for g in gens do #Print("start gen ",g,"\n"); if Length( cent ) = 0 then return []; fi; newgens := []; matrix := []; notcentral := []; for h in cent do comm := ExponentsByPcp( pcp, Comm( h, g ) ); if comm = 0 * comm then Add( newgens, h ); else Add( notcentral, h ); Add( matrix, comm ); fi; od; #Print(" got matrix \n"); if Length( matrix ) > 0 then # add the relations to the matrix. Append( matrix, rels ); # get nullspace null := PcpNullspaceIntMat( matrix ); #Print(" solved matrix \n"); # calculate elements corresponding to null l := Length( notcentral ); for j in [1..Length(null)] do elm := MappedVector( null[j]{[1..l]}, notcentral ); if elm <> elm^0 then Add( newgens, elm ); fi; od; fi; cent := newgens; od; return cent; end; ############################################################################# ## #F CentralizeByCentralSeries( G, gens, ser ) ## CentralizeByCentralSeries := function( G, gens, ser ) local cent, i, pcp; cent := ShallowCopy( GeneratorsOfPcp( Pcp( ser[1], ser[2] ) ) ); for i in [2..Length(ser)-1] do pcp := Pcp( ser[i], ser[i+1] ); cent := PcpNextStepCentralizer( gens, cent, pcp ); Append( cent, GeneratorsOfPcp( pcp ) ); od; Append( cent, GeneratorsOfGroup( ser[Length(ser)] ) ); return cent; end; ############################################################################# ## #F Centre( G ) ## CentreNilpotentPcpGroup := function(G) local ser, gens, cent; if Length(Igs(G)) = 0 then return G; fi; ser := LowerCentralSeriesOfGroup(G); gens := Reversed(GeneratorsOfPcp( Pcp( ser[1], ser[2] ) )); cent := CentralizeByCentralSeries( G, gens, ser ); return Subgroup( G, cent ); end; ############################################################################# ## #F Centralizer ## CentralizerNilpotentPcpGroup := function( G, g ) local sers, cent, U; if Length(Igs(G)) = 0 then return G; fi; if IsPcpElement(g) then if not g in G then TryNextMethod(); fi; sers := LowerCentralSeriesOfGroup(G); cent := CentralizeByCentralSeries( G, [g], sers ); elif IsPcpGroup(g) then if not IsSubgroup( G, g ) then TryNextMethod(); fi; SetIsNilpotentGroup( g, true ); sers := LowerCentralSeriesOfGroup(G); cent := CentralizeByCentralSeries( G, MinimalGeneratingSet(g), sers ); fi; return Subgroup( G, cent ); end; ############################################################################# ## #F UpperCentralSeriesNilpotentPcpGroup( G ) ## UpperCentralSeriesNilpotentPcpGroup := function( G ) local ser, gens, C, upp; ser := LowerCentralSeriesOfGroup(G); gens := GeneratorsOfPcp( Pcp( ser[1], ser[2] ) ); C := TrivialSubgroup( G ); upp := [C]; while IndexNC( G, C ) > 1 do ser := ModuloSeries( ser, C ); C := CentralizeByCentralSeries( G, gens, ser ); C := Subgroup( G, C ); Add( upp, C ); od; upp[ Length(upp) ] := G; return Reversed( upp ); end; polycyclic-2.16/gap/pcpgrp/nindex.gi0000644000076600000240000001517113706672341016511 0ustar mhornstaff############################################################################# ## #W nindex.gi Polycyc Bettina Eick ## ## A method to compute the normal subgroups of given index. ## ############################################################################# ## #F LowIndexNormalsEaLayer( G, U, pcp, d, act ) ## ## Compute low-index subgroups in not containing the elementary abelian ## subfactor corresponding to . The index of the computed subgroups ## is limited by p^d. ## LowIndexNormalsEaLayer := function( G, U, pcp, d, act ) local p, l, fld, C, modu, invs, orbs, com, o, sub, inv, e, stab, indu, L, fac, new, i, tmp, mats, t; # a first trivial case if d = 0 or Length( pcp ) = 0 then return []; fi; p := RelativeOrdersOfPcp(pcp)[1]; l := Length( pcp ); fld := GF(p); # create class record with action of U C := rec( group := U ); C.normal := pcp; C.factor := Pcp( U, GroupOfPcp( pcp ) ); C.super := Pcp( G, U ); # add matrix action on layer C.mats := MappedAction( C.factor, act ) * One( fld ); C.smats := MappedAction( C.super, act ) * One( fld ); # add info on extension AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); # invariant subspaces mats := Concatenation( C.mats, C.smats ); modu := GModuleByMats( mats, C.dim, C.field ); invs := MTX.BasesSubmodules( modu ); invs := Filtered( invs, x -> Length( x ) < C.dim ); invs := Filtered( invs, x -> l - Length( x ) <= d ); com := []; while Length( invs ) > 0 do o := Remove(invs); t := U!.open / p^(l - Length(o)); if IsInt( t ) then # copy sub and adjust the entries to the layer sub := InduceToFactor(C, rec(repr := o,stab := AsList(C.super))); AddInversesCR( sub ); # compute the desired complements new := InvariantComplementsCR( sub ); # add information on index for i in [1..Length(new)] do new[i]!.open := t; od; # append them Append( com, new ); # if there are no complements, then reduce invs if Length( new ) = 0 then invs := Filtered( invs, x -> not IsSubbasis( o, x ) ); fi; fi; od; return com; end; ############################################################################# ## #F LowIndexNormalsFaLayer( cl, pcplist, l, act ) ## ## Compute low-index subgroups in not containing the free abelian ## subfactor corresponding to . The index of the computed subgroups ## is limited by l. ## LowIndexNormalsFaLayer := function( G, U, adj, l, act ) local m, L, fac, grp, pr, todo, done, news, i, use, cl, d, tmp; fac := Collected( Factors( l ) ); grp := [U]; for pr in fac do todo := ShallowCopy( grp ); done := []; news := []; for i in [1..pr[2]] do use := adj[pr[1]][i]; for L in todo do d := Valuation( L!.open, pr[1] ); tmp := LowIndexNormalsEaLayer( G, L, use, d, act ); Append( news, tmp ); od; Append( done, todo ); todo := ShallowCopy( news ); news := []; od; grp := Concatenation( done, todo ); od; # return computed groups without the original group return grp{[2..Length(grp)]}; end; ############################################################################# ## #F LowIndexNormalsBySeries( G, n, pcps ) ## LowIndexNormalsBySeries := function( G, n, pcps ) local U, grps, all, i, pcp, p, A, mats, new, adj, cl, l, d, act, tmp; # set up all := Pcp( G ); # the first layer grps := SubgroupsFirstLayerByIndex( G, pcps[1], n ); for i in [1..Length(grps)] do grps[i].repr!.open := grps[i].open; grps[i] := grps[i].repr; od; # loop down the series for i in [2..Length(pcps)] do pcp := pcps[i]; p := RelativeOrdersOfPcp( pcp )[1]; A := GroupOfPcp( pcp ); Info( InfoPcpGrp, 1, "starting layer ",i, " of type ",p, " ^ ", Length(pcp), " with ",Length(grps), " groups"); # compute action on layer mats := List( all, x -> List(pcp, y -> ExponentsByPcp(pcp, y^x))); act := rec( pcp := all, mats := mats ); # loop over all subgroups new := []; adj := []; for U in grps do # now pass it on l := U!.open; if l > 1 and p = 0 then if not IsBound( adj[l] ) then adj[l] := PowerPcpsByIndex( pcp, l ); fi; tmp := LowIndexNormalsFaLayer( G, U, adj[l], l, act ); Info( InfoPcpGrp, 2, " found ", Length(tmp), " new groups"); Append( new, tmp ); elif l > 1 then d := Valuation( l, p ); tmp := LowIndexNormalsEaLayer( G, U, pcp, d, act ); Info( InfoPcpGrp, 2, " found ", Length(tmp), " new groups"); Append( new, tmp ); fi; od; Append( grps, new ); od; return Filtered( grps, x -> x!.open = 1 ); end; ############################################################################# ## #F LowIndexNormalSubgroups( G, n ) ## InstallMethod( LowIndexNormalSubgroupsOp, "for pcp groups", [IsPcpGroup, IsPosInt], function( G, n ) local efa; if n = 1 then return [G]; fi; efa := PcpsOfEfaSeries( G ); return LowIndexNormalsBySeries( G, n, efa ); end ); ############################################################################# ## #F NilpotentByAbelianNormalSubgroup( G ) ## ## Use the LowIndexNormals function to find a normal subgroup which is ## nilpotent - by - abelian. Every polycyclic group has such a normal ## subgroup. ## ## This is usually done more effectively by NilpotenByAbelianByFiniteSeries. ## We only use this function as alternative for special cases. ## InstallGlobalFunction( NilpotentByAbelianNormalSubgroup, function( G ) local sub, i, j, f, N, low, L; if IsNilpotent( DerivedSubgroup( G ) ) then return G; fi; sub := [[G]]; while true do i := Length( sub ) + 1; sub[i] := []; Info( InfoPcpGrp, 1, "test normal subgroups of index ", i ); f := Factors( i ); j := i / f[1]; for N in sub[j] do low := LowIndexNormalSubgroups( N, f[1] ); for L in low do if IsNilpotent( DerivedSubgroup( L ) ) then return L; else AddSet( sub[i], L ); fi; od; od; od; end ); polycyclic-2.16/gap/pcpgrp/polyz.gi0000644000076600000240000000364713706672341016406 0ustar mhornstaff############################################################################ ## #W polyz.gi Polycyc Bettina Eick ## ############################################################################ ## #F GeneratorsOfCentralizerOfPcp( gens, pcp ) ## GeneratorsOfCentralizerOfPcp := function( gens, pcp ) local idm, v, mats; idm := IdentityMat( Length( pcp ), GF( RelativeOrdersOfPcp(pcp)[1] ) ); for v in idm do mats := LinearActionOnPcp( gens, pcp ); gens := PcpOrbitStabilizer( v, gens, mats, OnRight ).stab; od; return gens; end; ############################################################################ ## #F PolyZNormalSubgroup( G ) ## ## returns a normal subgroup N of finite index in G such that N has a ## normal series with free abelian factors. ## InstallGlobalFunction( PolyZNormalSubgroup, function( G ) local N, F, U, ser, nat, pcps, m, i, free, j, p; # set up N := TrivialSubgroup( G ); ser := [N]; nat := IdentityMapping( G ); F := Image( nat ); # loop while not IsFinite( F ) do # get gens of free abelian normal subgroup pcps := PcpsOfEfaSeries(F); m := Length( pcps ); i := m; while RelativeOrdersOfPcp( pcps[i] )[1] > 0 do i := i - 1; od; free := AsList( pcps[i] ); for j in [i+1..m] do free := GeneratorsOfCentralizerOfPcp( free, pcps[j] ); p := RelativeOrdersOfPcp( pcps[j] )[1]; if p = 2 then free := List( free, x -> x^4 ); else free := List( free, x -> x^p ); fi; od; # reset U := Subgroup( F, free ); N := PreImage( nat, U ); Add( ser, N ); nat := NaturalHomomorphismByNormalSubgroup( G, N ); F := Image( nat ); od; SetEfaSeries( N, Reversed( ser ) ); return N; end ); polycyclic-2.16/gap/pcpgrp/findex.gi0000644000076600000240000002200413706672341016472 0ustar mhornstaff############################################################################# ## #W findex.gi Polycyc Bettina Eick ## ## Conjugacy classes of subgroups of given index. ## ############################################################################# ## #F SubgroupsFirstLayerByIndex( G, pcp, n ) ## ## All subgroup of index dividing n. ## SubgroupsFirstLayerByIndex := function( G, pcp, n ) local m, p, l, d, idm, exp, i, t, j, f, c, denom, dep, ind, base, k, e; # set up p := RelativeOrdersOfPcp( pcp )[1]; l := Length( pcp ); # reset n, if the layer is finite, and compute divisors if p > 0 then m := Gcd( n, p^l ); else m := n; fi; d := Filtered( DivisorsInt( m ), x -> x <> m ); if Length( d ) = 0 then return [rec( repr := G, norm := G, open := n )]; fi; # create all normed bases in m^l idm := IdentityMat( l ); exp := [[]]; for i in [1..l] do # create subspaces of same dimension t := []; for e in exp do for j in [1..m^Length(e)-1] do f := StructuralCopy( e ); c := CoefficientsQadic( j, m ); for k in [1..Length(c)] do f[k][i] := c[k]; od; Add( t, f ); od; od; Append( exp, t ); # add higher dimension t := []; for e in exp do for j in d do if ForAll( e, x -> x[i] < j ) then f := StructuralCopy( e ); Add( f, idm[i] * j ); Add( t, f ); fi; od; od; Append( exp, t ); od; # convert each basis to a pcs denom := DenominatorOfPcp( pcp ); for i in [1..Length(exp)] do e := exp[i]; dep := List( e, PositionNonZero ); ind := List( [1..Length(e)], x -> e[x][dep[x]] ); ind := Product( ind ) * m^(l - Length(e)); if ind <= m then base := []; for k in [1..l] do j := Position( dep, k ); if not IsBool( j ) then Add( base, MappedVector( e[j], pcp ) ); elif p = 0 then Add( base, MappedVector( m * idm[k], pcp ) ); fi; od; exp[i] := AddIgsToIgs( base, denom ); exp[i] := rec( repr := SubgroupByIgs( G, exp[i] ), norm := G, open := n / ind ); if not IsInt( exp[i].open ) then Error(); fi; else exp[i] := false; fi; od; return Filtered( exp, x -> not IsBool(x) ); end; ############################################################################# ## #F MappedAction( gens, rec ) ## MappedAction := function( gens, act ) return List(gens, x->MappedVector(ExponentsByPcp(act.pcp, x), act.mats)); end; ############################################################################# ## #F LowIndexSubgroupsEaLayer( cl, pcp, d, act ) ## ## Compute low-index subgroups in not containing the elementary abelian ## subfactor corresponding to . The index of the computed subgroups ## is limited by p^d. ## LowIndexSubgroupsEaLayer := function( cl, pcp, d, act ) local p, l, fld, C, modu, invs, orbs, com, o, sub, inv, e, stab, indu, L, fac, new, i, tmp, t; # a first trivial case if d = 0 or Length( pcp ) = 0 then return []; fi; p := RelativeOrdersOfPcp(pcp)[1]; l := Length( pcp ); fld := GF(p); # create class record with action of U C := rec( group := cl.repr ); C.normal := pcp; C.factor := Pcp( cl.repr, GroupOfPcp( pcp ) ); C.super := Pcp( cl.norm, cl.repr ); # add matrix action on layer C.central := act.central; C.mats := MappedAction( C.factor, act ) * One( fld ); C.smats := MappedAction( C.super, act ) * One( fld ); # add info on extension AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); # invariant subspaces orbs := OrbitsInvariantSubspaces( C, C.dim ); com := []; while Length( orbs ) > 0 do o := Remove(orbs); t := cl.open / p^(l - Length(o.repr)); if IsInt( t ) then # copy sub and adjust the entries to the layer sub := InduceToFactor( C, o ); AddInversesCR( sub ); # finally, compute the desired complements new := ComplementClassesCR( sub ); for i in [1..Length(new)] do new[i].open := t; od; Append( com, new ); # if there are no complements, then reduce invs if Length( new ) = 0 then orbs := Filtered( orbs, x -> not IsSubbasis( o.repr, x.repr ) ); fi; fi; od; return com; end; ############################################################################# ## #F LowIndexSubgroupsFaLayer( cl, pcplist, l, act ) ## ## Compute low-index subgroups in not containing the free abelian ## subfactor corresponding to . The index of the computed subgroups ## is limited by l. ## LowIndexSubgroupsFaLayer := function( clG, adj, l, act ) local fac, grp, pr, todo, done, news, i, use, cl, d, tmp; fac := Collected( Factors( l ) ); grp := [clG]; for pr in fac do todo := ShallowCopy( grp ); done := []; news := []; for i in [1..pr[2]] do use := adj[pr[1]][i]; for cl in todo do d := Valuation( cl.open, pr[1] ); tmp := LowIndexSubgroupsEaLayer( cl, use, d, act ); Append( news, tmp ); od; Append( done, todo ); todo := ShallowCopy( news ); news := []; od; grp := Concatenation( done, todo ); od; # return computed groups without the original group return grp{[2..Length(grp)]}; end; ############################################################################# ## #F PowerPcpsByIndex( pcp, l ) ## PowerPcpsByIndex := function( pcp, l ) local fac, ser, s, B, pr, i, A; # loop over series trough A/B fac := Collected( Factors( l ) ); # create pcp's ser := []; s := 1; B := GroupOfPcp( pcp ); for pr in fac do ser[pr[1]] := []; for i in [1..pr[2]] do s := s * pr[1]; A := ShallowCopy( B ); B := SubgroupByIgs( GroupOfPcp( pcp ), DenominatorOfPcp( pcp ), List( pcp, x -> x^s ) ); ser[pr[1]][i] := Pcp( A, B ); od; od; return ser; end; ############################################################################# ## #F LowIndexSubgroupsBySeries( G, n, pcps ) ## LowIndexSubgroupsBySeries := function( G, n, pcps ) local grps, all, i, pcp, p, A, mats, new, adj, cl, l, d, act, tmp; # set up all := Pcp( G ); # the first layer grps := SubgroupsFirstLayerByIndex( G, pcps[1], n ); # loop down the series for i in [2..Length(pcps)] do pcp := pcps[i]; p := RelativeOrdersOfPcp( pcp )[1]; A := GroupOfPcp( pcp ); Info( InfoPcpGrp, 1, "starting layer ",i, " of type ",p, " ^ ", Length(pcp), " with ",Length(grps), " groups"); # compute action on layer - note if it is central mats := List( all, x -> List(pcp, y -> ExponentsByPcp(pcp, y^x))); act := rec( pcp := all, mats := mats ); act.central := ForAll( mats, x -> x = x^0 ); # loop over all subgroups new := []; adj := []; for cl in grps do # now pass it on l := cl.open; if l > 1 and p = 0 then if not IsBound( adj[l] ) then adj[l] := PowerPcpsByIndex( pcp, l ); fi; tmp := LowIndexSubgroupsFaLayer( cl, adj[l], l, act ); Info( InfoPcpGrp, 2, " found ", Length(tmp), " new groups"); Append( new, tmp ); elif l > 1 then d := Valuation( l, p ); tmp := LowIndexSubgroupsEaLayer( cl, pcp, d, act ); Info( InfoPcpGrp, 2, " found ", Length(tmp), " new groups"); Append( new, tmp ); fi; od; Append( grps, new ); od; return Filtered( grps, x -> x.open = 1 ); end; ############################################################################# ## #F LowIndexSubgroupClasses( G, n ) ## LowIndexSubgroupClassesPcpGroup := function( G, n ) local efa, grps, i, tmp; # loop over series efa := PcpsOfEfaSeries( G ); grps := LowIndexSubgroupsBySeries( G, n, efa ); # translate to classes and return for i in [1..Length(grps)] do tmp := ConjugacyClassSubgroups( G, grps[i].repr ); SetStabilizerOfExternalSet( tmp, grps[i].norm ); grps[i] := tmp; od; return grps; end; InstallMethod( LowIndexSubgroupClassesOp, "for pcp groups", true, [IsPcpGroup, IsPosInt], 0, function( G, n ) return LowIndexSubgroupClassesPcpGroup( G, n ); end ); polycyclic-2.16/gap/pcpgrp/centcon.gi0000644000076600000240000002421613706672341016655 0ustar mhornstaff############################################################################ ## #W centcon.gi Polycyc Bettina Eick ## ## Computing centralizers of elements and subgroups. ## Solving the conjugacy problem for elements. ## ############################################################################# ## #F AffineActionByElement( gens, pcp, g ) ## AffineActionByElement := function( gens, pcp, g ) local lin, i, j, c; lin := LinearActionOnPcp( gens, pcp ); for i in [1..Length(gens)] do # add column for j in [1..Length(lin[i])] do Add( lin[i][j], 0 ); od; # add row c := ExponentsByPcp( pcp, Comm( g, gens[i] ) ); Add( c, 1 ); Add( lin[i], c ); od; return lin; end; ############################################################################# ## #F IsCentralLayer( G, pcp ) ## IsCentralLayer := function( G, pcp ) local g, h, e, f; for g in Igs(G) do for h in AsList(pcp) do e := ExponentsByPcp( pcp, Comm(g,h) ); if e <> 0*e then return false; fi; od; od; return true; end; ############################################################################# ## #F CentralizerByCentralLayer( gens, cent, pcp ) ## CentralizerByCentralLayer := function( gens, cent, pcp ) local rels, g, matrix, null; rels := ExponentRelationMatrix( pcp ); for g in gens do if Length( cent ) = 0 then return cent; fi; # set up matrix matrix := List( cent, h -> ExponentsByPcp( pcp, Comm(h,g) ) ); Append( matrix, rels ); # get nullspace null := PcpNullspaceIntMat( matrix ); null := null{[1..Length(null)]}{[1..Length(cent)]}; # calculate elements corresponding to null cent := List( null, x -> MappedVector( x, cent ) ); cent := Filtered( cent, x -> x <> x^0 ); od; return cent; end; ############################################################################# ## #F CentralizerBySeries(G, g, pcps) ## ## possible improvements: - refine layers of given series by fixedpoints ## - use translation subgroup induced by layers ## CentralizerBySeries := function( G, elms, pcps ) local i, C, R, pcp, rel, p, d, e, N, M, gen, lin, stb, F, fac, act, nat, CM, NM, gM, g; # do a simple check elms := Filtered( elms, x -> x <> One(G) ); if Length(elms) = 0 then return G; fi; # loop over series C := G; for i in [2..Length(pcps)] do # get infos on layer pcp := pcps[i]; rel := RelativeOrdersOfPcp( pcp ); p := rel[1]; d := Length( rel ); e := List( [1..d], x -> 0 ); Add( e, 1 ); # if the layer is central if IsCentralLayer( C, pcp ) then Info( InfoPcpGrp, 1, "got central layer of type ",p,"^",d); N := SubgroupByIgs( G, NumeratorOfPcp(pcp) ); gen := Pcp(C, N); stb := CentralizerByCentralLayer( elms, AsList(gen), pcp ); stb := AddIgsToIgs( stb, Igs(N) ); C := SubgroupByIgs( G, stb ); # if it is a non-central finite layer elif p > 0 then Info( InfoPcpGrp, 1, "got finite layer of type ",p,"^",d); F := GF(p); M := SubgroupByIgs( G, DenominatorOfPcp(pcp) ); for g in elms do fac := Pcp( C, M ); act := AffineActionByElement( fac, pcp, g ); act := InducedByField( act, F ); stb := PcpOrbitStabilizer( e*One(F), fac, act, OnRight ); stb := AddIgsToIgs( stb.stab, Igs(M) ); C := SubgroupByIgs( G, stb ); od; # if it is infinite and not-central else Info( InfoPcpGrp, 1, "got infinite layer of type ",p,"^",d); M := SubgroupByIgs( G, DenominatorOfPcp(pcp) ); N := SubgroupByIgs( G, NumeratorOfPcp(pcp) ); nat := NaturalHomomorphismByNormalSubgroup( G, M ); NM := Image( nat, N ); CM := Image( nat, C ); for g in elms do gM := Image( nat, g ); if gM <> gM^0 then act := AffineActionByElement( Pcp(CM), Pcp(NM), gM ); CM := StabilizerIntegralAction( CM, act, e ); fi; od; C := PreImage( nat, CM ); fi; od; # add checking if required if CHECK_CENT@ then Info( InfoPcpGrp, 1, "check result"); for g in elms do if ForAny( Igs(C), x -> Comm(g,x) <> One(G) ) then Error("centralizer is not centralizing"); fi; od; fi; # now return the result return C; end; ############################################################################# ## #F Centralizer ## CentralizerPcpGroup := function( G, g ) # get arguments if IsPcpGroup(g) then g := SmallGeneratingSet(g); elif IsPcpElement(g) then g := [g]; fi; # check if ForAny( g, x -> not x in G ) then Error("elements must be contained in group"); fi; # compute return CentralizerBySeries( G, g, PcpsOfEfaSeries(G) ); end; InstallMethod( CentralizerOp, "for a pcp group", IsCollsElms, [IsPcpGroup and IsNilpotentGroup, IsPcpElement], CentralizerNilpotentPcpGroup ); InstallMethod( CentralizerOp, "for a pcp group", IsIdenticalObj, [IsPcpGroup and IsNilpotentGroup, IsPcpGroup], CentralizerNilpotentPcpGroup ); InstallMethod( CentralizerOp, "for a pcp group", IsCollsElms, [IsPcpGroup, IsPcpElement], CentralizerPcpGroup ); InstallMethod( CentralizerOp, "for a pcp group", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], CentralizerPcpGroup ); ############################################################################# ## #F ConjugacyByCentralLayer( g, h, cent, pcp ) ## ConjugacyByCentralLayer := function( g, h, cent, pcp ) local matrix, c, solv, null; # first check c := ExponentsByPcp( pcp, g^-1 * h ); if Length(cent) = 0 then if c = 0*c then return rec( stab := cent, prei := g^0 ); else return false; fi; fi; # set up matrix matrix := List( cent, x -> ExponentsByPcp( pcp, Comm(x,g) ) ); Append( matrix, ExponentRelationMatrix( pcp ) ); # get solution solv := PcpSolutionIntMat( matrix, -c ); if IsBool( solv ) then return false; fi; solv := solv{[1..Length(cent)]}; # get nullspace null := PcpNullspaceIntMat( matrix ); null := null{[1..Length(null)]}{[1..Length(cent)]}; # calculate elements solv := MappedVector( solv, cent ); cent := List( null, x -> MappedVector( x, cent ) ); cent := Filtered( cent, x -> x <> x^0 ); return rec( stab := cent, prei := solv ); end; ############################################################################# ## #F ConjugacyElementsBySeries( G, g, h, pcps ) ## ConjugacyElementsBySeries := function( G, g, h, pcps ) local C, k, eg, eh, i, pcp, rel, p, d, e, f, c, j, N, M, fac, stb, F, act, nat; # do a simple check if Order(g) <> Order(h) then return false; fi; # the first layer eg := ExponentsByPcp(pcps[1], g); eh := ExponentsByPcp(pcps[1], h); if eg <> eh then return false; fi; C := G; k := One(G); # the other layers for i in [2..Length(pcps)] do # get infos on layer pcp := pcps[i]; rel := RelativeOrdersOfPcp( pcp ); p := rel[1]; d := Length( rel ); # set up for computation e := List( [1..d], x -> 0 ); Add( e, 1 ); c := g^k; if c = h then return k; fi; # if the layer is central if IsCentralLayer( C, pcp ) then Info( InfoPcpGrp, 1, "got central layer of type ",p,"^",d); N := SubgroupByIgs( G, NumeratorOfPcp(pcp) ); fac := Pcp(C, N); stb := ConjugacyByCentralLayer( c, h, AsList(fac), pcp ); # extract results if IsBool(stb) then return false; fi; k := k * stb.prei; stb := AddIgsToIgs( stb.stab, Igs(N) ); C := SubgroupByIgs( G, stb ); # if it is a non-central finite layer elif p > 0 then Info( InfoPcpGrp, 1, "got finite layer of type ",p,"^",d); F := GF(p); M := SubgroupByIgs( G, DenominatorOfPcp(pcp) ); f := ExponentsByPcp( pcp, c^-1*h ); Add( f, 1 ); fac := Pcp( C, M ); act := AffineActionByElement( fac, pcp, c ); act := InducedByField( act, F ); stb := PcpOrbitStabilizer( e*One(F), fac, act, OnRight ); # extract results j := Position( stb.orbit, f*One(F) ); if IsBool(j) then return false; fi; k := k * TransversalElement( j, stb, One(G) ); stb := AddIgsToIgs( stb.stab, Igs(M) ); C := SubgroupByIgs( G, stb ); # if it is infinite and not-central else Info( InfoPcpGrp, 1, "got infinite layer of type ",p,"^",d); M := SubgroupByIgs( G, DenominatorOfPcp(pcp) ); f := ExponentsByPcp( pcp, c^-1*h ); Add( f, 1 ); fac := Pcp( C, M ); act := AffineActionByElement( fac, pcp, g ); nat := NaturalHomomorphismByNormalSubgroup( C, M ); stb := OrbitIntegralAction( Image(nat), act, e, f ); # extract results if IsBool(stb) then return false; fi; C := PreImage( nat, stb.stab ); k := k * PreImagesRepresentative( nat, stb.prei ); fi; od; # add checking if required if CHECK_CENT@ then Info( InfoPcpGrp, 1, "check result"); if g^k <> h then Error("conjugating element is incorrect"); fi; fi; # now return the result return k; end; ############################################################################# ## #F IsConjugate( G, g, h ) #F ConjugacyElementsPcpGroup( G, g, h ) ## InstallMethod( IsConjugate, "for a pcp group", IsCollsElmsElms, [IsPcpGroup, IsPcpElement, IsPcpElement], function( G, g, h ) local c; c := ConjugacyElementsBySeries( G, g, h, PcpsOfEfaSeries(G) ); return (c <> false); end ); polycyclic-2.16/gap/pcpgrp/torsion.gd0000644000076600000240000000443413706672341016714 0ustar mhornstaff############################################################################ ## ## Polycyclic: Computation with polycyclic groups ## Copyright (C) 1999-2012 Bettina Eick ## Copyright (C) 1999-2007 Werner Nickel ## Copyright (C) 2010-2012 Max Horn ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 2 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ## DeclareAttribute( "TorsionSubgroup", IsGroup ); DeclareAttribute( "NormalTorsionSubgroup", IsGroup ); DeclareAttribute( "FiniteSubgroupClasses", IsGroup ); DeclareGlobalFunction( "RootSet" ); # TODO: declare IsTorsionFree for IsMagma or IsObject and not just IsGroup? DeclareProperty( "IsTorsionFree", IsGroup ); InstallSubsetMaintenance( IsTorsionFree, IsGroup and IsTorsionFree, IsGroup ); InstallTrueMethod( IsTorsionFree, IsGroup and IsTrivial ); InstallTrueMethod( HasIsTorsionFree, IsGroup and IsFinite and IsNonTrivial ); InstallTrueMethod( IsTorsionFree, IsFreeGroup ); InstallIsomorphismMaintenance( IsTorsionFree, IsGroup and IsTorsionFree, IsGroup ); # TODO: declare IsFreeAbelian for IsMagma or IsObject and not just IsGroup? DeclareProperty( "IsFreeAbelian", IsGroup ); InstallSubsetMaintenance( IsFreeAbelian, IsGroup and IsFreeAbelian, IsGroup ); InstallTrueMethod( IsFreeAbelian, IsGroup and IsTrivial ); InstallTrueMethod( HasIsFreeAbelian, IsGroup and IsFinite and IsNonTrivial ); InstallTrueMethod( IsFreeAbelian, IsFinitelyGeneratedGroup and IsTorsionFree and IsAbelian); InstallIsomorphismMaintenance( IsFreeAbelian, IsGroup and IsFreeAbelian, IsGroup ); # free abelian groups are abelian and torsion free InstallTrueMethod( IsAbelian, IsGroup and IsFreeAbelian ); InstallTrueMethod( IsTorsionFree, IsGroup and IsFreeAbelian ); polycyclic-2.16/gap/pcpgrp/README0000644000076600000240000000260713706672341015563 0ustar mhornstaff gap/pcpgrp: higher level functions for pcp groups: fitting.gi -- fitting subgroup, centre, FC-centre, etc. pcpattr.gi -- some general functions, not installed attributes maxsub.gi -- maximal subgroups findex.gi -- subgroups of finite index (LowIndex) nindex.gi -- normal subgroups of finite index polyz.gi -- compute a poly-Z normal subgroup grpinva.gi -- invariant subspaces torsion.gi -- finite subgroups (TorsionSubgroup / FiniteSubgroups) nilpot.gi -- centralizers in nilpotent groups inters.gi -- intersection with normal subgroup centnorm.gi -- centralizers and normalizers ############################################################################# Centalizer Normalizer Intersection A SemiSimpleEfaSeries (this is not unique for G!) A FittingSubgroup A Centre A UpperCentralSeriesOfGroup A FCCentre F NilpotentByAbelianByFiniteStructure A IsNilpotentByFinite A MinimalGeneratingSet A MaximalSubgroupClassesByIndex A SylowSubgroups A LowIndexSubgroupClasses A LowIndexNormalSubgroups (F NilpotentByAbelianNormalSubgroup - application of LowIndexNormals) F PolyZNormalSubgroup A TorsionSubgroup A NormalTorsionSubgroup P IsTorsionFree A FiniteSubgroupClasses F RootSet ############################################################################# Todo - Centralizer - Normalizer - Intersection (check with inters.gi) - clear up general.gi polycyclic-2.16/gap/pcpgrp/fitting.gi0000644000076600000240000001646213706672341016674 0ustar mhornstaff############################################################################# ## #W fitting.gi Polycyc Bettina Eick ## ## Fitting subgroup, Centre, FCCentre and NilpotentByAbelianByFiniteSeries. ## ############################################################################# ## #A SemiSimpleEfaSeries( G ) ## InstallMethod( SemiSimpleEfaSeries, "for pcp groups", [IsPcpGroup], function(G) local efas, pcps, refs, i, rels, d, mats, subs, j, gens, U, f; efas := EfaSeries( G ); pcps := PcpsBySeries( efas, "snf" ); refs := [G]; # loop over series and refine each factor for i in [1..Length(pcps)] do # compute radical series rels := RelativeOrdersOfPcp( pcps[i] ); mats := LinearActionOnPcp( Igs(G), pcps[i] ); d := Length( rels ); if rels[1] > 0 then f := GF( rels[1] ); mats := InducedByField( mats, f ); subs := RadicalSeriesOfFiniteModule( mats, d, f ); fi; if rels[1] = 0 then subs := RadicalSeriesOfRationalModule( mats, d ); subs := List( subs, x -> PurifyRationalBase( x ) ); fi; # refine pcp by subs for j in [2..Length(subs)] do gens := List( subs[j], x -> MappedVector(x, pcps[i]) ); gens := AddIgsToIgs( gens, DenominatorOfPcp( pcps[i] ) ); U := SubgroupByIgs( G, gens ); Add( refs, U ); od; od; # that's it return refs; end ); ## return LowerCentralSeries for nilpotent groups? ############################################################################# ## #F FittingSubgroup( G ) ## InstallMethod( FittingSubgroup, "for pcp groups", [IsPcpGroup], function( G ) local efas, pcps, l, F, i; efas := SemiSimpleEfaSeries( G ); pcps := PcpsBySeries( efas, "snf" ); l := Length( efas ) - 1; Info( InfoPcpGrp, 1, "determined semisimple series of length ",l); # compute centralizer of ssefa - finite cases first F := G; for i in [1..l] do Info( InfoPcpGrp, 1, "centralize ",i,"th layer - finite"); F := KernelOfFiniteAction( F, pcps[i] ); if RelativeOrdersOfPcp(pcps[i])[1] = 0 then Info( InfoPcpGrp, 1, "centralize ",i,"th layer - infinite"); F := KernelOfCongruenceAction( F, pcps[i] ); fi; od; SetIsNilpotentGroup( F, true ); if IndexNC( G, F ) = 1 then SetIsNilpotentGroup( G, true ); fi; return F; end ); InstallMethod( FittingSubgroup, "for pcp groups", [IsPcpGroup and IsNilpotentGroup], IdFunc ); ############################################################################# ## #F IsNilpotentByFinite( G ) ## InstallMethod( IsNilpotentByFinite, "for pcp groups", [IsPcpGroup], function( G ) local efas, pcps, l, F, i, mats, idmt; efas := SemiSimpleEfaSeries( G ); pcps := PcpsBySeries( efas, "snf" ); l := Length( efas ) - 1; Info( InfoPcpGrp, 1, "determined semisimple series of length ",l); F := G; for i in [1..l] do Info( InfoPcpGrp, 1, "centralize ",i,"th layer"); F := KernelOfFiniteAction( F, pcps[i] ); if RelativeOrdersOfPcp(pcps[i])[1] = 0 then mats := LinearActionOnPcp( Igs(F), pcps[i] ); idmt := IdentityMat( Length( pcps[i] ) ); if ForAny( mats, x -> x <> idmt ) then return false; fi; fi; od; return true; end ); ############################################################################# ## #F Centre( G ) ## CentrePcpGroup := function( G ) local F, C, pcp, mat, rel, fix, i, gens, g; # compute Z(Fit(G)) F := FittingSubgroup( G ); C := Centre( F ); # find iterated centralizer under action of G gens := Pcp( G, F ); for g in Reversed(AsList( gens )) do # get pcp and its relation matrix pcp := Pcp( C, "snf" ); rel := ExponentRelationMatrix( pcp ); if Length( pcp ) = 0 then return C; fi; # compute action by g on pcp mat := LinearActionOnPcp( [g], pcp )[1]; mat := mat - mat^0; Append( mat, rel ); # compute fixed point space fix := PcpNullspaceIntMat( mat, Length( mat ) ); for i in [1..Length(fix)] do fix[i] := MappedVector( fix[i]{[1..Length(pcp)]}, pcp ); od; C := Subgroup( G, fix ); od; return C; end; InstallMethod( Centre, "for pcp groups", [IsPcpGroup], function( G ) if IsAbelian(G) then return G; elif IsNilpotentGroup(G) then return CentreNilpotentPcpGroup(G); else return CentrePcpGroup(G); fi; end ); ############################################################################# ## #F UpperCentralSeriesOfGroup( G ) ## UpperCentralSeriesPcpGroup := function( G ) local C, upp, nat, N, H; C := TrivialSubgroup(G); upp := [C]; N := Centre(G); while IndexNC( N, C ) > 1 do C := N; Add( upp, C ); nat := NaturalHomomorphismByNormalSubgroup( G, C ); H := Image( nat ); N := PreImage( nat, Centre(H) ); od; return Reversed( upp ); end; InstallMethod( UpperCentralSeriesOfGroup, [IsPcpGroup], function( G ) if IsNilpotentGroup(G) then return UpperCentralSeriesNilpotentPcpGroup(G); fi; return UpperCentralSeriesPcpGroup(G); end ); ############################################################################# ## #F FCCentre( G ) ## FCCentrePcpGroup := function( G ) local N, hom, H, F, C, K, gens, g, pcp, mat, fix; # mod out torsion N := NormalTorsionSubgroup( G ); hom := NaturalHomomorphismByNormalSubgroup( G, N ); H := Image( hom ); # compute Z(Fit(H)) F := FittingSubgroup( H ); C := Centre( F ); if Size(C) = 1 then return N; fi; # find iterated centralizer under action of K_p(G) K := KernelOfFiniteAction( H, Pcp(C) ); gens := Pcp( K, F ); for g in AsList( gens ) do # get pcp pcp := Pcp( C ); if Length( pcp ) = 0 then return C; fi; # compute action by g on pcp mat := LinearActionOnPcp( [g], pcp )[1]; mat := mat - mat^0; # compute fixed point space fix := PcpNullspaceIntMat( mat, Length( mat ) ); C := Subgroup( C, List( fix, x -> MappedVector( x, pcp ) ) ); od; return PreImage( hom, C ); end; InstallMethod( FCCentre, "FCCentre for pcp groups", [IsPcpGroup], function( G ) if IsFinite(G) then return G; fi; return FCCentrePcpGroup(G); end ); InstallMethod( FCCentre, "FCCentre for finite groups", [IsGroup and IsFinite], IdFunc ); ############################################################################# ## #F NilpotentByAbelianByFiniteSeries( G ) ## InstallGlobalFunction( NilpotentByAbelianByFiniteSeries, function( G ) local F, U, nath, L, A; # first step - get the Fitting subgroup and check its index F := FittingSubgroup( G ); U := TrivialSubgroup( G ); if IndexNC( G, F ) < infinity then return [G, F, F, U]; fi; # if this is not sufficient, then use Fitting factor nath := NaturalHomomorphismByNormalSubgroup( G, F ); L := FittingSubgroup( Image( nath ) ); A := PreImage( nath, Centre(L) ); if IndexNC( G, A ) = infinity then Error("wrong subgroup"); fi; return [G, A, F, U]; end ); polycyclic-2.16/gap/pcpgrp/wreath.gi0000644000076600000240000001114213706672341016510 0ustar mhornstaff############################################################################# ## #W wreath.gi Package Polycyclic Bettina Eick ## ## Computing a wreath product of pcp groups. ## ############################################################################# ## #F WreathProductPcp( G, H, act ) ## InstallOtherMethod( WreathProduct, [IsPcpGroup, IsPcpGroup, IsMapping], function( G, H, act ) return WreathProduct( G, H, act, Maximum( 1, LargestMovedPoint( Image( act )))); end); InstallOtherMethod( WreathProduct, [IsPcpGroup, IsPcpGroup, IsMapping, IsPosInt], function( G, H, act, l ) local pcpG, relG, pcpH, relH, n, m, coll, i, k, c, e, o, j, W, a, h, ShiftedObject; ############################################################################# ## #F ShiftedObject( exp, shift ) ## ShiftedObject := function( exp, c ) local obj, i; obj := []; for i in [1..Length(exp)] do if exp[i] <> 0 then Append( obj, [c+i, exp[i]] ); fi; od; return obj; end; pcpG := Pcp(G); relG := RelativeOrdersOfPcp( pcpG ); pcpH := Pcp(H); relH := RelativeOrdersOfPcp( pcpH ); n := Length( pcpG ); m := Length( pcpH ); coll := FromTheLeftCollector( m + n*l ); # relations of G for i in [1..n] do if relG[i] > 0 then e := ExponentsByPcp( pcpG, pcpG[i]^relG[i] ); for k in [1..l] do c := m + (k-1)*n; o := ShiftedObject( e, c ); SetRelativeOrder( coll, c+i, relG[i] ); SetPower( coll, c+i, o ); od; fi; for j in [1..i-1] do e := ExponentsByPcp( pcpG, pcpG[i]^pcpG[j] ); for k in [1..l] do c := m + (k-1)*n; o := ShiftedObject( e, c ); SetConjugate( coll, c+i, c+j, o ); od; e := ExponentsByPcp( pcpG, pcpG[i]^(pcpG[j]^-1) ); for k in [1..l] do c := m + (k-1)*n; o := ShiftedObject( e, c ); SetConjugate( coll, c+i, -(c+j), o ); od; od; od; # relations of H for i in [1..m] do if relH[i] > 0 then e := ExponentsByPcp( pcpH, pcpH[i]^relH[i] ); o := ShiftedObject( e, 0 ); SetRelativeOrder( coll, i, relH[i] ); SetPower( coll, i, o ); fi; for j in [1..i-1] do e := ExponentsByPcp( pcpH, pcpH[i]^pcpH[j] ); o := ShiftedObject( e, 0 ); SetConjugate( coll, i, j, o ); e := ExponentsByPcp( pcpH, pcpH[i]^(pcpH[j]^-1) ); o := ShiftedObject( e, 0 ); SetConjugate( coll, i, -j, o ); od; od; # action of H for j in [1..m] do a := Image( act, pcpH[j] ); for k in [1..l] do h := k^a; for i in [1..n] do o := [m + (h-1)*n + i, 1]; SetConjugate( coll, m + (k-1)*n + i, j, o ); od; h := k^(a^-1); for i in [1..n] do o := [m + (h-1)*n + i, 1]; SetConjugate( coll, m + (k-1)*n + i, -j, o ); od; od; od; UpdatePolycyclicCollector( coll ); W := PcpGroupByCollectorNC( coll ); SetWreathProductInfo( W, rec(l := l, m := m, n := n, G := G, pcpG := pcpG, genG := GeneratorsOfGroup(G), H := H, pcpH := pcpH, genH := GeneratorsOfGroup(H), coll := coll, embeddings := []) ); return W; end ); InstallMethod(Embedding, "pcp wreath product", [IsPcpGroup and HasWreathProductInfo, IsPosInt], function(W,i) local info, FilledIn; FilledIn := function( exp, shift, len ) local s; s := List([1..len], i->0); s{shift+[1..Length(exp)]} := exp; return s; end; info := WreathProductInfo(W); if not IsBound(info.embeddings[i]) then if i<=info.l then info.embeddings[i] := GroupHomomorphismByImagesNC(info.G,W, info.genG, List(info.genG, x->PcpElementByExponents(info.coll, FilledIn(ExponentsByPcp(info.pcpG,x),info.m+(i-1)*info.n,info.m+info.l*info.n)))); elif i=info.l+1 then info.embeddings[i] := GroupHomomorphismByImagesNC(info.H,W, info.genH, List(info.genH, x->PcpElementByExponents(info.coll, FilledIn(ExponentsByPcp(info.pcpH,x),0,info.m+info.l*info.n)))); else return fail; fi; SetIsInjective(info.embeddings[i],true); fi; return info.embeddings[i]; end); polycyclic-2.16/gap/pcpgrp/tensor.gi0000644000076600000240000004024713706672341016540 0ustar mhornstaff############################################################################# ## #F AddSystem( sys, t1, t2) ## AddSystem := function( sys, t1, t2 ) if t1 = t2 then return; fi; t1 := t1 - t2; if not t1 in sys.base then Add(sys.base, t1); fi; end; ############################################################################# ## #F EvalConsistency( coll, sys ) ## InstallGlobalFunction( EvalConsistency, function( coll, sys ) local y, x, e, z, gn, gi, ps, a, w1, w2, i, j, k; # set up y := sys.len; x := NumberOfGenerators(coll)-y; e := RelativeOrders(coll); # set up zero z := List([1..x+y], x -> 0); # set up generators and inverses gn := []; gi := []; for i in [1..x] do a := ShallowCopy(z); a[i] := 1; gn[i] := a; a := ShallowCopy(z); a[i] := -1; gi[i] := a; od; # precompute pairs (i^e[i]) and (ij) and (i -j) for i > j ps := List( [1..x], x -> [] ); for i in [1..x] do if e[i] > 0 then a := ShallowCopy(z); a[i] := e[i]-1; CollectWordOrFail( coll, a, [i,1] ); ps[i][i] := a; fi; for j in [1..i-1] do a := ShallowCopy(gn[i]); CollectWordOrFail( coll, a, [j,1] ); ps[i][j] := a; a := ShallowCopy(gn[i]); CollectWordOrFail( coll, a, [j,-1] ); ps[i][i+j] := a; od; od; # consistency 1: k(ji) = (kj)i for i in [ x, x-1 .. 1 ] do for j in [ x, x-1 .. i+1 ] do for k in [ x, x-1 .. j+1 ] do # collect w1 := ShallowCopy(gn[k]); CollectWordOrFail(coll, w1, ObjByExponents(coll,ps[j][i])); w2 := ShallowCopy(ps[k][j]); CollectWordOrFail(coll, w2, [i,1]); # check and add if w1{[1..x]} <> w2{[1..x]} then Error( "k(ji) <> (kj)i" ); else AddSystem( sys, w1{[x+1..x+y]}, w2{[x+1..x+y]} ); fi; od; od; od; # consistency 2: j^(p-1) (ji) = j^p i for i in [x,x-1..1] do for j in [x,x-1..i+1] do if e[j] > 0 then # collect w1 := ShallowCopy(z); w1[j] := e[j]-1; CollectWordOrFail(coll, w1, ObjByExponents(coll, ps[j][i])); w2 := ShallowCopy(ps[j][j]); CollectWordOrFail(coll, w2, [i,1]); # check and add if w1{[1..x]} <> w2{[1..x]} then Error( "j^(p-1) (ji) <> j^p i" ); else AddSystem( sys, w1{[x+1..x+y]}, w2{[x+1..x+y]} ); fi; fi; od; od; # consistency 3: k (i^p) = (ki) i^p-1 for i in [x,x-1..1] do if e[i] > 0 then for k in [x,x-1..i+1] do # collect w1 := ShallowCopy(gn[k]); CollectWordOrFail(coll, w1, ObjByExponents(coll, ps[i][i])); w2 := ShallowCopy(ps[k][i]); CollectWordOrFail(coll, w2, [i,e[i]-1]); # check and add if w1{[1..x]} <> w2{[1..x]} then Error( "k i^p <> (ki) i^(p-1)" ); else AddSystem( sys, w1{[x+1..x+y]}, w2{[x+1..x+y]} ); fi; od; fi; od; # consistency 4: (i^p) i = i (i^p) for i in [ x, x-1 .. 1 ] do if e[i] > 0 then # collect w1 := ShallowCopy(ps[i][i]); CollectWordOrFail(coll, w1, [i,1]); w2 := ShallowCopy(gn[i]); CollectWordOrFail(coll, w2, ObjByExponents(coll,ps[i][i])); # check and add if w1{[1..x]} <> w2{[1..x]} then Error( "i i^p-1 <> i^p" ); else AddSystem( sys, w1{[x+1..x+y]}, w2{[x+1..x+y]} ); fi; fi; od; # consistency 5: j = (j -i) i for i in [x,x-1..1] do for j in [x,x-1..i+1] do if e[i] = 0 then # collect w1 := ShallowCopy(ps[j][i+j]); CollectWordOrFail( coll, w1, [i,1] ); # check and add if w1{[1..x]} <> gn[j]{[1..x]} then Error( "j <> (j -i) i" ); else AddSystem( sys, w1{[x+1..x+y]}, 0*w1{[x+1..x+y]} ); fi; fi; od; od; # consistency 6: i = -j (j i) for i in [x,x-1..1] do for j in [x,x-1..i+1] do if e[j] = 0 then # collect w1 := ShallowCopy(gi[j]); CollectWordOrFail( coll, w1, ObjByExponents(coll, ps[j][i])); # check and add if w1{[1..x]} <> gn[i]{[1..x]} then Error( "i <> -j (j i)" ); else AddSystem( sys, w1{[x+1..x+y]}, 0*w1{[x+1..x+y]} ); fi; fi; od; od; # consistency 7: -i = -j (j -i) for i in [x,x-1..1] do for j in [x,x-1..i+1] do if e[i] = 0 and e[j] = 0 then # collect w1 := ShallowCopy(gi[j]); CollectWordOrFail( coll, w1, ObjByExponents(coll, ps[j][i+j])); # check and add if w1{[1..x]} <> gi[i]{[1..x]} then Error( "-i <> -j (j -i)" ); else AddSystem( sys, w1{[x+1..x+y]}, 0*w1{[x+1..x+y]} ); fi; fi; od; od; return sys; end ); ############################################################################# ## #F EvalMueRelations( coll, sys, n ) ## EvalMueRelations := function( coll, sys, n ) local y, x, z, g, h, cm, cj1, cj2, ci1, ci2, i, j, k, w, v; # set up y := sys.len; x := NumberOfGenerators(coll)-y; z := List([1..x+y], i -> 0); # gens and inverses g := List([1..2*n], i -> [i,1]); h := List([1..2*n], i -> FromTheLeftCollector_Inverse(coll,[i,1])); # precompute commutators cm := List([1..n], i -> []); for i in [1..n] do for j in [1..n] do w := ShallowCopy(z); w[i] := 1; w[n+j] := 1; CollectWordOrFail(coll, w, h[i]); CollectWordOrFail(coll, w, h[n+j]); cm[i][j] := ObjByExponents(coll, w); od; od; # precompute conjugates and inverses cj1 := List([1..n], i -> []); ci1 := List([1..n], i -> []); cj2 := List([1..n], i -> []); ci2 := List([1..n], i -> []); for i in [1..n] do for j in [1..n] do # IActs( j, i ) if i = j then cj1[j][i] := ShallowCopy(g[i]); ci1[j][i] := ShallowCopy(h[i]); else w := ShallowCopy(z); w[i] := 1; CollectWordOrFail(coll, w, g[j]); CollectWordOrFail(coll, w, h[i]); cj1[j][i] := ObjByExponents(coll, w); ci1[j][i] := FromTheLeftCollector_Inverse(coll,cj1[j][i]); fi; # IActs( n+j, n+i ) if i = j then cj2[j][i] := ShallowCopy(g[n+i]); ci2[j][i] := ShallowCopy(h[n+i]); else w := ShallowCopy(z); w[n+i] := 1; CollectWordOrFail(coll, w, g[n+j]); CollectWordOrFail(coll, w, h[n+i]); cj2[j][i] := ObjByExponents(coll, w); ci2[j][i] := FromTheLeftCollector_Inverse(coll,cj2[j][i]); fi; od; od; # loop over relators for i in [1..n] do for j in [1..n] do for k in [1..n] do # the right hand side v := ShallowCopy(z); CollectWordOrFail(coll, v, cj1[i][k]); CollectWordOrFail(coll, v, cj2[j][k]); CollectWordOrFail(coll, v, ci1[i][k]); CollectWordOrFail(coll, v, ci2[j][k]); # first left hand side w := ShallowCopy(z); w[k] := 1; CollectWordOrFail(coll, w, cm[i][j]); CollectWordOrFail(coll, w, h[k]); if w{[1..x]} <> v{[1..x]} then Error("no epimorphism"); else AddSystem( sys, w{[x+1..x+y]}, v{[x+1..x+y]}); fi; # second left hand side w := ShallowCopy(z); w[n+k] := 1; CollectWordOrFail(coll, w, cm[i][j]); CollectWordOrFail(coll, w, h[n+k]); if w{[1..x]} <> v{[1..x]} then Error("no epimorphism"); else AddSystem( sys, w{[x+1..x+y]}, v{[x+1..x+y]}); fi; od; od; od; end; ############################################################################# ## #F CollectorCentralCover(S) ## CollectorCentralCover:= function(S) local s, x, r, y, coll, k, i, j, e, n; # get info on G n := Length(Igs(S!.group)); # get info s := Pcp(S); x := Length(s); r := RelativeOrdersOfPcp(s); # the size of the extension module y := x*(x-1)/2 # one new generator for each conjugate relation, # for each power relation, + Number( r{[2*n+1..Length(r)]}, i -> i > 0 ) - n*(n-1); # but not for the two copies of relations of the # original group. # Print( "# CollectorCentralCover: Setting up collector with ", x+y, # " generators\n" ); # set up coll := FromTheLeftCollector(x+y); # add relations of S k := x; for i in [1..x] do SetRelativeOrder(coll, i, r[i]); if r[i] > 0 then e := ObjByExponents(coll, ExponentsByPcp(s, s[i]^r[i])); if i > 2*n then k := k+1; Append(e, [k,1]); fi; SetPower(coll,i,e); fi; for j in [1..i-1] do e := ObjByExponents(coll, ExponentsByPcp(s, s[i]^s[j])); if (i>n) and (i>2*n or not (j in [n+1..2*n])) then k := k+1; Append(e, [k,1]); fi; SetConjugate(coll,i,j,e); od; od; # update and return UpdatePolycyclicCollector(coll); return coll; end; ############################################################################# ## #F QuotientBySystem( coll, sys, n ) ## InstallGlobalFunction( QuotientBySystem, function(coll, sys, n) local y, x, e, z, M, D, P, Q, d, f, l, c, i, k, j, a, b; # set up y := sys.len; x := NumberOfGenerators(coll)-y; e := RelativeOrders(coll); z := List([1..x], i->0); # set up module M := sys.base; if Length(M) = 0 then M := NullMat(sys.len, sys.len); fi; if Length(M) < Length(M[1]) then for i in [1..Length(M[1])-Length(M)] do Add(M, 0*M[1]); od; fi; # Print( "# QuotientBySystem: Dealing with ", # Length(M), "x", Length(M[1]), "-matrix\n" ); if M = 0*M or USE_NFMI@ then D := NormalFormIntMat(M,13); Q := D.coltrans; D := D.normal; d := DiagonalOfMat( D ); else D := NormalFormConsistencyRelations(M); Q := D.coltrans; D := D.normal; d := [1..Length(M[1])] * 0; d{List( D, r->PositionNot( r, 0 ) )} := List( D, r->First( r, e->e<>0 ) ); fi; # filter info f := Filtered([1..Length(d)], x -> d[x] <> 1); l := Length(f); # inialize new collector for extension # Print( "# QuotientBySystem: Setting up collector with ", x+l, # " generators\n" ); c := FromTheLeftCollector(x+l); # add relative orders of module for i in [1..l] do SetRelativeOrder(c, x+i, d[f[i]]); od; # add relations of factor k := 0; for i in [1..x] do SetRelativeOrder(c, i, e[i]); if e[i]>0 then a := GetPower(coll, i); a := ReduceTail( a, x, Q, d, f ); SetPower(c, i, a ); fi; for j in [1..i-1] do a := GetConjugate(coll, i, j); a := ReduceTail( a, x, Q, d, f ); SetConjugate(c, i, j, a ); if e[j] = 0 then a := GetConjugate(coll, i, -j); a := ReduceTail( a, x, Q, d, f ); SetConjugate(c, i, -j, a ); fi; od; od; if CHECK_SCHUR_PCP@ then return PcpGroupByCollector(c); else UpdatePolycyclicCollector(c); return PcpGroupByCollectorNC(c); fi; end ); ############################################################################# ## #F NonAbelianTensorSquarePlus(G) . . . . . . . . (G otimes G) by (G times G) ## ## This is the group nu(G) in our paper. The following function computes the ## epimorphisms of nu(G) onto tau(G). ## # FIXME: This function is documented and should be turned into an attribute NonAbelianTensorSquarePlusEpimorphism := function(G) local n, embed, S, coll, y, sys, T, lift; if Size(G) = 1 then return IdentityMapping( G ); fi; # some info n := Length(Igs(G)); # set up quotient embed := NonAbelianExteriorSquarePlusEmbedding(G); S := Range( embed ); S!.embedding := embed; # set up covering group coll := CollectorCentralCover(S); # extract module y := NumberOfGenerators(coll) - Length(Igs(S)); # set up system sys := CRSystem(1, y, 0); # evaluate EvalConsistency( coll, sys ); EvalMueRelations( coll, sys, n ); # get group defined by resulting system T := QuotientBySystem( coll, sys, n ); # enforce epimorphism T := Subgroup(T, Igs(T){[1..2*n]}); # construct homomorphism from nu(G) to tau(G) lift := GroupHomomorphismByImagesNC( T,S, Igs(T){[1..2*n]},Igs(S){[1..2*n]} ); SetIsSurjective( lift, true ); return lift; end; # FIXME: This function is documented and should be turned into an attribute NonAbelianTensorSquarePlus := function( G ) return Source( NonAbelianTensorSquarePlusEpimorphism( G ) ); end; ############################################################################# ## #F NonAbelianTensorSquare(G). . . . . . . . . . . . . . . . . . .(G otimes G) ## # FIXME: This function is documented and should be turned into an attribute NonAbelianTensorSquareEpimorphism := function( G ) local n, epi, T, U, t, r, c, i, j, GoG, gens, embed, imgs, alpha; if Size(G) = 1 then return IdentityMapping(G); fi; # set up n := Length(Pcp(G)); # tensor square plus epi := NonAbelianTensorSquarePlusEpimorphism(G); T := Source( epi ); U := Parent(T); t := Pcp(U); r := RelativeOrdersOfPcp(t); # get relevant subgroup using commutators c := []; for i in [1..n] do for j in [1..n] do Add(c, Comm(t[i], t[n+j])); if r[i]=0 then Add(c, Comm(t[i]^-1, t[n+j])); fi; if r[j]=0 then Add(c, Comm(t[i], t[n+j]^-1)); fi; if r[i]=0 and r[j]=0 then Add(c, Comm(t[i]^-1, t[n+j]^-1)); fi; od; od; ## construct homomorphism G otimes G --> G^G ## we don't just want G^G as a subgroup of tau(G) but we want to go back ## to G^G as constructed by NonAbelianExteriorSquarePlus. (G^G)+ has the ## component .embedding which embeds G^G into (G^G)+ GoG := Subgroup(U, c); gens := GeneratorsOfGroup( GoG ); embed := Image( epi )!.embedding; imgs := List( gens, g->PreImagesRepresentative( embed, Image( epi, g ) ) ); alpha := GroupHomomorphismByImagesNC( GoG, Source( embed ), gens, imgs ); SetIsSurjective( alpha, true ); return alpha; end; InstallMethod( NonAbelianTensorSquare, [IsPcpGroup], function(G) return Source( NonAbelianTensorSquareEpimorphism( G ) ); end ); ############################################################################# ## #F WhiteheadQuadraticFunctor(G) . . . . . . . . . . . . . . . . . (Gamma(G)) ## # FIXME: This function is documented and should be turned into an attribute WhiteheadQuadraticFunctor := function(G) local invs, news, i; invs := AbelianInvariants(G); news := []; for i in [1..Length(invs)] do if IsInt(invs[i]/2) then Add(news, 2*invs[i]); else Add(news, invs[i]); fi; Append(news, List([1..i-1], x -> Gcd(invs[i], invs[x]))); od; return AbelianPcpGroup(Length(news), news); end; polycyclic-2.16/gap/pcpgrp/tensor_nq.gi0000644000076600000240000001156613706672341017240 0ustar mhornstaff## ## This file contains code to compute non-abelian tensor squares ## of nilpotent groups via nq, by brute force. ## ## This is used by the function CheckGroupsByOrder to verify that ## the polycyclic NonAbelianTensorSquare operation yields correct ## results for groups in the small groups library. ## ## Note that this code is not loaded by polycyclic, it is only here ## for reference and debugging purposes. ## # We need NqEpimorphismNilpotentQuotient from nq ############################################################################# ## #F NonAbelianTensorSquareFp(G) . . . . . . . . . . . . . . . . . (G otimes G) ## NonAbelianTensorSquareFp := function(G) local e, F, f, r, i, j, k, a, b1, b2, b, c, c1, c2, T, t; if not IsFinite(G) then return fail; fi; # set up e := Elements(G); F := FreeGroup(Length(e)^2); f := GeneratorsOfGroup(F); r := []; # collect relators for i in [1..Length(e)] do for j in [1..Length(e)] do for k in [1..Length(e)] do # e[i]*e[j] tensor e[k] a := Position(e, e[i]*e[j]); a := (a-1)*Length(e)+k; b1 := Position(e, e[i]*e[j]*e[i]^-1); b2 := Position(e, e[i]*e[k]*e[i]^-1); b := (b1-1)*Length(e)+b2; c := (i-1)*Length(e)+k; Add(r, f[a]/(f[b]*f[c])); # e[i] tensor e[j]*e[k] a := Position(e, e[j]*e[k]); a := (i-1)*Length(e)+a; b := (i-1)*Length(e)+j; c1 := Position(e, e[j]*e[i]*e[j]^-1); c2 := Position(e, e[j]*e[k]*e[j]^-1); c := (c1-1)*Length(e)+c2; Add(r, f[a]/(f[b]*f[c])); od; od; od; # the tensor T := F/r; t := GeneratorsOfGroup(T); T!.elements := e; T!.group := G; return T; end; ############################################################################# ## #F NonAbelianTensorSquarePlusFp(G) . . . . . .(G otimes G) split (G times G) ## NonAbelianTensorSquarePlusFp := function(G) local IComm, IActs, g, e, n, F, f, r, i, j, k, w, v, M, m, u; IComm := function(g,h) return g*h*g^-1*h^-1; end; IActs := function(g,h) return h*g*h^-1; end; # set up g := Igs(G); n := Length(g); e := List(g, RelativeOrderPcp); # construct F := FreeGroup(2*n); f := GeneratorsOfGroup(F); r := []; # relators of GxG for i in [1..n] do # powers w := Exponents(g[i]^e[i]); Add(r, f[i]^e[i] / MappedVector( w, f{[1..n]}) ); Add(r, f[n+i]^e[i] / MappedVector( w, f{[n+1..2*n]}) ); # commutators for j in [1..i-1] do w := Exponents(Comm(g[i], g[j])); Add(r, Comm(f[i],f[j]) / MappedVector( w, f{[1..n]}) ); Add(r, Comm(f[n+i],f[n+j]) / MappedVector( w, f{[n+1..2*n]}) ); od; od; # commutator-relators for i in [1..n] do for j in [1..n] do for k in [1..n] do # the right hand side v := IComm(IActs(f[i], f[k]), IActs(f[n+j],f[n+k])); # the left hand sides w := IActs(IComm(f[i], f[n+j]),f[k]); Add( r, w/v ); w := IActs(IComm(f[i], f[n+j]),f[n+k]); Add( r, w/v ); od; od; od; # the tensor square plus as fp group M := F/r; # the tensor square as subgroup m := GeneratorsOfGroup(M); u := Flat(List([1..n], x -> List([1..n], y -> IComm(m[x], m[n+y])))); M!.tensor := Subgroup(M, u); # that's it return M; end; NonAbelianTensorSquareViaNq := function( G ) local tsfp, phi; if LoadPackage("nq") = fail then Error( "NQ package is not installed" ); fi; if not IsNilpotent( G ) then Error( "NonAbelianTensorSquareViaNq: Group is not nilpotent, ", "therefore nq might not terminate\n" ); fi; tsfp := NonAbelianTensorSquarePlusFp( G ); phi := NqEpimorphismNilpotentQuotient( tsfp ); return Image( phi, tsfp!.tensor ); end; ############################################################################# ## #F CheckGroupsByOrder(n, full) ## CheckGroupsByOrder := function(n,full) local m, i, G, A, B, t; m := NumberSmallGroups(n); for i in [1..m] do G := PcGroupToPcpGroup(SmallGroup(n,i)); if full or not IsAbelian(G) then Print("check ",i,"\n"); t := Runtime(); A := NonAbelianTensorSquare(G); Print(" ",Runtime() - t, " for pcp method \n"); if full then t := Runtime(); B := NonAbelianTensorSquareFp(G); Size(B); Print(" ",Runtime() - t, " for fp method \n"); if Size(A) <> Size(B) then Error(n," ",i,"\n"); fi; fi; Print(" got group of order ",Size(A),"\n\n"); fi; od; end; polycyclic-2.16/gap/pcpgrp/general.gi0000644000076600000240000001203713706672341016637 0ustar mhornstaff############################################################################# ## #F ExponentRelationMatrix( pcp ) ## ExponentRelationMatrix := function( pcp ) local rels, relo, i, r; rels := []; relo := RelativeOrdersOfPcp( pcp ); for i in [1..Length(pcp)] do if relo[i] > 0 then r := ExponentsByPcp( pcp, pcp[i]^relo[i] ); r[i] := -relo[i]; Add( rels, r ); fi; od; return rels; end; ############################################################################# ## #F MappedVector( , ). . . . . . . . . . . . . . . . . . . . local ## ## Redefine this library function such that it works for FFE vectors. ## MappedVector := function( exp, list ) local elm, i; if Length( list ) = 0 then Error("cannot compute this\n"); fi; if IsFFE( exp[1] ) then exp := IntVecFFE(exp); fi; elm := list[1]^exp[1]; for i in [2..Length(list)] do elm := elm * list[i]^exp[i]; od; return elm; end; ############################################################################# ## #F AbelianIntersection( baseN, baseU ) . . . . . . . . . . . . . . .U \cap N ## ## N and U are subgroups of a free abelian group given by exponents. ## AbelianIntersection := function( baseN, baseU ) local n, s, id, ls, rs, is, g, I, al, ar, d, l1, l2, e, tm; # if N or U is trivial if Length( baseN ) = 0 or Length( baseU ) = 0 then return []; fi; n := Length( baseN[1] ); # if N or U are equal to G if Length( baseN ) = n then return baseU; elif Length( baseU ) = n then return baseN; fi; # if N is a tail s := PositionNonZero( baseN[1] ); if Length( baseN ) = n-s+1 and ForAll( baseN, x -> x[PositionNonZero(x)] = 1 ) then return Filtered( baseU, x -> Depth(x) >= s ); fi; # otherwise compute id := List( [1..n], x -> 0 ); ls := IdentityMat( n ); rs := IdentityMat( n ); is := IdentityMat( n ); for g in baseU do d := PositionNonZero( g ); ls[d] := g; rs[d] := g; od; I := []; for g in baseN do d := PositionNonZero( g ); if ls[d] = id then ls[d] := g; else Add( I, g ); fi; od; # enter the pairs [ u, 1 ] of into [ , ] for al in I do ar := id; d := Depth( al ); # compute sum and intersection while al <> id and ls[d] <> id do l1 := ls[d][d]; l2 := al[d]; e := Gcdex( l1, l2 ); tm := e.coeff1 * ls[d] + e.coeff2 * al; al := e.coeff3 * ls[d] + e.coeff4 * al; ls[d] := tm; tm := e.coeff1 * rs[d] + e.coeff2 * ar; ar := e.coeff3 * rs[d] + e.coeff4 * ar; rs[d] := tm; d := Depth( al ); od; # we have a new sum generator if al <> id then ls[d] := al; rs[d] := ar; # we have a new intersection generator elif ar <> id then d := Depth( ar ); while ar <> id and is[d] <> id do l1 := is[d][d]; l2 := ar[d]; e := Gcdex( l1, l2 ); tm := e.coeff1 * is[d] + e.coeff2 * ar; ar := e.coeff3 * is[d] + e.coeff4 * ar; is[d] := tm; d := Depth( ar ); od; if ar <> id then is[d] := ar; fi; fi; od; return Filtered( is, x -> x <> id ); end; ############################################################################# ## #F FrattiniSubgroup( G ) ## InstallMethod( FrattiniSubgroup, "for pcp groups", [IsPcpGroup], function( G ) local H, K, F, f, h; if not IsFinite(G) then Error("Sorry - no algorithm available"); fi; H := RefinedPcpGroup(G); K := PcpGroupToPcGroup(H); F := FrattiniSubgroup(K); f := List( GeneratorsOfGroup(F), x -> MappedVector( ExponentsOfPcElement(Pcgs(K), x), Igs(H) ) ); h := List( f, x -> PreImagesRepresentative(H!.bijection,x)); return Subgroup( G, h ); end ); ############################################################################# ## #F NormalMaximalSubgroups(G) ## InstallMethod( NormalMaximalSubgroups, "for pcp groups", [IsPcpGroup], function(G) local D, nat, H, prm, max, p, rep; D := DerivedSubgroup(G); if Index(G,D) = infinity then return fail; fi; nat := NaturalHomomorphismByNormalSubgroup(G,D); H := Image(nat); prm := Set(Factors(Size(H))); max := []; for p in prm do rep := MaximalSubgroupClassesByIndex(H,p); rep := List(rep, Representative); Append(max,rep); od; return List(max, x -> PreImage(nat,x)); end); ############################################################################# ## #F AsList(G) ## InstallMethod( AsList, "for pcp groups", [IsPcpGroup], function(G) local pcp, exp; if Size(G) = infinity then return fail; fi; pcp := Pcp(G); exp := ExponentsByRels( RelativeOrdersOfPcp(pcp)); return List(exp, x -> MappedVector(x, pcp)); end); polycyclic-2.16/gap/pcpgrp/maxsub.gi0000644000076600000240000000502213706672341016515 0ustar mhornstaff############################################################################# ## #W maxsub.gi Polycyc Bettina Eick ## ## Maximal subgroups of p-power index. ## ############################################################################# ## #F MaximalSubgroupsByLayer := function( G, pcp, p ) ## ## A/B is an efa layer of G. We compute the maximal subgroups of p-index ## of G which do not contain A/B. ## MaximalSubgroupsByLayer := function( G, pcp, p ) local q, C, invs, max, inv, t, D, new; # get characteristic if Length( pcp ) = 0 then return []; fi; q := RelativeOrdersOfPcp( pcp )[1]; if q <> 0 and q <> p then return []; fi; if q = 0 then new := List( pcp, x -> x ^ p ); new := AddIgsToIgs( new, DenominatorOfPcp( pcp ) ); new := SubgroupByIgs( G, new ); new := Pcp( GroupOfPcp( pcp ), new ); else new := pcp; fi; # set up class record C := rec( ); C.group := G; C.super := []; C.factor := Pcp( G, GroupOfPcp( pcp ) ); C.normal := new; # add field C.char := p; C.field := GF(p); C.dim := Length( pcp ); C.one := IdentityMat( C.dim, C.field ); # add extension info AddRelatorsCR( C ); AddOperationCR( C ); # if it is a trivial factor if Length( pcp ) = 1 then AddInversesCR( C ); t := ComplementClassesCR( C ); fi; # get maximal subgroups invs := SMTX.BasesMaximalSubmodules(GModuleByMats(C.mats, C.dim, C.field)); # loop trough max := []; for inv in invs do D := InduceToFactor( C, rec( repr := inv, stab := [] ) ); AddInversesCR( D ); t := ComplementClassesCR( D ); Append( max, t ); od; return max; end; ############################################################################# ## #F MaximalSubgroupClassesByIndex( G, p ) ## ## The conjugacy classes of maximal subgroups of p-power index in G. ## InstallMethod( MaximalSubgroupClassesByIndexOp, "for pcp groups", [IsPcpGroup, IsPosInt], function( G, p ) local pcp, max, i, tmp; # loop over series and determine subgroups pcp := PcpsOfEfaSeries( G ); max := []; for i in [1..Length(pcp)] do Append( max, MaximalSubgroupsByLayer( G, pcp[i], p ) ); od; # translate to classes and return for i in [1..Length(max)] do tmp := ConjugacyClassSubgroups( G, max[i].repr ); SetStabilizerOfExternalSet( tmp, max[i].norm ); max[i] := tmp; od; return max; end ); polycyclic-2.16/gap/pcpgrp/pcpgrp.gd0000644000076600000240000000243713706672341016513 0ustar mhornstaff############################################################################# ## #W pcpgrp.gd Polycyc Bettina Eick ## # fitting.gi DeclareAttribute( "SemiSimpleEfaSeries", IsPcpGroup ); DeclareAttribute( "FCCentre", IsGroup ); DeclareGlobalFunction( "NilpotentByAbelianByFiniteSeries" ); DeclareProperty( "IsNilpotentByFinite", IsGroup ); InstallTrueMethod( IsNilpotentByFinite, IsNilpotentGroup ); InstallTrueMethod( IsNilpotentByFinite, IsGroup and IsFinite ); # maxsub.gi KeyDependentOperation( "MaximalSubgroupClassesByIndex", IsGroup, IsPosInt, ReturnTrue ); # findex/nindex.gi KeyDependentOperation( "LowIndexSubgroupClasses", IsGroup, IsPosInt, ReturnTrue ); KeyDependentOperation( "LowIndexNormalSubgroups", IsGroup, IsPosInt, ReturnTrue ); DeclareGlobalFunction( "NilpotentByAbelianNormalSubgroup" ); # polyz.gi DeclareGlobalFunction( "PolyZNormalSubgroup" ); # schur and tensor DeclareAttribute( "SchurExtension", IsGroup ); DeclareAttribute( "SchurExtensionEpimorphism", IsGroup ); DeclareGlobalFunction("EvalConsistency"); DeclareGlobalFunction("QuotientBySystem"); DeclareAttribute( "NonAbelianTensorSquare", IsGroup ); DeclareAttribute( "NonAbelianExteriorSquare", IsGroup ); polycyclic-2.16/gap/pcpgrp/torsion.gi0000644000076600000240000003725513706672341016730 0ustar mhornstaff############################################################################ ## #W torsion.gi Polycyc Bettina Eick ## ############################################################################ ## #F TorsionSubgroup( H ) ## ## Let T be the set of all elements of finite order in H. If T is a subgroup ## of H, then it is called the torsion subgroup of H. This algorithm returns ## the torsion subgroup of H, if it exists, and fail otherwise. ## ## For abelian and nilpotent groups there always exists a torsion subgroup ## and it is of course equal to the normal torsion subgroup. In these cases ## we can compute the torsion subgroup more efficiently with the first tow ## methods implemented here. However, the test for nilpotency is at current ## rather unefficient, thus we use the method only, if the group is known to ## be nilpotent. ## TorsionSubgroupAbelianPcpGroup := function( G ) local pcp, rels, subs; pcp := Pcp( G, "snf" ); rels := RelativeOrdersOfPcp( pcp ); subs := Filtered( [1..Length(pcp)], x -> rels[x] > 0 ); return Subgroup( G, pcp{subs} ); end; TorsionSubgroupNilpotentPcpGroup := function( G ) local U, D, pcp, rels, subs; U := ShallowCopy( G ); while not IsFinite( U ) do D := DerivedSubgroup( U ); Info( InfoPcpGrp, 1, "got layer ", RelativeOrdersOfPcp( Pcp(U,D) ) ); pcp := Pcp( U, D, "snf" ); rels := RelativeOrdersOfPcp( pcp ); Info( InfoPcpGrp, 1, "reduced to orders ", rels ); subs := Filtered( [1..Length(pcp)], x -> rels[x] > 0 ); U := SubgroupByIgs( G, Igs(D), pcp{subs} ); od; return U; end; TorsionSubgroupPcpGroup := function( G ) local efa, m, T, sub, i, pcp, gens, rels, H, N, new, com, g; # set up efa := PcpsOfEfaSeries( G ); # get the finite bit at the bottom of efa m := Length( efa ); T := []; while m >= 1 and RelativeOrdersOfPcp( efa[m] )[1] > 0 do T := AddIgsToIgs( GeneratorsOfPcp( efa[m] ), T ); m := m - 1; od; T := SubgroupByIgs( G, T ); sub := []; # loop over the rest for i in Reversed( [1..m] ) do # get the abelian layer pcp := efa[i]; gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); Info( InfoPcpGrp, 1, "start layer of orders ", rels ); # if it is finite, then we compute if rels[1] <> 0 then H := SubgroupByIgs( G, DenominatorOfPcp( efa[i] ) ); for g in gens do N := ShallowCopy( H ); H := SubgroupByIgs( G, AddIgsToIgs( [g], Igs( N ) ) ); # compute complement to N in H mod T new := ExtendedSeriesPcps( sub, N ); com := ComplementClassesEfaPcps( H, H, new ); # check classes if Length( com ) = 1 and IndexNC( H, com[1].norm ) = 1 then T := com[1].repr; sub := ModuloSeriesPcps( sub, T!.compgens, "snf" ); elif Length( com ) > 0 then return fail; fi; od; sub := ExtendedSeriesPcps( sub, GroupOfPcp( pcp ) ); else sub := Concatenation( [pcp], sub ); fi; od; return T; end; InstallMethod( TorsionSubgroup, "for pcp groups", [IsPcpGroup], function( G ) local U; if IsAbelian(G) then U := TorsionSubgroupAbelianPcpGroup( G ); elif HasIsNilpotentGroup( G ) and IsNilpotentGroup(G) then U := TorsionSubgroupNilpotentPcpGroup( G ); else U := TorsionSubgroupPcpGroup( G ); fi; if not IsBool(U) then SetNormalTorsionSubgroup( G, U ); SetIsTorsionFree( G, Size(U)=1 ); fi; return U; end ); InstallMethod( TorsionSubgroup, "for finite groups", [IsGroup and IsFinite], IdFunc ); InstallMethod( TorsionSubgroup, "for torsion free groups", [IsGroup and IsTorsionFree], TrivialSubgroup ); ############################################################################# ## #F NormalTorsionSubgroup( G ) ## ## This algorithm returns the (unique) largest finite normal subgroup of G. ## NormalTorsionSubgroupPcpGroup := function( G ) local efa, m, T, sub, i, pcp, gens, rels, H, N, new, com, g; # set up efa := PcpsOfEfaSeries( G ); # get the finite bit at the bottom of efa m := Length( efa ); T := []; while m >= 1 and RelativeOrdersOfPcp( efa[m] )[1] > 0 do T := AddIgsToIgs( GeneratorsOfPcp( efa[m] ), T ); m := m - 1; od; T := SubgroupByIgs( G, T ); sub := [ ]; # loop over the rest for i in Reversed( [1..m] ) do # get the abelian layer pcp := efa[i]; gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); Info( InfoPcpGrp, 1, "start layer of orders ", rels ); # if it is finite, then we compute if rels[1] <> 0 then H := SubgroupByIgs( G, DenominatorOfPcp( efa[i] ) ); for g in gens do N := ShallowCopy( H ); H := SubgroupByIgs( G, AddIgsToIgs( [g], Igs(N) )); # compute complement to N in H mod T new := ExtendedSeriesPcps( sub, N ); com := InvariantComplementsEfaPcps( H, H, new ); # check classes if Length( com ) > 0 then T := com[1]; sub := ModuloSeriesPcps( sub, T!.compgens, "snf" ); fi; od; sub := ExtendedSeriesPcps( sub, GroupOfPcp( pcp ) ); else sub := Concatenation( [pcp], sub ); fi; od; return T; end; InstallMethod( NormalTorsionSubgroup, "for pcp groups", [IsPcpGroup], function( G ) if IsAbelian(G) then return TorsionSubgroupAbelianPcpGroup( G ); elif HasIsNilpotentGroup( G ) and IsNilpotentGroup(G) then return TorsionSubgroupNilpotentPcpGroup( G ); else return NormalTorsionSubgroupPcpGroup( G ); fi; end ); InstallMethod( NormalTorsionSubgroup, "for finite groups", [IsGroup and IsFinite], IdFunc ); InstallMethod( NormalTorsionSubgroup, "for torsion free groups", [IsGroup and IsTorsionFree], TrivialSubgroup ); ############################################################################# ## #F IsTorsionFree( G ) ## InstallMethod( IsTorsionFree, "for pcp groups", [IsPcpGroup], function( G ) local pcs, rel, n, i, N, K, com; # the trival group if Size(G) = 1 then return true; fi; # now check pcs := RefinedIgs( G ); rel := pcs.rel; pcs := pcs.pcs; if ForAll( rel, x -> x > 0 ) then return false; fi; n := Length( pcs ); i := First( Reversed( [1..n] ), x -> rel[x] = 0 ); if i < n then return false; fi; # loop upwards while i >= 1 do if rel[i] > 0 then # compute subgroups N := Subgroup( G, pcs{[i+1..n]} ); K := Subgroup( G, pcs{[i..n]} ); # compute complements com := ComplementClasses( K, N ); # check them if Length( com ) > 0 then return false; fi; fi; i := i - 1; od; return true; end ); # In general, a finitely generated group is free abelian if and only # if it is abelian and its abelian invariants are all 0. InstallMethod( IsFreeAbelian, [IsFinitelyGeneratedGroup], grp -> IsAbelian(grp) and ForAll( AbelianInvariants( grp ), x -> x = 0 ) ); ############################################################################# ## #F IsSubbasis( big, small ) ## IsSubbasis := function( big, small ) if Length( small ) >= Length( big ) then return false; fi; return ForAll( small, x -> not IsBool( SolutionMat( big, x ) ) ); end; ############################################################################# ## #F OperationAndSpaces( pcpG, pcp ) ## OperationAndSpaces := function( pcpG, pcp ) local act; # construct matrices act := rec(); act.mats := LinearActionOnPcp( pcpG, pcp ); act.dim := Length(pcp); act.char := RelativeOrdersOfPcp( pcp )[1]; # add spaces if useful if act.char > 0 then act.one := IdentityMat( act.dim ); act.cent := ForAll( act.mats, x -> x = act.one ); if act.cent or act.char^act.dim <= 1000 then act.spaces := AllSubspaces( act.dim, act.char ); fi; fi; return act; end; ############################################################################# ## #F TranslateAction( C, pcp, mats ) ## TranslateAction := function( C, pcp, mats ) C.mats := List(C.factor, x -> MappedVector(ExponentsByPcp(pcp, x),mats)); C.smats := List(C.super, x -> MappedVector(ExponentsByPcp(pcp, x),mats)); if C.char > 0 then C.mats := C.mats * One( C.field ); C.smats := C.smats * One( C.field ); fi; end; ############################################################################# ## #F SubgroupBySubspace( pcp, exp ) ## SubgroupBySubspace := function( pcp, exp ) local gens; gens := List( exp, x -> MappedVector( IntVector( x ), pcp ) ); gens := AddIgsToIgs( gens, DenominatorOfPcp( pcp ) ); return SubgroupByIgs( GroupOfPcp( pcp ), gens ); end; ############################################################################# ## #F InduceMatricesAndExtension( C, sub ) ## InduceMatricesAndExtension := function( C, sub ) local e, l, all, new, A, ext, i, r, j, tmp; if Length( sub ) = 0 then return; fi; e := Length( sub ); l := Length( sub[1] ); all := Concatenation( C.mats, C.smats ); Add(all, IdentityMat(l, C.field)); new := SMTX.SubQuotActions( all, sub, l, e, C.field, 2 ); # get induced matrices C.mats := new.qmatrices{[1..Length(C.mats)]}; C.smats := new.qmatrices{[Length(C.mats)+1..Length(all)-1]}; # induce extension A := new.nbasis^-1; for i in [1..Length(C.extension)] do ext := []; for j in [e+1..l] do r := Sum( List( [1..l], k -> C.extension[i][k] * A[k][j] ) ); Add( ext, r ); od; C.extension[i] := ext; od; return; end; ############################################################################# ## #F InduceToFactor( C, sub ) ## ## C.normal is el ab and sub is an invariant subspace ## InduceToFactor := function( C, sub ) local D, L; # make a copy and adjust this D := StructuralCopy( C ); # adjust D.super if sub.stab <> AsList( D.super ) then D.smats := List( sub.stab, x -> MappedVector( ExponentsByPcp( D.super, x), D.smats)); D.super := sub.stab; fi; # adjust D.normal if Length( sub.repr ) > 0 then # adjust dim and one to correct dimension D.dim := Length(C.normal) - Length( sub.repr ); D.one := IdentityMat( D.dim, D.field ); # adjust the layer pcp L := SubgroupBySubspace( D.normal, sub.repr ); D.normal := Pcp( GroupOfPcp( D.normal ), L ); # induce matrices and add inverses InduceMatricesAndExtension( D, sub.repr ); fi; return D; end; ############################################################################# ## #F SupplementClassesCR( C ) . . . supplements to an elementary abelian layer ## SupplementClassesCR := function( C ) local orbs, com, orb, D, t; # catch a trivial case if Length( C.normal ) = 1 then AddInversesCR( C ); return ComplementClassesCR( C ); fi; # compute all U-invariant submodules in A orbs := OrbitsInvariantSubspaces( C, C.dim ); # lift from U to R-classes of complements com := []; while Length( orbs ) > 0 do orb := Remove(orbs); D := InduceToFactor( C, orb ); AddInversesCR( D ); t := ComplementClassesCR( D ); Append( com, t ); if Length( t ) = 0 then orbs := Filtered( orbs, x -> not IsSubbasis( orb.repr, x.repr ) ); fi; od; return com; end; ############################################################################# ## #F FiniteSubgroupClassesBySeries( N, G, pcps, avoid ) ## # FIXME: This function is documented and should be turned into a GlobalFunction FiniteSubgroupClassesBySeries := function( arg ) local N, G, pcps, avoid, pcpG, grps, pcp, act, new, grp, C, tmp, i, rels, U; if Length( arg ) = 2 then G := arg[1]; N := G; pcps := arg[2]; avoid := []; elif Length( arg ) = 4 then N := arg[1]; G := arg[2]; pcps := arg[3]; avoid := arg[4]; fi; pcpG := Pcp( G ); grps := [ rec( repr := G, norm := N )]; for pcp in pcps do rels := RelativeOrdersOfPcp( pcp ); Info( InfoPcpGrp, 1, "next layer of orders ", rels ); Info( InfoPcpGrp, 1, " with ", Length(grps), " groups"); act := OperationAndSpaces( pcpG, pcp ); new := []; for i in [1..Length( grps ) ] do grp := grps[i]; Info( InfoPcpGrp, 1, " group number ", i ); # set up class record C := rec( ); C.group := grp.repr; C.super := Pcp( grp.norm, grp.repr ); C.factor := Pcp( grp.repr, GroupOfPcp( pcp ) ); C.normal := pcp; # add extension info AddFieldCR( C ); AddRelatorsCR( C ); # add action TranslateAction( C, pcpG, act.mats ); # if it is free abelian, compute complements if C.char = 0 then AddInversesCR( C ); tmp := ComplementClassesCR( C ); Info( InfoPcpGrp, 1, " computed ", Length(tmp), " complements"); else if IsBound( act.spaces ) then C.spaces := act.spaces; fi; tmp := SupplementClassesCR( C ); Info( InfoPcpGrp, 1, " computed ", Length(tmp), " supplements"); fi; if Length( avoid ) > 0 then for U in avoid do tmp := Filtered( tmp, x -> not IsSubgroup( U, x.repr ) ); od; fi; Append( new, tmp ); od; if C.char = 0 then grps := ShallowCopy( new ); else Append( grps, new ); fi; od; # translate to classes and return for i in [1..Length(grps)] do tmp := ConjugacyClassSubgroups( G, grps[i].repr ); SetStabilizerOfExternalSet( tmp, grps[i].norm ); grps[i] := tmp; od; return grps; end; ############################################################################# ## #F FiniteSubgroupClasses( G ) ## InstallMethod( FiniteSubgroupClasses, "for pcp groups", [IsPcpGroup], function( G ) return FiniteSubgroupClassesBySeries( G, PcpsOfEfaSeries(G) ); end ); ############################################################################# ## #F RootSet( G, H ) . . . . . . . . . . . . . . . . . . . . . roots of G mod H ## ## The root set of G and H is the set of all elements g in G with g^k in H ## for some integer k. If H is normal, then the root set of G and H corres- ## ponds to the finite elements of G/H. If G/H has a torsion subgroup, then ## this is the root set. Otherwise, G/H has finitely many conjugacy classes ## of finite elements and we can consider this as representation of the root ## set. Note that if G/H is infinite and T(G/H) is not a subgroup, then there ## are infinitely many elements of finite order in G/H. ## InstallGlobalFunction( RootSet, function( G, H ) local nat, F, T; if not IsNormal( G, H ) then Print("function is available for normal subgroups only"); return fail; fi; nat := NaturalHomomorphismByNormalSubgroup( G, H ); F := Image( nat ); T := TorsionSubgroup( F ); if T = fail then Print( "RootSet is not a subgroup - not yet implemented" ); return fail; fi; return PreImage( nat, T ); end ); polycyclic-2.16/gap/pcpgrp/schur.gi0000644000076600000240000002725513706672341016356 0ustar mhornstaff############################################################################# ## #A ReduceTail ## ReduceTail := function( w, n, Q, d, f ) local i, a, v; i := 1; while i < Length(w) and w[i] <= n do i := i+2; od; a := w{[1..i-1]}; v := Q[1] * 0; while i < Length(w) do v := v + Q[ w[i] - n ] * w[i+1]; i := i+2; od; for i in [1..Length(v)] do if d[i] > 1 then v[i] := v[i] mod d[i]; fi; od; for i in [1..Length(f)] do Add( a, n+i ); Add( a, v[ f[i] ] ); od; return a; end; ############################################################################# ## #A SchurExtensionEpimorphism(G) . . . . . . . epimorphism from F/R to F/[R,F] ## InstallMethod( SchurExtensionEpimorphism, "for pcp groups", [IsPcpGroup], function(G) local g, r, n, y, coll, k, i, j, e, sys, ext, extgens, images, epi, ker; # handle the trivial group if IsTrivial(G) then return IdentityMapping(G); fi; # set up g := Igs(G); n := Length(g); r := List(g, x -> RelativeOrderPcp(x)); if n = 1 then ext := AbelianPcpGroup(1, [0]); # the infinite cyclic group return GroupHomomorphismByImagesNC( ext, G, GeneratorsOfGroup(ext), GeneratorsOfGroup(G) );; fi; # get collector for extension y := n*(n-1)/2 + Number(r, x -> x>0); coll := FromTheLeftCollector(n+y); # add a tail to each power and each positive conjugate relation k := n; for i in [1..n] do SetRelativeOrder(coll, i, r[i]); if r[i] > 0 then e := ObjByExponents(coll, ExponentsByIgs(g, g[i]^r[i])); k := k+1; Append(e, [k,1]); SetPower(coll,i,e); fi; for j in [1..i-1] do e := ObjByExponents(coll, ExponentsByIgs(g, g[i]^g[j])); k := k+1; Append(e, [k,1]); SetConjugate(coll,i,j,e); od; od; # update UpdatePolycyclicCollector(coll); # evaluate consistency sys := CRSystem(1, y, 0); EvalConsistency( coll, sys ); # determine quotient ext := QuotientBySystem( coll, sys, n ); # construct quotient epimorphism extgens := Igs( ext ); images := ListWithIdenticalEntries( Length(extgens), One(G) ); images{[1..n]} := g; epi := GroupHomomorphismByImagesNC( ext, G, extgens, images ); SetIsSurjective( epi, true ); ker := Subgroup( ext, extgens{[n+1..Length(extgens)]} ); SetKernelOfMultiplicativeGeneralMapping( epi, ker ); return epi; end ); ############################################################################# ## #A SchurExtension(G) . . . . . . . . . . . . . . . . . . . . . . . . F/[R,F] ## InstallMethod( SchurExtension, "for groups", [IsGroup], function(G) return Source( SchurExtensionEpimorphism( G ) ); end ); ############################################################################# ## #A AbelianInvariantsMultiplier(G) . . . . . . . . . . . . . . . . . . . M(G) ## InstallMethod( AbelianInvariantsMultiplier, "for pcp groups", [IsPcpGroup], function(G) local epi, H, M, T, D, I; # a simple check if IsCyclic(G) then return []; fi; # otherwise compute epi := SchurExtensionEpimorphism(G); H := Source(epi); M := KernelOfMultiplicativeGeneralMapping(epi); # the finite case if IsFinite(G) then T := TorsionSubgroup(M); return AbelianInvariants(T); fi; # the general case D := DerivedSubgroup(H); I := Intersection(M, D); return AbelianInvariants(I); end ); ############################################################################# ## #A EpimorphismSchurCover(G) . . . . . . . . . . . . . . . M(G) extended by G ## InstallMethod( EpimorphismSchurCover, "for pcp groups", [IsPcpGroup], function(G) local epi, H, M, I, C, cover, g, n, extgens, images, ker; if IsCyclic(G) then return IdentityMapping( G ); fi; # get full extension F/[R,F] epi := SchurExtensionEpimorphism(G); H := Source(epi); # get R/[R,F] M := KernelOfMultiplicativeGeneralMapping(epi); # get R cap F' I := Intersection(M, DerivedSubgroup(H)); # get complement to I in M C := Subgroup(H, GeneratorsOfPcp( Pcp(M,I,"snf"))); if not IsFreeAbelian(C) then Error("wrong complement"); fi; # get Schur cover (R cap F') / [R,F] cover := H/C; # construct quotient epimorphism g := Igs(G); n := Length(g); extgens := Igs( cover ); images := ListWithIdenticalEntries( Length(extgens), One(G) ); images{[1..n]} := g; epi := GroupHomomorphismByImagesNC( cover, G, extgens, images ); SetIsSurjective( epi, true ); ker := Subgroup( cover, extgens{[n+1..Length(extgens)]} ); SetKernelOfMultiplicativeGeneralMapping( epi, ker ); return epi; end ); ############################################################################# ## #A NonAbelianExteriorSquareEpimorphism(G) . . . . . . . . . G wegde G --> G' ## # FIXME: This function is documented and should be turned into a attribute NonAbelianExteriorSquareEpimorphism := function( G ) local lift, D, gens, imgs, epi, lambda; if Size(G) = 1 then return IdentityMapping( G ); fi; lift := SchurExtensionEpimorphism(G); D := DerivedSubgroup( Source(lift) ); gens := GeneratorsOfGroup( D ); imgs := List( gens, g->Image( lift, g ) ); epi := GroupHomomorphismByImagesNC( D, DerivedSubgroup(G), gens, imgs ); SetIsSurjective( epi, true ); lambda := function( g, h ) return Comm( PreImagesRepresentative( lift, g ), PreImagesRepresentative( lift, h ) ); end; D!.epimorphism := epi; # TODO: Make the crossedPairing accessible via an attribute! D!.crossedPairing := lambda; return epi; end; ############################################################################# ## #A NonAbelianExteriorSquare(G) . . . . . . . . . . . . . . . . . .(G wegde G) ## InstallMethod( NonAbelianExteriorSquare, "for pcp groups", [IsPcpGroup], function(G) return Source( NonAbelianExteriorSquareEpimorphism( G ) ); end ); ############################################################################# ## #A NonAbelianExteriorSquarePlus(G) . . . . . . . . . . (G wegde G) by (G x G) ## ## This is the group tau(G) in our paper. ## ## The following function computes the embedding of the non-abelian exterior ## square of G into tau(G). ## # FIXME: This function is documented and should be turned into an attribute NonAbelianExteriorSquarePlusEmbedding := function(G) local g, n, r, w, extlift, F, f, D, d, m, s, c, i, e, j, gens, imgs, k, alpha, S, embed; if Size(G) = 1 then return G; fi; # set up g := Igs(G); n := Length(g); r := List(g, x -> RelativeOrderPcp(x)); w := List([1..2*n], x -> 0); extlift := NonAbelianExteriorSquareEpimorphism( G ); # F/[R,F] = G* F := Parent( Source( extlift ) ); f := Pcp(F); # the non-abelian exterior square D = G^G D := Source( extlift ); d := Pcp(D); m := Length(d); s := RelativeOrdersOfPcp(d); # Print( "# NonAbelianExteriorSquarePlus: Setting up collector with ", 2*n+m, # " generators\n" ); # set up collector for non-abelian exterior square plus c := FromTheLeftCollector(2*n+m); # the relators of GxG for i in [1..n] do # relative order and power if r[i] > 0 then SetRelativeOrder(c, i, r[i]); e := ExponentsByIgs(g, g[i]^r[i]); SetPower(c, i, ObjByExponents(c,e)); SetRelativeOrder(c, n+i, r[i]); e := Concatenation(0*e, e); SetPower(c, n+i, ObjByExponents(c,e)); fi; # conjugates for j in [1..i-1] do e := ExponentsByIgs(g, g[i]^g[j]); SetConjugate(c, i, j, ObjByExponents(c,e)); e := Concatenation(0*e, e); SetConjugate(c, n+i, n+j, ObjByExponents(c,e)); if r[j] = 0 then e := ExponentsByIgs(g, g[i]^(g[j]^-1)); SetConjugate(c, i, -j, ObjByExponents(c,e)); e := Concatenation(0*e, e); SetConjugate(c, n+i, -(n+j), ObjByExponents(c,e)); fi; od; od; # the relators of G^G for i in [1..m] do # relative order and power if s[i] > 0 then SetRelativeOrder(c, 2*n+i, s[i]); e := ExponentsByPcp(d, d[i]^s[i]); e := Concatenation(w, e); SetPower(c, 2*n+i, ObjByExponents(c,e)); fi; # conjugates for j in [1..i-1] do e := ExponentsByPcp(d, d[i]^d[j]); e := Concatenation(w, e); SetConjugate(c, 2*n+i, 2*n+j, ObjByExponents(c,e)); if s[j] = 0 then e := ExponentsByPcp(d, d[i]^(d[j]^-1)); e := Concatenation(w, e); SetConjugate(c, 2*n+i, -(2*n+j), ObjByExponents(c,e)); fi; od; od; # the extension of G^G by GxG # # This is the computation of \lambda in our paper: For (g_i,g_j) we take # preimages (f_i,f_j) in G* and calculate the image of (g_i,g_j) under # \lambda as the commutator [f_i,f_j]. for i in [1..n] do for j in [1..n] do e := ExponentsByPcp(d, Comm(f[j], f[i])); e := Concatenation(w, e); e[n+j] := 1; SetConjugate(c, n+j, i, ObjByExponents(c,e)); if r[i] = 0 then e := ExponentsByPcp(d, Comm(f[j], f[i]^-1)); e := Concatenation(w, e); e[n+j] := 1; SetConjugate(c, n+j, -i, ObjByExponents(c,e)); fi; od; od; # the action on G^G by GxG for i in [1..n] do # create action homomorphism # G^G is generated by commutators of G* # G acts on G^G via conjugation with preimages in G*. gens := []; imgs := []; for k in [1..n] do for j in [1..n] do Add(gens, Comm(f[k], f[j])); Add(imgs, Comm(f[k]^f[i], f[j]^f[i])); od; od; alpha := GroupHomomorphismByImagesNC(D,D,gens,imgs); # compute conjugates for j in [1..m] do e := ExponentsByPcp(d, Image(alpha, d[j])); e := Concatenation(w, e); SetConjugate(c, 2*n+j, i, ObjByExponents(c,e)); SetConjugate(c, 2*n+j, n+i, ObjByExponents(c,e)); od; if r[i] = 0 then # create action homomorphism gens := []; imgs := []; for k in [1..n] do for j in [1..n] do Add(gens, Comm(f[k], f[j])); Add(imgs, Comm(f[k]^(f[i]^-1), f[j]^(f[i]^-1))); od; od; alpha := GroupHomomorphismByImagesNC(D,D,gens,imgs); # compute conjugates for j in [1..m] do e := ExponentsByPcp(d, Image(alpha, d[j])); e := Concatenation(w, e); SetConjugate(c, 2*n+j, -i, ObjByExponents(c,e)); SetConjugate(c, 2*n+j, -(n+i), ObjByExponents(c,e)); od; fi; od; if CHECK_SCHUR_PCP@ then S := PcpGroupByCollector(c); else UpdatePolycyclicCollector(c); S := PcpGroupByCollectorNC(c); fi; S!.group := G; gens := GeneratorsOfGroup(S){[2*n+1..2*n+m]}; embed := GroupHomomorphismByImagesNC( D, S, GeneratorsOfPcp(d), gens ); return embed; end; NonAbelianExteriorSquarePlus := function( G ) return Range( NonAbelianExteriorSquarePlusEmbedding( G ) ); end; ############################################################################# ## #A Epicentre ## InstallMethod(Epicentre, "for pcp groups", [IsPcpGroup], function (G) local epi; epi := SchurExtensionEpimorphism(G); return Image(epi,Centre(Source(epi))); end); polycyclic-2.16/gap/matrep/0000755000076600000240000000000013706672341014673 5ustar mhornstaffpolycyclic-2.16/gap/matrep/matrep.gi0000644000076600000240000005364513706672341016521 0ustar mhornstaff############################################################################# ## #W matrep.gi Polycyclic Werner Nickel ## Representation := "defined later"; InstallGlobalFunction( "IsMatrixRepresentation", function( G, matrices ) local coll, conjugates, d, I, i, j, conj, rhs, k; coll := Collector( G ); conjugates := coll![PC_CONJUGATES]; d := NumberOfGenerators( coll ); I := matrices[1]^0; for i in [1..d] do for j in [i+1..d] do conj := matrices[j]^matrices[i]; if IsBound( conjugates[j] ) and IsBound( conjugates[j][i] ) then rhs := I; for k in [1,3..Length(conjugates[j][i])-1] do rhs := rhs * matrices[ conjugates[j][i][k] ] ^ conjugates[j][i][k+1]; od; else rhs := matrices[j]; fi; if conj <> rhs then Error( "relation ", [j,i], " not satisfied" ); fi; od; od; return true; end ); InstallMethod( UnitriangularMatrixRepresentation, "for torsion free fin. gen. nilpotent pcp-groups", true, [ IsPcpGroup and IsNilpotentGroup ], 0, function( tgroup ) local coll, mats, mgroup, phi; ## Does the group have power relations? if not IsTorsionFree( tgroup ) then Error("there are power relations in the collector of the pcp-group"); ## Here we could compute the upper central series and construct an ## isomorphism to a group defined along the upper central series. fi; coll := Collector( tgroup ); mats := LowerUnitriangularForm( Representation( coll ) ); mgroup := Group( mats, mats[1]^0 ); UseIsomorphismRelation( tgroup, mgroup ); phi := GroupHomomorphismByImagesNC( tgroup, mgroup, GeneratorsOfGroup(tgroup), GeneratorsOfGroup(mgroup) ); SetIsBijective( phi, true ); SetIsHomomorphismIntoMatrixGroup( phi, true ); # FIXME: IsHomomorphismIntoMatrixGroup should perhaps be # a plain filter not a property. Especially since no methods # for it are installed. return phi; end ); InstallMethod( ViewObj, "for homomorphisms into matrix groups", true, [ IsHomomorphismIntoMatrixGroup ], 0, function( hom ) local mapi, d; mapi := MappingGeneratorsImages( hom ); View( mapi[1] ); Print( " -> " ); d := Length(mapi[2][1]); Print( "<", Length(mapi[2]), " ", d, "x", d, "-matrices>" ); return; end ); #### Willem's code ########################################################## ## ExtendRep:=function( col, new, mats) # Here `col' is a from-the-left collector. Let G be the group defined # by `col' and H the subgroup generated by the generators with indices # `new+1'...`nogens'. Then we assume that the list `mats' defines a # representation of H (`new+1' is represented by `mats[1]' and so on. # This function extends the representation of the subgroup H to the # subgroup generated by H together with the element with index `new'. # Elements of `H' are represented as words. For example [ 0, 0, 1, 2 ] # is u_3*u_4^2. The length of these words is equal to the number of # polycyclic generators of G. So the first entries of such a word # will be zero (to be precise: the entries until and including `new'). local MakeWord, EvaluateFunction, TriangulizeRows, nogen, nulev, dim, commutes, i, ev, M, exrep, k, l, asbas, last_inds, hdeg, sp, deg, ready, le, j, m, cc, cf, inds, mons, tup, ff, vecs, f, vec, vecs1, B, B1, finished, cls, done, changeocc, vv, m1, num, isrep, newmons; MakeWord:= function( p ) # Represents the element `p' of `H' in the form [ ind, exp, ...] # e.g., [ 1,0,0,2] is transformed to [1,1,4,2]. local p1,i; p1:=[]; for i in [1..Length(p)] do if p[i]<>0 then Append(p1,[i,p[i]]); fi; od; return p1; end; EvaluateFunction:=function( f, a ) # Here `f' is an element of the dual space of the group algebra of `H'. # So it is represented as [ [ i, j ], k ]. # `a' is a monomial in the group algebra of `H'. We evaluate # `f(a)'. local p, wd, j, of, M; p:= a; if f[2] <> 0 then # we calculate new^{-f[2]}*a*new^{f[2]}: p:= List( [1..NumberOfGenerators( col )], x -> 0 ); wd:= [ new, -f[2] ]; for j in [1..Length(a)] do if a[j]<>0 then Append( wd, [ j, a[j] ] ); fi; od; Append( wd, [ new,f[2] ] ); CollectWordOrFail( col, p, wd ); fi; # We calculate the matrix corresponding to `p'. of:= Length(mats)-NumberOfGenerators( col ); M:= IdentityMat( Length(mats[1]) ); for j in [1..Length(p)] do if of+j >= 1 then if p[j] <> 0 then M:= M*(mats[of+j]^p[j]); fi; fi; od; # We return the correct entry of the matrix. return M[f[1][2]][f[1][1]]; end; TriangulizeRows:= function( vecs ) # Here `vecs' is a list of integer vectors. This function # returns a list of integer vectors spanning the same space # over the integers (i.e., the same lattice), in triangular form. # The algorithm is similar to the one for Hermite normal form: # Suppose we have already taken care of the rows 1..k-1, and suppose that # we are dealing with column `col' (here col >= k, and the inequality # may be strict because there can be zero columns that do not contribute). # Then we look for the minimal entry in column `col' on and below position # `k'. We swap the corresponding row to position `k', and subtract it # as many times as possible from the rows below. If this produces # zeros everywehere then we are happy, and move on. If not then # we do this again: move the minimal entry to position `k' etc. # # The output of this function also contains a second list, in bijection # with the rowvectors in the output. The k-th entry of this list contains # the position of the first nonzero entry in the k-th row. local col, k, i, pos, fac, cols, v, min, c; col:=1; k:=1; cols:= [ ]; while k <= Length(vecs) do # We look for the minimal nonzero element in column `col', where we # run through the rows with index >= k. min:= 0; i:= k-1; # First we get the first nonzero entry... while min = 0 and i < Length( vecs ) do i:=i+1; min:= vecs[i][col]; od; if min = 0 then pos:= fail; else if min < 0 then min:= -min; fi; pos:= i; while i < Length(vecs) do i:=i+1; c:= vecs[i][col]; if c < 0 then c:= -c; fi; if c < min and c <> 0 then min:= c; pos:= i; fi; od; fi; if pos = fail then # there is no nonzero entry in this column, that means that it # will not contribute to the triangular form, we move one column. col:= col+1; else if pos <> k then # We swap rows `k' and `pos', so that the minimal value will be # in row `k'. v:= vecs[k]; vecs[k]:= vecs[pos]; vecs[pos]:= v; fi; # Subtract row `k' as many times as possible. for i in [k+1..Length(vecs)] do fac:= (vecs[i][col]- (vecs[i][col] mod vecs[k][col]))/vecs[k][col]; vecs[i]:=vecs[i]-fac*vecs[k]; od; # If all entries in the column `col' below position `k' are zero, # then we are done. Otherwise we just go through the process again. if ForAll( List( [k+1..Length(vecs)], x-> vecs[x][col] ), IsZero ) then Add( cols, col ); col:=col+1; k:=k+1; fi; # Get rid of zero rows... vecs:= Filtered( vecs, x -> x <> 0*x ); fi; od; return [vecs,cols]; end; nogen:= NumberOfGenerators( col ); nulev:= List([1..nogen],x->0); dim:= Length( mats[1] ); # We check whether the generator with index `new' commutes with all # elements of `H'. In that case we can easily extend the representation. commutes:= true; for i in [new+1..nogen] do ev:= ShallowCopy( nulev ); CollectWordOrFail( col, ev, [i,-1,new,-1,i,1,new,1] ); if ev<>0*ev then commutes:= false; break; fi; od; if commutes then # We represent the generator with index `new' by the matrix # # / I 0 \ # \ 0 E_{12} / # # where I is the dim x dim identity matrix, and E_{12} # is the 2x2 matrix with ones on the diagonal, and a one on pos. (1,2). # A generator with index `new+i' is represented by the matrix # # / mats[i] 0 \ # \ 0 I_2 / # # where I_2 is the 2x2 identity matrix. M:= IdentityMat( dim+2 ); M[dim+1][dim+2]:=1; exrep:= [ M ]; for i in [1..Length(mats)] do M:= NullMat( dim+2, dim+2 ); for k in [1..dim] do for l in [1..dim] do M[k][l]:=mats[i][k][l]; od; od; M[dim+1][dim+1]:=1; M[dim+2][dim+2]:=1; Add( exrep, M ); od; return exrep; fi; # In the other case we compute the space spanned by C_{\rho}. This is # the space spanned by the coefficient-functions on the matrix space # spanned by all products # # mats[1]^k1...mats[s]^ks # # where k_i\in \Z. So first we calculate a basis of this space. asbas:=[ IdentityMat( dim ) ]; # The basis to be. last_inds:= [ 1 ]; # `last_inds' is an array in bijection with `asbas'. # If `last_inds[i]=k' then the last letter in the word that defines # `asbas[i]' is `k'. (So we only need to multiply with higher # elements in order to (maybe) get new basis elements). # We basically loop through `asbas' and multiply each element in there # with elements with the same or a higher index. If all such products # are in `asbas' then we have found our basis. Of course, we only have # to try the elements added in the previous round. So `hdeg' is the # index where the first of thoses elements is in `asbas'. # `deg' will record the maximum degree of a word corresponding to a # basis element (for later use). hdeg:= 1; sp:= MutableBasis( Rationals, asbas ); deg:= 0; ready:= false; while not ready do deg:= deg + 1; i:= hdeg; le:= Length( asbas ); ready:= true; while i <= le do for j in [ last_inds[i]..Length( mats )] do m:= asbas[i]*mats[j]; if not IsContainedInSpan( sp, m ) then ready:= false; Add( asbas, m ); Add( last_inds, j ); CloseMutableBasis( sp, m ); fi; od; i:= i+1; od; hdeg:= le+1; od; deg:= deg - 1; # Compute the functions. A coefficient function m -> m[j][i] is represented # by [i,j]. `cc' will be the list of all such functions. # We note that the elements of `asbas' form a discriminating set for # the coefficient space. So we represent the coefficcients as vectors using # the set `asbas'. cc:=[ ]; sp:= MutableBasis( Rationals, [ List(asbas,m->0)] ); for i in [1..dim] do for j in [1..dim] do cf:= List( asbas, m -> m[j][i] ); if not IsContainedInSpan( sp, cf ) then Add( cc, [i,j] ); CloseMutableBasis( sp, cf ); fi; od; od; # 'mons' will be a list of all monomials in the group H up to degree 'deg'. inds:=[ new+1 .. nogen ]; mons:=[ ShallowCopy( nulev ) ]; for i in [1..deg] do tup:= UnorderedTuples( inds, i ); for j in [1..Length(tup)] do ev:= ShallowCopy( nulev ); for k in tup[j] do ev[k]:= ev[k]+1; od; Add( mons, ev ); od; od; # 'ff' will be a basis of the subspace of ZH^* spanned by the functions. # A function is either coefficient function or new^k applied to a coefficient # function. So we represent a function as a list [ [i,j], k]. # 'vecs' will contain the vectorial representation of the elements of 'ff' # relative to the monomials in 'mons'. ff:=[]; vecs:=[]; for i in [1..Length(cc)] do f:= [ cc[i], 0 ]; vec:= List( mons, a -> EvaluateFunction( f, a ) ); Add( ff, f ); Add( vecs, ShallowCopy( vec ) ); od; while true do # We determine the module generated by C_{\rho} (as a subspace # of the dual of the vector space spanned by the monomials in `mons'). # This module is generated by all new^k.f for f\in ff. # # `vecs1' will be a set of vectors spanning the module over the # integers, wheras `vecs' spans the module over the rationals, # and `vecs[i]' corresponds exactly to the function `ff[i]' (so we # need to keep this information as we # do not allow for linear combinations of functions in `ff'). vecs1:= ShallowCopy( List( vecs, ShallowCopy ) ); sp:= VectorSpace( Rationals, vecs ); B:= Basis( sp, vecs ); k:= 1; le:= Length( ff ); B1:= Basis( sp, vecs1 ); while k <= le do f:= List( ff[k], ShallowCopy ); finished:= false; while not finished do f[2]:= f[2]+1; # we let `new' act by increasing # `f[2]' by 1. if not f in ff then vec:= List( mons, a -> EvaluateFunction( f, a ) ); cf:= Coefficients( B1, vec ); if cf <> fail then if not ForAll( cf, IsInt ) then # `vec' lies in the space `sp' # but not in the space over the # integers spanned by `vecs1'. # So we add it, triangularize # and then we get a new basis # `vecs1' that spans the whole # space over the integers. Add( vecs1, vec ); vecs1:=TriangulizeRows(vecs1)[1]; B1:= Basis( sp, vecs1 ); fi; finished:= true; # we are finished with letting `new' act on `f'. else # we add the new vector, function etc... Add( ff, List( f, ShallowCopy ) ); Add( vecs, ShallowCopy( vec ) ); Add( vecs1, ShallowCopy( vec ) ); sp:= VectorSpace( Rationals, vecs ); B1:= Basis( sp, vecs1 ); fi; else finished:= true; fi; od; k:= k+1; od; # Now we determine a list of monomials sufficient to "distinguish" the # functions. It is of length equal to the dimension of the module. # It consists of the monomials corresponding to the columns that # come from a call to TriangularizeRows. cls:= TriangulizeRows( vecs1 )[2]; mons:= mons{cls}; vecs:= List( vecs, u -> u{cls} ); vecs1:= List( vecs1, u -> u{cls} ); # We calculate the action of the generators of the group, starting with # the new element. `exrep' will contain the matrices of the action. # It is possible that the Z-module spanned by `vecs1' is not closed # under the action of `new', *over Z*, i.e., that `new.f' is not # a Z-linear combination of the elements of `vecs1'. In that case we # add the vector we get, triangularize and start again. For that # we need the rather complicated loop `while not done do..' etc. sp:= VectorSpace( Rationals, vecs ); B:= Basis( sp, vecs ); done:= false; while not done do changeocc:= false; B1:= Basis( sp, vecs1 ); exrep:= [ ]; M:= [ ]; for j in [1..Length(vecs1)] do vv:= [ ]; for m in mons do # `ev' will be the element new^{-1}.m.new m1:= [new,-1]; Append( m1, MakeWord(m) ); Append( m1, [new,1] ); ev:= ShallowCopy( nulev ); CollectWordOrFail( col, ev, m1 ); # Now we calculate the vector corresponding to the function # new.f, where f is the function corresponding to the vector # vecs1[j]. Now this vector is a linear combination of vectors # in `vecs1', i.e., the function is a linear combination of # elementary functions (i.e., fcts of the form new^k.c_{ij}). # So when evaluating we have to loop over the elements of this # linear combination. cf:= Coefficients( B, vecs1[j] ); num:= 0; for i in [1..Length(cf)] do if cf[i]<>0 then num:= num + cf[i]*EvaluateFunction( ff[i], ev ); fi; od; Add(vv,num); od; cf:= Coefficients( B1, vv ); if not ForAll( cf, x -> IsInt( x ) ) then Add( vecs1, vv ); vecs1:= TriangulizeRows(vecs1)[1]; changeocc:= true; break; else Add( M, Coefficients( B1, vv ) ); fi; od; if not changeocc then Add( exrep, TransposedMat( M ) ); # We calculate the action of the "old" generators. Basically works the # same as the code for the "new" generator. for i in [new+1..nogen] do if changeocc then break; fi; M:= [ ]; for j in [1..Length(vecs1)] do vv:= [ ]; cf:= Coefficients( B, vecs1[j] ); for m in mons do m1:= MakeWord( m ); Append( m1, [i,1] ); ev:= ShallowCopy( nulev ); CollectWordOrFail( col, ev, m1 ); num:= 0; for l in [1..Length(cf)] do if cf[l]<>0 then num:= num + cf[l]* EvaluateFunction( ff[l], ev ); fi; od; Add(vv,num); od; cf:= Coefficients( B1, vv ); if not ForAll( cf, x -> IsInt( x ) ) then Add( vecs1, vv ); vecs1:= TriangulizeRows(vecs1)[1]; changeocc:= true; break; else Add( M, Coefficients( B1, vv ) ); fi; od; Add( exrep, TransposedMat( M ) ); od; fi; if not changeocc then done:= true; fi; od; # If the representation we get is a group representation, then we are # happy, if not then we increase the degree. isrep:= true; for i in [new..nogen] do if not isrep then break; fi; for j in [i+1..nogen] do # We calculate `ev' such that # u_j*u_i = u_i*u_j*ev, and we check whether the matrices # satisfy this relation. ev:= List( [1..nogen], x -> 0 ); CollectWordOrFail( col, ev, [j,-1,i,-1,j,1,i,1] ); M:= exrep[1]^0; for k in [new..Length(ev)] do if ev[k] <> 0 then M:= M*( exrep[k-new+1]^ev[k] ); fi; od; M:= exrep[j-new+1]*M; M:= exrep[i-new+1]*M; if M <> exrep[j-new+1]*exrep[i-new+1] then isrep:= false; break; fi; od; od; if not isrep then # We increase the degree and compute our new guess for a # discriminating set. deg:=deg+1; newmons:= []; tup:= UnorderedTuples( inds, deg ); for j in [1..Length(tup)] do ev:= ShallowCopy( nulev ); for k in [1..Length(tup[j])] do ev[tup[j][k]]:= ev[tup[j][k]]+1; od; Add( newmons, ShallowCopy(ev) ); od; for i in [1..Length(vecs)] do Append( vecs[i], List( newmons, w -> EvaluateFunction( ff[i], w ) ) ); od; Append( mons, newmons ); else return exrep; fi; od; # end of big loop `while true ..etc' end; Representation:= function( col ) local n,m,mats,i; n:= NumberOfGenerators( col ); m:=IdentityMat(2); m[1][2]:=1; mats:=[m]; for i in [2..n] do mats:= ExtendRep( col, n-i+1, mats ); od; return mats; end; ## ## #### End of Willem's code ################################################### polycyclic-2.16/gap/matrep/affine.gi0000644000076600000240000000373213706672341016451 0ustar mhornstaff AdjustPresentation := function( G ) G := G / TorsionSubgroup(G); return PcpGroupBySeries( UpperCentralSeriesOfGroup(G), "snf" ); end; ExtendAffine := function( mats, coc ) local d, l, r, c; d := Length( mats[1] ); l := Length( mats ); mats := StructuralCopy( mats ); coc := List( [1..l], x -> coc{[(x-1)*d+1..x*d]} ); for r in [1..l] do for c in [1..d] do Add( mats[r][c], 0 ); od; Add( coc[r], 1 ); Add( mats[r], coc[r] ); od; return mats; end; NextStepRepresentation := function( G, i, mats ) local pcp, N, hom, F, C, cc, rc, co, j, coc, news, d, z; Print("starting level ",i,"\n"); pcp := Pcp(G); N := SubgroupByIgs( G, pcp{[i+1..Length(pcp)]} ); hom := NaturalHomomorphismByNormalSubgroup( G, N ); F := Image( hom, G ); Add( mats, mats[1]^0 ); # determine cohomology C := CRRecordByMats( F, mats ); cc := OneCohomologyCR( C ); Print(" got cohomology with orders ", cc.factor.rels, "\n"); # choose a cocycle rc := List( cc.gcc, Reversed ); rc := NormalFormIntMat( rc, 2 ).normal; rc := Filtered( rc, x -> PositionNonZero(x) <= Length(mats[1]) ); if Length(rc) = 0 then return false; fi; co := Reversed( rc[Length(rc)] ); for j in [1..Length(cc.factor.prei)] do if co = cc.factor.prei[j] then coc := co; else coc := co + cc.factor.prei[j]; fi; news := ExtendAffine( mats, coc ); if not IsMatrixRepresentation( F, news ) then Error("no mat rep"); fi; news := NextStepRepresentation( G, i+1, news ); if not IsBool( news ) then return news; fi; od; return false; end; AffineRepresentation := function( G ) local mats, news; mats := [[[1,0],[1,1]]]; news := NextStepRepresentation( G, 2, mats ); if IsBool( news ) then return fail; fi; if not IsMatrixRepresentation( G, news ) then Error("no representation "); fi; return news; end; polycyclic-2.16/gap/matrep/unitri.gi0000644000076600000240000003177313706672341016541 0ustar mhornstaff############################################################################# ## #W unitri.gi Polycyclic Werner Nickel ## InfoMatrixNq := Ignore; ## ## Test if a matrix is the identity matrix. ## ## For non-identity matrices this is faster than comparing with an identity ## matrix. ## InstallGlobalFunction( "IsIdentityMat", function( M ) local zero, one, i, j; zero := Zero( M[1][1] ); one := zero + 1; if Set( List( M, Length ) ) <> [Length(M)] then return false; fi; for i in [1..Length(M)] do if M[i][i] <> one then return false; fi; for j in [i+1..Length(M)] do if M[i][j] <> zero then return false; fi; if M[j][i] <> zero then return false; fi; od; od; return true; end ); ## ## Test if a matrix is upper triangular with 1s on the diagonal. ## InstallGlobalFunction( "IsUpperUnitriMat", function( M ) local one, i; one := One( M[1][1] ); if not IsUpperTriangularMat( M ) then return false; fi; for i in [1..Length(M)] do if M[i][i] <> one then return false; fi; od; return true; end ); ## ## The weight of an upper unitriangular matrix is the number of diagonals ## above the main diagonal that contain only zeroes. ## InstallGlobalFunction( "WeightUpperUnitriMat", function( M ) local n, s, i, w; n := Length(M); w := 0; s := 1; while s < n do s := s+1; for i in [1..n-s+1] do if M[i][i+s-1] <> 0 then return w; fi; od; w := w+1; od; return w; end ); ## ## UpperDiagonal() returns the -th diagonal above the main diagonal of ## the matrix . ## InstallGlobalFunction( "UpperDiagonalOfMat", function( M, s ) local d, i; d := []; for i in [1..Length(M)-s] do d[i] := M[i][i+s]; od; return d; end ); ## ## Initialise a new level for the recursive sifting structure. ## ## At each level we store matrices of the apprpriate weight and for each ## matrix its inverse and the diagonal of the correct weight. ## InstallGlobalFunction( "MakeNewLevel", function( w ) InfoMatrixNq( "#I MakeNewLevel( ", w, " ) called\n" ); return rec( weight := w, matrices := [], inverses := [], diags := [] ); end ); ## ## When a matrix was added to a level of the sifting structure, then ## commutators with this matrix and the generators of the group have to be ## computed and sifted in order to keep the sifting structure from this ## level on closed under taking commutators. ## ## This function computes the necessary commutators and sifts each of ## them. ## InstallGlobalFunction( "FormCommutators", function( gens, level, j ) local C, Mj, i; InfoMatrixNq( "#I Forming commutators on level ", level.weight, "\n" ); Mj := level.matrices[j]; for i in [1..Length(gens)] do C := Comm( Mj,gens[i] ); if not IsIdentityMat(C) then if not IsBound( level.nextlevel ) then level.nextlevel := MakeNewLevel( level.weight+1 ); fi; SiftUpperUnitriMat( gens, level.nextlevel, C ); fi; od; return; end ); ## ## Sift the unitriangular matrix through the recursive sifting ## structure . It is assumed that the weight of is equal to or ## larger than the weight of . ## ## This function checks, if there is a matrix N at this level such that M ## N^k for suitable k has higher weight than M. If N does not exist, M is ## added to and all commutators of M with the generators of the ## group are formed and sifted. If there is such a matrix N, then M N^k ## is sifted through the next level. ## InstallGlobalFunction( "SiftUpperUnitriMat", function( gens, level, M ) local w, d, h, r, R, Ri, c, rr, RR; w := WeightUpperUnitriMat( M ); if w > level.weight then if not IsBound( level.nextlevel ) then level.nextlevel := MakeNewLevel( level.weight+1 ); fi; SiftUpperUnitriMat( gens, level.nextlevel, M ); return; fi; InfoMatrixNq( "#I Sifting at level ", level.weight, " with " ); d := UpperDiagonalOfMat( M, w+1 ); h := 1; while h <= Length(d) and d[h] = 0 do h := h+1; od; while h <= Length(d) do if IsBound(level.diags[h]) then r := level.diags[ h ]; R := level.matrices[ h ]; Ri := level.inverses[ h ]; c := Int( d[h] / r[h] ); InfoMatrixNq( " ", c ); if c <> 0 then d := d - c * r; if c > 0 then M := Ri^c * M; else M := R^(-c) * M; fi; fi; rr := r; r := d; d := rr; RR := R; R := M; M := RR; while r[h] <> 0 do c := Int( d[h] / r[h] ); InfoMatrixNq( " ", c ); if c <> 0 then d := d - c * r; M := R^(-c) * M; fi; rr := r; r := d; d := rr; RR := R; R := M; M := RR; od; if d <> level.diags[ h ] then level.diags[ h ] := d; level.matrices[ h ] := M; level.inverses[ h ] := M^-1; InfoMatrixNq( "\n" ); FormCommutators( gens, level, h ); InfoMatrixNq( "#I continuing reduction on level ", level.weight, " with " ); fi; d := r; M := R; else level.matrices[ h ] := M; level.inverses[ h ] := M^-1; level.diags[ h ] := d; InfoMatrixNq( "\n" ); FormCommutators( gens, level, h ); return; fi; while h <= Length(d) and d[h] = 0 do h := h+1; od; od; InfoMatrixNq( "\n" ); if WeightUpperUnitriMat(M) < Length(M)-1 then if not IsBound( level.nextlevel ) then level.nextlevel := MakeNewLevel( level.weight+1 ); fi; SiftUpperUnitriMat( gens, level.nextlevel, M ); fi; end ); ## ## The subgroup U of GL(n,Z) of upper unitriangular matrices is a ## nilpotent group. The n-th term of the lower central series of U ## consists of all unitriangular matrices of weight at least n-1. This ## defines a filtration on each subgroup of U. ## ## This function computes this filtration for the unitriangular matrix ## group G. ## InstallGlobalFunction( "SiftUpperUnitriMatGroup", function( G ) local firstlevel, g; firstlevel := MakeNewLevel( 0 ); for g in GeneratorsOfGroup(G) do SiftUpperUnitriMat( GeneratorsOfGroup(G), firstlevel, g ); od; return firstlevel; end ); ## ## Return the Z-ranks of the level of the sifting structure. ## InstallGlobalFunction( "RanksLevels", function( L ) local ranks; ranks := []; Add( ranks, Length( Filtered( L.diags, x->IsBound(x) ) ) ); while IsBound( L.nextlevel ) do L := L.nextlevel; Add( ranks, Length( Filtered( L.diags, x->IsBound(x) ) ) ); od; return ranks; end ); ## ## This function decomposes the given matrix into the generators of ## the sifting structure . ## InstallGlobalFunction( "DecomposeUpperUnitriMat", function( level, M ) local w, d, h, r, R, c; InfoMatrixNq( "#I Decomposition on level ", level.weight, "\n" ); w := WeightUpperUnitriMat(M); if w = Length(M[1])-1 then return []; fi; if w > level.weight then if not IsBound( level.nextlevel ) then return false; fi; return DecomposeUpperUnitriMat( level.nextlevel, M ); fi; w := []; d := UpperDiagonalOfMat( M, WeightUpperUnitriMat(M)+1 ); h := 1; while h <= Length(d) and d[h] = 0 do h := h+1; od; while h <= Length(d) do if not IsBound( level.diags[ h ] ) then return false; fi; r := level.diags[ h ]; R := level.matrices[ h ]; if d[h] mod r[h] <> 0 then return false; fi; c := Int( d[h] / r[h] ); d := d - c * r; M := R^(-c) * M; Add( w, [[level.weight, h], c] ); InfoMatrixNq( "#I gen: ", h ); InfoMatrixNq( " coeff: ", c, "\n" ); while h <= Length(d) and d[h] = 0 do h := h+1; od; od; if not IsIdentityMat( M ) then if not IsBound( level.nextlevel ) then return false; fi; h := DecomposeUpperUnitriMat( level.nextlevel, M ); if h = false then return false; fi; return Concatenation( w,h ); fi; return w; end ); ## InstallGlobalFunction( "PolycyclicGenerators", function( L ) local matrices, gens, i, l; matrices := Compacted( L.matrices ); gens := []; for i in [1..Length(L.diags)] do if IsBound( L.diags[i] ) then Add( gens, [L.weight,i] ); fi; od; l := L; while IsBound( l.nextlevel ) do l := l.nextlevel; Append( matrices, Compacted( l.matrices ) ); for i in [1..Length(l.diags)] do if IsBound( l.diags[i] ) then Add( gens, [l.weight,i] ); fi; od; od; return rec( gens := gens, matrices := matrices ); end ); InstallGlobalFunction( "WordPolycyclicGens", function( gens, w ) local r, g; r := ShallowCopy(w); for g in r do g[1] := Position( gens.gens, g[1] ); od; return Flat( r ); end ); InstallGlobalFunction( "PresentationMatNq", function( L ) local matrices, gens, i, l, rels, j, r, g; matrices := Compacted( L.matrices ); gens := []; for i in [1..Length(L.diags)] do if IsBound( L.diags[i] ) then Add( gens, [L.weight,i] ); fi; od; l := L; while IsBound( l.nextlevel ) do l := l.nextlevel; Append( matrices, Compacted( l.matrices ) ); for i in [1..Length(l.diags)] do if IsBound( l.diags[i] ) then Add( gens, [l.weight,i] ); fi; od; od; rels :=[]; for j in [1..Length(matrices)] do rels[j] := []; for i in [1..j-1] do r := DecomposeUpperUnitriMat( L,Comm( matrices[j],matrices[i] ) ); for g in r do g[1] := Position( gens, g[1] ); od; rels[j][i] := r; od; od; return rec( generators := [1..Length(rels)], relators := rels ); end ); InstallGlobalFunction( "PrintMatPres", function( P ) local r; Print( P.generators, "\n" ); for r in P.relators do Print( r, "\n" ); od; end ); InstallGlobalFunction( "PrintNqPres", function( P ) local x, CommString, PowerString, s, i, j, r, g; x := "x"; CommString := function( j, i ) local s; s := "[ "; Append( s, x ); Append( s, String(j) ); Append( s, ", " ); Append( s, x ); Append( s, String(i) ); Append( s, " ]" ); return s; end; PowerString := function( g ) local s; s := ""; Append( s, x ); Append( s, String(g[1]) ); Append( s, "^" ); Append( s, String(g[2]) ); Append( s, "*" ); return s; end; s := "< "; for i in P.generators do Append(s,x); Append(s,String(i)); Append(s,","); od; Unbind( s[ Length(s) ] ); # remove trailing comma Append( s, " | \n" ); for j in P.generators do for i in [1..j-1] do r := P.relators[j][i]; Append( s, " " ); Append( s, CommString( j, i ) ); if r <> [] then Append( s, " = " ); fi; for g in r do Append( s, PowerString(g) ); od; # remove trailing asterisk if s[ Length(s) ] = '*' then Unbind( s[ Length(s) ] ); fi; Append( s, ",\n" ); od; od; Unbind( s[ Length(s) ] ); Unbind( s[ Length(s) ] ); # remove trailing comma & newline Append( s, "\n>\n" ); return s; end ); InstallGlobalFunction( "CollectorByMatNq", function( levels ) local pres, n, coll, j, i; pres := PresentationMatNq( levels ); n := Length( pres.generators ); coll := FromTheLeftCollector( n ); for j in [1..n] do for i in [1..j-1] do if IsBound( pres.relators[j][i] ) and pres.relators[j][i] <> [] then SetCommutator( coll, j, i, Flat( pres.relators[j][i] ) ); fi; od; od; UpdatePolycyclicCollector( coll ); return coll; end ); InstallGlobalFunction( "IsomorphismUpperUnitriMatGroupPcpGroup", function( G ) local levs, pcgens, coll, H, images, M, w, phi; levs := SiftUpperUnitriMatGroup( G ); pcgens := PolycyclicGenerators( levs ); coll := CollectorByMatNq( levs ); H := PcpGroupByCollectorNC( coll ); images := []; for M in GeneratorsOfGroup( G ) do w := DecomposeUpperUnitriMat( levs, M ); w := WordPolycyclicGens( pcgens, w ); Add( images, PcpElementByGenExpListNC( coll, w ) ); od; phi := GroupHomomorphismByImagesNC( G, H, GeneratorsOfGroup( G ), images ); SetIsBijective( phi, true ); return phi; end ); polycyclic-2.16/gap/matrep/README0000644000076600000240000000030413706672341015550 0ustar mhornstaff gap/matrep: matrep.gd/gi -- Function to compute an integral matrix representation for a polycyclic group. unitri.gd/gi -- Convert a unitriangular matrix group into a pcp group. polycyclic-2.16/gap/matrep/matrep.gd0000644000076600000240000000103313706672341016474 0ustar mhornstaff############################################################################# ## ## This defines the operation for computing (lower) unitriangular ## matrix representations for finitely generated torsion-free nilpotent ## groups. ## #G UnitriangularMatrixRepresentation . . . . . . . . . . . . . . . . . . . . ## DeclareOperation( "UnitriangularMatrixRepresentation", [IsGroup] ); DeclareProperty( "IsHomomorphismIntoMatrixGroup", IsGroupGeneralMappingByImages ); DeclareGlobalFunction( "IsMatrixRepresentation" ); polycyclic-2.16/gap/matrep/unitri.gd0000644000076600000240000000167713706672341016534 0ustar mhornstaff############################################################################# ## #W unitri.gd Polycyclic Werner Nickel ## DeclareGlobalFunction( "IsIdentityMat" ); DeclareGlobalFunction( "IsUpperUnitriMat" ); DeclareGlobalFunction( "WeightUpperUnitriMat" ); DeclareGlobalFunction( "UpperDiagonalOfMat" ); DeclareGlobalFunction( "MakeNewLevel" ); DeclareGlobalFunction( "FormCommutators" ); DeclareGlobalFunction( "SiftUpperUnitriMat" ); DeclareGlobalFunction( "SiftUpperUnitriMatGroup" ); DeclareGlobalFunction( "RanksLevels" ); DeclareGlobalFunction( "DecomposeUpperUnitriMat" ); DeclareGlobalFunction( "PolycyclicGenerators" ); DeclareGlobalFunction( "WordPolycyclicGens" ); DeclareGlobalFunction( "PresentationMatNq" ); DeclareGlobalFunction( "PrintMatPres" ); DeclareGlobalFunction( "PrintNqPres" ); DeclareGlobalFunction( "CollectorByMatNq" ); DeclareGlobalFunction( "IsomorphismUpperUnitriMatGroupPcpGroup" ); polycyclic-2.16/gap/basic/0000755000076600000240000000000013706672341014464 5ustar mhornstaffpolycyclic-2.16/gap/basic/chngpcp.gi0000644000076600000240000002455413706672341016441 0ustar mhornstaff############################################################################ ## #W chngpcp.gi Polycyc Bettina Eick ## ## Algorithms to compute a new pcp groups whose defining pcp runs through ## a given series or is a prime-infinite pcp. ## ############################################################################# ## #F RefinedIgs( ) ## ## returns a polycyclic generating sequence of G G with prime or infinite ## relative orders only. NOTE: this might be not induced! ## RefinedIgs := function( G ) local pcs, rel, ref, ord, map, i, f, g, j; # get old pcp pcs := Igs(G); rel := List( pcs, RelativeOrderPcp ); # create new pcp ref := []; ord := []; map := []; for i in [1..Length(pcs)] do if rel[i] = 0 or IsPrime( rel[i] ) then Add( ref, pcs[i] ); Add( ord, rel[i] ); else f := Factors( rel[i] ); g := pcs[i]; for j in [1..Length(f)] do Add( ref, g ); Add( ord, f[j] ); g := g^f[j]; od; map[i] := f; fi; od; return rec( pcs := ref, rel := ord, map := map ); end; ############################################################################# ## #F RefinedPcpGroup( ) . . . . . . . . refine to infinite or prime factors ## ## this function returns a new pcp group H isomorphic to G such that the ## defining pcp of H is refined. H!.bijection contains the bijection between ## H and G. ## # FIXME: This function is documented and should be turned into a GlobalFunction RefinedPcpGroup := function( G ) local refExponents, pcs, rel, new, ord, map, i, f, g, j, n, c, t, H; # refined exponents refExponents := function( pcs, g, map ) local exp, new, i, c; exp := ExponentsByIgs( pcs, g ); new := []; for i in [1..Length(exp)] do if IsBound( map[i] ) then c := CoefficientsMultiadic( Reversed(map[i]), exp[i] ); Append( new, Reversed( c ) ); else Add( new, exp[i] ); fi; od; return new; end; # refined pcp pcs := Igs( G ); new := RefinedIgs( G ); ord := new.rel; map := new.map; new := new.pcs; # rewrite relations n := Length( new ); c := FromTheLeftCollector( n ); for i in [1..n] do # power if ord[i] > 0 then SetRelativeOrder( c, i, ord[i] ); t := refExponents( pcs, new[i]^ord[i], map ); SetPower( c, i, ObjByExponents(c, t) ); fi; # conjugates for j in [1..i-1] do t := refExponents( pcs, new[i]^new[j], map ); SetConjugate( c, i, j, ObjByExponents(c, t) ); if ord[i] = 0 then t := refExponents( pcs, new[i]^(new[j]^-1), map ); SetConjugate( c, i, -j, ObjByExponents(c, t) ); fi; od; od; # create group and add a bijection H := PcpGroupByCollector( c ); H!.bijection := GroupHomomorphismByImagesNC( G, H, new, Igs(H) ); SetIsBijective( H!.bijection, true ); UseIsomorphismRelation( G, H ); return H; end; ############################################################################# ## #F ExponentsByPcpList( pcps, g, k ) ## ExponentsByPcpList := function( pcps, g, k ) local exp, pcp, e, f, h; h := g; exp := Concatenation( List(pcps{[1..k-1]}, x -> List(x, y -> 0) ) ); for pcp in pcps{[k..Length(pcps)]} do e := ExponentsByPcp( pcp, h ); if e <> 0*e then f := MappedVector( e, pcp ); h := f^-1 * h; fi; Append( exp, e ); od; if not h = h^0 then Error("wrong exponents"); fi; return exp; end; ############################################################################# ## #F PcpGroupByPcps( ). . . . . . . . . . . . . pcps is a list of pcp's ## ## This function returns a new pcp group G. Its defining igs corresponds to ## the given series. G!.bijection contains a bijection from the old group ## to the new one. ## PcpGroupByPcps := function( pcps ) local gens, rels, n, coll, i, j, h, e, w, G, H; if Length( pcps ) = 0 then return fail; fi; gens := Concatenation( List( pcps, x -> GeneratorsOfPcp( x ) ) ); rels := Concatenation( List( pcps, x -> RelativeOrdersOfPcp( x ) ) ); n := Length( gens ); coll := FromTheLeftCollector( n ); for i in [1..n] do if rels[i] > 0 then SetRelativeOrder( coll, i, rels[i] ); h := gens[i] ^ rels[i]; e := ExponentsByPcpList( pcps, h, 1 ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetPower( coll, i, w ); fi; fi; for j in [1..i-1] do h := gens[i]^gens[j]; e := ExponentsByPcpList( pcps, h, 1 ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, j, w ); fi; if rels[j] = 0 then h := gens[i]^(gens[j]^-1); e := ExponentsByPcpList( pcps, h, 1 ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, -j, w ); fi; fi; od; od; # return result H := GroupOfPcp( pcps[1] ); G := PcpGroupByCollector( coll ); G!.bijection := GroupHomomorphismByImagesNC( G, H, Igs(G), gens ); SetIsBijective( G!.bijection, true ); UseIsomorphismRelation( H, G ); return G; end; ############################################################################# ## #F PcpGroupByEfaPcps( ) . . . . . . . . . . . pcps is a list of pcp's ## ## This function returns a new pcp group G. Its defining igs corresponds to ## the given series. G!.bijection contains a bijection from the old group ## to the new one. ## PcpGroupByEfaPcps := function( pcps ) local gens, rels, indx, n, coll, i, j, h, e, w, G, H, l; l := Length(pcps); if l = 0 then return fail; fi; gens := Concatenation( List( pcps, x -> GeneratorsOfPcp( x ) ) ); indx := Concatenation( List( [1..l], x -> List(pcps[x], y -> x) )); rels := Concatenation( List( pcps, x -> RelativeOrdersOfPcp( x ) ) ); n := Length( gens ); coll := FromTheLeftCollector( n ); for i in [1..n] do if rels[i] > 0 then SetRelativeOrder( coll, i, rels[i] ); h := gens[i] ^ rels[i]; e := ExponentsByPcpList( pcps, h, indx[i]+1 ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetPower( coll, i, w ); fi; fi; for j in [1..i-1] do #Print(i," by ",j,"\n"); h := gens[i]^gens[j]; e := ExponentsByPcpList( pcps, h, indx[i] ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, j, w ); fi; if rels[j] = 0 then h := gens[i]^(gens[j]^-1); e := ExponentsByPcpList( pcps, h, indx[i] ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, -j, w ); fi; fi; od; od; # return result H := GroupOfPcp( pcps[1] ); G := PcpGroupByCollector( coll ); G!.bijection := GroupHomomorphismByImagesNC( G, H, Igs(G), gens ); SetIsBijective( G!.bijection, true ); UseIsomorphismRelation( H, G ); return G; end; ############################################################################# ## #F PcpGroupBySeries( [, ] ) ## ## Computes a new pcp presentation through series. If two arguments are ## given, then the factors will be reduced to SNF. ## # FIXME: This function is documented and should be turned into a GlobalFunction PcpGroupBySeries := function( arg ) local ser, r, G, pcps; # get arguments ser := arg[1]; r := Length( ser ) - 1; # the trivial case if r = 0 then G := ser[1]; G!.bijection := IdentityMapping( G ); return G; fi; # otherwise pass arguments on if Length( arg ) = 2 then pcps := List( [1..r], i -> Pcp( ser[i], ser[i+1], "snf" ) ); else pcps := List( [1..r], i -> Pcp( ser[i], ser[i+1] ) ); fi; G := PcpGroupByPcps( pcps ); UseIsomorphismRelation( ser[1], G ); return G; end; ############################################################################# ## #F PcpGroupByEfaSeries(G) ## InstallMethod( PcpGroupByEfaSeries, [IsPcpGroup], function(G) local efa, GG, iso, new; efa := EfaSeries(G); GG := PcpGroupBySeries(efa); iso := GG!.bijection; new := List( efa, x -> PreImage(iso,x) ); SetEfaSeries(GG, new); return GG; end ); ############################################################################# ## #F ExponentsByPcpFactors( pcps, g ) ## ExponentsByPcpFactors := function( pcps, g ) local red, exp, pcp, e; red := g; exp := []; for pcp in pcps do e := ExponentsByPcp( pcp, red ); if e <> 0 * e then red := MappedVector(e,pcp)^-1 * red; fi; Append( exp, e ); od; return exp; end; ############################################################################# ## #F PcpFactorByPcps( H, pcps ) ## PcpFactorByPcps := function(H, pcps) local gens, rels, n, coll, i, j, h, e, w, G; # catch args gens := Concatenation(List(pcps, x -> GeneratorsOfPcp(x))); rels := Concatenation(List(pcps, x -> RelativeOrdersOfPcp(x))); n := Length( gens ); # create new collector coll := FromTheLeftCollector( n ); for i in [ 1 .. n ] do if rels[i] > 0 then SetRelativeOrder( coll, i, rels[i] ); h := gens[i] ^ rels[i]; e := ExponentsByPcpFactors( pcps, h ); w := ObjByExponents( coll, e ); if Length(w) > 0 then SetPower( coll, i, w ); fi; fi; for j in [ 1 .. i - 1 ] do h := gens[i] ^ gens[j]; e := ExponentsByPcpFactors( pcps, h ); w := ObjByExponents( coll, e ); if Length(w) > 0 then SetConjugate( coll, i, j, w ); fi; if rels[j] = 0 then h := gens[i] ^ (gens[j] ^ -1); e := ExponentsByPcpFactors( pcps, h ); w := ObjByExponents( coll, e ); if Length(w) > 0 then SetConjugate( coll, i, - j, w ); fi; fi; od; od; # create new group return PcpGroupByCollector( coll ); end; polycyclic-2.16/gap/basic/coldt.gi0000644000076600000240000000263313706672341016116 0ustar mhornstaff ## #F AddHallPolynomials ## BindGlobal( "AddHallPolynomials", function( coll ) if not IsWeightedCollector( coll ) then Error( "Hall polynomials can be computed ", "for weighted collectors only" ); fi; if not IsBound( coll![PC_DEEP_THOUGHT_POLS] ) or coll![PC_DEEP_THOUGHT_POLS] = [] then # Compute the deep thought polynomials coll![PC_DEEP_THOUGHT_POLS] := Calcreps2(coll![PC_CONJUGATES], 8, 1); # Compute the orders of the genrators of dtrws CompleteOrdersOfRws(coll); # reduce the coefficients of the deep thought polynomials ReduceCoefficientsOfRws(coll); SetIsPolynomialCollector( coll, true ); fi; end ); ## ## Methods for CollectWordOrFail. ## InstallMethod(CollectWordOrFail, "FTL collector with Hall polynomials, exponent vector, gen-exp-pairs", [ IsFromTheLeftCollectorRep and IsPolynomialCollector and IsUpToDatePolycyclicCollector, IsList, IsList], function( coll, l, genexp ) local res, i, n; if Length(genexp) = 0 then return true; fi; res := ObjByExponents( coll, l ); i := 1; while i < Length(genexp) do res := DTMultiply( res, [genexp[i], genexp[i+1]], coll ); i := i + 2; od; for i in [1..Length(l)] do l[i] := 0; od; n := Length( res ); l{ res{[1,3..n-1]} } := res{ [2,4..n] }; return true; end ); polycyclic-2.16/gap/basic/pcpsers.gi0000644000076600000240000003573113706672341016475 0ustar mhornstaff############################################################################# ## #W pcpseries.gi Polycyc Bettina Eick ## ############################################################################# ## #M LowerCentralSeriesOfGroup( G ) ## ## As usual, this function calls CommutatorSubgroup repeatedly. However, ## is sets U!.isNormal before to avoid unnecessary normal closures in ## CommutatorSubgroup. ## InstallMethod( LowerCentralSeriesOfGroup, [IsPcpGroup], function( G ) local ser, U; ser := [G]; U := DerivedSubgroup( G ); G!.isNormal := true; while U <> ser[Length( ser )] do Add( ser, U ); U := CommutatorSubgroup( U, G ); od; Unbind( G!.isNormal ); return ser; end ); InstallMethod( PCentralSeriesOp, [IsPcpGroup, IsPosInt], function( G, p ) local ser, U, C, pcp, new; ser := [G]; U := G; G!.isNormal := true; while Size(U) > 1 do C := CommutatorSubgroup( U, G ); pcp := Pcp( U, C ); new := List( pcp, x -> x^p ); U := SubgroupByIgs( G, Igs(C), new ); Add( ser, U ); od; Unbind( G!.isNormal ); return ser; end ); ############################################################################# ## #F PcpSeries( G ) ## ## Compute a polycyclic series of G - we use the series defined by Igs(G). ## InstallGlobalFunction( PcpSeries, function( G ) local pcs, ser; pcs := Igs( G ); ser := List( [1..Length(pcs)], i -> SubgroupByIgs( G, pcs{[i..Length(pcs)]} ) ); Add( ser, TrivialSubgroup(G) ); return ser; end ); ############################################################################# ## #F CompositionSeries(G) ## InstallMethod( CompositionSeries, [IsPcpGroup], function(G) local g, r, n, s, i, f, m, j, e, U; if not IsFinite(G) then Error("composition series is infinite"); fi; # set up g := Pcp(G); r := RelativeOrdersOfPcp(g); n := Length(g); # construct series s := [G]; for i in [1..n] do if r[i] > 1 then f := Factors(r[i]); m := Length(f); for j in [1..m-1] do e := Product(f{[1..j]}); U := SubgroupByIgs(G, Concatenation([g[i]^e], g{[i+1..n]})); Add(s, U); od; Add(s, SubgroupByIgs(G, g{[i+1..n]})); fi; od; return s; end ); ############################################################################# ## #M EfaSeries( G ) ## ## Computes a normal series of G whose factors are elementary or free ## abelian (efa). The normal series will also be normal under action ## of the parent of G ## InstallGlobalFunction( IsEfaFactorPcp, function( pcp ) local gens, denm, i, j, c, cycl, rels, p; gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); # if there are no generators, then the factor is trivial if Length( gens ) = 0 then return true; fi; # if the factor is finite, then it must be elementary if ForAll( rels, x -> x <> 0 ) then p := rels[1]; if not IsPrime( p ) or ForAny( rels, x-> x <> p ) then return false; fi; fi; # if the factor is infinite, then no finite bits may occur at its end if ForAny( rels, x -> x = 0 ) and rels[Length(rels)] > 0 then return false; fi; # check if factor is abelian denm := DenominatorOfPcp( pcp ); for i in [1..Length( gens )] do for j in [1..i-1] do c := Comm( gens[i], gens[j] ); c := ReducedByIgs( denm, c ); if c <> c^0 then return false; fi; od; od; # check if factor is elementary or free cycl := CyclicDecomposition( pcp ); rels := cycl.rels; p := rels[1]; if not (p = 0 or IsPrime(p)) then return false; fi; if ForAny( rels, x -> x <> p ) then return false; fi; # otherwise it is efa pcp!.cyc := cycl; return true; end ); InstallGlobalFunction( EfaSeriesParent, function( G ) local ser, new, i, U, V, pcp, nat, ref; # take the normal subgroups in the defining pc series ser := Filtered( PcpSeries(G), x -> IsNormal(G,x) ); # check the factors new := [G]; for i in [1..Length(ser)-1] do U := ser[i]; V := ser[i+1]; # if the factor is free or elementary abelian, then # everything is fine pcp := Pcp( U, V ); if IsEfaFactorPcp( pcp ) then U!.efapcp := pcp; Add( new, V ); # otherwise we need to refine this factor else nat := NaturalHomomorphismByPcp( pcp ); ref := RefinedDerivedSeries( Image( nat ) ); ref := ref{[2..Length(ref)]}; ref := List( ref, x -> PreImage( nat, x ) ); Append( new, ref ); fi; od; return new; end ); InstallMethod( EfaSeries, [IsPcpGroup], function( G ) local efa, new, L, A, B; # use the series of the parent which has a defining pcs if G = Parent( G ) then return EfaSeriesParent(G); fi; efa := EfaSeries( Parent( G ) ); # intersect each subgroup into G new := [G]; for L in efa do # compute new factor A := new[Length(new)]; B := NormalIntersection( L, G ); # check if it is a proper factor and if so, then add it if IndexNC( A, B ) <> 1 then Unbind( A!.efapcp ); Add( new, B ); fi; # check if the series arrived at the trivial subgroup if Length( Igs( B ) ) = 0 then return new; fi; od; end ); ############################################################################# ## #F RefinedDerivedSeries( ) ## ## Compute an efa series of G which refines the derived series by ## finite - by - (torsion-free) factors. ## InstallGlobalFunction( RefinedDerivedSeries, function( G ) local ser, ref, i, A, B, pcp, gens, rels, n, free, fini, U, s, t, f; ser := DerivedSeriesOfGroup( G ); ref := [G]; for i in [1..Length( ser ) - 1] do # refine abelian factor A/B A := ser[i]; B := ser[i+1]; pcp := Pcp( A, B, "snf" ); gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); n := Length( gens ); # take the free part for the top factor free := Filtered( [1..n], x -> rels[x] = 0 ); fini := Filtered( [1..n], x -> rels[x] > 0 ); if Length( free ) > 0 then f := AddToIgs( Igs(B), gens{fini} ); U := SubgroupByIgs( G, f ); Add( ref, U ); else U := A; fi; # the torsion subgroup if Length( fini ) > 0 then s := Factors( Lcm( rels{fini} ) ); f := gens{fini}; for t in s do f := List( f, x -> x ^ t ); f := AddToIgs( Igs(B), f ); U := SubgroupByIgs( G, f ); Add( ref, U ); od; fi; od; return ref; end ); ############################################################################# ## #F RefinedDerivedSeriesDown( ) ## ## Compute an efa series of G which refines the derived series by ## (torsion-free) - by - finite factors. ## InstallGlobalFunction( RefinedDerivedSeriesDown, function( G ) local ser, ref, i, A, B, pcp, gens, rels, n, free, fini, U, s, f, t; ser := DerivedSeriesOfGroup( G ); ref := [G]; for i in [1..Length( ser ) - 1] do # refine abelian factor A/B A := ser[i]; B := ser[i+1]; pcp := Pcp( A, B, "snf" ); gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); n := Length( gens ); # get info free := Filtered( [1..n], x -> rels[x] = 0 ); fini := Filtered( [1..n], x -> rels[x] > 0 ); U := A; # first the torsion part if Length( fini ) > 0 then s := Factors( Lcm( rels{fini} ) ); f := ShallowCopy( gens ); for t in s do f := List( f, x -> x ^ t ); f := AddToIgs( Igs(B), f ); U := SubgroupByIgs( G, f ); Add( ref, U ); od; fi; # now it remains to add the free part if Length( free ) > 0 then Add( ref, B ); fi; od; return ref; end ); ############################################################################# ## #F TorsionByPolyEFSeries( G ) ## ## Compute an efa-series of G which has only torsion-free factors at the ## top and only finite factors at the bottom. It might happen that such a ## series does not exists. In this case the function returns fail. ## InstallGlobalFunction( TorsionByPolyEFSeries, function( G ) local ref, U, D, pcp, gens, rels, n, fini, tmp; ref := []; U := ShallowCopy( G ); while Size(U) = infinity do # factorise abelian factor group D := DerivedSubgroup( U ); pcp := Pcp( U, D, "snf" ); gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); n := Length( gens ); # check that there is a free part if Length( Filtered( [1..n], x -> rels[x] = 0 ) ) = 0 then return fail; fi; # take the free part into the series fini := Filtered( [1..n], x -> rels[x] > 0 ); U := SubgroupByIgs( G, Igs(D), gens{fini} ); Add( ref, U ); od; # now U is a finite group and we refine it as we like tmp := RefinedDerivedSeries( U ); Append( ref, tmp{[2..Length(tmp)]} ); return ref; end ); ############################################################################# ## #F PStepCentralSeries(G, p) ## PStepCentralSeries := function(G, p) local ser, new, i, N, M, pcp, j, U; ser := PCentralSeries(G, p); new := [G]; for i in [1..Length(ser)-1] do N := ser[i]; M := ser[i+1]; pcp := Pcp(N,M); for j in [1..Length(pcp)] do U := SubgroupByIgs( G, pcp{[j+1..Length(pcp)]}, Igs(M) ); Add( new, U ); od; od; return new; end; ############################################################################# ## #M PcpsBySeries( ser [,"snf"] ) ## ## Usually it's better to work with pcp's instead of series. Here are ## the functions to get them. ## InstallGlobalFunction( PcpsBySeries, function( arg ) local ser; ser := arg[1]; if Length( arg ) = 1 then return List( [1..Length(ser)-1], x -> Pcp( ser[x], ser[x+1] ) ); else return List( [1..Length(ser)-1], x -> Pcp( ser[x], ser[x+1], "snf" )); fi; end ); ############################################################################# ## #M PcpsOfEfaSeries( G ) ## ## Some of the factors in this series know already its pcp. The other must ## be computed. ## InstallMethod( PcpsOfEfaSeries, [IsPcpGroup], function( G ) local ser, i, new, pcp; ser := EfaSeries( G ); pcp := []; for i in [1..Length(ser)-1] do if IsBound( ser[i]!.efapcp ) then Add( pcp, ser[i]!.efapcp ); Unbind( ser[i]!.efapcp ); else new := Pcp( ser[i], ser[i+1], "snf" ); Add( pcp, new ); fi; od; return pcp; end ); ############################################################################# ## #M ModuloSeries( ser, N ) ## ## N is assumed to normalize each subgroup in ser. This function returns an ## induced series of ser[1] mod N. The last subgroup in the returned series ## is N. ## InstallGlobalFunction( ModuloSeries, function( ser, N ) local gens, new, L, A, B; gens := GeneratorsOfGroup( N ); new := []; for L in ser do B := SubgroupByIgs( Parent( ser[1] ), Igs(L), gens ); if Length( new ) = 0 then Add( new, B ); else A := new[Length(new)]; if IndexNC( A, B ) <> 1 then Add( new, B ); fi; fi; if IsGroup(N) and IsSubgroup( N, L ) then return new; fi; od; return new; end ); ############################################################################# ## #M ModuloSeriesPcps( pcps, N [,"snf] ) ## ## Same as above, but input and output are pcp's. Note that this function ## has more flexible argumentlists. ## InstallGlobalFunction( ModuloSeriesPcps, function( arg ) local pcps, gens, new, A, B, pcp, G; pcps := arg[1]; if Length( pcps ) = 0 then return fail; fi; if IsList( arg[2] ) then gens := arg[2]; else gens := GeneratorsOfGroup( arg[2] ); fi; G := GroupOfPcp( pcps[1] ); A := SubgroupByIgs( G, AddIgsToIgs(gens, NumeratorOfPcp( pcps[1] ))); new := []; for pcp in pcps do B := SubgroupByIgs( G, AddIgsToIgs(gens,DenominatorOfPcp(pcp))); if IndexNC( A, B ) <> 1 and Length( arg ) = 2 then Add( new, Pcp( A, B ) ); A := ShallowCopy( B ); elif IndexNC( A, B ) <> 1 then Add( new, Pcp( A, B, "snf" ) ); A := ShallowCopy( B ); fi; if IsGroup(arg[2]) and IsSubgroup( arg[2], B ) then return new; fi; od; return new; end ); ############################################################################# ## #M ExtendedSeriesPcps( pcps, N [,"snf"]). . . . . . . . extend series with N ## InstallGlobalFunction( ExtendedSeriesPcps, function( arg ) local N, L, new; N := arg[2]; L := GroupOfPcp( arg[1][1] ); if IndexNC( N, L ) <> 1 then if Length( arg ) = 2 then new := Pcp( N, L ); else new := Pcp( N, L, "snf" ); fi; return Concatenation( [new], arg[1] ); else return arg[1]; fi; end ); ############################################################################# ## #M ReducedEfaSeriesPcps( pcps ). . . . . . . . . . . . try to shorten series ## InstallGlobalFunction( ReducedEfaSeriesPcps, function( pcps ) local new, old, i, V, U, pcp; new := []; old := pcps[Length(pcps)]; for i in Reversed( [1..Length(pcps)-1] ) do U := GroupOfPcp( pcps[i] ); V := SubgroupByIgs( U, DenominatorOfPcp( old ) ); pcp := Pcp( U, V ); if not IsEfaFactorPcp( pcp ) then Add( new, old ); old := pcps[i]; else old := pcp; fi; od; Add( new, old ); return Reversed( new ); end ); ############################################################################# ## #M PcpsOfPowerSeries( pcp, n ) ## PcpsOfPowerSeries := function( pcp, n ) local facs, gens, sers, B, f, A, p, new; facs := Factors(n); gens := GeneratorsOfPcp( pcp ); sers := []; B := GroupOfPcp( pcp ); p := 1; for f in facs do p := p * f; A := ShallowCopy( B ); B := AddIgsToIgs(List( gens, x -> x^p ), DenominatorOfPcp(pcp)); B := SubgroupByIgs( A, B ); new := Pcp(A, B); new!.power := p; Add( sers, new ); od; return sers; end; ############################################################################# ## #F LinearActionOnPcp( gens, pcp ) ## InstallGlobalFunction( LinearActionOnPcp, function( gens, pcp ) return List( gens, x -> List( pcp, y -> ExponentsByPcp( pcp, y ^ x ) ) ); end ); polycyclic-2.16/gap/basic/pcppcps.gi0000644000076600000240000006556013706672341016471 0ustar mhornstaff############################################################################# ## #W pcppcgs.gi Polycyc Bettina Eick #W Werner Nickel ## ############################################################################# ## ## At the moment the pcgs of a pcp group is called pcp. This is to keep ## it separated from the GAP library. ## ############################################################################# ## #F UpdateCounter( ind, gens, c ) . . . . . . . . . . . . small help function ## UpdateCounter := function( ind, gens, c ) local i, g; # first reset c by ind i := c - 1; while i > 0 and not IsBool(ind[i]) and LeadingExponent(ind[i]) = 1 do i := i - 1; od; if IsSortedList(gens) and not IsEmpty(gens) and Depth(gens[Length(gens)]) < i then return i + 1; fi; # now try to add elements from gens repeat g := First( gens, x -> Depth(x) = i and LeadingExponent(x) = 1 ); if not IsBool( g ) then ind[i] := g; i := i - 1; fi; until IsBool( g ); # return value for counter return i + 1; end; ############################################################################# ## #F TailLimit ## TailLimit := function( ind, c ) local k, i; k := List(ind, x -> not IsBool(x) and LeadingExponent(x)=1); i := c-1; while i > 0 and k[i]=true do i := i-1; od; i := i+1; return i; end; ############################################################################# ## #F ReduceExpo ## ReduceExpo := function( ind, gen, rel ) local i, j, a, b, q, f, k; for i in [1..Length(ind)] do if not IsBool(ind[i]) and rel[i]=0 then b := LeadingExponent(ind[i]); for j in [1..i-1] do if not IsBool( ind[j] ) then a := Exponents(ind[j])[i]; q := QuoInt(a,b); if q <> 0 then ind[j] := ind[j]*ind[i]^-q; fi; fi; od; for j in [1..Length(gen)] do a := Exponents(gen[j])[i]; q := QuoInt(a,b); if q <> 0 then gen[j] := gen[j]*ind[i]^-q; fi; od; fi; od; end; ############################################################################# ## #F CheckIgs ## CheckIgs := function( igs, gen ) local i, g, e, j; for i in [1..Length(igs)] do g := igs[i]^RelativeOrderPcp(igs[i]); e := ExponentsByIgs(igs, g); if e = fail then return i; fi; for j in [1..i-1] do g := Comm(igs[i], igs[j]); e := ExponentsByIgs(igs,g); if e = fail then return [i,j]; fi; od; od; for i in [1..Length(gen)] do e := ExponentsByIgs(igs, gen[i]); if e = fail then return [i]; fi; od; return true; end; ############################################################################# ## #F ValFuns ## IGSValFun1 := function(g) return 0; end; IGSValFun2 := function(g) return AbsInt(LeadingExponent(g)); end; IGSValFun3 := function(g) return [AbsInt(LeadingExponent(g)),Length(Exponents(g))-Depth(g)]; end; IGSValFun4 := function(g) return [Length(Exponents(g))-Depth(g), AbsInt(LeadingExponent(g))]; end; IGSValFun := IGSValFun4; ############################################################################# ## #F AddToIgs( , ) ## InstallGlobalFunction(AddToIgs, function(igs, gens) local coll, rels, n, c, ind, g, d, todo, val, j, f, h, e, a, k, b, u, t, r; if Length(gens) = 0 then return igs; fi; # get information coll := Collector(gens[1]); rels := RelativeOrders(coll); n := NumberOfGenerators(coll); c := n+1; # set up ind := ListWithIdenticalEntries(n, false); for g in igs do ind[Depth(g)] := g; od; # do a reduction step c := TailLimit(ind, c); todo := Set(Filtered(gens, x -> Depth(x) < c)); val := List(todo, x -> IGSValFun(x)); # loop over to-do list until it is empty while Length(todo) > 0 and c > 1 do j := Position(val, Minimum(val)); g := Remove(todo, j); d := Depth(g); f := []; # shift g into ind while d < c do h := ind[d]; r := FactorOrder(g); a := LeadingExponent(g); # shift in if IsBool(h) then ind[d] := NormedPcpElement(g); Add(f,d); h := ind[d]; elif not IsPrime(r) then b := LeadingExponent(h); e := Gcdex(a, b); if e.coeff1 <> 0 then ind[d] := NormedPcpElement((g^e.coeff1)*(h^e.coeff2)); Add(f,d); fi; fi; # divide off if g = h then g := g^0; else b := LeadingExponent(h); e := Gcdex(a,b); g := g^e.coeff3 * h^e.coeff4; fi; d := Depth(g); od; # adjust c := TailLimit(ind, c); ReduceExpo(ind, todo, rels); # add powers and commutators for d in f do g := ind[d]; if rels[d] > 0 then k := g ^ RelativeOrderPcp(g); if Depth(k) < c then Add(todo, k); fi; fi; for j in [1..c-1] do if not IsBool(ind[j]) then k := Comm(g, ind[j]); if Depth(k) < c then Add(todo, k); fi; if rels[j] = 0 then k := Comm(g, ind[j]^-1); if Depth(k) < c then Add(todo, k); fi; fi; fi; od; od; # reduce todo := Filtered(todo, x -> Depth(x) IGSValFun(x)); Info(InfoPcpGrp, 3, Length(val)," versus ", ind); od; # return resulting list ind := Filtered(ind, x -> not IsBool(x)); if CHECK_IGS@ then Info(InfoPcpGrp, 1, "checking igs "); t := CheckIgs(ind, gens); if t <> true then Error("igs is incorrect at ",t); fi; fi; return ind; end); AddToIgs_Old := function(igs, gens) local coll, rels, todo, n, ind, g, d, h, k, a, b, e, f, c, i, l; if Length(gens) = 0 then return igs; fi; # get information coll := Collector(gens[1]); rels := RelativeOrders(coll); n := NumberOfGenerators(coll); # create new list from igs ind := ListWithIdenticalEntries(n, false); for g in igs do ind[Depth(g)] := g; od; # set counter and add tail as far as possible c := UpdateCounter(ind, gens, n+1); # create a to-do list and a pointer todo := Set(Filtered(gens, x -> Depth(x) < c)); # loop over to-do list until it is empty while Length(todo) > 0 and c > 1 do g := Remove(todo); d := Depth(g); f := []; # shift g into ind while d < c do h := ind[d]; if IsBool(h) then ind[d] := g; g := g^0; Add(f, d); else # reduce g with h a := LeadingExponent(g); b := LeadingExponent(h); e := Gcdex(a, b); # adjust ind[d] by gcd ind[d] := (g^e.coeff1) * (h^e.coeff2); if e.coeff1 <> 0 then Add(f, d); fi; # adjust g g := (g^e.coeff3) * (h^e.coeff4); fi; d := Depth(g); c := UpdateCounter(ind, todo, c); od; # add powers and commutators for d in f do g := ind[d]; if d <= Length(rels) and rels[d] > 0 and d < c then k := g ^ RelativeOrderPcp(g); if Depth(k) < c then Add(todo, k); fi; fi; for l in [1..n] do if not IsBool(ind[l]) and (d < c or l < c) then k := Comm(g, ind[l]); if Depth(k) < c then Add(todo, k); fi; k := Comm(g, ind[l]^-1); if Depth(k) < c then Add(todo, k); fi; fi; od; od; # try sorting Sort(todo); od; # return resulting list return Filtered(ind, x -> not IsBool(x)); end; ############################################################################# ## #F Igs( ) ## InstallOtherMethod( Igs, [IsList], function( gens ) return AddToIgs( [], gens ); end ); ############################################################################# ## #F Ngs( ) . . . . . . . . . . . . . . . compute normed version of igs ## InstallOtherMethod( Ngs, [IsList], function( igs ) return List( igs, x -> NormedPcpElement( x ) ); end ); ############################################################################# ## #F Cgs( ) . . . . . .. . . . . . . . . compute canonical version of igs ## InstallOtherMethod( Cgs, [IsList], function( igs ) local ind, can, i, e, j, l, d, r, s; # first norm leading coefficients can := List( igs, x -> NormedPcpElement( x ) ); # reduce entries in matrix for i in [1..Length(can)] do e := LeadingExponent( can[i] ); d := Depth( can[i] ); for j in [1..i-1] do l := Exponents( can[j] )[d]; if l > 0 then r := QuoInt( l, e ); can[j] := can[j] * can[i]^-r; elif l < 0 then r := QuoInt( -l, e ); s := RemInt( -l, e ); if s = 0 then can[j] := can[j] * can[i]^r; else can[j] := can[j] * can[i]^(r+1); fi; fi; od; od; # set flag `normed' and return for i in [1..Length(can)] do can[i]!.normed := true; od; return can; end ); ############################################################################# ## #F AddIgsToIgs( pcs1, pcs2 ); ## ## Combines an igs of a normal subgroup with an igs of a ## factor. Typically, is induced wrt to a pcp and is the ## denominator of this pcp. ## # FIXME: This function is documented and should be turned into a GlobalFunction AddIgsToIgs := function( pcs1, pcs2 ) local coll, rels, n, ind, todo, g, c, h, eg, eh, e, d; if Length( pcs1 ) = 0 then return AsList( pcs2 ); elif Length( pcs2 ) = 0 then return AsList( pcs1 ); elif Depth( pcs1[Length(pcs1)] ) < Depth( pcs2[1] ) then return Concatenation( AsList( pcs1 ), AsList( pcs2 ) ); elif Depth( pcs2[Length(pcs2)] ) < Depth( pcs1[1] ) then return Concatenation( AsList( pcs2 ), AsList( pcs1 ) ); fi; # merge the two pcs' coll := Collector( pcs1[1] ); rels := RelativeOrders( coll ); n := NumberOfGenerators( coll ); ind := List( [1..n], x -> false ); todo := []; for g in pcs2 do ind[Depth(g)] := g; od; for g in pcs1 do if IsBool( ind[Depth(g)] ) then ind[Depth(g)] := g; else Add( todo, g ); fi; od; # set counter c := UpdateCounter( ind, todo, n+1 ); # create a to-do list and a pointer todo := Filtered( todo, x -> Depth( x ) < c ); # loop over to-do list until it is empty while Length( todo ) > 0 and c > 1 do g := Remove(todo); d := Depth( g ); # shift g into ind while d < c do h := ind[d]; if not IsBool( h ) then # reduce g with h eg := LeadingExponent( g ); eh := LeadingExponent( h ); e := Gcdex( eg, eh ); # adjust g and ind[d] by gcd ind[d] := (g^e.coeff1) * (h^e.coeff2); g := (g^e.coeff3) * (h^e.coeff4); else ind[d] := g; g := g^0; fi; c := UpdateCounter( ind, todo, c ); d := Depth( g ); od; od; return Filtered( ind, x -> not IsBool( x ) ); end; ############################################################################# ## #F ModuloInfo( igsH, igsN ) ## ## igsH and igsN are igs'ses for H and N. We assume N <= H and N normal ## in H. The function computes information for the factor H/N. ## ModuloInfo := function( igsH, igsN ) local depN, gens, rels, h, r, j, l, e; depN := List( igsN, Depth ); gens := []; rels := []; # get modulo generators and their relative orders for h in igsH do r := RelativeOrderPcp( h ); j := PositionSet( depN, Depth(h) ); if IsBool( j ) then Add( rels, r ); Add( gens, h ); elif r > 0 then if not IsPrime( r ) then l := RelativeOrderPcp( igsN[j] ); if l <> r then Add( rels, r / l ); Add( gens, h ); fi; fi; else e := AbsInt( LeadingExponent( igsN[j] ) / LeadingExponent( h ) ); if e > 1 then Add( rels, e ); Add( gens, h ); fi; fi; od; return rec( gens := gens, rels := rels ); end; ############################################################################# ## #F CyclicDecomposition( pcp ) ## CyclicDecomposition := function( pcp ) local rels, n, mat, i, row, new, cyc, ord, chg, inv, g, tmp, imgs, prei; # catch a trivial case if Length( pcp ) = 0 then return rec( gens := [], rels := [], chg := [], inv := [] ); fi; # set up rels := RelativeOrdersOfPcp( pcp ); n := Length( pcp ); # create relator matrix for power relators - this is in upper # triangular form mat := []; for i in [1..n] do if rels[i] > 0 then row := ExponentsByPcp( pcp, pcp[i]^rels[i] ); row[i] := row[i] - rels[i]; Add( mat, row ); else Add( mat, List( [1..n], x -> 0 ) ); fi; od; # solve matrix # new := SmithNormalFormSQ( mat ); new := NormalFormIntMat( mat, 9 ); # get new generators, relators and the basechange cyc := []; ord := []; chg := []; inv := []; imgs := TransposedMat( new.coltrans ); prei := Inverse( new.coltrans ); for i in [1..n] do if new.normal[i][i] <> 1 then g := MappedVector( prei[i], pcp ); Add( cyc, g ); Add( ord, new.normal[i][i] ); Add( chg, prei[i] ); if new.normal[i][i] > 0 then Add( inv, List( imgs[i], x -> x mod new.normal[i][i] ) ); else Add( inv, imgs[i] ); fi; fi; od; return rec( gens := cyc, rels := ord, chg := chg, inv := TransposedMat( inv ) ); end; ############################################################################# ## #F AddTailInfo( pcp ) . . . . . . . ## ## The info in pcp!.tail is used to compute exponent vectors. ## 1.) pcp!.tail is a list, then exponents are just looked up. ## 2.) pcp!.tail is an integer, then the computation of exponents ## stops at pcp!.tail-1; ## AddTailInfo := function( pcp ) local gens, sub, n, deps, depg, i, d, mult; gens := pcp!.gens; sub := pcp!.denom; # if there are no gens, then it does not matter if Length( gens ) = 0 then return; fi; n := NumberOfGenerators( Collector( gens[1] ) ); # get depths deps := List( sub, Depth ); depg := List( gens, Depth ); # if not IsSortedList( deps ) then Error("add tail info"); fi; # set tail to an integer pcp!.tail := Maximum( depg ) + 1; # FIXME: the remainder of this function does not what it is supposed to # do, so we skip it for now return; if not IsSortedList( deps ) then return; fi; # now figure out whether we can do better for i in [1..Length(sub)] do if deps[i] < pcp!.tail - 1 then d := IsPowerOfGenerator( sub[i], pcp!.tail ); if IsBool( d ) then return; fi; fi; od; # add multiplication list mult := []; for i in [1..Length(gens)] do if depg[i] < pcp!.tail then d := IsPowerOfGenerator( gens[i], pcp!.tail ); if IsBool( d ) then return; fi; Add( mult, d ); fi; od; # if we arrive here, then we may read off exponents pcp!.tail := depg; if ForAny( mult, x -> x <> 1 ) then pcp!.mult := mult; fi; end; ############################################################################# ## #F Creation function for pcp's. ## ## Pcp( U ) pcp for U ## Pcp( U, N ) pcp for U mod N ## Pcp( U, "snf" ) pcp for abelian group U in SNF ## Pcp( U, N, "snf" ) pcp for abelian factor U mod N in SNF ## InstallGlobalFunction( Pcp, function( arg ) local U, gens, rels, denom, numer, info, pcp; # catch arguments U and N U := arg[1]; if Length( arg ) = 1 or IsString( arg[2] ) then denom := []; elif Length( arg ) > 1 and IsGroup( arg[2] ) then denom := arg[2]; fi; # do we want to norm the pcs or make it canonical? if USE_CANONICAL_PCS@ then numer := Cgs( U ); denom := Cgs( denom ); elif USE_NORMED_PCS@ then numer := Ngs( U ); denom := Ngs( denom ); else numer := Igs( U ); denom := Igs( denom ); fi; # set up modulo info if Length( denom ) > 0 then info := ModuloInfo( numer, denom ); gens := info.gens; rels := info.rels; else gens := numer; rels := List( gens, RelativeOrderPcp ); fi; # create pcp record and objectify pcp := rec( gens := gens, rels := rels, denom := denom, numer := numer, one := One( U ), group := U ); pcp := Objectify( PcpType, pcp ); # add info on tails AddTailInfo( pcp ); # add info on snf if desired if arg[Length(arg)] = "snf" then pcp!.cyc := CyclicDecomposition( pcp ); fi; # return return pcp; end ); ############################################################################# ## #F Basic attributes and properties - for IsPcpRep ## InstallGlobalFunction( RelativeOrdersOfPcp, function( pcp ) if IsBound( pcp!.cyc ) then return pcp!.cyc.rels; else return pcp!.rels; fi; end ); InstallGlobalFunction( GeneratorsOfPcp, function( pcp ) if IsBound( pcp!.cyc ) then return pcp!.cyc.gens; else return pcp!.gens; fi; end ); InstallGlobalFunction( DenominatorOfPcp, pcp -> pcp!.denom ); InstallGlobalFunction( NumeratorOfPcp, pcp -> pcp!.numer ); InstallGlobalFunction( OneOfPcp, pcp -> pcp!.one ); InstallGlobalFunction( GroupOfPcp, pcp -> pcp!.group ); InstallGlobalFunction( IsSNFPcp, pcp -> IsBound(pcp!.cyc) ); InstallGlobalFunction( IsTailPcp, pcp -> IsList(pcp!.tail) ); ############################################################################# ## #F Higher-level attributes and properties - to make pcp's look like lists ## ############################################################################# ## #M Length( ) ## InstallOtherMethod( Length, [ IsPcp ], pcp -> Length( GeneratorsOfPcp( pcp ) ) ); ############################################################################# ## #M AsList( ) ## InstallOtherMethod( AsList, [ IsPcp ], pcp -> GeneratorsOfPcp( pcp ) ); ############################################################################# ## #M Position( , , ) ## InstallOtherMethod( Position, [ IsPcp, IsPcpElement, IsInt ], function( pcp, elm, from ) return Position( AsList( pcp ), elm, from ); end ); ############################################################################# ## #M ListOp( pcp, function ) ## InstallOtherMethod( ListOp, [ IsPcp, IsObject ], function( pcp, f ) return List( AsList(pcp), f ); end ); ############################################################################# ## #M [ ] ## InstallOtherMethod( \[\], [ IsPcp, IsPosInt ], function( pcp, pos ) return GeneratorsOfPcp(pcp)[pos]; end ); ############################################################################# ## #M {[ ]} ## InstallOtherMethod( ELMS_LIST, [ IsPcp, IsDenseList ], function( pcp, ran ) return GeneratorsOfPcp( pcp ){ran}; end ); ############################################################################# ## #M Print pcp ## InstallMethod( PrintObj, "for pcp", [IsPcp], function( pcp ) Print( "Pcp ", GeneratorsOfPcp( pcp ), " with orders ", RelativeOrdersOfPcp(pcp)); end ); InstallMethod( ViewObj, [ IsPcp ], SUM_FLAGS, PrintObj ); ############################################################################# ## #F small helper ## WordByExps := function( exp ) local w, i; w := []; for i in [1..Length(exp)] do if exp[i] <> 0 then Add( w, i ); Add( w, exp[i] ); fi; od; return w; end; ############################################################################# ## #M a small helper ## PrintWord := function(gen,exp) local w, i, g; w := WordByExps(exp); if Length(w) = 0 then Print("id "); else for i in [1,3..Length(w)-1] do g := Concatenation(gen,String(w[i])); if w[i+1] = 1 then Print(g); else Print(g,"^",w[i+1]); fi; if i < Length(w)-1 then Print(" * "); fi; od; fi; Print("\n"); end; ############################################################################# ## #M Print pcp presentation ## PrintPresentationByPcp := function( pcp, flag ) local gens, rels, i, r, g, j, h, c; gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); # print relations for i in [1..Length(gens)] do if rels[i] > 0 then r := rels[i]; g := gens[i]; Print("g",i,"^",r," = "); PrintWord("g",ExponentsByPcp(pcp, g^r)); fi; od; for i in [1..Length(gens)] do for j in [1..i-1] do g := gens[i]; h := gens[j]; c := gens[i]^gens[j]; if c <> g or flag = "all" then Print("g",i," ^ g",j," = "); PrintWord("g",ExponentsByPcp(pcp, c)); fi; if rels[j] = 0 or flag = "all" then c := gens[i]^(gens[j]^-1); if c <> g or flag = "all" then Print("g",i," ^ g",j,"^-1 = "); PrintWord("g",ExponentsByPcp(pcp, c)); fi; fi; od; od; end; ############################################################################# ## #M Print pcp presentation ## # FIXME: This function is documented and should be turned into a GlobalFunction PrintPcpPresentation := function( arg ) local G, flag; G := arg[1]; if Length(arg) = 2 then flag := arg[2]; else flag := false; fi; if IsGroup(G) then PrintPresentationByPcp( Pcp(G), flag ); else PrintPresentationByPcp( G, flag ); fi; end; ############################################################################# ## #M GapInputPcpGroup( file, pcp ) ## GapInputPcpGroup := function( file, pcp ) local gens, rels, i, j, obj; gens := GeneratorsOfPcp( pcp ); rels := RelativeOrdersOfPcp( pcp ); PrintTo(file, "coll := FromTheLeftCollector( ", Length(gens)," );\n"); for i in [1..Length(rels)] do if rels[i] > 0 then obj := WordByExps(ExponentsByPcp( pcp, gens[i]^rels[i] )); AppendTo(file, "SetRelativeOrder( coll, ",i,", ",rels[i]," );\n"); AppendTo(file, "SetPower( coll, ",i,", ",obj," );\n"); fi; od; for i in [1..Length(rels)] do for j in [1..i-1] do obj := WordByExps(ExponentsByPcp( pcp, gens[i]^gens[j] )); if obj <> [ i, 1 ] then AppendTo(file, "SetConjugate( coll, ",i,", ",j,", ",obj," );\n"); fi; obj := WordByExps(ExponentsByPcp( pcp, gens[i]^(gens[j]^-1) )); if obj <> [ i, 1 ] then AppendTo(file, "SetConjugate( coll, ",i,", ",-j,", ",obj," );\n"); fi; od; od; AppendTo(file, "UpdatePolycyclicCollector( coll );\n" ); AppendTo(file, "G := PcpGroupByCollectorNC( coll ); \n"); if HasIsNilpotentGroup( GroupOfPcp(pcp) ) and IsNilpotentGroup( GroupOfPcp(pcp) ) then AppendTo(file, "SetIsNilpotentGroup( G, true );\n" ); fi; end; ############################################################################# ## #M PcpGroupByPcp( pcp ) . . . . . . . . . . . . . . . . . create a new group ## # FIXME: This function is documented and should be turned into a GlobalFunction PcpGroupByPcp := function( pcp ) local g, r, n, coll, i, j, h, e, w, G; # write down a presentation g := GeneratorsOfPcp( pcp ); r := RelativeOrdersOfPcp( pcp ); n := Length( g ); # create a collector coll := FromTheLeftCollector( n ); for i in [1..n] do if r[i] > 0 then SetRelativeOrder( coll, i, r[i] ); h := g[i] ^ r[i]; e := ExponentsByPcp( pcp, h ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetPower( coll, i, w ); fi; fi; for j in [1..i-1] do h := g[i]^g[j]; e := ExponentsByPcp( pcp, h ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, j, w ); fi; h := g[i]^(g[j]^-1); e := ExponentsByPcp( pcp, h ); w := ObjByExponents( coll, e ); if Length( w ) > 0 then SetConjugate( coll, i, -j, w ); fi; od; od; UpdatePolycyclicCollector( coll ); G := PcpGroupByCollectorNC( coll ); return G; end; DisplayPcpGroup := function( G ) local collector, gens, rods, n, g, h, conj; collector := Collector( G ); gens := Pcp( G ); rods := RelativeOrdersOfPcp( gens ); n := Length( gens ); Print( "<" ); for g in [1..n] do Print( " ", gens[g] ); od; Print( " | \n\n" ); for g in [1..n] do if rods[g] <> 0 then ## print the power relation for g. Print( " ", gens[g], "^", rods[g], " = ", gens[g]^rods[g], "\n" ); fi; od; if rods <> 0 * rods then Print( "\n" ); fi; for h in [1..n] do for g in [1..h-1] do conj := gens[h]^gens[g]; if conj <> gens[h] then ## print the conjuagte relation for h^g. Print( " ", gens[h], "^", gens[g], " = ", gens[h]^gens[g], "\n" ); fi; od; od; Print( ">\n" ); end; polycyclic-2.16/gap/basic/colsave.gi0000644000076600000240000001156613706672341016452 0ustar mhornstaff############################################################################# ## #F StringByFTLCollector . . . . . . . convert an ftl collector into a string ## InstallMethod( String, "from-the-left collector", [ IsFromTheLeftCollectorRep ], function( coll ) local name, n, S, i, j; name := ValueOption( "name" ); if name = fail then name := "ftl"; fi; # Initialise the collector n := coll![PC_NUMBER_OF_GENERATORS]; S := ShallowCopy( name ); Append( S, " := FromTheLeftCollector( " ); Append( S, String(n) ); Append( S, " );\n" ); # install power relations for i in [1..n] do if IsBound( coll![PC_EXPONENTS][i] ) then Append( S, "SetRelativeOrder( " ); Append( S, name ); Append( S, ", " ); Append( S, String( i ) ); Append( S, ", " ); Append( S, String( coll![PC_EXPONENTS][i] ) ); Append( S, " );\n" ); Append( S, "SetPower( " ); Append( S, name ); Append( S, ", " ); Append( S, String(i) ); Append( S, ", " ); if IsBound( coll![PC_POWERS][i] ) then Append( S, String( coll![PC_POWERS][i] ) ); else Append( S, "[]" ); fi; Append( S, " );\n" ); fi; od; # install conjugate relations for j in [1..n] do for i in [1..j-1] do if IsBound(coll![PC_CONJUGATES][j][i]) then Append( S, "SetConjugate( " ); Append( S, name ); Append( S, ", " ); Append( S, String(j) ); Append( S, ", " ); Append( S, String(i) ); Append( S, ", " ); Append( S, String( coll![PC_CONJUGATES][j][i] ) ); Append( S, " );\n" ); fi; od; od; for j in [1..n] do for i in [1..j-1] do if IsBound(coll![PC_CONJUGATESINVERSE][j][i]) then Append( S, "SetConjugate( " ); Append( S, name ); Append( S, ", " ); Append( S, String( j) ); Append( S, ", " ); Append( S, String(-i) ); Append( S, ", " ); Append( S, String( coll![PC_CONJUGATESINVERSE][j][i] ) ); Append( S, " );\n" ); fi; od; od; for j in [1..n] do for i in [1..j-1] do if IsBound(coll![PC_INVERSECONJUGATES][j][i]) then Append( S, "SetConjugate( " ); Append( S, name ); Append( S, ", " ); Append( S, String(-j) ); Append( S, ", " ); Append( S, String( i) ); Append( S, ", " ); Append( S, String( coll![PC_INVERSECONJUGATES][j][i] ) ); Append( S, " );\n" ); fi; od; od; for j in [1..n] do for i in [1..j-1] do if IsBound(coll![PC_INVERSECONJUGATESINVERSE][j][i]) then Append( S, "SetConjugate( " ); Append( S, name ); Append( S, ", " ); Append( S, String(-j) ); Append( S, ", " ); Append( S, String(-i) ); Append( S, ", " ); Append( S, String( coll![PC_INVERSECONJUGATESINVERSE][j][i] ) ); Append( S, " );\n" ); fi; od; od; return S; end ); ############################################################################# ## #F PcpUserInfo ## PcpUserInfo := function() local str, dir, date, out; str := ""; dir := Directory( "./" ); out := OutputTextString( str, true ); ## get user name Process( dir, Filename( DirectoriesSystemPrograms(), "whoami" ), InputTextNone(), out, [] ); ## remove trailing newline character str[ Length(str) ] := '@'; ## get hostname name Process( dir, Filename( DirectoriesSystemPrograms(), "hostname" ), InputTextNone(), out, [] ); ## remove trailing newline character Unbind( str[ Length(str) ] ); Append( str, ": " ); ## get date Process( dir, Filename( DirectoriesSystemPrograms(), "date" ), InputTextNone(), out, [] ); CloseStream( out ); return str; end; ############################################################################# ## #F FTLCollectorPrintTo( , , ) ## BindGlobal( "FTLCollectorPrintTo", function( file, name, coll ) PrintTo( file, "##\n## ", PcpUserInfo(), "##\n" ); AppendTo( file, String( coll : name := name ) ); AppendTo( file, "\n\n" ); end ); ############################################################################# ## #F FTLCollectorAppendTo( , , ) ## BindGlobal( "FTLCollectorAppendTo", function( file, name, coll ) AppendTo( file, "##\n## ", PcpUserInfo(), "##\n" ); AppendTo( file, String( coll : name := name ) ); AppendTo( file, "\n\n" ); end ); polycyclic-2.16/gap/basic/pcpexpo.gi0000644000076600000240000001256313706672341016472 0ustar mhornstaff############################################################################# ## #W pcpexpo.gi Polycyc Bettina Eick ## ############################################################################# ## #F ReducingCoefficient( , ) . . . . . . . . .f with g * h^-f = 1 mod r #F ReducingCoefficient( , , ) . . . . . . . . . . . . . . a/b mod r ## ReducingCoefficient := function( arg ) local e, f, a, b, r, n; if Length( arg ) = 2 then a := LeadingExponent( arg[1] ); b := LeadingExponent( arg[2] ); r := FactorOrder( arg[1] ); n := IsBound( arg[2]!.normed ) and arg[2]!.normed; elif Length( arg ) = 3 then a := arg[1]; b := arg[2]; r := arg[3]; n := false; fi; if b = 0 then return fail; elif b = 1 then return a; elif r = 0 then e := a/b; if not IsInt( e ) then return fail; fi; return e; elif IsPrime( r ) then return a/b mod r; elif n then f := a/b; if not IsInt( f ) then return fail; fi; return f mod r; else e := Gcdex( r, b ); f := a / e.gcd; if not IsInt(f) then return fail; fi; return f * e.coeff2 mod r; fi; end; ############################################################################# ## #F ReducedByIgs( , ) ## InstallGlobalFunction( ReducedByIgs, function( igs, g ) local dep, j, e; if Length( igs ) = 0 then return g; fi; dep := List( igs, Depth ); j := Position( dep, Depth(g) ); while not IsBool( j ) do e := ReducingCoefficient( g, igs[j] ); if IsBool( e ) then return g; fi; g := g * igs[j]^-e; j := Position( dep, Depth( g ) ); od; return g; end ); ############################################################################# ## #F ExponentsByIgs( igs, g ) . . . . . . . . . . exponents of g wrt to an igs ## ## Note that this functions returns fail, if g is not in . ## InstallGlobalFunction( ExponentsByIgs, function( pcs, g ) local dep, exp, j, e; # pcs is an induced pc sequence dep := List( pcs, Depth ); exp := List( pcs, x -> 0 ); # go through and reduce j := Position( dep, Depth(g) ); while not IsBool( j ) do e := ReducingCoefficient( g, pcs[j] ); if IsBool( e ) then return fail; fi; exp[j] := e; g := pcs[j]^-e * g; j := Position( dep, Depth(g) ); od; # return exp or fail if g <> g^0 then return fail; fi; return exp; end ); ############################################################################# ## #F ReduceByRels( rels, exp ) ## ReduceByRels := function( rels, exp ) local i; for i in [1..Length(exp)] do if rels[i] > 0 then exp[i] := exp[i] mod rels[i]; fi; od; return exp; end; ############################################################################# ## #F ExponentsByPcp( pcp, g ).. . . . . . . . . . . . . exponents of g wrt pcp ## ## Note that this function might return fail, if g is not in . But ## it might also just return a wrong exponent vector. ## InstallGlobalFunction( ExponentsByPcp, function( pcp, g ) local gens, rels, dept, pcpN, depN, exp, d, j, e, i; # the trivial case if Length( pcp ) = 0 then return []; fi; # first the special case of tail pcps if IsList( pcp!.tail ) then exp := Exponents(g){pcp!.tail}; if IsBound( pcp!.mult ) then for i in [1..Length(exp)] do if pcp!.rels[i] = 0 then exp[i] := exp[i] / pcp!.mult[i]; else exp[i] := exp[i] / pcp!.mult[i] mod pcp!.rels[i]; fi; od; else for i in [1..Length(exp)] do if pcp!.rels[i] <> 0 then exp[i] := exp[i] mod pcp!.rels[i]; fi; od; fi; if IsBound( pcp!.cyc ) then exp := TranslateExp( pcp!.cyc, exp ); fi; return exp; fi; # get info from pcp gens := pcp!.gens; rels := pcp!.rels; dept := List( gens, Depth ); exp := List( gens, x -> 0 ); if Length( gens ) = 0 then return exp; fi; # get denominator pcp - might be the empty list pcpN := DenominatorOfPcp( pcp ); depN := List( pcpN, Depth ); # go through and reduce g d := Depth( g ); while d < pcp!.tail and (d in dept or d in depN) do # get exponent in pcpF if d in dept then j := Position( dept, d ); e := ReducingCoefficient( g, gens[j] ); if IsBool( e ) then return fail; fi; if rels[j] > 0 then e := e mod rels[j]; fi; exp[j] := e; g := gens[j]^-e * g; fi; # reduce with pcpN if d in depN and Depth( g ) = d then j := Position( depN, d ); e := ReducingCoefficient( g, pcpN[j] ); if IsBool( e ) then return fail; fi; g := pcpN[j]^-e * g; fi; # if g has still depth d then there is something wrong if Depth(g) <= d then Error("wrong reduction in ExponentsByPcp"); fi; d := Depth( g ); od; # if it is an snf pcp then we need to rewrite exponents if IsBound( pcp!.cyc ) then exp := TranslateExp( pcp!.cyc, exp ); fi; # finally return return exp; end ); polycyclic-2.16/gap/basic/collect.gi0000644000076600000240000010671713706672341016446 0ustar mhornstaff############################################################################# ## #W collect.gi Polycyclic Werner Nickel ## ############################################################################# ## #M FromTheLeftCollector( ) ## ## This function constructs a basic from-the-left collector. A ## from-the-left collector is a positional object. The components defined ## in this function are the ingredients used by the simple from-the-left ## collector. ## InstallMethod( FromTheLeftCollector, "for positive integer", [ IsInt ], function( nrgens ) local pcp; if nrgens < 0 then return Error( "number of generators must not be negative" ); fi; pcp := []; pcp[ PC_NUMBER_OF_GENERATORS ] := nrgens; pcp[ PC_GENERATORS ] := List( [1..nrgens], i -> [i, 1] ); pcp[ PC_INVERSES ] := List( [1..nrgens], i -> [i,-1] ); pcp[ PC_COMMUTE ] := []; pcp[ PC_POWERS ] := []; pcp[ PC_INVERSEPOWERS ] := []; pcp[ PC_EXPONENTS ] := []; pcp[ PC_CONJUGATES ] := List( [1..nrgens], i -> [] ); pcp[ PC_INVERSECONJUGATES ] := List( [1..nrgens], i -> [] ); pcp[ PC_CONJUGATESINVERSE ] := List( [1..nrgens], i -> [] ); pcp[ PC_INVERSECONJUGATESINVERSE ] := List( [1..nrgens], i -> [] ); pcp[ PC_COMMUTATORS ] := List( [1..nrgens], i -> [] ); pcp[ PC_INVERSECOMMUTATORS ] := List( [1..nrgens], i -> [] ); pcp[ PC_COMMUTATORSINVERSE ] := List( [1..nrgens], i -> [] ); pcp[ PC_INVERSECOMMUTATORSINVERSE ] := List( [1..nrgens], i -> [] ); pcp[ PC_DEEP_THOUGHT_POLS ] := []; # Initialise the various stacks. # starting with GAP 4.10, these are not needed anymore, # which is signaled by the global constant NO_STACKS_INSIDE_COLLECTORS if not NO_STACKS_INSIDE_COLLECTORS then pcp[ PC_STACK_SIZE ] := 1024 * nrgens; pcp[ PC_WORD_STACK ] := ListWithIdenticalEntries( pcp[ PC_STACK_SIZE], 0 ); pcp[ PC_WORD_EXPONENT_STACK ] := ListWithIdenticalEntries( pcp[ PC_STACK_SIZE], 0 ); pcp[ PC_SYLLABLE_STACK ] := ListWithIdenticalEntries( pcp[ PC_STACK_SIZE], 0 ); pcp[ PC_EXPONENT_STACK ] := ListWithIdenticalEntries( pcp[ PC_STACK_SIZE], 0 ); pcp[ PC_STACK_POINTER ] := 0; fi; pcp[ PC_PCP_ELEMENTS_FAMILY ] := NewFamily( "ElementsFamily<>", IsPcpElement, IsPcpElement and CanEasilySortElements, CanEasilySortElements ); pcp[ PC_PCP_ELEMENTS_TYPE ] := NewType( pcp![PC_PCP_ELEMENTS_FAMILY], IsPcpElementRep ); return Objectify( NewType( FromTheLeftCollectorFamily, IsFromTheLeftCollectorRep and IsMutable ), pcp ); end ); InstallMethod( FromTheLeftCollector, "for free groups", [ IsFreeGroup and IsWholeFamily ], F -> FromTheLeftCollector( Length( GeneratorsOfGroup( F ) ) ) ); ############################################################################# ## ## Functions to view and print a from-the-left collector. ## #M ViewObj( ) ## InstallMethod( ViewObj, "for from-the-left collector", [ IsFromTheLeftCollectorRep ], function( coll ) Print( "<>" ); end ); ## #M PrintObj( ) ## InstallMethod( PrintObj, "for from-the-left collector", [ IsFromTheLeftCollectorRep ], function( coll ) Print( "<>" ); end ); #T install a better `PrintObj' method! ############################################################################# ## ## Setter and getter functions for from-the-left collectors: ## NumberOfGenerators ## SetRelativeOrder/NC, RelativeOrders ## SetPower/NC, GetPower/NC ## SetConjugate/NC, GetConjugateNC ## SetCommutator ## ## The NC functions do not perform any checks. The NC setters do not copy ## the argument before it is inserted into the collector. They also do not ## outdate the collector. The NC getter functions do not copy the data ## returned from the collector. ## ## #F NumberOfGenerators( ) ## InstallGlobalFunction( NumberOfGenerators, coll -> coll![PC_NUMBER_OF_GENERATORS] ); ## #M SetRelativeOrder( , , ) ## InstallMethod( SetRelativeOrderNC, "for from-the-left collector", [ IsFromTheLeftCollectorRep and IsMutable, IsPosInt, IsInt ], function( coll, g, order ) if order = 0 then Unbind( coll![ PC_EXPONENTS ][g] ); Unbind( coll![ PC_POWERS ][g] ); else coll![ PC_EXPONENTS ][g] := order; fi; end ); InstallMethod( SetRelativeOrder, "for from-the-left collector", [ IsFromTheLeftCollectorRep and IsMutable, IsPosInt, IsInt ], function( coll, g, order ) local n; if order < 0 then Error( "relatve order must be non-negative" ); fi; n := coll![ PC_NUMBER_OF_GENERATORS ]; if g < 1 or g > n then Error( "Generator ", g, " out of range (1-", n, ")" ); fi; SetRelativeOrderNC( coll, g, order ); OutdatePolycyclicCollector( coll ); end ); ############################################################################# ## #M RelativeOrders( ) ## InstallMethod( RelativeOrders, "from-the-left collector", [ IsFromTheLeftCollectorRep ], function( coll ) local n, r, i; n := coll![PC_NUMBER_OF_GENERATORS]; r := ShallowCopy( coll![PC_EXPONENTS] ); for i in [1..n] do if not IsBound( r[i] ) then r[i] := 0; fi; od; return r; end ); ## #M SetPower( , , ) ## InstallMethod( SetPowerNC, "for from-the-left collector, word as list", [ IsFromTheLeftCollectorRep and IsMutable, IsPosInt, IsList ], function( pcp, g, w ) if Length(w) mod 2 <> 0 then Error( "List has odd length: not a generator exponent list" ); fi; if w = [] then Unbind( pcp![ PC_POWERS ][g] ); else pcp![ PC_POWERS ][g] := w; fi; end ); InstallMethod( SetPower, "for from-the-left collector, word as list", [ IsFromTheLeftCollectorRep and IsMutable, IsPosInt, IsList ], function( pcp, g, w ) local n, i, rhs; if not IsBound( pcp![ PC_EXPONENTS ][g] ) or pcp![ PC_EXPONENTS ][g] = 0 then Error( "relative order unknown of generator ", g ); fi; n := pcp![ PC_NUMBER_OF_GENERATORS ]; if g < 1 or g > n then Error( "Generator ", g, " out of range (1-", n, ")" ); fi; rhs := []; for i in [1,3..Length(w)-1] do if not IsInt(w[i]) or not IsInt(w[i+1]) then Error( "List of integers expected" ); fi; if w[i] <= g or w[i] > n then Error( "Generator ", w[i], " in rhs out of range (1-", n, ")" ); fi; if w[i+1] <> 0 then Add( rhs, w[i] ); Add( rhs, w[i+1] ); fi; od; SetPowerNC( pcp, g, rhs ); OutdatePolycyclicCollector( pcp ); end ); InstallMethod( SetPower, "from-the-left collector, word", [ IsFromTheLeftCollectorRep and IsMutable, IsPosInt, IsWord ], function( pcp, g, w ) SetPower( pcp, g, ExtRepOfObj(w) ); end ); ## #M GetPower( , ) ## InstallMethod( GetPowerNC, "from-the-left collector", [ IsFromTheLeftCollectorRep, IsPosInt ], function( coll, g ) if IsBound( coll![PC_POWERS][g] ) then return coll![PC_POWERS][g]; fi; # return the identity. return []; end ); InstallMethod( GetPower, "from-the-left collector", [ IsFromTheLeftCollectorRep, IsPosInt ], function( coll, g ) if IsBound( coll![PC_POWERS][g] ) then return ShallowCopy( coll![PC_POWERS][g] ); fi; # return the identity. return []; end ); ## #M SetConjugate( , , , ) ## InstallMethod( SetConjugateNC, "for from-the-left collector, words as lists", [ IsFromTheLeftCollectorRep and IsMutable, IsInt, IsInt, IsList ], function( coll, h, g, w ) if Length(w) mod 2 <> 0 then Error( "List has odd length: not a generator exponent list" ); fi; if g > 0 then if h > 0 then if w = coll![ PC_GENERATORS ][h] then Unbind( coll![ PC_CONJUGATES ][h][g] ); else coll![ PC_CONJUGATES ][h][g] := w; fi; else if w = coll![ PC_INVERSES ][-h] then Unbind( coll![ PC_INVERSECONJUGATES ][-h][g] ); else coll![ PC_INVERSECONJUGATES ][-h][g] := w; fi; fi; else if h > 0 then if w = coll![ PC_GENERATORS ][h] then Unbind( coll![ PC_CONJUGATESINVERSE ][h][-g] ); else coll![ PC_CONJUGATESINVERSE ][h][-g] := w; fi; else if w = coll![ PC_INVERSES ][-h] then Unbind( coll![ PC_INVERSECONJUGATESINVERSE ][-h][-g] ); else coll![ PC_INVERSECONJUGATESINVERSE ][-h][-g] := w; fi; fi; fi; end ); InstallMethod( SetConjugate, "for from-the-left collector, words as lists", [ IsFromTheLeftCollectorRep and IsMutable, IsInt, IsInt, IsList ], function( coll, h, g, w ) local i, rhs; if AbsInt( h ) <= AbsInt( g ) then Error( "Left generator not smaller than right generator" ); fi; if AbsInt( h ) > coll![ PC_NUMBER_OF_GENERATORS ] then Error( "Left generators too large" ); fi; if AbsInt( g ) < 1 then Error( "Right generators too small" ); fi; # check the conjugate and copy it rhs := []; for i in [1,3..Length(w)-1] do if not IsInt(w[i]) or not IsInt(w[i+1]) then Error( "List of integers expected" ); fi; if w[i] <= g or w[i] > coll![PC_NUMBER_OF_GENERATORS ] then Error( "Generator in word out of range" ); fi; if w[i+1] <> 0 then Add( rhs, w[i] ); Add( rhs, w[i+1] ); fi; od; SetConjugateNC( coll, h, g, rhs ); OutdatePolycyclicCollector( coll ); end ); InstallMethod( SetConjugate, "from-the-left collector, words", [ IsFromTheLeftCollectorRep and IsMutable, IsInt, IsInt, IsWord ], function( coll, h, g, w ) SetConjugate( coll, h, g, ExtRepOfObj( w ) ); end ); ## #M GetConjugate( , , ) ## InstallMethod( GetConjugateNC, "from the left collector", [ IsFromTheLeftCollectorRep, IsInt, IsInt ], function( coll, h, g ) if g > 0 then if h > 0 then if IsBound( coll![PC_CONJUGATES][h] ) and IsBound( coll![PC_CONJUGATES][h][g] ) then return coll![PC_CONJUGATES][h][g]; else return coll![PC_GENERATORS][h]; fi; else h := -h; if IsBound( coll![PC_INVERSECONJUGATES][h] ) and IsBound( coll![PC_INVERSECONJUGATES][h][g] ) then return coll![PC_INVERSECONJUGATES][h][g]; else return coll![PC_INVERSES][h]; fi; fi; else g := -g; if h > 0 then if IsBound( coll![PC_CONJUGATESINVERSE][h] ) and IsBound( coll![PC_CONJUGATESINVERSE][h][g] ) then return coll![PC_CONJUGATESINVERSE][h][g]; else return coll![PC_GENERATORS][h]; fi; else h := -h; if IsBound( coll![PC_INVERSECONJUGATESINVERSE][h] ) and IsBound( coll![PC_INVERSECONJUGATESINVERSE][h][g] ) then return coll![PC_INVERSECONJUGATESINVERSE][h][g]; else return coll![PC_INVERSES][h]; fi; fi; fi; end ); InstallMethod( GetConjugate, "from the left collector", [ IsFromTheLeftCollectorRep, IsInt, IsInt ], function( coll, h, g ) if AbsInt( h ) <= AbsInt( g ) then Error( "Left generator not smaller than right generator" ); fi; if AbsInt( h ) > coll![ PC_NUMBER_OF_GENERATORS ] then Error( "Left generators too large" ); fi; if AbsInt( g ) < 1 then Error( "Right generators too small" ); fi; return ShallowCopy( GetConjugateNC( coll, h, g ) ); end ); ## #M SetCommutator( , , , ) ## InstallMethod( SetCommutator, "for from-the-left collector, words as lists", [ IsFromTheLeftCollectorRep and IsMutable, IsInt, IsInt, IsList ], function( coll, h, g, comm ) local i, conj; if AbsInt( h ) <= AbsInt( g ) then Error( "Left generator not smaller than right generator" ); fi; if AbsInt( h ) > coll![ PC_NUMBER_OF_GENERATORS ] then Error( "Left generators too large" ); fi; if AbsInt( g ) < 1 then Error( "Right generators too small" ); fi; for i in [1,3..Length(comm)-1] do if not IsInt(comm[i]) or not IsInt(comm[i+1]) then Error( "List of integers expected" ); fi; if comm[i] <= g or comm[i] > coll![PC_NUMBER_OF_GENERATORS ] then Error( "Generator in word out of range" ); fi; od; # h^g = h * [h,g] conj := [ h, 1 ]; Append( conj, comm ); SetConjugateNC( coll, h, g, conj ); OutdatePolycyclicCollector( coll ); end ); InstallMethod( SetCommutator, "from-the-left collector, words", [ IsFromTheLeftCollectorRep and IsMutable, IsInt, IsInt, IsWord ], function( coll, h, g, w ) SetCommutator( coll, h, g, ExtRepOfObj( w ) ); end ); ############################################################################# ## ## The following two conversion functions convert the two main ## representations of elements into each other: exponent lists and ## generator exponent lists. ## ## #M ObjByExponents( , ) ## InstallMethod( ObjByExponents, [ IsFromTheLeftCollectorRep, IsList ], function( coll, exps ) local w, i; if Length(exps) > NumberOfGenerators(coll) then return Error( "more exponents than generators" ); fi; w := []; for i in [1..Length(exps)] do if exps[i] <> 0 then Add( w, i ); Add( w, exps[i] ); fi; od; return w; end ); ## #M ExponentsByObj( , ## InstallMethod( ExponentsByObj, "from-the-left collector, gen-exp-list", [ IsFromTheLeftCollectorRep, IsList ], function( coll, word ) local exp, i; exp := [1..coll![PC_NUMBER_OF_GENERATORS]] * 0; for i in [1,3..Length(word)-1] do exp[word[i]] := word[i+1]; od; return exp; end ); ############################################################################# ## ## The following functions implement part of the fundamental arithmetic ## based on from-the-left collector collectors. These functions are ## ## FromTheLeftCollector_Solution, ## FromTheLeftCollector_Inverse. ## ## #F FromTheLeftCollector_Solution( , , ) ## solve the equation u x = v for x ## BindGlobal( "FromTheLeftCollector_Solution", function( coll, u, v ) local e, n, x, i, g, uu; n := coll![ PC_NUMBER_OF_GENERATORS ]; u := ExponentsByObj( coll, u ); v := ExponentsByObj( coll, v ); x := []; for i in [1..n] do e := v[i] - u[i]; if IsBound(coll![ PC_EXPONENTS ][i]) and e < 0 then e := e + coll![ PC_EXPONENTS ][i]; fi; if e <> 0 then g := ShallowCopy( coll![ PC_GENERATORS ][i] ); g[2] := e; Append( x, g ); uu := ShallowCopy( u ); while CollectWordOrFail( coll, u, g ) = fail do u := ShallowCopy( uu ); od; fi; od; return x; end ); ## #F FromTheLeftCollector_Inverse( , ) ## inverse of a word wrt a pc presentation ## BindGlobal( "FromTheLeftCollector_Inverse", function( coll, w ) Info( InfoFromTheLeftCollector, 3, "computing an inverse" ); return FromTheLeftCollector_Solution( coll, w, [] ); end ); ############################################################################# ## ## The following functions are used to complete a fresh from-the-left ## collector. The are mainly called from UpdatePolycyclicCollector(). ## ## The functions are: ## FromTheLeftCollector_SetCommute ## FromTheLeftCollector_CompleteConjugate ## FromTheLeftCollector_CompletePowers ## FromTheLeftCollector_SetNilpotentCommute ## FromTheLeftCollector_SetWeights ## #F FromTheLeftCollector_SetCommute( ) ## InstallGlobalFunction( FromTheLeftCollector_SetCommute, function( coll ) local com, cnj, icnj, cnji, icnji, n, g, again, h; Info( InfoFromTheLeftCollector, 1, "Computing commute array" ); n := coll![ PC_NUMBER_OF_GENERATORS ]; cnj := coll![ PC_CONJUGATES ]; icnj := coll![ PC_INVERSECONJUGATES ]; cnji := coll![ PC_CONJUGATESINVERSE ]; icnji := coll![ PC_INVERSECONJUGATESINVERSE ]; ## ## Commute[i] is the smallest j >= i such that a_i,...,a_n ## commute with a_(j+1),...,a_n. ## com := ListWithIdenticalEntries( n, n ); for g in [n-1,n-2..1] do ## ## After the following loop two cases can occur : ## a) h > g+1. In this case h is the first generator among ## a_n,...,a_(j+1) with which g does not commute. ## b) h = g+1. Then Commute[g+1] = g+1 follows and g ## commutes with all generators a_(g+2),..,a_n. So it ## has to be checked whether a_g and a_(g+1) commute. ## If that is the case, then Commute[g] = g. If not ## then Commute[g] = g+1 = h. ## again := true; h := n; while again and h > com[g+1] do if IsBound( cnj[h][g] ) or IsBound( icnj[h][g] ) or IsBound( cnji[h][g] ) or IsBound( icnji[h][g] ) then again := false; else h := h-1; fi; od; if h = g+1 and not (IsBound( cnj[h][g] ) or IsBound( icnj[h][g] ) or IsBound( cnji[h][g] ) or IsBound( icnji[h][g] ) ) then com[g] := g; else com[g] := h; fi; od; coll![ PC_COMMUTE ] := com; end ); ## #F FromTheLeftCollector_CompleteConjugate ## ## # The following approach only works if the presentation is ## # nilpotent. ## # [b,a^-1] = a * [a,b] * a^-1; ## cnj := coll![ PC_CONJUGATES ][j][i]; ## comm := cnj{[3..Length(cnj)]}; ## # compute [a,b] * a^-1 ## comm := FromTheLeftCollector_Inverse( coll, comm ); ## ev := ExponentsByObj( coll, comm ); ## CollectWordOrFail( coll, ev, [ i, -1 ] ); ## # wipe out a, prepend b ## ev[i] := 0; ev[j] := 1; ## InstallGlobalFunction( FromTheLeftCollector_CompleteConjugate, function( coll ) local G, gens, n, i, missing, j, images; Info( InfoFromTheLeftCollector, 1, "Completing conjugate relations" ); G := PcpGroupByCollectorNC( coll ); gens := GeneratorsOfGroup( G ); n := coll![ PC_NUMBER_OF_GENERATORS ]; for i in [n,n-1..1 ] do Info( InfoFromTheLeftCollector, 2, "Conjugating by generator ", i ); # Does generator i have infinite order? if not IsBound( coll![ PC_EXPONENTS ][i] ) then missing := false; for j in [n,n-1..i+1] do if IsBound( coll![ PC_CONJUGATES ][j][i] ) and not IsBound( coll![ PC_CONJUGATESINVERSE ][j][i] ) then missing := true; break; fi; od; if missing then Info( InfoFromTheLeftCollector, 2, "computing images for generator ", i ); # fill in the missing conjugate relations images := []; # build the images under conjugation for j in [i+1..n] do if IsBound( coll![PC_CONJUGATES][j][i] ) then Add( images, PcpElementByGenExpListNC( coll, coll![PC_CONJUGATES][j][i] ) ); else Add( images, gens[j] ); fi; od; Info( InfoFromTheLeftCollector, 2, "images for generator ", i, " done" ); images := CgsParallel( images, gens{[i+1..n]} ); Info( InfoFromTheLeftCollector, 2, "canonical coll done" ); # is conjugation an epimorphism ? if images[1] <> gens{[i+1..n]} then Error( "group presentation is not polycyclic" ); fi; images := images[2]; for j in [n,n-1..i+1] do if IsBound( coll![ PC_CONJUGATES ][j][i] ) and not IsBound( coll![ PC_CONJUGATESINVERSE ][j][i] ) then coll![ PC_CONJUGATESINVERSE ][j][i] := ObjByExponents( coll, images[j-i]!.exponents ); fi; od; fi; fi; Info( InfoFromTheLeftCollector, 2, "computing inverses of conjugate relations" ); # now fill in the other missing conjugate relations for j in [n,n-1..i+1] do if not IsBound( coll![ PC_EXPONENTS ][j] ) then if IsBound( coll![ PC_CONJUGATES ][j][i] ) and not IsBound( coll![ PC_INVERSECONJUGATES ][j][i] ) then coll![ PC_INVERSECONJUGATES ][j][i] := FromTheLeftCollector_Inverse( coll, coll![ PC_CONJUGATES ][j][i] ); fi; if IsBound( coll![ PC_CONJUGATESINVERSE ][j][i] ) and not IsBound( coll![ PC_INVERSECONJUGATESINVERSE ][j][i] ) then coll![ PC_INVERSECONJUGATESINVERSE ][j][i] := FromTheLeftCollector_Inverse( coll, coll![ PC_CONJUGATESINVERSE ][j][i] ); fi; fi; od; od; end ); ## #F FromTheLeftCollector_CompletePowers( ) ## InstallGlobalFunction( FromTheLeftCollector_CompletePowers, function( coll ) local n, i; Info( InfoFromTheLeftCollector, 1, "Completing power relations" ); n := coll![ PC_NUMBER_OF_GENERATORS ]; coll![ PC_INVERSEPOWERS ] := []; for i in [n,n-1..1] do if IsBound( coll![ PC_POWERS ][i] ) then coll![ PC_INVERSEPOWERS ][i] := FromTheLeftCollector_Inverse( coll, coll![ PC_POWERS ][i] ); fi; od; end ); ## #F FromTheLeftCollector_SetNilpotentCommute( ) ## BindGlobal( "FromTheLeftCollector_SetNilpotentCommute", function( coll ) local n, wt, cl, ncomm, g, h; # number of generators n := coll![PC_NUMBER_OF_GENERATORS]; # class and weights of collector wt := coll![PC_WEIGHTS]; cl := wt[ Length(wt) ]; ncomm := [1..n]; for g in [1..n] do if 3*wt[g] > cl then break; fi; h := coll![PC_COMMUTE][g]; while g < h and 2*wt[h] + wt[g] > cl do h := h-1; od; ncomm[g] := h; od; # set the avector coll![PC_NILPOTENT_COMMUTE] := ncomm; end ); ## #F FromTheLeftCollector_SetWeights( ) ## BindGlobal( "FromTheLeftCollector_SetWeights", function( cc ) local astart, class, ngens, weights, h, g, cnj, i; ngens := cc![ PC_NUMBER_OF_GENERATORS ]; if ngens = 0 then return fail; fi; weights := [1..ngens] * 0 + 1; ## wt: --> Z such that ## -- wt is increasing ## -- wt(j) + wt(i) <= wt(g) for j > i and all g in the rhs ## commutator relations [j,i] ## Run through the (positive) commutator relations and make the weight ## of each generator of a rhs large enough. for h in [1..ngens] do for g in [1..h-1] do cnj := GetConjugateNC( cc, h, g ); if cnj[1] <> h or cnj[2] <> 1 then ## The conjugate relation is not a commutator. return fail; fi; for i in [3,5..Length(cnj)-1] do if weights[cnj[i]] < weights[g] + weights[h] then weights[cnj[i]] := weights[g] + weights[h]; fi; od; od; od; cc![PC_WEIGHTS] := weights; class := weights[ Length(weights) ]; astart := 1; while 2 * weights[ astart ] <= class do astart := astart+1; od; cc![PC_ABELIAN_START] := astart; return true; end ); InstallMethod( IsWeightedCollector, "from-the-left collector", [ IsPolycyclicCollector and IsFromTheLeftCollectorRep and IsMutable ], function( coll ) if FromTheLeftCollector_SetWeights( coll ) <> fail then # FIXME: properties should never depend on external state! return USE_COMBINATORIAL_COLLECTOR; fi; return false; end ); ############################################################################ ## #F IsPcpNormalFormObj ( , ) ## ## checks whether is in normal form. ## InstallGlobalFunction( IsPcpNormalFormObj, function( ftl, w ) local k; # loop variable if not IsSortedList( w{[1,3..Length(w)-1]} ) then return false; fi; for k in [1,3..Length(w)-1] do if IsBound( ftl![ PC_EXPONENTS ][ w[k] ]) and ( not w[k+1] < ftl![ PC_EXPONENTS ][ w[k] ] or not w[k+1] >= 0 ) then return false; fi; od; return true; end); ############################################################################ ## #P IsPolycyclicPresentation( ) ## ## checks whether the input-presentation is a polycyclic presentation, i.e. ## whether the right-hand-sides of the relations are normal. ## InstallMethod( IsPolycyclicPresentation, "FromTheLeftCollector", [ IsFromTheLeftCollectorRep ], function( ftl ) local n, # number of generators of i,j; # loop variables n := ftl![ PC_NUMBER_OF_GENERATORS ]; # check power relations for i in [1..n] do if IsBound( ftl![ PC_POWERS ][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_POWERS ][i]) then Info( InfoFromTheLeftCollector, 1, "bad power relation g",i,"^",ftl![ PC_EXPONENTS ][i], " = ", ftl![ PC_POWERS ][i] ); return false; fi; od; # check conjugacy relations for i in [ 1 .. n ] do for j in [ i+1 .. n ] do if IsBound( ftl![ PC_CONJUGATES ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_CONJUGATES ][j][i] ) then Info( InfoFromTheLeftCollector, 1, "bad conjugacy relation g",j,"^g",i, " = ", ftl![ PC_CONJUGATES ][j][i] ); return false; elif IsBound( ftl![ PC_INVERSECONJUGATES ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_INVERSECONJUGATES ][j][i] ) then Info( InfoFromTheLeftCollector, 1, "bad conjugacy relation g",j,"^-g",i, " = ", ftl![ PC_INVERSECONJUGATES ][j][i] ); return false; elif IsBound( ftl![ PC_CONJUGATESINVERSE ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_CONJUGATESINVERSE ][j][i] ) then Info( InfoFromTheLeftCollector, 1, "bad conjugacy relation -g",j,"^g",i, " = ", ftl![ PC_CONJUGATESINVERSE ][j][i] ); return false; elif IsBound( ftl![ PC_INVERSECONJUGATESINVERSE ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![PC_INVERSECONJUGATESINVERSE][j][i] ) then Info( InfoFromTheLeftCollector, 1, "bad conjugacy relation -g",j,"^-g",i, " = ", ftl![ PC_INVERSECONJUGATESINVERSE ][j][i] ); return false; fi; od; od; # check commutator relations for i in [ 1 .. n ] do for j in [ i+1 .. n ] do if IsBound( ftl![ PC_COMMUTATORS ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_COMMUTATORS ][j][i] ) then return false; elif IsBound( ftl![ PC_INVERSECOMMUTATORS ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_INVERSECOMMUTATORS ][j][i] ) then return false; elif IsBound( ftl![ PC_COMMUTATORSINVERSE ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![ PC_COMMUTATORSINVERSE ][j][i] ) then return false; elif IsBound( ftl![ PC_INVERSECOMMUTATORSINVERSE ][j][i] ) and not IsPcpNormalFormObj( ftl, ftl![PC_INVERSECOMMUTATORSINVERSE][j][i] ) then return false; fi; od; od; return true; end); ############################################################################# ## ## Complete a modified from-the-left collector so that it can be used by ## the collection routines. Also check here if a combinatorial collector ## can be used. ## #M UpdatePolycyclicCollector( ) ## InstallMethod( UpdatePolycyclicCollector, "FromTheLeftCollector", [ IsFromTheLeftCollectorRep ], function( coll ) if not IsPolycyclicPresentation( coll ) then Error("the input presentation is not a polcyclic presentation"); fi; FromTheLeftCollector_SetCommute( coll ); ## We have to declare the collector up to date now because the following ## functions need to collect and are careful enough. SetFilterObj( coll, IsUpToDatePolycyclicCollector ); FromTheLeftCollector_CompleteConjugate( coll ); FromTheLeftCollector_CompletePowers( coll ); if IsWeightedCollector( coll ) then FromTheLeftCollector_SetNilpotentCommute( coll ); fi; end ); ############################################################################# ## #M IsConfluent . . . . . . . . . . . . . . . . . . . polycyclic presentation ## ## This method checks the confluence (or consistency) of a polycyclic ## presentation. It implements the checks from Sims: Computation ## with Finitely Presented Groups, p. 424: ## ## k (j i) = (k j) i k > j > i ## j^m i = j^(m-1) (j i) j > i, j in I ## j * i^m = (j i) * i^(m-1) j > i, i in I ## i^m i = i i^m i in I ## j = (j -i) i j > i, i not in I ## i = -j (j i) j > i, j not in I ## -i = -j (j -i) j > i, i,j not in I ## if not IsBound( InfoConsistency ) then BindGlobal( "InfoConsistency", function( arg ) end ); fi; InstallMethod( IsConfluent, "FromTheLeftCollector", [ IsFromTheLeftCollectorRep ], function( coll ) local n, k, j, i, ev1, w, ev2; n := coll![ PC_NUMBER_OF_GENERATORS ]; # k (j i) = (k j) i for k in [n,n-1..1] do for j in [k-1,k-2..1] do for i in [j-1,j-2..1] do InfoConsistency( "checking ", k, " ", j, " ", i, "\n" ); ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [j,1,i,1] ); w := ObjByExponents( coll, ev1 ); ev1 := ExponentsByObj( coll, [k,1] ); CollectWordOrFail( coll, ev1, w ); ev2 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev2, [k,1,j,1,i,1] ); if ev1 <> ev2 then Print( "Inconsistency at ", k, " ", j, " ", i, "\n" ); return false; fi; od; od; od; # j^m i = j^(m-1) (j i) for j in [n,n-1..1] do for i in [j-1,j-2..1] do if IsBound(coll![ PC_EXPONENTS ][j]) then InfoConsistency( "checking ", j, "^m ", i, "\n" ); ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [j, coll![ PC_EXPONENTS ][j]-1, j, 1, i,1] ); ev2 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev2, [j,1,i,1] ); w := ObjByExponents( coll, ev2 ); ev2 := ExponentsByObj( coll, [j,coll![ PC_EXPONENTS ][j]-1] ); CollectWordOrFail( coll, ev2, w ); if ev1 <> ev2 then Print( "Inconsistency at ", j, "^m ", i, "\n" ); return false; fi; fi; od; od; # j * i^m = (j i) * i^(m-1) for i in [n,n-1..1] do if IsBound(coll![ PC_EXPONENTS ][i]) then for j in [n,n-1..i+1] do InfoConsistency( "checking ", j, " ", i, "^m\n" ); ev1 := ExponentsByObj( coll, [j,1] ); if IsBound( coll![ PC_POWERS ][i] ) then CollectWordOrFail( coll, ev1, coll![ PC_POWERS ][i] ); fi; ev2 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev2, [ j,1,i,coll![ PC_EXPONENTS ][i] ] ); if ev1 <> ev2 then Print( "Inconsistency at ", j, " ", i, "^m\n" ); return false; fi; od; fi; od; # i^m i = i i^m for i in [n,n-1..1] do if IsBound( coll![ PC_EXPONENTS ][i] ) then ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [ i,coll![ PC_EXPONENTS ][i]+1 ] ); ev2 := ExponentsByObj( coll, [i,1] ); if IsBound( coll![ PC_POWERS ][i] ) then CollectWordOrFail( coll, ev2, coll![ PC_POWERS ][i] ); fi; if ev1 <> ev2 then Print( "Inconsistency at ", i, "^(m+1)\n" ); return false; fi; fi; od; # j = (j -i) i for i in [n,n-1..1] do if not IsBound( coll![ PC_EXPONENTS ][i] ) then for j in [i+1..n] do InfoConsistency( "checking ", j, " ", -i, " ", i, "\n" ); ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [j,1,i,-1,i,1] ); ev1[j] := ev1[j] - 1; if ev1 <> ListWithIdenticalEntries( n, 0 ) then Print( "Inconsistency at ", j, " ", -i, " ", i, "\n" ); return false; fi; od; fi; od; # i = -j (j i) for j in [n,n-1..1] do if not IsBound( coll![ PC_EXPONENTS ][j] ) then for i in [j-1,j-2..1] do InfoConsistency( "checking ", -j, " ", j, " ", i, "\n" ); ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [ j,1,i,1 ] ); w := ObjByExponents( coll, ev1 ); ev1 := ExponentsByObj( coll, [j,-1] ); CollectWordOrFail( coll, ev1, w ); if ev1 <> ExponentsByObj( coll, [i,1] ) then Print( "Inconsistency at ", -j, " ", j, " ", i, "\n" ); return false; fi; # -i = -j (j -i) if not IsBound( coll![ PC_EXPONENTS ][i] ) then InfoConsistency( "checking ", -j, " ", j, " ", -i, "\n" ); ev1 := ListWithIdenticalEntries( n, 0 ); CollectWordOrFail( coll, ev1, [ j,1,i,-1 ] ); w := ObjByExponents( coll, ev1 ); ev1 := ExponentsByObj( coll, [j,-1] ); CollectWordOrFail( coll, ev1, w ); if ExponentsByObj( coll, [i,-1] ) <> ev1 then Print( "Inconsistency at ", -j, " ", j, " ", -i, "\n" ); return false; fi; fi; od; fi; od; return true; end ); polycyclic-2.16/gap/basic/basic.gd0000644000076600000240000000363613706672341016071 0ustar mhornstaff############################################################################# ## #W basic.gd Polycyc Bettina Eick ## ############################################################################# ## #F The collector ## DeclareOperation( "Collector", [ IsObject ] ); ############################################################################# ## #F Exponent vectors. ## DeclareGlobalFunction( "ReducedByIgs" ); DeclareGlobalFunction( "ExponentsByIgs"); DeclareGlobalFunction( "ExponentsByPcp"); ############################################################################# ## #F Subgroup series of a pcp group. ## DeclareGlobalFunction( "PcpSeries" ); DeclareGlobalFunction( "RefinedDerivedSeries"); DeclareGlobalFunction( "RefinedDerivedSeriesDown"); DeclareGlobalFunction( "TorsionByPolyEFSeries"); DeclareGlobalFunction( "ModuloSeries" ); DeclareGlobalFunction( "EfaSeriesParent" ); DeclareAttribute( "EfaSeries", IsPcpGroup ); ############################################################################# ## #F Their corresponding pcp sequences. ## DeclareGlobalFunction( "PcpsBySeries"); DeclareGlobalFunction( "ModuloSeriesPcps" ); DeclareGlobalFunction( "ReducedEfaSeriesPcps" ); DeclareGlobalFunction( "ExtendedSeriesPcps" ); DeclareGlobalFunction( "IsEfaFactorPcp" ); DeclareAttribute( "PcpsOfEfaSeries", IsPcpGroup ); ############################################################################# ## #F Isomorphisms and natural homomorphisms ## DeclareAttribute( "IsomorphismPcpGroup", IsGroup ); DeclareAttribute( "PcpGroupByEfaSeries", IsGroup ); DeclareGlobalFunction( "NaturalHomomorphismByPcp" ); # The following is for backwards compatibility with older versions of # polycyclic, which provided a NaturalHomomorphism operation. A few # packages use that directly, so we need to provide this until they # change. DeclareOperation( "NaturalHomomorphism", [IsPcpGroup, IsPcpGroup] ); polycyclic-2.16/gap/basic/grphoms.gd0000644000076600000240000000146513706672341016465 0ustar mhornstaff############################################################################# ## #W grphoms.gd Polycyc Bettina Eick #W Werner Nickel ## ############################################################################# ## #R IsFromPcpGHBI( ) #R IsToPcpGHBI( ) ## ## These declarations are a slight hack copied from the corresponding ## See the corresponding code for fp-groups for further background. ## DeclareRepresentation( "IsFromPcpGHBI", IsGroupGeneralMappingByImages and NewFilter("Extrarankfilter",11), [ "igs_gens_to_imgs" ] ); DeclareRepresentation( "IsToPcpGHBI", IsGroupGeneralMappingByImages and NewFilter("Extrarankfilter",11), [ "igs_imgs_to_gens" ] ); polycyclic-2.16/gap/basic/pcpelms.gd0000644000076600000240000000373313706672341016451 0ustar mhornstaff############################################################################# ## #W pcpelms.gd Polycyc Bettina Eick ## ############################################################################# ## ## Introduce the category of pcp elements ## DeclareCategory( "IsPcpElement", IsMultiplicativeElementWithInverse ); DeclareCategoryFamily( "IsPcpElement" ); DeclareCategoryCollections( "IsPcpElement" ); InstallTrueMethod( IsGeneratorsOfMagmaWithInverses, IsPcpElementCollection ); ############################################################################# ## ## Introduce the representation of pcp elements ## DeclareRepresentation( "IsPcpElementRep", IsComponentObjectRep, ["collector", "exponents", "depth", "leading", "name" ] ); ############################################################################# ## ## Operations ## DeclareOperation( "Exponents", [ IsPcpElementRep ] ); DeclareOperation( "NameTag", [ IsPcpElementRep ] ); DeclareOperation( "GenExpList", [ IsPcpElementRep ] ); DeclareOperation( "Depth", [ IsPcpElementRep ] ); DeclareOperation( "LeadingExponent", [ IsPcpElementRep ] ); ############################################################################# ## ## Some functions ## DeclareGlobalFunction( "PcpElementConstruction" ); DeclareGlobalFunction( "PcpElementByExponentsNC" ); DeclareGlobalFunction( "PcpElementByExponents" ); DeclareGlobalFunction( "PcpElementByGenExpListNC" ); DeclareGlobalFunction( "PcpElementByGenExpList" ); ############################################################################# ## ## Some attributes ## DeclareAttribute( "TailOfElm", IsPcpElement ); DeclareAttribute( "RelativeOrderPcp", IsPcpElement ); DeclareAttribute( "RelativeIndex", IsPcpElement ); DeclareAttribute( "FactorOrder", IsPcpElement ); polycyclic-2.16/gap/basic/pcpgrps.gi0000644000076600000240000003755713706672341016504 0ustar mhornstaff############################################################################# ## #W pcpgrps.gi Polycyc Bettina Eick ## ############################################################################# ## #F Create a pcp group by collector ## ## The trivial group is missing. ## InstallGlobalFunction( PcpGroupByCollectorNC, function( coll ) local n, l, e, f, G, rels; n := coll![ PC_NUMBER_OF_GENERATORS ]; if n > 0 then l := IdentityMat( n ); fi; e := List( [1..n], x -> PcpElementByExponentsNC( coll, l[x] ) ); f := PcpElementByGenExpListNC( coll, [] ); G := GroupWithGenerators( e, f ); SetCgs( G, e ); SetIsWholeFamily( G, true ); if 0 in RelativeOrders( coll ) then SetIsFinite( G, false ); else SetIsFinite( G, true ); SetSize( G, Product( RelativeOrders( coll ) ) ); fi; return G; end ); InstallGlobalFunction( PcpGroupByCollector, function( coll ) UpdatePolycyclicCollector( coll ); if not IsConfluent( coll ) then return fail; else return PcpGroupByCollectorNC( coll ); fi; end ); ############################################################################# ## #F Print( ) ## InstallMethod( PrintObj, "for a pcp group", [ IsPcpGroup ], function( G ) Print("Pcp-group with orders ", List( Igs(G), RelativeOrderPcp ) ); end ); InstallMethod( ViewObj, "for a pcp group", [ IsPcpGroup ], SUM_FLAGS, function( G ) Print("Pcp-group with orders ", List( Igs(G), RelativeOrderPcp ) ); end ); ############################################################################# ## #M Igs( ) #M Ngs( ) #M Cgs( ) ## InstallMethod( Igs, [ IsPcpGroup ], function( G ) if HasCgs( G ) then return Cgs(G); fi; if HasNgs( G ) then return Ngs(G); fi; return Igs( GeneratorsOfGroup(G) ); end ); InstallMethod( Ngs, [ IsPcpGroup ], function( G ) if HasCgs( G ) then return Cgs(G); fi; return Ngs( Igs( GeneratorsOfGroup(G) ) ); end ); InstallMethod( Cgs, [ IsPcpGroup ], function( G ) return Cgs( Igs( GeneratorsOfGroup(G) ) ); end ); ############################################################################# ## #M Membershiptest for pcp groups ## InstallMethod( \in, "for a pcp element and a pcp group", IsElmsColls, [IsPcpElement, IsPcpGroup], function( g, G ) return ReducedByIgs( Igs(G), g ) = One(G); end ); ############################################################################# ## #M Random( G ) ## InstallMethodWithRandomSource( Random, "for a random source and a pcp group", [ IsRandomSource, IsPcpGroup ], function( rs, G ) local pcp, rel, g, i; pcp := Pcp(G); rel := RelativeOrdersOfPcp( pcp ); g := []; for i in [1..Length(rel)] do if rel[i] = 0 then g[i] := Random( rs, Integers ); else g[i] := Random( rs, 0, rel[i]-1 ); fi; od; return MappedVector( g, pcp ); end ); ############################################################################# ## #M SubgroupByIgs( G, igs [, gens] ) ## ## create a subgroup and set igs. If gens is given, compute pcs defined ## by and use this. Note: this function does not check if the ## generators are in G. ## InstallGlobalFunction( SubgroupByIgs, function( arg ) local U, pcs; if Length( arg ) = 3 then pcs := AddToIgs( arg[2], arg[3] ); else pcs := arg[2]; fi; U := SubgroupNC( Parent(arg[1]), pcs ); SetIgs( U, pcs ); return U; end); ############################################################################# ## #M SubgroupByIgsAndIgs( G, fac, nor ) ## ## fac and nor are igs for a factor and a normal subgroup. This function ## computes an igs for the subgroups generated by fac and nor and sets it ## in the subgroup. This is a no-check function ## SubgroupByIgsAndIgs := function( G, fac, nor ) return SubgroupByIgs( G, AddIgsToIgs( fac, nor ) ); end; ############################################################################# ## #M IsSubset for pcp groups ## InstallMethod( IsSubset, "for pcp groups", IsIdenticalObj, [ IsPcpGroup, IsPcpGroup ], SUM_FLAGS, function( H, U ) if Parent(U) = H then return true; fi; return ForAll( GeneratorsOfGroup(U), x -> x in H ); end ); ############################################################################# ## #M IsNormal( H, U ) . . . . . . . . . . . . . . .test if U is normalized by H ## InstallMethod( IsNormalOp, "for pcp groups", IsIdenticalObj, [ IsPcpGroup, IsPcpGroup ], function( H, U ) local u, h; for h in GeneratorsOfPcp( Pcp(H, U)) do for u in Igs(U) do if not u^h in U then return false; fi; od; od; return true; end ); ############################################################################# ## #M Size( ) ## InstallMethod( Size, [ IsPcpGroup ], function( G ) local pcs, rel; pcs := Igs( G ); rel := List( pcs, RelativeOrderPcp ); if ForAny( rel, x -> x = 0 ) then return infinity; else return Product( rel ); fi; end ); ############################################################################# ## #M CanComputeIndex( , ) #M CanComputeSize( ) #M CanComputeSizeAnySubgroup( ) ## InstallMethod( CanComputeIndex, IsIdenticalObj, [IsPcpGroup, IsPcpGroup], ReturnTrue ); InstallTrueMethod( CanComputeSize, IsPcpGroup ); InstallTrueMethod( CanComputeSizeAnySubgroup, IsPcpGroup ); ############################################################################# ## #M IndexNC/Index( , ) ## InstallMethod( IndexNC, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( H, U ) local pcp, rel; pcp := Pcp( H, U ); rel := RelativeOrdersOfPcp( pcp ); if ForAny( rel, x -> x = 0 ) then return infinity; else return Product( rel ); fi; end ); InstallMethod( IndexOp, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( H, U ) if not IsSubgroup( H, U ) then Error("H must be contained in G"); fi; return IndexNC( H, U ); end ); ############################################################################# ## #M = ## InstallMethod( \=, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( G, H ) return Cgs( G ) = Cgs( H ); end); ############################################################################# ## #M ClosureGroup( , ) ## InstallMethod( ClosureGroup, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( G, H ) local P; P := PcpGroupByCollectorNC( Collector(G) ); return SubgroupByIgs( P, Igs(G), GeneratorsOfGroup(H) ); end ); ############################################################################# ## #F HirschLength( ) ## InstallMethod( HirschLength, [ IsPcpGroup ], function( G ) local pcs, rel; pcs := Igs( G ); rel := List( pcs, RelativeOrderPcp ); return Length( Filtered( rel, x -> x = 0 ) ); end ); ############################################################################# ## #M CommutatorSubgroup( G, H ) ## InstallMethod( CommutatorSubgroup, "for pcp groups", IsIdenticalObj, [ IsPcpGroup, IsPcpGroup], function( G, H ) local pcsG, pcsH, coms, i, j, U, u; pcsG := Igs(G); pcsH := Igs(H); # if G = H then we need fewer commutators coms := []; if pcsG = pcsH then for i in [1..Length(pcsG)] do for j in [1..i-1] do Add( coms, Comm( pcsG[i], pcsH[j] ) ); od; od; coms := Igs( coms ); U := SubgroupByIgs( Parent(G), coms ); else for u in pcsG do coms := AddToIgs( coms, List( pcsH, x -> Comm( u, x ) ) ); od; U := SubgroupByIgs( Parent(G), coms ); # In order to conjugate with fewer elements, compute . If one is # normal than we do not need the normal closure, see Glasby 1987. if not (IsBound( G!.isNormal ) and G!.isNormal) and not (IsBound( H!.isNormal ) and H!.isNormal) then U := NormalClosure( ClosureGroup( G, H ), U ); fi; fi; return U; end ); ############################################################################# ## #M DerivedSubgroup( G ) ## InstallMethod( DerivedSubgroup, "for a pcp group", [ IsPcpGroup ], G -> CommutatorSubgroup(G, G) ); ############################################################################# ## #M PRump( G, p ). . . . . smallest normal subgroup N of G with G/N elementary ## abelian p-group. ## InstallMethod( PRumpOp, "for a pcp group and a prime", [IsPcpGroup, IsPosInt], function( G, p ) local D, pcp, new; D := DerivedSubgroup(G); pcp := Pcp( G, D ); new := List( pcp, x -> x^p ); return SubgroupByIgs( G, Igs(D), new ); end ); ############################################################################# ## #M IsNilpotentGroup( ) ## InstallMethod( IsNilpotentGroup, "for a pcp group with known lower central series", [ IsPcpGroup and HasLowerCentralSeriesOfGroup ], function( G ) local lcs; lcs := LowerCentralSeriesOfGroup( G ); return IsTrivial( lcs[ Length(lcs) ] ); end ); InstallMethod( IsNilpotentGroup, "for a pcp group", [IsPcpGroup], function( G ) local l, U, V, pcp, n; l := HirschLength(G); U := ShallowCopy( G ); repeat # take next term of lc series U!.isNormal := true; V := CommutatorSubgroup( G, U ); # if we arrive at the trivial group if Size( V ) = 1 then return true; fi; # get quotient U/V pcp := Pcp( U, V ); # if U=V then the series has terminated at a non-trivial group if Length( pcp ) = 0 then return false; fi; # get the Hirsch length of U/V n := Length( Filtered( RelativeOrdersOfPcp( pcp ), x -> x = 0)); # compare it with l if n = 0 and l <> 0 then return false; fi; l := l - n; # iterate U := ShallowCopy( V ); until false; end ); ############################################################################# ## #M IsElementaryAbelian( ) ## InstallMethod( IsElementaryAbelian, "for a pcp group", [ IsPcpGroup ], function( G ) local rel, p; if not IsFinite(G) or not IsAbelian(G) then return false; fi; rel := List( Igs(G), RelativeOrderPcp ); if Length(Set(rel)) > 1 then return false; fi; if ForAny( rel, x -> not IsPrime(x) ) then return false; fi; p := rel[1]; return ForAll( RelativeOrdersOfPcp( Pcp( G, "snf" ) ), x -> x = p ); end ); ############################################################################# ## #F AbelianInvariants( ) ## InstallMethod( AbelianInvariants, "for a pcp group", [ IsPcpGroup ], function( G ) return AbelianInvariantsOfList( RelativeOrdersOfPcp( Pcp(G, DerivedSubgroup(G), "snf") ) ); end ); InstallMethod( AbelianInvariants, "for an abelian pcp group", [IsPcpGroup and IsAbelian], function( G ) return AbelianInvariantsOfList( RelativeOrdersOfPcp( Pcp(G, "snf") ) ); end ); ############################################################################# ## #M CanEasilyComputeWithIndependentGensAbelianGroup( ) ## if IsBound(CanEasilyComputeWithIndependentGensAbelianGroup) then # CanEasilyComputeWithIndependentGensAbelianGroup was introduced in GAP 4.5.x InstallTrueMethod(CanEasilyComputeWithIndependentGensAbelianGroup, IsPcpGroup and IsAbelian); fi; BindGlobal( "ComputeIndependentGeneratorsOfAbelianPcpGroup", function ( G ) local pcp, id, mat, base, ord, i, g, o, cf, j; # Get a pcp in Smith normal form if not IsBound( G!.snfpcp ) then pcp := Pcp(G, "snf"); G!.snfpcp := pcp; else pcp := G!.snfpcp; fi; if IsBound( G!.indgens ) and IsBound( G!.indgenmat ) then return; fi; # Unfortunately, this is not *quite* what we need; in order to match # the Abelian invariants, we now have to further refine the generator # list to ensure only generators of prime power order are in the list. id := IdentityMat( Length(pcp) ); mat := []; base := []; ord := []; for i in [1..Length(pcp)] do g := pcp[i]; o := Order(g); if o = 1 then continue; fi; if o = infinity then Add(base, g); Add(mat, id[i]); Add(ord, 0); continue; fi; cf:=Collected(Factors(o)); if Length(cf) > 1 then for j in cf do j := j[1]^j[2]; Add(base, g^(o/j)); Add(mat, id[i] * (j/o mod j)); Add(ord, j); od; else Add(base, g); Add(mat, id[i]); Add(ord, o); fi; od; SortParallel(ShallowCopy(ord),base); SortParallel(ord,mat); mat := TransposedMat( mat ); G!.indgens := base; G!.indgenmat := mat; end ); ############################################################################# ## #A IndependentGeneratorsOfAbelianGroup( ) ## InstallMethod(IndependentGeneratorsOfAbelianGroup, "for an abelian pcp group", [IsPcpGroup and IsAbelian], function( G ) if not IsBound( G!.indgens ) then ComputeIndependentGeneratorsOfAbelianPcpGroup( G ); fi; return G!.indgens; end ); ############################################################################# ## #O IndependentGeneratorExponents( , ) ## if IsBound( IndependentGeneratorExponents ) then # IndependentGeneratorExponents was introduced in GAP 4.5.x InstallMethod(IndependentGeneratorExponents, "for an abelian pcp group and an element", IsCollsElms, [IsPcpGroup and IsAbelian, IsPcpElement], function( G, elm ) local exp, rels, i; # Ensure everything has been set up if not IsBound( G!.indgenmat ) then ComputeIndependentGeneratorsOfAbelianPcpGroup( G ); fi; # Convert elm into an exponent vector with respect to a snf pcp exp := ExponentsByPcp( G!.snfpcp, elm ); rels := AbelianInvariants( G ); # Convert the exponent vector with respect to pcp into one # with respect to our independent abelian generators. exp := exp * G!.indgenmat; for i in [1..Length(exp)] do if rels[i] > 0 then exp[i] := exp[i] mod rels[i]; fi; od; return exp; end); fi; ############################################################################# ## #F NormalClosure( K, U ) ## InstallMethod( NormalClosureOp, "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup], function( K, U ) local tmpN, newN, done, id, gensK, pcsN, k, n, c, N; # take initial pcs pcsN := ShallowCopy( Cgs(U) ); if Length( pcsN ) = 0 then return U; fi; # take generating sets id := One( K ); gensK := GeneratorsOfGroup(K); gensK := List( gensK, x -> ReducedByIgs( pcsN, x ) ); gensK := Filtered( gensK, x -> x <> id ); repeat done := true; tmpN := ShallowCopy( pcsN ); for k in gensK do for n in tmpN do c := ReducedByIgs( pcsN, Comm( k, n ) ); if c <> id then newN := AddToIgs( pcsN, [c] ); if newN <> pcsN then done := false; pcsN := Cgs( newN ); fi; fi; od; od; #Print(Length(pcsN)," obtained \n"); until done; # set up result N := Group( pcsN ); SetIgs( N, pcsN ); return N; end); ############################################################################# ## #F ExponentsByRels . . . . . . . . . . . . . . .elements written as exponents ## ExponentsByRels := function( rel ) local exp, idm, i, t, j, e, f; exp := [List( rel, x -> 0 )]; idm := IdentityMat( Length( rel ) ); for i in Reversed( [1..Length(rel)] ) do t := []; for j in [1..rel[i]] do for e in exp do f := ShallowCopy( e ); f[i] := j-1; Add( t, f ); od; od; exp := t; od; return exp; end; polycyclic-2.16/gap/basic/convert.gi0000644000076600000240000003532713706672341016477 0ustar mhornstaff############################################################################# ## #W convert.gi Polycyc Bettina Eick ## Werner Nickel ############################################################################# ## ## Convert finite pcp groups to pc groups. ## PcpGroupToPcGroup := function( G ) local pcp, rel, n, F, f, i, rws, h, e, w, j; pcp := Pcp( G ); rel := RelativeOrdersOfPcp( pcp ); if ForAny( rel, x -> x = 0 ) then return fail; fi; n := Length( pcp ); F := FreeGroup( n ); f := GeneratorsOfGroup( F ); rws := SingleCollector( F, rel ); for i in [1..n] do # set power h := pcp[i] ^ rel[i]; e := ExponentsByPcp( pcp, h ); w := MappedVector( e, f ); SetPower( rws, i, w ); # set conjugates for j in [1..i-1] do h := pcp[i]^pcp[j]; e := ExponentsByPcp( pcp, h ); w := MappedVector( e, f ); SetConjugate( rws, i, j, w ); od; od; return GroupByRwsNC( rws ); end; InstallMethod( IsomorphismPcGroup, [IsPcpGroup], NICE_FLAGS, function( G ) local K, H, g, k, h, hom; if not IsFinite(G) then TryNextMethod(); fi; K := RefinedPcpGroup(G); H := PcpGroupToPcGroup(K); g := Igs(G); k := List(g, x -> Image(K!.bijection,x)); h := List(k, x -> MappedVector(Exponents(x), Pcgs(H))); hom := GroupHomomorphismByImagesNC( G, H, g, h); SetIsBijective( hom, true ); SetIsGroupHomomorphism( hom, true ); return hom; end ); ############################################################################# ## ## Convert pcp groups to fp groups. ## PcpGroupToFpGroup := function( G ) local pcp, rel, n, F, f, r, i, j, e, w, v; pcp := Pcp( G ); rel := RelativeOrdersOfPcp( pcp ); n := Length( pcp ); F := FreeGroup( n ); f := GeneratorsOfGroup( F ); r := []; for i in [1..n] do # set power e := ExponentsByPcp( pcp, pcp[i]^rel[i] ); w := MappedVector( e, f ); v := f[i]^rel[i]; Add( r, v/w ); # set conjugates for j in [1..i-1] do e := ExponentsByPcp( pcp, pcp[i]^pcp[j] ); w := MappedVector( e, f ); v := f[i]^f[j]; Add( r, v/w ); if rel[j] = 0 then e := ExponentsByPcp( pcp, pcp[i]^(pcp[j]^-1) ); w := MappedVector( e, f ); v := f[i]^(f[j]^-1); Add( r, v/w ); fi; od; od; return F/r; end; InstallMethod( IsomorphismFpGroup, [IsPcpGroup], NICE_FLAGS, function( G ) local H, hom; H := PcpGroupToFpGroup( G ); hom := GroupHomomorphismByImagesNC( G, H, AsList(Pcp(G)), GeneratorsOfGroup(H)); SetIsBijective( hom, true ); return hom; end ); ############################################################################# ## ## Convert pc groups to pcp groups. ## PcGroupToPcpGroup := function( G ) local g, r, n, i, coll, h, e, w, j; g := Pcgs( G ); r := RelativeOrders( g ); n := Length( g ); coll := FromTheLeftCollector( n ); for i in [1..n] do # set power h := g[i] ^ r[i]; e := ExponentsOfPcElement( g, h ); w := ObjByExponents( coll, e ); SetRelativeOrder( coll, i, r[i] ); SetPower( coll, i, w ); # set conjugates for j in [1..i-1] do h := g[i]^g[j]; e := ExponentsOfPcElement( g, h ); w := ObjByExponents( coll, e ); SetConjugate( coll, i, j, w ); h := g[i]^(g[j]^-1); e := ExponentsOfPcElement( g, h ); w := ObjByExponents( coll, e ); SetConjugate( coll, i, -j, w ); od; od; return PcpGroupByCollector( coll ); end; InstallMethod( IsomorphismPcpGroup, [IsPcGroup], NICE_FLAGS, function( G ) local H, hom; H := PcGroupToPcpGroup( G ); hom := GroupHomomorphismByImagesNC( G, H, AsList(Pcgs(G)), AsList(Pcp(H))); SetIsBijective( hom, true ); return hom; end ); InstallMethod( IsomorphismPcpGroup, [ IsPcpGroup ], SUM_FLAGS, IdentityMapping ); ############################################################################# ## ## Convert perm groups to pcp groups. ## InstallMethod( IsomorphismPcpGroup, [IsPermGroup], function( G ) local iso, F,H, gens, hom; if not IsSolvableGroup( G ) then return fail; fi; iso := IsomorphismPcGroup( G ); F := Image( iso ); H := PcGroupToPcpGroup( F ); gens := List( Pcgs(F), x -> PreImagesRepresentative( iso, x ) ); hom := GroupHomomorphismByImagesNC( G, H, gens, AsList(Pcp(H)) ); SetIsBijective( hom, true ); return hom; end ); ############################################################################# ## ## Convert abelian groups to pcp groups. ## if IsBound(CanEasilyComputeWithIndependentGensAbelianGroup) then # CanEasilyComputeWithIndependentGensAbelianGroup was introduced in GAP 4.5.x InstallMethod( IsomorphismPcpGroup, [ IsGroup and IsAbelian and CanEasilyComputeWithIndependentGensAbelianGroup ], # this method is better than the one for perm groups RankFilter(IsPermGroup), G -> IsomorphismAbelianGroupViaIndependentGenerators( IsPcpGroup, G ) ); fi; ############################################################################# ## ## Convert special fp groups to pcp groups. ## ClassifyRelationsOfFpGroup := function( fpgroup ) local gens, rels, allpowers, conflicts, relations, rel, n, l, g1, e1, g2, e2, g3, e3, g4, e4; gens := GeneratorsOfGroup( FreeGroupOfFpGroup(fpgroup) ); rels := RelatorsOfFpGroup( fpgroup ); allpowers := []; # list to collect power relations conflicts := []; # conflicts are collected and tested later relations := rec(); # power relations relations.rods := List( gens, x -> 0 ); relations.powersp := []; # positive exponent relations.powersn := []; # negative exponent # commutator relations relations.commpp := List( gens, x -> [] ); # [b,a] relations.commpn := List( gens, x -> [] ); # [b,a^-1] relations.commnp := List( gens, x -> [] ); # [b^-1,a] relations.commnn := List( gens, x -> [] ); # [b^-1,a^-1] # conjugate pos, pos relations.conjpp := List( gens, x -> [] ); # b^a relations.conjpn := List( gens, x -> [] ); # b^(a^-1) relations.conjnp := List( gens, x -> [] ); # (b^-1)^a relations.conjnn := List( gens, x -> [] ); # (b^-1)^(a^-1) # sort relators into power and commutator/conjugate relators for rel in rels do n := NumberSyllables( rel ); l := Length( rel ); if n = 1 or n = 2 then Add( allpowers, rel ); # ignore the trivial word elif 2 < n then # extract the first four entries g1 := GeneratorSyllable( rel, 1 ); e1 := ExponentSyllable( rel, 1 ); g2 := GeneratorSyllable( rel, 2 ); e2 := ExponentSyllable( rel, 2 ); g3 := GeneratorSyllable( rel, 3 ); e3 := ExponentSyllable( rel, 3 ); if 3 < n then g4 := GeneratorSyllable( rel, 4 ); e4 := ExponentSyllable( rel, 4 ); fi; # a word starting with a^-1 x a is a conjugate or commutator if e1 = -1 and e3 = 1 and g1 = g3 then # a^-1 b^-1 a b is a commutator if 3 < n and e2 = -1 and e4 = 1 and g2 = g4 and g2 < g1 then if IsBound( relations.commpp[g1][g2] ) or IsBound( relations.conjpp[g1][g2] ) then Add( conflicts, rel ); else #Print( rel, " -> [", g1, ", ", g2, "]\n" ); relations.commpp[g1][g2] := Subword( rel, 5, l )^-1; fi; # a^-1 b a b^-1 is a commutator elif 3 < n and e2 = 1 and e4 = -1 and g2 = g4 and g2 < g1 then if IsBound(relations.commpn[g1][g2]) or IsBound(relations.conjpn[g1][g2]) then Add( conflicts, rel ); else #Print( rel, " -> [", g1, ", ", -g2, "]\n" ); relations.commpn[g1][g2] := Subword( rel, 5, l )^-1; fi; # a^-1 b a is a conjugate elif e2 = 1 and g1 < g2 then if IsBound(relations.conjpp[g2][g1]) or IsBound(relations.commpp[g2][g1]) then Add( conflicts, rel ); else #Print( rel, " -> ", g2, "^", g1, "\n" ); relations.conjpp[g2][g1] := Subword( rel, 4, l )^-1; fi; # a^-1 b^-1 a is a conjugate elif e2 = -1 and g1 < g2 then if IsBound(relations.conjnp[g2][g1]) or IsBound(relations.commnp[g2][g1]) then Add( conflicts, rel ); else #Print( rel, " -> ", -g2, "^", g1, "\n" ); relations.conjnp[g2][g1] := Subword( rel, 4, l )^-1; fi; else Error( "not a power/commutator/conjugate relator ", rel ); fi; # a word starting with a b a^-1 is a conjugate or commutator elif e1 = 1 and e3 = -1 and g1 = g3 then # a b a^-1 b^-1 is a commutator if 3 < n and e2 = 1 and e4 = -1 and g2 = g4 and g2 < g1 then if IsBound(relations.commnn[g1][g2]) or IsBound(relations.conjnn[g1][g2]) then Add( conflicts, rel ); else #Print( rel, " -> [", -g1, ", ", -g2, "]\n" ); relations.commnn[g1][g2] := Subword( rel, 5, l )^-1; fi; # a b^-1 a^-1 b is a commutator elif 3 < n and e2 = -1 and e4 = 1 and g2 = g4 and g2 < g1 then if IsBound(relations.commnp[g1][g2]) or IsBound(relations.conjnp[g1][g2]) then Add( conflicts, rel ); else #Print( rel, " -> [", -g1, ", ", g2, "]\n" ); relations.commnp[g1][g2] := Subword( rel, 5, l )^-1; fi; # a b a^-1 is a conjugate elif e2 = 1 and g1 < g2 then if IsBound(relations.conjpn[g2][g1]) or IsBound(relations.commpn[g2][g1]) then Add( conflicts, rel ); else #Print( rel, " -> ", g2, "^", -g1, "\n" ); relations.conjpn[g2][g1] := Subword( rel, 4, l )^-1; fi; # a b^-1 a^-1 b is a conjugate elif e2 = -1 and g1 < g2 then if IsBound(relations.conjnp[g2][g1]) or IsBound(relations.commnp[g2][g1]) then Add( conflicts, rel ); else #Print( rel, " -> ", -g2, "^", -g1, "\n" ); relations.conjnn[g2][g1] := Subword( rel, 4, l )^-1; fi; else Error( "not a power/commutator/conjugate relator ", rel ); fi; # it must be a power else Add( allpowers, rel ); fi; fi; od; # now check the powers for rel in allpowers do g1 := GeneratorSyllable( rel, 1 ); e1 := ExponentSyllable( rel, 1 ); l := Length( rel ); if e1 > 0 then if (relations.rods[g1] <> 0 and relations.rods[g1] <> e1) or IsBound(relations.powersp[g1]) then Add( conflicts, rel ); fi; relations.rods[g1] := e1; relations.powersp[g1] := Subword( rel, e1+1, l )^-1; else if (relations.rods[g1] <> 0 and relations.rods[g1] <> -e1) or IsBound(relations.powersp[g1]) then Add( conflicts, rel ); fi; relations.rods[g1] := -e1; relations.powersn[ g1] := Subword( rel, -e1+1, l )^-1; fi; od; relations.conflicts := conflicts; return relations; end; FromTheLeftCollectorByRelations := function( gens, rels ) local ftl, j, i; ftl := FromTheLeftCollector( Length(gens) ); for i in [ 1 .. Length(gens) ] do SetRelativeOrder( ftl, i, rels.rods[i] ); if IsBound( rels.powersp[i] ) then SetPower( ftl, i, rels.powersp[i] ); Unbind( rels.powersp[i] ); fi; od; for j in [ 1 .. Length(gens) ] do for i in [ 1 .. j-1 ] do if IsBound( rels.conjpp[j][i] ) then SetConjugate( ftl, j, i, rels.conjpp[j][i] ); #Print( "conjpp", [j,i], ": ", rels.conjpp[j][i], "\n" ); Unbind( rels.conjpp[j][i] ); elif IsBound( rels.commpp[j][i] ) then SetConjugate( ftl, j, i, gens[j]*rels.commpp[j][i] ); #Print( "commpp", [j,i], ": ", gens[j]*rels.commpp[j][i], "\n" ); Unbind( rels.commpp[j][i] ); elif IsBound( rels.conjnp[j][i] ) then SetConjugate( ftl, j, i, rels.conjnp[j][i]^-1 ); #Print( "conjnp", [j,i], ": ", rels.conjnp[j][i]^-1, "\n" ); Unbind( rels.conjnp[j][i] ); elif IsBound( rels.commnp[j][i] ) then SetConjugate( ftl, j, i, (gens[j] * rels.commnp[j][i])^-1 ); #Print( "commnp", [j,i], ": ", (gens[j] * rels.commnp[j][i])^-1, "\n" ); Unbind( rels.commnp[j][i] ); fi; od; od; return ftl; end; PcpGroupFpGroupPcPres := function( G ) local gens, rels, ftl, ev, rel; gens := GeneratorsOfGroup( FreeGroupOfFpGroup( G ) ); rels := ClassifyRelationsOfFpGroup( G ); ftl := FromTheLeftCollectorByRelations( gens, rels ); ev := List( gens, g->0 ); for rel in rels.conflicts do while CollectWordOrFail( ftl, ev, ExtRepOfObj( rel ) ) = fail do od; if ev <> ev * 0 then Error( "finitely presented group is not a pcp group" ); fi; od; return PcpGroupByCollector( ftl ); end; IsomorphismPcpGroupFromFpGroupWithPcPres := function(G) local H, hom; H := PcpGroupFpGroupPcPres( G ); hom := GroupHomomorphismByImagesNC( G, H, GeneratorsOfGroup( G ), GeneratorsOfGroup(H) ); SetIsBijective( hom, true ); return hom; end; polycyclic-2.16/gap/basic/README0000644000076600000240000000224413706672341015346 0ustar mhornstaff gap/basic: Basic functions to compute with infinite polycyclic groups. Incorporates everything related to collecting and elements of pcp groups. Further, includes computation with subgroups and factor groups of pcp groups as well as homomorphisms. collect.gd -- declaration of all collector related things collect.gi -- methods shared by all collector types colftl.gi -- from the left collection colcom.gi -- combinatorial collection coldt.gi -- deep thought collection colsave.gi -- save functions for collectors colrec.gi -- recursive collection (not yet available) pcpelms.gd/gi -- pcp elements pcpgrps.gd/gi -- groups of pcp elements pcppcps.gd/gi -- induced and canonical pcp's of groups pcppara.gi -- parallel pcp's pcpexpo.gd/gi -- exponents wrt induced/mod pc pcpsers.gd/gi -- various series' in pcp groups grphoms.gd/gi -- group homomorphisms pcpfact.gd/gi -- factor groups and modulo pcp 's pcplib.gd/gi -- some generic pcp groups (AbelianPcpGroup etc) convert.gi -- convert PcpGroup to PcGroup or FpGroup chngpcp.gi -- change defining pcp (RefinedPcp etc) orbstab.gi -- finite orbit-stabilizer method polycyclic-2.16/gap/basic/pcppara.gi0000644000076600000240000001260713706672341016441 0ustar mhornstaff############################################################################# ## #W pcppara.gi Polycyc Bettina Eick #W Werner Nickel ## ## Parallel versions of the non-commuatative gauss algorithm. ## ############################################################################# ## #F UpdateCounterPara( ind, c ) . . . . . . . . . . . . small help function ## UpdateCounterPara := function( ind, c ) local i; i := c - 1; while i > 0 and not IsBool(ind[i]) and LeadingExponent(ind[i]) = 1 do i := i - 1; od; return i + 1; end; ############################################################################# ## #F AddToIgsParallel( , , , ) ## ## This function adds the elements in to the induced pcs . ## It acts simultaneously on and as well as and . ## InstallGlobalFunction( AddToIgsParallel, function( pcs, gens, ppcs, pgens ) local coll, rels, n, id, todo, tododo, ind, indd, g, gg, d, h, hh, k, eg, eh, e, changed, c, i, r, sub; if Length( gens ) = 0 then return [pcs, ppcs]; fi; # get information coll := Collector( gens[1] ); rels := RelativeOrders( coll ); n := NumberOfGenerators( coll ); id := gens[1]^0; # create new list from pcs/ppcs ind := List( [1..n], x -> false ); indd := List( [1..n], x -> false ); for i in [1..Length(pcs)] do d := Depth( pcs[i] ); ind[d] := pcs[i]; indd[d] := ppcs[i]; od; # set counter c := UpdateCounterPara( ind, n+1 ); # create a to-do list from gens/pgens sub := Filtered( [1..Length(gens)], x -> Depth(gens[x]) < c ); todo := gens{sub}; tododo:= pgens{sub}; # loop over to-do list until it is empty while Length( todo ) > 0 and c > 1 do g := Remove(todo); gg := Remove(tododo); d := Depth( g ); # shift g into ind changed := []; while d < c do h := ind[d]; hh := indd[d]; if not IsBool( h ) then # reduce g with h eg := LeadingExponent( g ); eh := LeadingExponent( h ); e := Gcdex( eg, eh ); # adjust ind[d] by gcd ind[d] := (g^e.coeff1) * (h^e.coeff2); indd[d] := (gg^e.coeff1) * (hh^e.coeff2); if e.coeff1 <> 0 then Add( changed, d ); fi; # adjust g g := (g^e.coeff3) * (h^e.coeff4); gg := (gg^e.coeff3) * (hh^e.coeff4); else # just add g into ind ind[d] := g; indd[d] := gg; g := g^0; gg := gg^0; Add( changed, d ); fi; c := UpdateCounterPara( ind, c ); d := Depth( g ); od; for d in changed do g := ind[d]; gg := indd[d]; if d <= Length( rels ) and rels[d] > 0 then r := RelativeOrderPcp( g ); k := g ^ r; if Depth(k) < c then Add( todo, k ); Add( tododo, gg^r ); fi; fi; for i in [1..Length(ind)] do if not IsBool( ind[i] ) then k := Comm( g, ind[i] ); if Depth(k) < c then Add( todo, k ); Add( tododo, Comm( gg, indd[i] ) ); fi; fi; od; od; od; # return resulting list return [Filtered( ind, x -> not IsBool( x ) ), Filtered( indd, x -> not IsBool( x ) ) ]; end ); ############################################################################# ## ## IgsParallel( ,

 )
##
InstallGlobalFunction( IgsParallel, function( gens, pre )
    return AddToIgsParallel( [], gens, [], pre );
end );

#############################################################################
##
## CgsParallel( , 
 )
##
## parallel version of Cgs. Note: this function performes an
## induced pcs computation as well.
##
InstallGlobalFunction( CgsParallel, function( gens, pre )
    local   can,  cann,  i,  f,  e,  j,  l,  d,  r, s;

    if Length( gens ) = 0 then return []; fi;

    can  := IgsParallel( gens, pre );
    cann := can[2];
    can  := can[1];

    # first norm leading coefficients
    for i in [1..Length(can)] do
        f := NormingExponent( can[i] );
        can[i]  := can[i]^f;
        cann[i] := cann[i]^f;
    od;

    # reduce entries in matrix
    for i in [1..Length(can)] do
        e := LeadingExponent( can[i] );
        r := Depth( can[i] );
        for j in [1..i-1] do
            l := Exponents( can[j] )[r];
            if l > 0 then
                d := QuoInt( l, e );
                can[j]  := can[j]  * can[i]^-d;
                cann[j] := cann[j] * cann[i]^-d;
            elif l < 0 then
                d := QuoInt( -l, e );
                s := RemInt( -l, e );
                if s = 0 then
                    can[j] := can[j] * can[i]^d;
                    cann[j] := cann[j] * cann[i]^d;
                else
                    can[j] := can[j] * can[i]^(d+1);
                    cann[j] := cann[j] * cann[i]^(d+1);
                fi;

            fi;
        od;
    od;
    return[ can, cann ];
end );
polycyclic-2.16/gap/basic/construct.gi0000644000076600000240000001512113706672341017031 0ustar  mhornstaff#############################################################################
##
#W  construct.gi               Polycyclic                            Max Horn
##


#############################################################################
##
#M  TrivialGroupCons(  )
##
InstallMethod( TrivialGroupCons,
    "pcp group",
    [ IsPcpGroup and IsFinite ],
function( filter )
    return PcpGroupByCollectorNC( FromTheLeftCollector( 0 ) );
end );


#############################################################################
##
#M  AbelianGroupCons( ,  )
##
InstallMethod( AbelianGroupCons,
    "pcp group",
    [ IsPcpGroup, IsList ],
function( filter, ints )
    local coll, i, n, r, grp;

    if not ForAll( ints, IsInt )  then
        Error( " must be a list of integers" );
    fi;
    # We allow 0, and interpret it as indicating an infinite factor.
    if not ForAll( ints, x -> 0 <= x )  then
        TryNextMethod();
    fi;

    n := Length(ints);
    r := ints;

    # construct group
    coll := FromTheLeftCollector( n );
    for i in [1..n] do
        if IsBound( r[i] ) and r[i] > 0 then
            SetRelativeOrder( coll, i, r[i] );
        fi;
    od;
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );
    SetIsAbelian( grp, true );
    return grp;
end );


#############################################################################
##
#M  ElementaryAbelianGroupCons( ,  )
##
InstallMethod( ElementaryAbelianGroupCons,
	"pcp group",
    [ IsPcpGroup and IsFinite, IsPosInt ],
function(filter,size)

    local grp;

    if size = 1 or IsPrimePowerInt( size )  then
        grp := AbelianGroup( filter, Factors(size) );
    else
        Error( " must be a prime power" );
    fi;
    SetIsElementaryAbelian( grp, true );
    return grp;
end);


#############################################################################
##
#M  FreeAbelianGroupCons( ,  )
##

if IsBound(FreeAbelianGroupCons) then

InstallMethod( FreeAbelianGroupCons,
    "pcp group",
    [ IsPcpGroup,  IsInt and IsPosRat ],
function( filter, rank )
    local coll, grp;
    # construct group
    coll := FromTheLeftCollector( rank );
    UpdatePolycyclicCollector( coll );
    grp := PcpGroupByCollectorNC( coll );
    SetIsFreeAbelian( grp, true );
    return grp;
end );

fi;


#############################################################################
##
#M  CyclicGroupCons( ,  )
##
InstallMethod( CyclicGroupCons,
    "pcp group",
    [ IsPcpGroup and IsFinite, IsPosInt ],
function( filter, n )
    local coll, grp;

    # construct group
    coll := FromTheLeftCollector( 1 );
    SetRelativeOrder( coll, 1, n );
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );

    if n > 1 then
        SetMinimalGeneratingSet(grp, [grp.1]);
    else
        SetMinimalGeneratingSet(grp, []);
    fi;
    return grp;
end );

#############################################################################
##
#M  CyclicGroupCons( , infinity )
##
InstallOtherMethod( CyclicGroupCons,
    "pcp group",
    [ IsPcpGroup, IsInfinity ],
function( filter, n )
    local coll, grp;

    # construct group
    coll := FromTheLeftCollector( 1 );
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );

    SetMinimalGeneratingSet(grp, [grp.1]);
    return grp;
end );

#############################################################################
##
#M  DihedralGroupCons( ,  )
##
InstallMethod( DihedralGroupCons,
    "pcp group",
    [ IsPcpGroup and IsFinite, IsPosInt ],
function( filter, n )
    local coll, grp;

    if n mod 2 = 1  then
        TryNextMethod();
    elif n = 2 then
        return CyclicGroup( filter, 2 );
    fi;

    coll := FromTheLeftCollector( 2 );
    SetRelativeOrder( coll, 1, 2 );
    SetRelativeOrder( coll, 2, n/2 );
    SetConjugate( coll, 2,  1, [2,n/2-1] );
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );
    return grp;
end );

#############################################################################
##
#M  DihedralGroupCons( , infinity )
##
InstallOtherMethod( DihedralGroupCons,
    "pcp group",
    [ IsPcpGroup, IsInfinity ],
function( filter, n )
    local coll, grp;

    coll := FromTheLeftCollector( 2 );
    SetRelativeOrder( coll, 1, 2 );
    SetConjugate( coll, 2,  1, [2,-1] );
    SetConjugate( coll, 2, -1, [2,-1] );
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );
    return grp;
end );

#############################################################################
##
#M  QuaternionGroupCons( ,  )
##

InstallMethod( QuaternionGroupCons,
    "pcp group",
    [ IsPcpGroup and IsFinite, IsPosInt ],
function( filter, n )
    local coll, grp;

    if 0 <> n mod 4  then
        TryNextMethod();
    elif n = 4 then return
        CyclicGroup( filter, 4 );
    fi;

    coll := FromTheLeftCollector( 2 );
    SetRelativeOrder( coll, 1, 2 );
    SetRelativeOrder( coll, 2, n/2 );
    SetPower( coll, 1, [2, n/4] );
    SetConjugate( coll, 2,  1, [2,n/2-1] );
    UpdatePolycyclicCollector(coll);
    grp := PcpGroupByCollectorNC( coll );
    return grp;
end );

#############################################################################
##
#M  ExtraspecialGroupCons( , ,  )
##
InstallMethod( ExtraspecialGroupCons,
    "pcp group",
    [ IsPcpGroup and IsFinite,
      IsInt,
      IsObject ],
function( filters, order, exp )
    local G;
    G := ExtraspecialGroupCons( IsPcGroup and IsFinite, order, exp );
    return PcGroupToPcpGroup( G );
end );

#############################################################################
##
#M  AlternatingGroupCons( ,  )
##
InstallMethod( AlternatingGroupCons,
    "pcp group with degree",
    [ IsPcpGroup and IsFinite,
      IsPosInt ],
function( filter, deg )
    local   alt;
    if 4 < deg  then
        Error( " must be at most 4" );
    fi;
    alt := AlternatingGroupCons(IsPcGroup and IsFinite,deg);
    alt := PcGroupToPcpGroup(alt);
    SetIsAlternatingGroup( alt, true );
    return alt;
end );


#############################################################################
##
#M  SymmetricGroupCons( ,  )
##
InstallMethod( SymmetricGroupCons,
    "pcp group with degree",
    [ IsPcpGroup and IsFinite,
      IsPosInt ],
function( filter, deg )
    local sym;
    if 4 < deg  then
        Error( " must be at most 4" );
    fi;
    sym := SymmetricGroupCons(IsPcGroup and IsFinite,deg);
    sym := PcGroupToPcpGroup(sym);
    SetIsSymmetricGroup( sym, true );
    return sym;
end );
polycyclic-2.16/gap/basic/pcppcps.gd0000644000076600000240000000317613706672341016457 0ustar  mhornstaff#############################################################################
##
#W  pcppcgs.gd                   Polycyc                         Bettina Eick
##

#############################################################################
##
## induced and canonical generating sets + parallel versions
##
DeclareGlobalFunction( "AddToIgs" );
DeclareGlobalFunction( "AddToIgsParallel" );
DeclareGlobalFunction( "IgsParallel" );
DeclareGlobalFunction( "CgsParallel" );

#############################################################################
##
## Introduce the category and representation of Pcp's
##
DeclareCategory( "IsPcp", IsObject );
DeclareRepresentation( "IsPcpRep",
                        IsComponentObjectRep,
                        ["gens", "rels", "denom", "numer", "one", "group" ] );

#############################################################################
##
## Create their family and their type
##
BindGlobal( "PcpFamily", NewFamily( "PcpFamily", IsPcp, IsPcp ) );
BindGlobal( "PcpType", NewType( PcpFamily, IsPcpRep ) );

#############################################################################
##
## Basic attributes and properties
##
DeclareGlobalFunction( "GeneratorsOfPcp" );
DeclareGlobalFunction( "RelativeOrdersOfPcp" );
DeclareGlobalFunction( "DenominatorOfPcp" );
DeclareGlobalFunction( "NumeratorOfPcp" );
DeclareGlobalFunction( "GroupOfPcp" );
DeclareGlobalFunction( "OneOfPcp" );

DeclareGlobalFunction( "IsSNFPcp" );
DeclareGlobalFunction( "IsTailPcp" );

#############################################################################
##
## The main function to create an pcp
##
DeclareGlobalFunction( "Pcp" );

polycyclic-2.16/gap/basic/orbstab.gi0000644000076600000240000001742013706672341016445 0ustar  mhornstaff#############################################################################
##
#W  orbstab.gi                   Polycyc                         Bettina Eick
#W                                                              Werner Nickel
##

#############################################################################
##
#F TransversalInverse( j, trels )
##
TransversalInverse := function( j, trels )
    local l, w, s, p, t;
    l := Product( trels );
    j := j - 1;
    w := [];
    for s in Reversed( [1..Length( trels )] ) do
        p := trels[s];
        l := l/p;
        t := QuoInt( j, l );
        j := RemInt( j, l );
        if t > 0 then Add( w, [s,t] ); fi;
    od;
    return w;
end;

#############################################################################
##
#F SubsWord( word, list )
##
SubsWord := function( word, list )
    local g, w;
    g := list[1]^0;
    for w in word do
        g := g * list[w[1]]^w[2];
    od;
    return g;
end;

#############################################################################
##
#F TransversalElement( j, stab, id )
##
TransversalElement := function( j, stab, id )
    local t;
    if Length( stab.trels ) = 0 then return id; fi;
    t := TransversalInverse(j, stab.trels);
    return SubsWord( t, stab.trans )^-1;
end;

#############################################################################
##
#F Translate( word, t )
##
Translate := function( word, t )
    return List( word, x -> [t[x[1]], -x[2]] );
end;

#############################################################################
##
#F PcpOrbitStabilizer( e, pcp, act, op )
##
## Warning: this function runs forever, if the orbit is infinite!
##
# FIXME: This function is documented and should be turned into a GlobalFunction
PcpOrbitStabilizer := function( e, pcp, act, op )
    local  rels, orbit, dict, trans, trels, tword, stab, word, w, i, f, j, n, t, s, k;

    # check relative orders
    if IsList( pcp ) then
        rels := List( pcp, x -> 0 );
    else
        rels := RelativeOrdersOfPcp( pcp );
    fi;

    # set up
    orbit := [e];
    dict := NewDictionary(e, true);
    AddDictionary(dict, e, 1);
    trans := [];
    trels := [];
    tword := [];
    stab  := [];
    word  := [];

    # construct orbit and stabilizer
    for i in Reversed( [1..Length(pcp)] ) do

        # get new point
        f := op( e, act[i] );
        j := LookupDictionary( dict, f );

        # if it is new, add all blocks
        n := orbit;
        t := [];
        s := 1;
        while IsBool( j ) do
            n := List( n, x -> op( x, act[i] ) );
            Append( t, n );
            j := LookupDictionary( dict, op( n[1], act[i] ) );
            s := s + 1;
        od;

        # add to orbit
        for k in [1..Length(t)] do
            AddDictionary( dict, t[k], Length(orbit) + k );
        od;
        Append( orbit, t );

        # add to transversal
        if s > 1 then
            Add( trans, pcp[i]^-1 );
            Add( trels, s );
            Add( tword, i );
        fi;

        # compute stabiliser element
        if rels[i] = 0 or s < rels[i] then
            if j = 1 then
                Add( stab, pcp[i]^s );
                Add( word, [[i,s]] );
            else
                t := TransversalInverse(j, trels);
                Add( stab, pcp[i]^s * SubsWord( t, trans ) );
                Add( word, Concatenation( [[i,s]], Translate( t, tword )));
            fi;
        fi;
    od;

    # return orbit and stabilizer
    return rec( orbit := orbit,
                trels := trels,
                trans := trans,
                stab  := Reversed(stab),
                word  := Reversed(word) );
end;

#############################################################################
##
#F  PcpOrbitsStabilizers( dom, pcp, act, op )
##
##  dom is the operation domain
##  pcp is a igs or pcp of a group
##  act is the action corresponding to pcp
##  op is the operation of act on dom
##
##  The function returns a list of records - one for each orbit. Each record
##  contains a representative and an igs of the stabilizer.
##
##  Warning: this function runs forever, if one of the orbits is infinite!
##
# FIXME: This function is documented and should be turned into a GlobalFunction
PcpOrbitsStabilizers := function( dom, pcp, act, op )
    local todo, orbs, e, o;
    todo := [1..Length(dom)];
    orbs := [];
    while Length( todo ) > 0 do
        e := dom[todo[1]];
        o := PcpOrbitStabilizer( e, pcp, act, op );
        Add( orbs, rec( repr := o.orbit[1],
                        leng := Length(o.orbit),
                        stab := o.stab,
                        word := o.word ) );
        todo := Difference( todo, List( o.orbit, x -> Position(dom,x)));
    od;
    return orbs;
end;

#############################################################################
##
#F RandomPcpOrbitStabilizer( e, pcp, act, op )
##
RandomPcpOrbitStabilizer := function( e, pcp, act, op )
    local  one, acts, gens, O, dict, T, S, count, i, j, t, g, im, index, l, s;

    # a trivial check
    if Length( pcp ) = 0 then return rec( orbit := [e], stab := pcp ); fi;

    # generators and inverses
    acts := Concatenation( AsList( act ), List( act, g -> g^-1 ) );
    gens := Concatenation( AsList( pcp ), List( pcp, g -> g^-1 ) );
    one  := gens[1]^0;

    # set up
    O := [ e ];            # orbit
    dict := NewDictionary(e, true);
    AddDictionary(dict, e, 1);
    T := [ one ];          # transversal
    S := [];               # stabilizer

    # set counter
    count := 0;

    i := 1;
    while i <= Length(O) do
        e := O[ i ];
        t := T[ i ];

        for j in [1..Length(gens)] do
            im := op( e, acts[j] );

            index := LookupDictionary( dict, im );
            if index = fail then
                Add( O, im );
                AddDictionary( dict, im, Length(O) );
                Add( T, t * gens[j] );

                if Length(O) > 500 then
                    Print( "#I  Orbit longer than limit: exiting.\n" );
                    return rec( orbit := O, stab := S );
                fi;
            else
                l := Length( S );
                s := t * gens[j] * T[ index ]^-1;
                if s <> one then
                    S := AddToIgs( S, [s] );
                    if l = Length(S) then
                        count := count + 1;
                    else
                        count := 0;
                    fi;
                    if count > 100 then
                        Print( "#I  Stabilizer not increasing: exiting.\n" );
                        return rec( orbit := O, stab := S );
                    fi;
                fi;
            fi;
        od;

        i := i+1;
    od;
    Print( "#I  Orbit calculation complete.\n" );
    return rec( orbit := O, stab := S );
end;

#############################################################################
##
#F RandomCentralizerPcpGroup( G, g )
##
# FIXME: This function is documented and should be turned into a GlobalFunction
RandomCentralizerPcpGroup := function( G, g )
    local gens, stab, h;
    gens := Igs( G );
    if IsPcpElement( g ) then
        stab := RandomPcpOrbitStabilizer( g, gens, gens, OnPoints ).stab;
    elif IsSubgroup( G, g ) then
        stab := ShallowCopy( gens );
        for h in GeneratorsOfGroup( g ) do
            stab := RandomPcpOrbitStabilizer( h, stab, stab, OnPoints ).stab;
        od;
    else
        Print("g must be a subgroup or an element of G \n");
    fi;
    return Subgroup( G, stab );
end;

#############################################################################
##
#F RandomNormalizerPcpGroup( G, N )
##
# FIXME: This function is documented and should be turned into a GlobalFunction
RandomNormalizerPcpGroup := function( G, N )
    local gens, stab;
    gens := Igs(G);
    stab := RandomPcpOrbitStabilizer( N, gens, gens, OnPoints);
    return Subgroup( G, stab.stab );
end;
polycyclic-2.16/gap/basic/collect.gd0000644000076600000240000001127413706672341016432 0ustar  mhornstaff#############################################################################
##
#W  collect.gd                 Polycyclic                       Werner Nickel
##

#############################################################################
##
##  First we need a new representation for a power-conjugate collector, which
##  will  implement the  generic collector  for  groups given by a polycyclic
##  presentation.
##
#R  IsFromTheLeftCollectorRep(  )
##
DeclareRepresentation( "IsFromTheLeftCollectorRep",
                        IsPowerConjugateCollector, [] );

BindGlobal( "FromTheLeftCollectorFamily",
    NewFamily( "FromTheLeftCollector", IsFromTheLeftCollectorRep ) );

#############################################################################
##
#P  The following property is set if a collector presents a nilpotent group
##  and has a weight array and a second commute array.  . . . . . . . . . . .
##
DeclareProperty( "IsWeightedCollector", IsPolycyclicCollector );

#############################################################################
##
#P  The following property is set if a collector presents a nilpotent group
##  and has Hall polynomials (computed by Deep Thought)
##
DeclareProperty( "IsPolynomialCollector", IsFromTheLeftCollectorRep );

#############################################################################
##
#P  The following property is used to dispatch between a GAP level collector
##  and the kernel collector.  By default the property is false.  Its main
##  use is for debugging purposes.
##
DeclareProperty( "UseLibraryCollector", IsFromTheLeftCollectorRep  );

#############################################################################
##
#V  The following variables are global flags mainly intended for debugging
##  purposes.
##
BindGlobal( "USE_LIBRARY_COLLECTOR", false );
BindGlobal( "DEBUG_COMBINATORIAL_COLLECTOR", false );
BindGlobal( "USE_COMBINATORIAL_COLLECTOR", false );

#############################################################################
##
##  Next the operation for creating a from-the-left collector is defined.
##
#O  FromTheLeftCollector. . . . . . . . . . . . . . . . . . . . . . . . . . .
##
DeclareOperation( "FromTheLeftCollector", [IsObject] );

#############################################################################
##
##  This is the inverse operation for ObjByExponents.
##
#O  ExponentsByObj
##
DeclareOperation( "ExponentsByObj", [IsPolycyclicCollector, IsObject] );

#############################################################################
##
##  These operations should be defined in the GAP library.
##
#O  GetPower
#O  GetConjugate
##
DeclareOperation( "GetPower", [IsPolycyclicCollector, IsObject] );
DeclareOperation( "GetConjugate",
        [IsPolycyclicCollector, IsObject, IsObject] );

#############################################################################
##
#I  InfoFromTheLeftCollector
#I  InfoCombinatorialFromTheLeftCollector
##
DeclareInfoClass( "InfoFromTheLeftCollector" );
DeclareInfoClass( "InfoCombinatorialFromTheLeftCollector" );

############################################################################
##
#F  NumberOfGenerators
#F  FromTheLeftCollector_SetCommute
#F  FromTheLeftCollector_CompletePowers
#F  FromTheLeftCollector_CompleteConjugate
##
DeclareGlobalFunction( "NumberOfGenerators" );
DeclareGlobalFunction( "FromTheLeftCollector_SetCommute" );
DeclareGlobalFunction( "FromTheLeftCollector_CompletePowers" );
DeclareGlobalFunction( "FromTheLeftCollector_CompleteConjugate" );

############################################################################
##
#F  IsPcpNormalFormObj( ,  )
##
DeclareGlobalFunction( "IsPcpNormalFormObj" );

############################################################################
##
#P  IsPolycyclicPresentation
##
## checks whether the input-presentation is a polycyclic presentation, i.e.
## whether the right-hand-sides of the relations are normal.
##
DeclareProperty( "IsPolycyclicPresentation", IsFromTheLeftCollectorRep );

#############################################################################
##
#H The following indices point into a from the left collector.  They are used
##  in addition to the ones  defined in the GAP source file src/objcftl.h/.c.
##  Eventually,  there  will be  one  place for  defining  the  indices of  a
##  from-the-left collector.
##
BindGlobal( "PC_PCP_ELEMENTS_FAMILY", 22 );
BindGlobal( "PC_PCP_ELEMENTS_TYPE",   23 );

BindGlobal( "PC_COMMUTATORS",               24 );
BindGlobal( "PC_INVERSECOMMUTATORS",        25 );
BindGlobal( "PC_COMMUTATORSINVERSE",        26 );
BindGlobal( "PC_INVERSECOMMUTATORSINVERSE", 27 );

BindGlobal( "PC_NILPOTENT_COMMUTE", 28 );
BindGlobal( "PC_WEIGHTS",           29 );
BindGlobal( "PC_ABELIAN_START",     30 );
polycyclic-2.16/gap/basic/pcpfact.gi0000644000076600000240000000465013706672341016432 0ustar  mhornstaff#############################################################################
##
#W  pcpfact.gi                   Polycyc                         Bettina Eick
##

#############################################################################
##
#M FactorGroupNC( H, N )
##
InstallMethod( FactorGroupNC, IsIdenticalObj, [IsPcpGroup, IsPcpGroup],
function( H, N )
    local  F;
    if not IsNormal( H, N ) then return fail; fi;
    if not IsSubgroup( H, N ) then H := ClosureGroup( H, N ); fi;

    F := PcpGroupByPcp( Pcp( H, N ) );
    UseFactorRelation( H, N, F );
    return F;
end );

#############################################################################
##
#F NaturalHomomorphismByPcp( pcp )
##
## compute factor and natural homomorphism.
## Setting up F and setting up the homomorphism are time-consuming.
## Speed up homomorphisms by `AddToIgsParallel'
##
InstallGlobalFunction( NaturalHomomorphismByPcp, function( pcp )
    local G, F, N, gens, imgs, hom;

    # G/N = F
    G := GroupOfPcp( pcp );
    N := SubgroupByIgs( G, DenominatorOfPcp( pcp ) );
    F := PcpGroupByPcp( pcp );
    UseFactorRelation( G, N, F );

    # get generators in G and images in F
    gens := ShallowCopy( GeneratorsOfPcp( pcp ) );
    imgs := ShallowCopy( Igs( F ) );
    Append( gens, DenominatorOfPcp( pcp ) );
    Append( imgs, List( DenominatorOfPcp( pcp ), x -> One(F) ) );

    # set up homomorphism
    hom := GroupHomomorphismByImagesNC( G, F, gens, imgs );
    SetKernelOfMultiplicativeGeneralMapping( hom, N );
    return hom;
end );

#############################################################################
##
#F NaturalHomomorphism( G, N )
##
# This exists only for backwards compatibility; we may remove it once all
# packages have switched to using NaturalHomomorphismByNormalSubgroup. Or at
# least change it to print a warning...
InstallMethod( NaturalHomomorphism,
        "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup],
function( G, N )
    if Size(N) = 1 then return IdentityMapping( G ); fi;
    return NaturalHomomorphismByPcp( Pcp( G, N ) );
end );

#############################################################################
##
#F NaturalHomomorphismByNormalSubgroupOp( G, N )
##
InstallMethod( NaturalHomomorphismByNormalSubgroupOp,
        "for pcp groups", IsIdenticalObj, [IsPcpGroup, IsPcpGroup],
function( G, N )
    if Size(N) = 1 then return IdentityMapping( G ); fi;
    return NaturalHomomorphismByPcp( Pcp( G, N ) );
end );
polycyclic-2.16/gap/basic/colftl.gi0000644000076600000240000001656213706672341016302 0ustar  mhornstaffCollectPolycyclicGap := function( pcp, ev, w )

    local   ngens,  pow,  exp,  com,  wst,  west,  sst,  est,  bottom,
            stp,  g,  word,  exponent,  i,  h,  m,  u,  j,  cnj,
            icnj,  hh;

    if Length( w ) = 0 then return true; fi;


    ngens := pcp![PC_NUMBER_OF_GENERATORS];

    pow := pcp![ PC_POWERS ];
    exp := pcp![ PC_EXPONENTS ];
    com := pcp![ PC_COMMUTE ];

    wst  := [ ];
    west := [ ];
    sst  := [ ];
    est  := [ ];

    bottom    := 0;
    stp       := bottom + 1;
    wst[stp]  := w;
    west[stp] := 1;
    sst[stp]  := 1;
    est[stp]  := w[ 2 ];

    # collect
    while stp > bottom do

        if est[stp] = 0 then
            # initialise est
            sst[stp] := sst[stp] + 1;
            if sst[stp] > Length(wst[stp])/2 then
                west[stp] := west[stp] - 1;
                if west[stp] <= 0 then
                    ## clear stacks before going down
                    wst[  stp ] := 0;
                    west[ stp ] := 0;
                    sst[  stp ] := 0;
                    est[  stp ] := 0;

                    stp := stp - 1;
                else
                    sst[stp] := 1;
                    est[stp] := wst[stp][2];
                fi;
            else
                est[stp] := wst[stp][ 2*sst[stp] ];
            fi;
        else

            # get next generator
            g := wst[stp][ 2*sst[stp]-1 ];

            if stp > 1 and sst[stp] = 1 and g = com[g] then
                ## collect word ^ exponent in one go

                word      := wst[stp];
                exponent  := west[stp];
                ##  Add the word into ev
                for i in [1,3..Length(word)-1] do
                    h     := word[ i ];
                    ev[h] := ev[h] + word[ i+1 ] * exponent;
                od;

                ##  Now reduce.
                for h in [word[1]..ngens] do
                    if IsBound( exp[h] ) and ev[h] >= exp[h] then
                        m     := QuoInt( ev[h], exp[h] );
                        ev[h] := ev[h] mod exp[h];

                        if IsBound( pow[h] ) then
                            u := pow[h];
                            for j in [1,3..Length(u)-1] do
                                ev[ u[j] ] := ev[ u[j] ] + u[j+1] * m;
                            od;
                        fi;
                    fi;
                od;

                west[ stp ] := 0;
                est[  stp ] := 0;
                sst[  stp ] := Length( word );

            elif g = com[g] then
                # move generator directly to its correct position
                ev[g] := ev[g] + est[stp];
                est[stp] := 0;
            else

                if est[stp] > 0 then
                    est[stp] := est[stp] - 1;
                    ev[g] := ev[g] + 1;
                    cnj   := pcp![ PC_CONJUGATES ];
                    icnj  := pcp![ PC_INVERSECONJUGATES ];
                else
                    est[stp] := est[stp] + 1;
                    ev[g] := ev[g] - 1;
                    cnj   := pcp![ PC_CONJUGATESINVERSE ];
                    icnj  := pcp![ PC_INVERSECONJUGATESINVERSE ];
                fi;

                h := com[g];

                # Find first position where we need to collect
                while h > g do
                    if ev[h] <> 0 then
                        if ev[h] > 0 then
                            if IsBound(  cnj[h][g] ) then break; fi;
                        else
                            if IsBound( icnj[h][g] ) then break; fi;
                        fi;
                    fi;
                    h := h-1;
                od;


                # Put that part on the stack, if necessary
                if h > g or
                   ( IsBound(exp[g])
                     and (ev[g] < 0 or ev[g] >= exp[g])
                     and IsBound(pow[g]) ) then

                    for hh in [com[g],com[g]-1..h+1] do
                        if ev[hh] <> 0 then
                            stp := stp+1;
                            if ev[hh] > 0 then
                                wst[stp]  := pcp![ PC_GENERATORS ][hh];
                                west[stp] := ev[hh];
                            else
                                wst[stp]  := pcp![ PC_INVERSES ][hh];
                                west[stp] := -ev[hh];
                            fi;
                            sst[stp] := 1;
                            est[stp] := wst[stp][ 2 ];
                            ev[hh] := 0;
                        fi;
                    od;
                fi;


                # move generator across the exponent vector
                while h > g do
                    if ev[h] <> 0 then
                        stp := stp+1;
                        if ev[h] > 0 then
                            if IsBound( cnj[h][g] ) then
                                wst[stp]  := cnj[h][g];
                                west[stp] := ev[h];
                            else
                                wst[stp]  := pcp![ PC_GENERATORS ][h];
                                west[stp] := ev[h];
                            fi;
                        else
                            if IsBound( icnj[h][g] ) then
                                wst[stp]  := icnj[h][g];
                                west[stp] := -ev[h];
                            else
                                wst[stp]  := pcp![ PC_INVERSES ][h];
                                west[stp] := -ev[h];
                            fi;
                        fi;
                        sst[stp] := 1;
                        est[stp] := wst[stp][ 2 ];
                        ev[h] := 0;
                    fi;
                    h := h-1;
                od;
            fi;

            # reduce exponent if necessary
            if IsBound( exp[g] ) and ev[g] >= exp[g] then
                ev[g] := ev[g] - exp[g];
                if IsBound( pow[g] ) then
                    stp := stp+1;
                    wst[stp]  := pow[g];
                    west[stp] := 1;
                    sst[stp]  := 1;
                    est[stp] := wst[stp][ 2 ];
                fi;
            fi;
        fi;
    od;
    return true;
end;

PrintCollectionStack := function( stp, wst, west, sst, est )

    while stp > 0 do
        Print( wst[stp], "^", west[stp],
               " at ", sst[stp], " with exponent ", est[stp], "\n" );
        stp := stp - 1;
    od;
end;

#############################################################################
##
#M  CollectWordOrFail . . . . . . . . . . . . . . . . . . . . . . . . . . . .
##
InstallMethod( CollectWordOrFail,
        "FromTheLeftCollector (outdated)",
        [ IsFromTheLeftCollectorRep,
          IsList, IsList ],
function( pcp, ev, w )
    Error( "Collector is out of date" );
end );

InstallMethod( CollectWordOrFail,
        "FromTheLeftCollector",
        [ IsFromTheLeftCollectorRep and IsUpToDatePolycyclicCollector,
          IsList, IsList ],
function( pcp, a, b )

    if USE_LIBRARY_COLLECTOR then
        return CollectPolycyclicGap( pcp, a, b );
    else
    	# CollectPolycyclic is implemented by the GAP C kernel, in file src/objcftl.c
        CollectPolycyclic( pcp, a, b );
        return true;
    fi;
end );

InstallMethod( CollectWordOrFail,
        "FromTheLeftCollector",
        [ IsFromTheLeftCollectorRep and IsUpToDatePolycyclicCollector and
          UseLibraryCollector,
          IsList, IsList ],
        CollectPolycyclicGap );
polycyclic-2.16/gap/basic/pcpelms.gi0000644000076600000240000004015013706672341016450 0ustar  mhornstaff#############################################################################
##
#W  pcpelms.gi                   Polycyc                         Bettina Eick
##

InstallGlobalFunction( PcpElementConstruction,
function( coll, list, word )
    local   elm;
    elm := rec( collector := coll,
                exponents := Immutable(list),
                word      := Immutable(word),
                name      := "g" );

    # objectify and return
    return Objectify( coll![PC_PCP_ELEMENTS_TYPE], elm );
end );

#############################################################################
##
## Functions to create pcp elements by exponent vectors or words.
## In the NC versions we assume that elements are in normal form.
## In the other versions we collect before we return an element.
##
InstallGlobalFunction( PcpElementByExponentsNC,
function( coll, list )
    local   i,  word;
    word := ObjByExponents( coll, list );
    return PcpElementConstruction( coll, list, word );
end );

InstallGlobalFunction( PcpElementByExponents, function( coll, list )
    local h, k;

    if Length(list) > NumberOfGenerators(coll) then
        Error( "more exponents than generators" );
    fi;

    h := ObjByExponents( coll, list );
    k := list * 0;
    while CollectWordOrFail( coll, k, h ) = fail do od;
    return PcpElementByExponentsNC( coll, k );
end );

InstallGlobalFunction( PcpElementByGenExpListNC,
function( coll, word )
    local   list,  i;
    list := ExponentsByObj( coll, word );
    word := ObjByExponents( coll, list );
    return PcpElementConstruction( coll, list, word );
end );

InstallGlobalFunction( PcpElementByGenExpList, function( coll, word )
    local k;
    k := [1..coll![PC_NUMBER_OF_GENERATORS]] * 0;
    while CollectWordOrFail( coll, k, word ) = fail do od;
    return PcpElementByExponentsNC( coll, k );
end );

#############################################################################
##
#A Basic attributes of pcp elements - for IsPcpElementRep
##
InstallMethod( Collector,
        "for pcp groups",
        [ IsPcpGroup ],
        G -> Collector( One(G) ) );


InstallMethod( Collector,
        "for pcp elements",
        [ IsPcpElementRep ],
        g -> g!.collector );

InstallMethod( Exponents,
        "for pcp elements",
        [ IsPcpElementRep ],
        g -> g!.exponents );

InstallMethod( NameTag,
        "for pcp elements",
        [ IsPcpElementRep ],
        g -> g!.name );

InstallMethod( GenExpList,
        "for pcp elements",
        [ IsPcpElementRep ],
        g -> g!.word );

InstallMethod( Depth,
        "for pcp elements",
        [ IsPcpElementRep ],
function( elm )
    if Length(elm!.word) = 0 then
        return elm!.collector![PC_NUMBER_OF_GENERATORS] + 1;
    else
        return elm!.word[1];
    fi;
end );

InstallMethod( TailOfElm,
        "for pcp elements",
        [ IsPcpElement and IsPcpElementRep ],
function( elm )
    if Length( elm!.word ) = 0 then
        return 0;
    else
        return elm!.word[ Length(elm!.word) - 1 ];
    fi;
end );

InstallMethod( LeadingExponent,
        "for pcp elements",
        [ IsPcpElementRep ],
function( elm )
    if Length(elm!.word) = 0 then
        return fail;
    else
        return elm!.word[2];
    fi;
end );

##  Note, that inverses of generators with relative order > 0 are not treated
##  as inverses as they should never appear here with a negative exponent.
IsGeneratorOrInverse := function( elm )
    return Length(elm!.word) = 2 and
           (elm!.word[2] = 1 or elm!.word[2] = -1);
end;

##
## Is elm the power of a generator modulo depth d?
## If so, then return the power, otherwise return fail;
##
IsPowerOfGenerator := function( elm, d )
    if Length( elm!.word ) = 0 or
       (Length( elm!.word ) > 2 and elm!.word[3] <= d) then
        return fail;
    fi;
    return elm!.word[2];
end;

#############################################################################
##
#F FactorOrder( g )
##
InstallMethod( FactorOrder, [IsPcpElement],
function( g )
    if Length( g!.word ) = 0 then return fail; fi;
    return RelativeOrders( Collector(g) )[Depth(g)];
end );

#############################################################################
##
#F RelativeOrderPcp( g )
##
InstallMethod( RelativeOrderPcp, [IsPcpElement],
function( g )
    local r, l;
    if Length( g!.word ) = 0 then return fail; fi;
    r := FactorOrder( g );

    # the infinite case
    if r = 0 then return 0; fi;

    # the finite case
    l := LeadingExponent( g );
    if l = 1 then
       return r;
    elif IsBound( g!.normed ) and g!.normed then
        return r / LeadingExponent(g);
    elif IsPrime( r ) then
        return r;
    else
        return r / Gcd( r, l );
    fi;
end );
# TODO: Replace this by something like DeclareSynonymAttr.
# However, we cannot use DeclareSynonymAttr directly, because for
# collectors there is already an operation SetRelativeOrder.
RelativeOrder := function( g ) return RelativeOrderPcp(g); end;

#############################################################################
##
#F RelativeIndex( g )
##
InstallMethod( RelativeIndex, [IsPcpElement],
function( g )
    local r, l;
    if Length( g!.word ) = 0 then return fail; fi;
    r := FactorOrder( g );
    l := LeadingExponent( g );

    if IsBound( g!.normed ) and g!.normed then
        return l;
    elif r > 0 then
        return Gcd( r, l );
    else
        return AbsInt( l );
    fi;
end );

#############################################################################
##
#F Order( g )
##
InstallMethod( Order, [IsPcpElement],
function( g )
    local o, r;
    o := 1;
    while g <> g^0 do
        r := RelativeOrderPcp( g );
        if r = 0 then return infinity; fi;
        o := o*r;
        g := g^r;
    od;
    return o;
end );

#############################################################################
##
#F NormingExponent( g ) . . . . . . . . .returns f such that g^f is normed
##
## Note that g is normed, if the LeadingExponent of g is its RelativeIndex.
##
# FIXME: This function is documented and should be turned into a GlobalFunction
NormingExponent := function( g )
    local r, l, e;
    r := FactorOrder( g );
    l := LeadingExponent( g );
    if IsBool( l ) then
        return 1;
    elif r = 0 and l < 0 then
        return -1;
    elif r = 0 then
        return 1;
    elif IsPrime( r ) then
        return l^-1 mod r;
    else
        e := Gcdex( r, l );     # = RelativeIndex
        return e.coeff2 mod r;  # l * c2 = e mod r
    fi;
end;

#############################################################################
##
#F NormedPcpElement( g )
##
# FIXME: This function is documented and should be turned into a GlobalFunction
NormedPcpElement := function( g )
    local h;
    h := g^NormingExponent( g );
    h!.normed := true;
    return h;
end;

#############################################################################
##
#M Print pcp elements
##
InstallMethod( PrintObj,
               "for pcp elements",
               [IsPcpElement],
function( elm )
    local g, l, e, d;
    g := NameTag( elm );
    e := Exponents( elm );
    d := Depth( elm );
    if d > Length( e ) then
        Print("id");
    elif e[d] = 1 then
        Print(Concatenation(g,String(d)));
    else
        Print(Concatenation(g,String(d)),"^",e[d]);
    fi;
    for l in [d+1..Length(e)] do
        if e[l] = 1 then
            Print("*",Concatenation(g,String(l)));
        elif e[l] <> 0 then
            Print("*",Concatenation(g,String(l)),"^",e[l]);
        fi;
    od;
end );

InstallMethod( String,
               "for pcp elements",
               [IsPcpElement],
function( elm )
    local g, l, e, d, str;
    g := NameTag( elm );
    e := Exponents( elm );
    d := Depth( elm );
    if d > Length( e ) then
        return "id";
    fi;
	str := Concatenation(g,String(d));
    if e[d] <> 1 then
        Append(str, Concatenation("^",String(e[d])));
    fi;
    for l in [d+1..Length(e)] do
    	if e[l] = 0 then continue; fi;
        Append(str, Concatenation("*",g,String(l)));
		if e[l] <> 1 then
			Append(str, Concatenation("^",String(e[l])));
		fi;
    od;
    return str;
end );

#############################################################################
##
#M g * h
##
InstallMethod( \*,
               "for pcp elements",
               IsIdenticalObj,
               [IsPcpElement, IsPcpElement],
               20,
function( g1, g2 )
    local clt, e, f;

    clt := Collector( g1 );
    if TailOfElm( g1 ) < Depth( g2 ) then
        e := Exponents( g1 ) + Exponents( g2 );

    else
        e  := ShallowCopy( Exponents( g1 ) );
        f  := GenExpList( g2 );
        while CollectWordOrFail( clt, e, f ) = fail do
            e  := ShallowCopy( Exponents( g1 ) );
        od;
    fi;

    return PcpElementByExponentsNC( clt, e );
end );

#############################################################################
##
#M Inverse
##
InstallMethod( Inverse,
               "for pcp elements",
               [IsPcpElement],
function( g )
    local   clt,  k;

    clt := Collector( g );
    if IsGeneratorOrInverse( g ) and RelativeOrderPcp(g) = 0 then
        if LeadingExponent( g ) = 1 then
            k := clt![PC_INVERSES][ Depth(g) ];
        else
            k := clt![PC_GENERATORS][ Depth(g) ];
        fi;

    else

        k := FromTheLeftCollector_Inverse( clt, GenExpList(g) );
    fi;

    return PcpElementByGenExpListNC( clt, k );
end );

InstallMethod( INV,
               "for pcp elements",
               [IsPcpElement],
function( g )
    local   clt,  k;

    clt := Collector( g );
    if IsGeneratorOrInverse( g ) and RelativeOrderPcp(g) = 0 then
        if LeadingExponent( g ) = 1 then
            k := clt![PC_INVERSES][ Depth(g) ];
        else
            k := clt![PC_GENERATORS][ Depth(g) ];
        fi;

    else

        k := FromTheLeftCollector_Inverse( clt, GenExpList(g) );
    fi;

    return PcpElementByGenExpListNC( clt, k );
end );

#############################################################################
##
#M \^
##
InstallMethod( \^,
               "for a pcp element and an integer",
               [IsPcpElement, IsInt],
               SUM_FLAGS + 10,
function( g, d )
    local   res;

    # first catch the trivial cases
    if d = 0 then
        return PcpElementByExponentsNC( Collector(g), 0*Exponents(g) );
    elif d = 1 then
        return g;
    elif d = -1 then
        return Inverse(g);
    fi;

#    # use collector function
#    c := Collector(g);
#    k := FromTheLeftCollector_Power(c, ObjByExponents(c, Exponents(g)), d);
#    return PcpElementByGenExpListNC( c, k );

    # set up for computation
    if d < 0 then
        g := Inverse(g);
        d := -d;
    fi;

    # compute power
    res := g^0;
    while d > 0 do
        if d mod 2 = 1 then   res := res * g;   fi;

        d := QuoInt( d, 2 );
        if d <> 0 then   g := g * g;    fi;
    od;

    return res;
end );

InstallMethod( \^,
               "for two pcp elements",
               IsIdenticalObj,
               [IsPcpElement, IsPcpElement],
function( h, g )
    local   clt,  conj;

    clt := Collector( g );
    if IsGeneratorOrInverse( h ) and IsGeneratorOrInverse( g ) then

        if Depth( g ) = Depth( h ) then

            conj := h;

        elif Depth( g ) < Depth( h ) then

            conj := GetConjugateNC( clt,
                            Depth( h ) * LeadingExponent( h ),
                            Depth( g ) * LeadingExponent( g ) );

            conj := PcpElementByGenExpListNC( clt, conj );

        elif Depth( g ) > Depth( h ) then
            #  h^g = g^-1 * h * g

            conj := ShallowCopy( Exponents( g^-1 ) );
            while CollectWordOrFail( clt, conj,
                    [ Depth(h), LeadingExponent( h ),
                      Depth(g), LeadingExponent( g ) ] ) = fail do

                conj := ShallowCopy( Exponents( g^-1 ) );
            od;

            conj := PcpElementByExponentsNC( clt, conj );
        fi;

    elif Depth(g) = TailOfElm(g) and Depth( g ) < Depth( h ) then

        ##
        ## nicht klar ob dies etwas bringt
        ##
        g := [ Depth(g), LeadingExponent(g) ];
        conj := ShallowCopy( Exponents( h ) );
        while CollectWordOrFail( clt, conj, g ) = fail do
            conj := ShallowCopy( Exponents( h ) );
        od;

        conj[ g[1] ] := 0;
        conj := PcpElementByExponentsNC( clt, conj );

    else
        conj := g^-1 * h * g;
    fi;

    return conj;

end );


InstallMethod( GetCommutatorNC,
        "for from the left collector",
        [ IsFromTheLeftCollectorRep, IsInt, IsInt ],
function( coll, h, g )

    if g > 0 then
        if h > 0 then
            if IsBound( coll![PC_COMMUTATORS][h] ) and
               IsBound( coll![PC_COMMUTATORS][h][g] ) then
                return coll![PC_COMMUTATORS][h][g];
            else
                return fail;
            fi;
        else
            h := -h;
            if IsBound( coll![PC_INVERSECOMMUTATORS][h] ) and
               IsBound( coll![PC_INVERSECOMMUTATORS][h][g] ) then
                return coll![PC_INVERSECOMMUTATORS][h][g];
            else
                return fail;
            fi;
        fi;
    else
        g := -g;
        if h > 0 then
            if IsBound( coll![PC_COMMUTATORSINVERSE][h] ) and
               IsBound( coll![PC_COMMUTATORSINVERSE][h][g] ) then
                return coll![PC_COMMUTATORSINVERSE][h][g];
            else
                return fail;
            fi;
        else
            h := -h;
            if IsBound( coll![PC_INVERSECOMMUTATORSINVERSE][h] ) and
               IsBound( coll![PC_INVERSECOMMUTATORSINVERSE][h][g] ) then
                return coll![PC_INVERSECOMMUTATORSINVERSE][h][g];
            else
                return fail;
            fi;
        fi;

    fi;
end );

#############################################################################
##
#M Comm
##
InstallMethod( Comm,
               "for two pcp elements",
               [ IsPcpElement, IsPcpElement ],
function( h, g )
    local   clt,  conj,  ev;

    clt := Collector( g );

    if IsGeneratorOrInverse( h ) and IsGeneratorOrInverse( g ) then

        if Depth( g ) = Depth( h ) then return g^0; fi;


        if Depth( g ) < Depth( h ) then

            ##  Do we know the commutator?

            conj := GetCommutatorNC( clt, Depth( h ) * LeadingExponent( h ),
                                          Depth( g ) * LeadingExponent( g ) );
            if conj  <> fail then
                return conj;
            fi;

            ##  [h,g] = h^-1 h^g

            conj := GetConjugateNC( clt, Depth( h ) * LeadingExponent( h ),
                                         Depth( g ) * LeadingExponent( g ) );

            ev := ShallowCopy( Exponents( h^-1 ) );
            while CollectWordOrFail( clt, ev, conj ) = fail do
                ev := ShallowCopy( Exponents( h^-1 ) );
            od;

            return PcpElementByExponentsNC( clt, ev );
        fi;

        if Depth( g ) > Depth( h ) and RelativeOrderPcp( g ) = 0 then
            ##  [h,g] = (g^-1)^h * g

            conj := GetConjugateNC( clt, Depth( g ) *  -LeadingExponent( g ),
                                         Depth( h ) *   LeadingExponent( h ) );

            ev := ExponentsByObj( clt, conj );
            while CollectWordOrFail( clt, ev, GenExpList(g) ) = fail do
                ev := ExponentsByObj( clt, conj );
            od;

            return PcpElementByExponentsNC( clt, ev );
        fi;

    fi;

    return PcpElementByGenExpListNC( clt,
                   FromTheLeftCollector_Solution( clt,
                   GenExpList(g*h),GenExpList(h*g) ) );

end );



#############################################################################
##
#M One
##
InstallMethod( One, "for pcp elements", [IsPcpElement],
g -> PcpElementByExponentsNC( Collector(g), 0*Exponents(g) ) );

#############################################################################
##
#M \=
##
InstallMethod( \=,
               "for pcp elements",
               IsIdenticalObj,
               [IsPcpElement, IsPcpElement],
function( g, h )
    return Exponents( g ) = Exponents( h );
end );

#############################################################################
##
#M \<
##
InstallMethod( \<,
               "for pcp elements",
               IsIdenticalObj,
               [IsPcpElement, IsPcpElement],
function( g, h )
    return Exponents( g ) > Exponents( h );
end );

polycyclic-2.16/gap/basic/colrec.gi0000644000076600000240000000524413706672341016261 0ustar  mhornstaff#############################################################################
##
#F  FromTheLeftCollector_Power  . . . . . . . . . . . . . . . . . . . . . .
##
#BindGlobal( "FromTheLeftCollector_Power", function( coll, w, e )
#
#    if e < 0 then
#        w := FromTheLeftCollector_Inverse( coll, w );
#        e := -e;
#    fi;
#
#    return BinaryPower( coll, w, e );
#end );
#
#############################################################################
##
#F  ProductAutomorphisms  . . . . . . . . . . . . . . . . . . . . . . . . .
##
#BindGlobal( "ProductAutomorphisms", function( coll, alpha, beta )
#    local   ngens,  gamma,  i,  w,  ev,  g;
#    ngens := NumberGeneratorsOfRws( coll );
#    gamma := [];
#    for i in [1..ngens] do
#        if IsBound( alpha[i] ) then
#            w := alpha[i];
#            ev := ListWithIdenticalEntries( ngens, 0 );
#            for g in [1,3..Length(w)-1] do
#                if w[g+1] <> 0 then
#                    CollectWordOrFail( coll, ev,
#                            FromTheLeftCollector_Power(
#                                    coll, beta[ w[g] ], w[g+1] ) );
#                fi;
#            od;
#            gamma[i] := ObjByExponents( coll, ev );
#        fi;
#    od;
#    return gamma;
#end );

#############################################################################
##
#F  PowerAutomorphism . . . . . . . . . . . . . . . . . . . . . . . . . . .
##
#BindGlobal( "PowerAutomorphism", function( coll, g, e )
#    local   n,  a,  power,  h,  ipower;
#
#    n := NumberGeneratorsOfRws( coll );
#
#    # initialise automorphism
#    a := [];
#    power := [];
#    for h in [g+1..n] do
#        if e > 0 then
#            if IsBound( coll![ PC_CONJUGATES][h] ) and
#                       IsBound( coll![ PC_CONJUGATES ][h][g] ) then
#                a[h] := coll![ PC_CONJUGATES ][h][g];
#            else
#                a[h] := [h,1];
#            fi;
#        else
#            if IsBound( coll![ PC_CONJUGATESINVERSE ][h] ) and
#                       IsBound( coll![ PC_CONJUGATESINVERSE ][h][g] ) then
#                a[h] := coll![ PC_CONJUGATESINVERSE ][h][g];
#            else
#                a[h] := [h,1];
#            fi;
#        fi;
#        power[h] := [h,1];
#    od;
#    if e < 0 then
#        e := -e;
#    fi;
#
#    while e > 0 do
#        if e mod 2 = 1 then
#            power := ProductAutomorphisms( coll, power, a );
#        fi;
#        e := Int( e / 2 );
#        if e > 0 then
#            a := ProductAutomorphisms( coll, a, a );
#        fi;
#    od;
#    ipower := [];
#    for h in [g+1..n] do
#        ipower[h] := FromTheLeftCollector_Inverse( coll, power[h] );
#    od;
#
#    return [ power, ipower ];
#end );
#
polycyclic-2.16/gap/basic/grphoms.gi0000644000076600000240000002761313706672341016475 0ustar  mhornstaff#############################################################################
##
#W  grphoms.gi                   Polycyc                         Bettina Eick
##

#############################################################################
##
## Functions to deal with homomorphisms to and from pcp groups.
##
## This function is modified version of GAP's DoGGMBINC

BindGlobal( "GroupGeneralMappingByImages_for_pcp", function( G, H, gens, imgs )
  local mapi, filter, type, hom, pcgs, p, l, obj_args;

  hom  := rec( );
  if Length(gens)<>Length(imgs) then
    Error(" and  must be lists of same length");
  fi;

#   if not HasIsHandledByNiceMonomorphism(G) and ValueOption("noassert")<>true then
#     Assert( 2, ForAll( gens, x -> x in G ) );
#   fi;
#   if not HasIsHandledByNiceMonomorphism(H) and ValueOption("noassert")<>true then
#     Assert( 2, ForAll( imgs, x -> x in H ) );
#   fi;

  mapi := [Immutable(gens), Immutable(imgs)];

  filter := IsGroupGeneralMappingByImages and HasSource and HasRange
            and HasMappingGeneratorsImages;

  if IsPcpGroup(G) then
    hom!.igs_gens_to_imgs := IgsParallel( gens, imgs );
    filter := filter and IsFromPcpGHBI;
  elif IsPcGroup( G ) and IsPrimeOrdersPcgs(Pcgs(G))  then
    filter := filter and IsPcGroupGeneralMappingByImages;
    pcgs  := CanonicalPcgsByGeneratorsWithImages( Pcgs(G), mapi[1], mapi[2] );
    hom.sourcePcgs       := pcgs[1];
    hom.sourcePcgsImages := pcgs[2];
    if pcgs[1]=Pcgs(G) then
      filter := filter and IsTotal;
    fi;
  elif IsPcgs( gens )  then
    filter := filter and IsGroupGeneralMappingByPcgs;
    hom.sourcePcgs       := mapi[1];
    hom.sourcePcgsImages := mapi[2];

  # Do we map a subgroup of a free group or an fp group by a subset of its
  # standard generators?
  # (So we can used MappedWord for mapping)?
  elif IsSubgroupFpGroup(G) then
    if HasIsWholeFamily(G) and IsWholeFamily(G)
      # total on free generators
      and Set(FreeGeneratorsOfFpGroup(G))=Set(List(gens,UnderlyingElement))
      then
        l:=List(gens,UnderlyingElement);
        p:=List(l,i->Position(FreeGeneratorsOfFpGroup(G),i));
        # test for duplicate generators, same images
        if Length(gens)=Length(FreeGeneratorsOfFpGroup(G)) or
          ForAll([1..Length(gens)],x->imgs[x]=imgs[Position(l,l[x])]) then
          filter := filter and IsFromFpGroupStdGensGeneralMappingByImages;
          hom.genpositions:=p;
        else
          filter := filter and IsFromFpGroupGeneralMappingByImages;
        fi;
    else
      filter := filter and IsFromFpGroupGeneralMappingByImages;
    fi;
  elif IsPermGroup(G) then
      filter := filter and IsPermGroupGeneralMappingByImages;
  fi;

  if IsPermGroup(H) then
    filter := filter and IsToPermGroupGeneralMappingByImages;
  elif IsPcGroup(H) then
    filter := filter and IsToPcGroupGeneralMappingByImages;
  elif IsSubgroupFpGroup(H) then
    filter := filter and IsToFpGroupGeneralMappingByImages;
  elif IsPcpGroup(H) then
    hom!.igs_imgs_to_gens := IgsParallel( imgs, gens );
    filter := filter and IsToPcpGHBI;
  fi;

  obj_args := [
    hom,
    , # Here the type will be inserted
    Source, G,
    Range, H,
    MappingGeneratorsImages, mapi ];

  if HasGeneratorsOfGroup(G)
     and IsIdenticalObj(GeneratorsOfGroup(G),mapi[1]) then
    Append(obj_args, [PreImagesRange, G]);
    filter := filter and IsTotal and HasPreImagesRange;
  fi;

  if HasGeneratorsOfGroup(H)
     and IsIdenticalObj(GeneratorsOfGroup(H),mapi[2]) then
    Append(obj_args, [ImagesSource, H]);
    filter := filter and IsSurjective and HasImagesSource;
  fi;

  obj_args[2] :=
    NewType( GeneralMappingsFamily( ElementsFamily( FamilyObj( G ) ),
                                    ElementsFamily( FamilyObj( H ) ) ),
             filter );

  CallFuncList(ObjectifyWithAttributes, obj_args);

  return hom;
end );

#############################################################################
##
#M GGMBI( G, H ) . . . . . . . . . . . . . . . . . . . for G and H pcp groups
##
InstallMethod( GroupGeneralMappingByImagesNC,
               "for pcp group, pcp group, list, list",
               [IsPcpGroup, IsPcpGroup, IsList, IsList],
               GroupGeneralMappingByImages_for_pcp );

#############################################################################
##
#M GGMBI( G, H ) . . . . . . . . . . . . . . . . . . . . . .  for G pcp group
##
InstallMethod( GroupGeneralMappingByImagesNC,
               "for pcp group, group, list, list",
               [IsPcpGroup, IsGroup, IsList, IsList],
               GroupGeneralMappingByImages_for_pcp );

#############################################################################
##
#M GGMBI( G, H ) . . . . . . . . . . . . . . . . . . . . . .  for H pcp group
##
InstallMethod( GroupGeneralMappingByImagesNC,
               "for group, pcp group, list, list",
               [IsGroup, IsPcpGroup, IsList, IsList],
               GroupGeneralMappingByImages_for_pcp );

#############################################################################
##
#M  IsSingleValued(  )
##
##  This method is very similar to our CoKernelOfMultiplicativeGeneralMapping
##  method. However, a crucial difference is the call to 'NormalClosure'
##  at the end of CoKernelOfMultiplicativeGeneralMapping, which won't
##  terminate if the range is e.g. an infinite matrix group.
InstallMethod( IsSingleValued,
    "for IsFromPcpGHBI",
    [ IsFromPcpGHBI ],
function( hom )
	local gens, imgs, i, j, a, b, mapi;

	if IsTrivial(Range(hom)) then
		return true;
	fi;

    gens := hom!.igs_gens_to_imgs[1];
    imgs := hom!.igs_gens_to_imgs[2];

    # check relators
    for i in [1..Length( gens )] do
        if RelativeOrderPcp( gens[i] ) > 0 then
            a := gens[i]^RelativeOrderPcp( gens[i] );
            a := MappedVector(ExponentsByIgs(gens, a), imgs);
            b := imgs[i]^RelativeOrderPcp( gens[i] );
            if a <> b then return false; fi;
        fi;
        for j in [1..i-1] do
            a := gens[i] ^ gens[j];
            a := MappedVector(ExponentsByIgs(gens, a), imgs);
            b := imgs[i] ^ imgs[j];
            if a <> b then return false; fi;

            if RelativeOrderPcp( gens[i] ) = 0 then
                a := gens[i] ^ (gens[j]^-1);
                a := MappedVector(ExponentsByIgs(gens, a), imgs);
                b := imgs[i] ^ (imgs[j]^-1);
                if a <> b then return false; fi;
            fi;
        od;
    od;

	# we still need to test any additional generators. This matters
	# for generalized mappings which are not total or not single valued,
	# such as the "inverse" of a non-surjective / non-injective group
	# homomorphism.
	mapi := MappingGeneratorsImages( hom );
	for i in [1..Length(mapi[1])] do
		a := mapi[1][i];
		a := MappedVector(ExponentsByIgs(gens, a), imgs);
		b := mapi[2][i];
        if a <> b then return false; fi;
	od;

	return true;
end );


#############################################################################
##
#M  CoKernelOfMultiplicativeGeneralMapping
##
InstallMethod( CoKernelOfMultiplicativeGeneralMapping,
               "for IsFromPcpGHBI",
               [ IsFromPcpGHBI ],
function( hom )
	local C, gens, imgs, i, j, a, b, mapi;

	if IsTrivial(Range(hom)) then
		return Range(hom);
	fi;

    gens := hom!.igs_gens_to_imgs[1];
    imgs := hom!.igs_gens_to_imgs[2];

    C := TrivialSubgroup(Range(hom)); # the cokernel

    # check relators
    for i in [1..Length( gens )] do
        if RelativeOrderPcp( gens[i] ) > 0 then
            a := gens[i]^RelativeOrderPcp( gens[i] );
            a := MappedVector(ExponentsByIgs(gens, a), imgs);
            b := imgs[i]^RelativeOrderPcp( gens[i] );
			C := ClosureSubgroupNC(C, a/b);
        fi;
        for j in [1..i-1] do
            a := gens[i] ^ gens[j];
            a := MappedVector(ExponentsByIgs(gens, a), imgs);
            b := imgs[i] ^ imgs[j];
			C := ClosureSubgroupNC(C, a/b);

            if RelativeOrderPcp( gens[i] ) = 0 then
                a := gens[i] ^ (gens[j]^-1);
                a := MappedVector(ExponentsByIgs(gens, a), imgs);
                b := imgs[i] ^ (imgs[j]^-1);
				C := ClosureSubgroupNC(C, a/b);
            fi;
        od;
    od;

	# we still need to test any additional generators. This matters
	# for generalized mappings which are not total or not single valued,
	# such as the "inverse" of a non-surjective / non-injective group
	# homomorphism.
	mapi := MappingGeneratorsImages( hom );
	for i in [1..Length(mapi[1])] do
		a := mapi[1][i];
		a := MappedVector(ExponentsByIgs(gens, a), imgs);
		b := mapi[2][i];
		C := ClosureSubgroupNC(C, a/b);
	od;

	C := NormalClosure(ImagesSource(hom),C);
	return C;
end );


#############################################################################
##
#M  Images
##
InstallMethod( ImagesRepresentative,
               "for FromPcpGHBI",
               FamSourceEqFamElm,
               [ IsFromPcpGHBI, IsPcpElement ],
function( hom, elm )
    local e;
    if Length(hom!.igs_gens_to_imgs[1]) = 0 then return One(Range(hom)); fi;
    e := ExponentsByIgs( hom!.igs_gens_to_imgs[1], elm );
    if e = fail then return fail; fi;
    return MappedVector( e, hom!.igs_gens_to_imgs[2] );
end );

# TODO: Also implement ImagesSet methods, like we have PreImagesSet methods ?
# Any particular reason for / against each?

#############################################################################
##
#M  PreImages
##
InstallMethod( PreImagesRepresentative,
               "for ToPcpGHBI",
               FamRangeEqFamElm,
               [ IsToPcpGHBI, IsPcpElement ],
function( hom, elm )
    local e;
    e := ExponentsByIgs(hom!.igs_imgs_to_gens[1], elm);
    if e = fail then return fail; fi;
    if Length(e) = 0 then return One(hom!.Source); fi;
    return MappedVector(e, hom!.igs_imgs_to_gens[2]);
end );

InstallMethod( PreImagesSet,
               "for PcpGHBI",
               CollFamRangeEqFamElms,
               [ IsFromPcpGHBI and IsToPcpGHBI, IsPcpGroup ],
function( hom, U )
    local prei, kern;
    prei := List( Igs(U), x -> PreImagesRepresentative(hom,x) );
    if fail in prei then
    	TryNextMethod();
		# Potential solution: Intersect U with ImagesSource(hom)
		# and then compute the preimage of that.
		#gens := GeneratorsOfGroup( Intersection( ImagesSource(hom), U ) );
        #prei := List( gens, x -> PreImagesRepresentative(hom,x) );
    fi;
    kern := Igs( KernelOfMultiplicativeGeneralMapping( hom ) );
    return SubgroupByIgs( Source(hom), kern, prei );
end );

#############################################################################
##
#M  KernelOfMultiplicativeGeneralMapping
##
InstallMethod( KernelOfMultiplicativeGeneralMapping,
               "for PcpGHBI",
               [ IsFromPcpGHBI and IsToPcpGHBI],
function( hom )
    local A, a, B, b, D, u, kern, i, g;

    # set up
    A := Source(hom);
    a := MappingGeneratorsImages(hom)[1];
    B := Range(hom);
    b := MappingGeneratorsImages(hom)[2];
    D := DirectProduct(B,A);
    u := Cgs(Subgroup(D, List([1..Length(a)], x ->
          Image(Embedding(D,1),b[x])*Image(Embedding(D,2),a[x]))));

    # filter kernel gens
    kern := [];
    for i in [1..Length(u)] do
        g := Image(Projection(D,1),u[i]);
        if g = One(B) then
            Add(kern, Image(Projection(D,2),u[i]));
        fi;
    od;

    # create group
    return Subgroup( Source(hom), kern);
end );

# TODO: Add KernelOfMultiplicativeGeneralMapping method for IsToPcpGHBI
# Slower than the one above but more general.

#############################################################################
##
#M  IsInjective(  )
##
InstallMethod( IsInjective,
               "for PcpGHBI",
               [ IsFromPcpGHBI and IsToPcpGHBI],
function( hom )
    return Size( KernelOfMultiplicativeGeneralMapping(hom) ) = 1;
end );

#############################################################################
##
#M  KnowsHowToDecompose( ,  )
##
InstallMethod( KnowsHowToDecompose,
               "pcp group and generators: always true",
               IsIdenticalObj,
               [ IsPcpGroup, IsList ], 0,
               ReturnTrue);
polycyclic-2.16/gap/basic/colcom.gi0000644000076600000240000004730113706672341016266 0ustar  mhornstaff
##  The elements of combinatorial collection from the left are:
##
##      the exponent vector:     contains the result of the collection process
##
##      the word stack:          stacks words which need to be collected into
##                               the exponent vector
##      the word exponent stack: stacks the exponents corresponding to each
##                               word on the word stack
##      the syllable stack:      stacks indices into the words on the word
##                               stack.  This is necessary because words may
##                               have to be collected only partially before
##                               other words are put onto the word stack.
##      the exponent stack:      stacks exponents of the generator to which
##                               the corresponding entry on the syllable
##                               stack points.  This is needed because a
##                               power of generator in a word may have to be
##                               collected partially before new words are put
##                               on the stack.
##
##     the two commute arrays:
##
##     the 4 conjugation arrays:
##     the exponent array:
##     the power array:
##
##  For this collector we need normed right hand sides in the presentation.


# Collect various statistics about the combinatorial collection process
# for debugging purposes.
CombCollStats := rec(
    Counter         := 0,
    CompleteCommGen := 0,
    WholeCommWord   := 0,
    CommRestWord    := 0,
    CommGen         := 0,
    CombColl        := 0,
    CombCollStack   := 0,
    OrdColl         := 0,
    StepByStep      := 0,
    ThreeWtGen      := 0,
    ThreeWtGenStack := 0,

    Count_Length := 0,
    Count_Weight := 0,
);


DisplayCombCollStats := function()

    Print( "Calls to combinatorial collector: ", CombCollStats.Counter,         "\n" );
    Print( "Completely collected generators:  ", CombCollStats.CompleteCommGen, "\n" );
    Print( "Whole words collected:            ", CombCollStats.WholeCommWord,   "\n" );
    Print( "Rest of word collected:           ", CombCollStats.CommRestWord,    "\n" );
    Print( "Commuting generator collected:    ", CombCollStats.CommGen,         "\n" );
    Print( "Triple weight generators:         ", CombCollStats.ThreeWtGen,      "\n" );
    Print( "    of those had to be stacked:   ", CombCollStats.ThreeWtGenStack, "\n" );
    Print( "Step by step collection:          ", CombCollStats.StepByStep,      "\n" );
    Print( "Combinatorial collection:         ", CombCollStats.CombColl,        "\n" );
    Print( "    of those had to be stacked:   ", CombCollStats.CombCollStack,   "\n" );
    Print( "Ordinary collection:              ", CombCollStats.OrdColl,         "\n" );
end;

ClearCombCollStats := function()

    CombCollStats.Counter         := 0;
    CombCollStats.CompleteCommGen := 0;
    CombCollStats.WholeCommWord   := 0;
    CombCollStats.CommRestWord    := 0;
    CombCollStats.CommGen         := 0;
    CombCollStats.CombColl        := 0;
    CombCollStats.CombCollStack   := 0;
    CombCollStats.OrdColl         := 0;
    CombCollStats.StepByStep      := 0;
    CombCollStats.ThreeWtGen      := 0;
    CombCollStats.ThreeWtGenStack := 0;
end;



CombinatorialCollectPolycyclicGap := function( coc, ev, w )
    local   com,  com2,  wt,  class,  wst,  west,
            sst,  est,  bottom,  stp,  g,  cnj,  icnj,  h,  m,  i,  j,
            astart,  IsNormed,  InfoCombi,
            ngens, pow, exp,
            ReduceExponentVector,
            AddIntoExponentVector;

##   The following is more elegant since it avoids the if-statment but it
##   uses two divisions.
#    m := ev[h];
#    ev[h] := ev[h] mod exp[h];
#    m := (m - ev[h]) / exp[h];
ReduceExponentVector := function( ev, g )
    ##  We assume that all generators after g commute with g.
    local   h,  m,  u,  j;
    Info( InfoCombinatorialFromTheLeftCollector, 5,
          " Reducing ", ev, " from ", g );

    for h in [g..ngens] do
        if IsBound( exp[h] ) and (ev[h] < 0  or ev[h] >= exp[h]) then
            m := QuoInt( ev[h], exp[h] );
            ev[h] := ev[h] - m * exp[h];
            if ev[h] < 0 then
                m := m - 1;
                ev[h] := ev[h] + exp[h];
            fi;

            if ev[h] < 0  or ev[h] >= exp[h] then
                Error( "incorrect reduction of exponent vector" );
            fi;

            if IsBound( pow[h] ) then
                u := pow[h];
                for j in [1,3..Length(u)-1] do
                    ev[ u[j] ] := ev[ u[j] ] + u[j+1] * m;
                od;
            fi;
        fi;
    od;
end;

##  ev := ev * word^exp
##  We assume that all generators after g commute with g.
AddIntoExponentVector := function( ev, word, start, e )
    local   i,  h;
    Info( InfoCombinatorialFromTheLeftCollector, 5,
          " Adding ", word, "^", e, " from ", start );

    CombCollStats.Count_Length := CombCollStats.Count_Length + Length(word);
    if start <= Length(word) then
        CombCollStats.Count_Weight := CombCollStats.Count_Weight + word[start];
    fi;

    for i in [start,start+2..Length(word)-1] do
        h     := word[ i ];
        ev[h] := ev[h] + word[ i+1 ] * e;
        if IsBound( exp[h] ) and (ev[h] < 0 or ev[h] >= exp[h]) then
            ReduceExponentVector( ev, h );
        fi;
    od;
end;

   if Length(w) = 0 then return true; fi;

    InfoCombi := InfoCombinatorialFromTheLeftCollector;

    CombCollStats.Counter := CombCollStats.Counter + 1;
    Info( InfoCombi, 4,
          "Entering combinatorial collector (", CombCollStats.Counter, ") ",
           ev, " * ", w );

    ## Check if the word is normed
    IsNormed := true;
    for i in [3,5..Length(w)-1] do
        if not w[i-2] < w[i] then IsNormed := false; break; fi;
    od;

    ##  The following variables are global because they are needed by the
    ##  two routines above.
    ngens := coc![PC_NUMBER_OF_GENERATORS];
    pow   := coc![ PC_POWERS ];
    exp   := coc![ PC_EXPONENTS ];

    ##  weight and commutator information
    wt     := coc![ PC_WEIGHTS ];
    class  := wt[ Length(wt) ];
    com    := coc![ PC_COMMUTE ];
    com2   := coc![ PC_NILPOTENT_COMMUTE ];
    astart := coc![ PC_ABELIAN_START ];

    ##  the four stacks
    wst   := [ ];
    west  := [ ];
    sst   := [ ];
    est   := [ ];

    ##  initialise
    bottom    := 0;
    stp       := bottom + 1;
    wst[stp]  := w;
    west[stp] := 1;
    sst[stp]  := 1;
    est[stp]  := w[ 2 ];

    # collect
    while stp > bottom do
        Info( InfoCombi, 5,
              " Next iteration: exponent vector ", ev );

        ##  Stack Management
        if est[stp] = 0 then
            ##  The current generator has been collected completely,
            ##  advance syllable pointer.
            sst[stp] := sst[stp] + 2;
            if sst[stp] <= Length(wst[stp]) then
                ##  Get the corresponding exponent.
                est[stp] := wst[stp][ sst[stp]+1 ];
            else
                ##  The current word has been collected completely,
                ##  reduce the wrd exponent.
                west[stp] := west[stp] - 1;
                if west[stp] > 0 then
                    ##  Initialise the syllable pointer and exponent
                    ##  counter.
                    sst[stp] := 1;
                    est[stp] := wst[stp][2];
                else
                    ##  The current word/exponent pair has been collected
                    ##  completely, move down the stacks and clear stacks
                    ##  before going down.
                    wst[ stp ] := 0; west[ stp ] := 0;
                    sst[ stp ] := 0; est[  stp ] := 0;
                    stp := stp - 1;
                fi;
            fi;

        ##  Collection
        else    ## now move the next generator/word to the correct position

            g := wst[stp][ sst[stp] ];             ##  get generator number

            if est[stp] > 0 then
                cnj  := coc![PC_CONJUGATES];
                icnj := coc![PC_INVERSECONJUGATES];
            elif est[stp] < 0 then
                cnj  := coc![PC_CONJUGATESINVERSE];
                icnj := coc![PC_INVERSECONJUGATESINVERSE];
            else
                Error( "exponent stack has zero entry" );
            fi;

            ##  Check if there is a single commuting generator on the stack
            ##  and collect.
            if Length( wst[stp] ) = 1 and com[g] = g then
                CombCollStats.CompleteCommGen := CombCollStats.CompleteCommGen + 1;

                Info( InfoCombi, 5,
                      " collecting single generator ", g );
                ev[ g ] := ev[ g ] + west[stp] * wst[stp][ sst[stp]+1 ];

                west[ stp ] := 0; est[ stp ]  := 0; sst[ stp ]  := 1;

                ##  Do we need to reduce ev[ g ] ?
                if IsBound( exp[g] ) and
                   ( ev[g] < 0  or ev[ g ] >= exp[ g ]) then
                    ReduceExponentVector( ev, g );
                fi;

            ##  Check if we can collect a whole commuting word into ev[].  We
            ##  can only do this if the word on the stack is normed.
            ##  Therefore, we cannot do this for the first word on the stack.
            elif (IsNormed or stp > 1) and sst[stp] = 1 and g = com[g] then
                CombCollStats.WholeCommWord := CombCollStats.WholeCommWord + 1;

                Info( InfoCombi, 5,
                      " collecting a whole word ",
                      wst[stp], "^", west[stp] );

                ##  Collect word ^ exponent in one go.
                AddIntoExponentVector( ev, wst[stp], sst[stp], west[stp] );
#                ReduceExponentVector( ev, g );

                ##  Adjust the stack.
                west[ stp ] := 0;
                est[  stp ] := 0;
                sst[  stp ] := Length( wst[stp] ) - 1;

            elif (IsNormed or stp > 1) and g = com[g] then
                CombCollStats.CommRestWord := CombCollStats.CommRestWord + 1;

                Info( InfoCombi, 5,
                      " collecting the rest of a word ",
                      wst[stp], "[", sst[stp], "]" );

                ##  Here we must only add the word from g onwards.
                AddIntoExponentVector( ev, wst[stp], sst[stp], 1 );
#                ReduceExponentVector( ev, g );

                # Adjust the stack.
                est[  stp ] := 0;
                sst[  stp ] := Length( wst[ stp ] ) - 1;

            elif g = com[g] then
                CombCollStats.CommGen := CombCollStats.CommGen + 1;

                Info( InfoCombi, 5,
                      " collecting a commuting generators ",
                      g, "^", est[stp] );

                ##  move generator directly to its correct position ...
                ev[g] := ev[g] + est[stp];

                ##  ... and reduce if necessary.
                if IsBound( exp[g] ) and (ev[g] < 0 or ev[g] >= exp[g]) then
                    ReduceExponentVector( ev, g );
                fi;

                est[stp] := 0;

            elif (IsNormed or stp > 1) and 3*wt[g] > class then
                CombCollStats.ThreeWtGen := CombCollStats.ThreeWtGen + 1;

                Info( InfoCombi, 5,
                      " collecting generator ", g, " with w(g)=", wt[g],
                      " and exponent ", est[stp] );

                ##  Collect ^ without stacking commutators.
                ##  This is step 6 in (Vaughan-Lee 1990).
                for h in Reversed( [ g+1 .. com[g] ] ) do
                    if ev[h] > 0 and IsBound( cnj[h][g] ) then
                        AddIntoExponentVector( ev, cnj[h][g],
                                3, ev[h] * AbsInt(est[ stp ]) );
                    elif ev[h] < 0 and IsBound( icnj[h][g] ) then
                        AddIntoExponentVector( ev, icnj[h][g],
                                3, -ev[h] * AbsInt(est[ stp ]) );
                    fi;
                od;
                ReduceExponentVector( ev, astart );

                ev[g] := ev[g] + est[ stp ];
                est[ stp ] := 0;

                ##  If the exponent is out of range, we have to stack up the
                ##  entries of the exponent vector because the rhs of the
                ##  power relation need not satisfy the weight condition.
                if IsBound( exp[g] ) and (ev[g] < 0 or ev[g] >= exp[g] ) then
                    m := QuoInt( ev[g], exp[g] );
                    ev[g] := ev[g] - m * exp[g];
                    if ev[g] < 0 then
                        m := m - 1;
                        ev[g] := ev[g] + exp[g];
                    fi;
                    if IsBound(pow[g]) then
                        ##  Put entries of the exponent vector onto the stack
                        CombCollStats.ThreeWtGenStack := CombCollStats.ThreeWtGenStack + 1;
                        for i in Reversed( [g+1 .. com[g]] ) do
                            if ev[i] <> 0 then
                                stp := stp + 1;
                                ##  Can we use gen[i] here and put ev[i] onto
                                ##  est[]?
                                wst[stp]  := [ i, ev[i] ];
                                west[stp] := 1;
                                sst[stp]  := 1;
                                est[stp]  := wst[stp][ sst[stp] + 1 ];
                                ev[i] := 0;
                            fi;
                        od;
                        ##  m must be 1, otherwise we cannot add the power
                        ##  relation into the exponent vector.  Lets check.
                        if m <> 1 then
                            Error( "illegal add operation in collection" );
                        fi;
                        AddIntoExponentVector( ev, pow[g], 1, m );
                        ##  Start reducing from com[g] on because the entries
                        ##  before that have been put onto the stack and are
                        ##  now zero.
#                        ReduceExponentVector( ev, astart );
                    fi;
                fi;

            else                 ##  we have to move  step by step
                CombCollStats.StepByStep := CombCollStats.StepByStep + 1;

                Info( InfoCombi, 5, " else-case, generator ", g );

                if est[ stp ] > 0 then
                    est[ stp ] := est[ stp ] - 1;
                    ev[ g ] := ev[ g ] + 1;
                else
                    est[ stp ] := est[ stp ] + 1;
                    ev[ g ] := ev[ g ] - 1;
                fi;

                if IsNormed or stp > 1 then
                    ##  Do combinatorial collection as far as possible.
                    CombCollStats.CombColl := CombCollStats.CombColl + 1;
                    for h in Reversed( [com2[g]+1..com[g]] ) do
                        if ev[h] > 0 and IsBound( cnj[h][g] ) then
                            AddIntoExponentVector( ev, cnj[h][g], 3, ev[h] );
                        elif ev[h] < 0 and IsBound( icnj[h][g] ) then
                            AddIntoExponentVector( ev, icnj[h][g], 3, -ev[h] );
                        fi;
                    od;
#                    ReduceExponentVector( ev, astart );
                    h := com2[g];
                else
                    h := com[g];
                fi;

                ##  Find the first position in v from where on ordinary
                ##  collection  has to be applied.
                while h > g do
                    if ev[h] <> 0 and IsBound( cnj[h][g] ) then
                        break;
                    fi;
                    h := h - 1;
                od;

                ##  Stack up this part of v if we run through the next
                ##  for-loop or if a power relation will be applied
                if g < h or
                   IsBound( exp[g] ) and
                   (ev[g] < 0 or ev[g] >= exp[g]) and IsBound(pow[g]) then

                    if h+1 <= com[g] then
                        CombCollStats.CombCollStack := CombCollStats.CombCollStack + 1;
                    fi;

                    for j in Reversed( [h+1..com[g]] ) do
                        if ev[j] <> 0 then
                            stp := stp + 1;
                            ##  Can we use gen[h] here and put ev[h] onto
                            ##  est[]?
                            wst[stp]  := [ j, ev[j] ];
                            west[stp] := 1;
                            sst[stp]  := 1;
                            est[stp]  := wst[stp][ sst[stp] + 1 ];
                            ev[j] := 0;
                            Info( InfoCombi, 5,
                                  "   Putting ", wst[ stp ], "^", west[stp],
                                  " onto the stack" );
                        fi;
                    od;
                fi;

                ##  We finish with ordinary collection from the left.
                if g <> h then
                    CombCollStats.OrdColl := CombCollStats.OrdColl + 1;
                fi;

                Info( InfoCombi, 5,
                      " Ordinary collection: g = ", g, ", h = ", h );
                while g < h do
                    Info( InfoCombi, 5,
                          "Executing while loop with h = ", h );

                    if  ev[h] <> 0 then
                        stp := stp + 1;
                        if ev[h] > 0 and IsBound( cnj[h][g] ) then
                            wst[stp]  := cnj[h][g];
                            west[stp] := ev[h];
                        elif ev[h] < 0 and IsBound( icnj[h][g] ) then
                            wst[stp]  := icnj[h][g];
                            west[stp] := -ev[h];
                        else  ##  Can we use gen[h] here and put ev[h]
                              ##  onto est[]?
                            wst[stp]  := [ h, ev[h] ];
                            west[stp] := 1;
                        fi;
                        sst[stp]  := 1;
                        est[stp]  := wst[stp][ sst[stp]+1 ];
                        ev[h] := 0;
                        Info( InfoCombi, 5,
                              "   Putting ", wst[ stp ], "^", west[stp],
                               " onto the stack" );
                    fi;

                    h := h - 1;
                od;

                ##  check that the exponent is not too big
                if IsBound( exp[g] ) and (ev[g] < 0 or ev[g] >= exp[g]) then
                    m := ev[g] / exp[g];
                    ev[g] := ev[g] - m * exp[g];
                    if ev[g] < 0 then
                        m := m - 1;
                        ev[g] := ev[g] + exp[g];
                    fi;

                    if IsBound( pow[g] ) then
                        stp := stp + 1;
                        wst[stp]  := pow[g];
                        west[stp] := m;
                        sst[stp]  := 1;
                        est[stp]  := wst[stp][ sst[stp]+1 ];
                        Info( InfoCombi, 5,
                              "   Putting ", wst[ stp ], "^", west[stp],
                               " onto the stack" );
                    fi;
                fi;
            fi;
        fi;
    od;
    return true;
end;




#############################################################################
##
##  Methods for  CollectWordOrFail.
##
InstallMethod( CollectWordOrFail,
        "CombinatorialFromTheLeftCollector",
        [ IsFromTheLeftCollectorRep and IsUpToDatePolycyclicCollector
          and IsWeightedCollector,
          IsList, IsList ],
function( pcp, a, b )
    local   aa,  aaa;

    if DEBUG_COMBINATORIAL_COLLECTOR then
        aa  := ShallowCopy(a);
        aaa := ShallowCopy(a);
        CombinatorialCollectPolycyclicGap( pcp, a, b );
        CollectPolycyclicGap( pcp, aa, b );
        if aa <> a then
            Error( "combinatorial collection failed" );
        fi;
    else
        CombinatorialCollectPolycyclicGap( pcp, a, b );
    fi;
    return true;
end );

polycyclic-2.16/gap/basic/pcpgrps.gd0000644000076600000240000000206513706672341016461 0ustar  mhornstaff#############################################################################
##
#W  pcpgrps.gd                   Polycyc                         Bettina Eick
##

#############################################################################
##
## Declare pcp groups as groups of pcp elements.
##
DeclareSynonym( "IsPcpGroup", IsGroup and IsPcpElementCollection );
InstallTrueMethod( IsPolycyclicGroup, IsPcpGroup );

InstallTrueMethod( CanEasilySortElements, IsPcpGroup );
InstallTrueMethod( KnowsHowToDecompose, IsPcpGroup );


#############################################################################
##
## An igs/ngs/cgs is an attribute of a pcp group.
##
DeclareAttribute( "Igs", IsPcpGroup );
DeclareAttribute( "Ngs", IsPcpGroup );
DeclareAttribute( "Cgs", IsPcpGroup );

#############################################################################
##
## Some global functions
##
DeclareGlobalFunction( "PcpGroupByCollectorNC" );
DeclareGlobalFunction( "PcpGroupByCollector" );
DeclareGlobalFunction( "LinearActionOnPcp" );

DeclareGlobalFunction( "SubgroupByIgs" );
polycyclic-2.16/gap/basic/infos.gd0000644000076600000240000000075013706672341016120 0ustar  mhornstaff#############################################################################
##
#W  infos.gd                     Polycyc                         Bettina Eick
##

#############################################################################
##
#I InfoClass
##
DeclareInfoClass( "InfoIntStab" );       # for the element orbit-stabilizer
DeclareInfoClass( "InfoIntNorm" );       # for the subgroup orbit-stabilizer
DeclareInfoClass( "InfoPcpGrp"  );       # for all higher level functions

polycyclic-2.16/gap/README0000644000076600000240000000122413706672341014262 0ustar  mhornstaff
gap:

This directory contains the gap code to compute with infinite
polycyclic groups. It is splitted in various subdirectories:

matrix     --  basic stuff for rational modules and related
basic      --  basic stuff for pcp groups, collector etc.
cohom      --  cohomology for pcp groups, complements and extensions
action     --  actions of polycyclic groups and orbit-stabilizer methods
pcpgrp     --  higher level functions for pcp groups
matrep     --  computing a matrix representation for a pcp group
exam       --  examples of pcp groups

Each directory has its own README file containing more information
about the various files containing the code.
polycyclic-2.16/gap/obsolete.gd0000644000076600000240000000537213706672341015542 0ustar  mhornstaff############################################################################
##
## Polycyclic: Computation with polycyclic groups
## Copyright (C) 1999-2012  Bettina Eick
## Copyright (C) 1999-2007  Werner Nickel
## Copyright (C) 2010-2012  Max Horn
##
## This program is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License
## as published by the Free Software Foundation; either version 2
## of the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
##

#############################################################################
##
##  <#GAPDoc Label="Obsolete">
##  Over time, the interface of &Polycyclic; has changed. This
##  was done to get the names of &Polycyclic; functions to agree with the
##  general naming conventions used throughout GAP. Also, some &Polycyclic;
##  operations duplicated functionality that was already available in
##  the core of GAP under a different name. In these cases, whenever possible
##  we now install the &Polycyclic; code as methods for the existing GAP
##  operations instead of introducing new operations.
##  

## For backward compatibility, we still provide the old, obsolete ## names as aliases. However, please consider switching to the new names ## as soon as possible. The old names may be completely removed at some ## point in the future. ##

## The following function names were changed. ##

## SchurCovering ## SchurMultPcpGroup ## ## ## OLD ## NOW USE ## ## ## ## SchurCovering ## ## ## ## SchurMultPcpGroup ## ## ##
## <#/GAPDoc> DeclareSynonymAttr("SchurCovering", SchurCover); #DeclareSynonymAttr("SchurExtensionEpimorphism", EpimorphismSchurExtension); #DeclareSynonymAttr("NonAbelianExteriorSquareEpimorphism", EpimorphismNonabelianExteriorSquare); #DeclareSynonymAttr("NonAbelianExteriorSquare", NonabelianExteriorSquare); # The following does not use DeclareSynonymAttr on purpose SchurMultPcpGroup := AbelianInvariantsMultiplier; polycyclic-2.16/gap/action/0000755000076600000240000000000013706672341014660 5ustar mhornstaffpolycyclic-2.16/gap/action/orbnorm.gi0000644000076600000240000005371513706672341016672 0ustar mhornstaff############################################################################# ## #W orbnorm.gi Polycyc Bettina Eick ## ## The orbit-stabilizer algorithm for subgroups of Z^d. ## ############################################################################# ## #F Action function LatticeBases( base, mat ) ## OnLatticeBases := function( base, mat ) local imgs; imgs := base * mat; return NormalFormIntMat( imgs, 2 ).normal; end; ############################################################################# ## #F CheckNormalizer( G, S, linG, U ) ## CheckNormalizer := function( G, S, linG, U ) local linS, m, u, R; # the trivial case if Length( Pcp(G) ) = 0 then return true; fi; # first check that S is stabilizing linS := InducedByPcp( Pcp(G), Pcp(S), linG ); for m in linS do for u in U do if IsBool( PcpSolutionIntMat( U, u*m ) ) then return false; fi; od; od; # now consider the random stabilizer R := RandomPcpOrbitStabilizer( U, Pcp(G), linG, OnLatticeBases ); if ForAny( R.stab, x -> not x in S ) then return false; fi; return true; end; ############################################################################# ## #F CheckConjugacy( G, g, linG, U, W ) ## CheckConjugacy := function( G, g, linG, U, W ) local m, u; if Length( U ) <> Length( W ) then return IsBool( g ); fi; if Length(Pcp(G)) = 0 then return U = W; fi; m := InducedByPcp( Pcp(G), g, linG ); for u in U do if IsBool( PcpSolutionIntMat( W, u*m ) ) then return false; fi; od; return true; end; ############################################################################# ## #F BasisOfNormalizingSubfield( baseK, baseU ) ## BasisOfNormalizingSubfield := function( baseK, baseU ) local d, e, baseL, i, syst, subs; d := Length(baseK); e := Length(baseU ); baseL := IdentityMat( d ); for i in [1..e] do syst := List( baseK, x -> baseU[i] * x ); Append( syst, baseU ); subs := TriangulizedNullspaceMat( syst ); subs := subs{[1..Length(subs)]}{[1..d]}; baseL := SumIntersectionMat( baseL, subs )[2]; od; return List( baseL, x -> LinearCombination( baseK, x ) ); end; ############################################################################# ## #F NormalizerHomogeneousAction( G, linG, baseU ) . . . . . . . . . . . N_G(U) ## ## V is a homogenous G-module via linG (and thus linG spans a field). ## U is a subspace of V and baseU is an echelonised basis for U. ## NormalizerHomogeneousAction := function( G, linG, baseU ) local K, baseK, baseL, L, exp, U, linU; # check for trivial cases if ForAll(linG, x -> x = x^0) or Length(baseU) = 0 or Length(baseU) = Length(baseU[1]) then return G; fi; # get field K := FieldByMatricesNC( linG ); baseK := BasisVectors( Basis( K ) ); # determine normalizing subfield and its units baseL := BasisOfNormalizingSubfield( baseK, baseU ); L := FieldByMatrixBasisNC( baseL ); U := UnitGroup( L ); linU := GeneratorsOfGroup(U); # find G cap L = G cap U as subgroup of G exp := IntersectionOfUnitSubgroups( K, linG, linU ); return Subgroup( G, List( exp, x -> MappedVector( x, Pcp(G) ) ) ); end; ############################################################################# ## #F ConjugatingFieldElement( baseK, baseU, baseW ) . . . . . . . . . U^k = W ## ConjugatingFieldElement := function( baseK, baseU, baseW ) local d, e, baseL, i, syst, subs, k; # compute the full space of conjugating elements d := Length(baseK); e := Length(baseW ); baseL := IdentityMat( d ); for i in [1..e] do syst := List( baseK, x -> baseU[i] * x ); Append( syst, baseW ); subs := TriangulizedNullspaceMat( syst ); subs := subs{[1..Length(subs)]}{[1..d]}; baseL := SumIntersectionMat( baseL, subs )[2]; od; # if baseL is empty, then there is no solution if Length(baseL) = 0 then return false; fi; # get one (integral) solution k := baseL[Length(baseL)]; k := k * Lcm( List( k, DenominatorRat ) ); return LinearCombination( baseK, k ); end; ############################################################################# ## #F ConjugacyHomogeneousAction( G, linG, baseU, baseW ) . . . . . . . U^g = W? ## ## V is a homogenous G-module via linG. U and W are subspaces of V with bases ## baseU and baseW, respectively. The function computes N_G(U) and U^g = W if ## g exists. If no g exists, then false is returned. ## ConjugacyHomogeneousAction := function( G, linG, baseU, baseW ) local K, baseK, baseL, L, U, a, f, b, C, g, N, k, h; # check for trivial cases if Length(baseU) <> Length(baseW) then return false; fi; if baseU = baseW then return rec( norm := NormalizerHomogeneousAction( G, linG, baseU ), conj := One(G) ); fi; # get field - we need the maximal order in this case! K := FieldByMatricesNC( linG ); baseK := BasisVectors( MaximalOrderBasis( K ) ); # determine conjugating field element k := ConjugatingFieldElement( baseK, baseW, baseU ); if IsBool(k) then return false; fi; h := k^-1; # determine normalizing subfield baseL := BasisOfNormalizingSubfield( baseK, baseU ); L := FieldByMatrixBasisNC( baseL ); # get norm and root a := Determinant( k ); f := Length(baseK) / Length(baseL); b := RootInt( a, f ); if b^f <> a then return false; fi; # solve norm equation in L and sift C := NormCosetsOfNumberField( L, b ); C := List( C, x -> x * h ); C := Filtered( C, x -> IsUnitOfNumberField( K, x ) ); if Length(C) = 0 then return false; fi; # add unit group of L U := GeneratorsOfGroup(UnitGroup(L)); C := rec( reprs := C, units := U{[2..Length(U)]} ); # find an element of G cap Lh in G h := IntersectionOfTFUnitsByCosets( K, linG, C ); if IsBool( h ) then return false; fi; g := MappedVector( h.repr, Pcp(G) ); N := Subgroup( G, List( h.ints, x -> MappedVector( x, Pcp(G) ) ) ); # that's it return rec( norm := N, conj := g ); end; ############################################################################# ## #F AffineActionAsTensor( linG, nath ) ## AffineActionAsTensor := function( linG, nath ) local actsF, actsS, affG, i, t, j, d, b; # action on T / S for T = U + S and action on S actsF := List(linG, x -> InducedActionFactorByNHLB(x, nath )); actsS := List(linG, x -> InducedActionSubspaceByNHLB(x, nath )); # determine affine action on H^1 wrt U affG := []; for i in [1..Length(linG)] do # the linear part is the diagonal action on the tensor t := KroneckerProduct( actsF[i], actsS[i] ); for j in [1..Length(t)] do Add( t[j], 0 ); od; # the affine part is determined by the derivation wrt nath.factor b := PreimagesBasisOfNHLB( nath ); d := (actsF[i]^-1 * b) * linG[i] - b; d := Flat( List( d, x -> ProjectionByNHLB( x, nath ) ) ); Add( d, 1 ); Add( t, d ); # t is the affine action - store it Add( affG, t ); od; return affG; end; ############################################################################# ## #F DifferenceVector( base, nath ) ## ## Determines the vector (s1, ..., se) with nath.factor[i]+si in base. ## DifferenceVector := function( base, nath ) local b, k, f, v; b := PreimagesBasisOfNHLB( nath ); k := KernelOfNHLB( nath ); f := Concatenation( k, base ); v := List(b, x -> PcpSolutionIntMat(f, x){[1..Length(k)]}); v := - Flat(v); Add( v, 1 ); return v; end; ############################################################################# ## #F NormalizerComplement( G, linG, baseU, baseS ) . . . . . . . . . . . N_G(U) ## ## U and S are free abelian subgroups of V such that U cap S = 0. The group ## acts via linG on the full space V. ## NormalizerComplement := function( G, linG, baseU, baseS ) local baseT, nathT, affG, e; # catch the trivial cases if Length(baseS)=0 or Length(baseU)=0 then return G; fi; if ForAll( linG, x -> x = x^0 ) then return G; fi; baseT := LatticeBasis( Concatenation( baseU, baseS ) ); nathT := NaturalHomomorphismByLattices( baseT, baseS ); # compute a stabilizer under the affine action affG := AffineActionAsTensor( linG, nathT ); e := DifferenceVector( baseU, nathT ); return StabilizerIntegralAction( G, affG, e ); end; ############################################################################# ## #F ConjugacyComplements( G, linG, baseU, baseW, baseS ) . . . . . . .U^g = W? ## ConjugacyComplements := function( G, linG, baseU, baseW, baseS ) local baseT, nathT, affG, e, f, os; # catch the trivial cases if Length(baseU)<>Length(baseW) then return false; fi; if baseU = baseW then return rec( norm := NormalizerComplement( G, linG, baseU, baseS ), conj := One(G) ); fi; baseT := LatticeBasis( Concatenation( baseU, baseS ) ); nathT := NaturalHomomorphismByLattices( baseT, baseS ); # compute the stabilizer of (0,..,0,1) under an affine action affG := AffineActionAsTensor( linG, nathT ); e := DifferenceVector( baseU, nathT ); f := DifferenceVector( baseW, nathT ); os := OrbitIntegralAction( G, affG, e, f ); if IsBool(os) then return os; fi; return rec( norm := os.stab, conj := os.prei ); end; ############################################################################# ## #F NormalizerCongruenceAction( G, linG, baseU, ser ) . . . . . . . . . N_G(U) ## NormalizerCongruenceAction := function( G, linG, baseU, ser ) local V, S, i, d, linS, nath, indG, indS, U, M, I, H, subh, actS, T, F, fach, UH, MH, s; # catch a trivial case if ForAll( linG, x -> x = x^0 ) then return G; fi; if Length(baseU) = 0 then return G; fi; # set up for induction over the module series V := IdentityMat( Length(baseU[1]) ); S := G; # use induction over the module series for i in [1..Length(ser)-1] do d := Length( ser[i] ) - Length( ser[i+1] ); Info( InfoIntNorm, 2, " "); Info( InfoIntNorm, 2, " consider layer ", i, " of dim ",d); # do a check if Length(Pcp(S)) = 0 then return S; fi; # induce to the current layer V/ser[i+1]; Info( InfoIntNorm, 2, " induce to current layer"); nath := NaturalHomomorphismByLattices( V, ser[i+1] ); indG := List( linG, x -> InducedActionFactorByNHLB( x, nath ) ); indS := InducedByPcp( Pcp(G), Pcp(S), indG ); U := LatticeBasis( List( baseU, x -> ImageByNHLB( x, nath ) ) ); M := LatticeBasis( List( ser[i], x -> ImageByNHLB( x, nath ) ) ); F := IdentityMat(Length(indG[1])); # compute intersection I := StructuralCopy( LatticeIntersection( U, M ) ); H := PurifyRationalBase( I ); # first, use the action on the module M subh := NaturalHomomorphismByLattices( M, [] ); actS := List( indS, x -> InducedActionFactorByNHLB( x, subh ) ); I := LatticeBasis( List( I, x -> ImageByNHLB( x, subh ) ) ); Info( InfoIntNorm, 2, " normalize intersection "); T := NormalizerHomogeneousAction( S, actS, I ); if Length(Pcp(T)) = 0 then return T; fi; # reset action for the next step if Index(S,T) <> 1 then indS := InducedByPcp( Pcp(G), Pcp(T), indG ); fi; S := T; # next, consider the factor modulo the intersection hull H if Length(F) > Length(H) then fach := NaturalHomomorphismByLattices( F, H ); UH := LatticeBasis( List( U, x -> ImageByNHLB( x, fach ) ) ); MH := LatticeBasis( List( M, x -> ImageByNHLB( x, fach ) ) ); actS := List( indS, x -> InducedActionFactorByNHLB( x, fach ) ); Info( InfoIntNorm, 2, " normalize complement "); T := NormalizerComplement( S, actS, UH, MH ); if Length(Pcp(T)) = 0 then return T; fi; # again, reset action for the next step if Index(S,T) <> 1 then indS := InducedByPcp( Pcp(G), Pcp(T), indG ); fi; S := T; fi; # finally, add a finite orbit-stabilizer computation if H <> I then Info( InfoIntNorm, 2, " add finite stabilizer computation"); s := PcpOrbitStabilizer( U, Pcp(S), indS, OnLatticeBases ); S := SubgroupByIgs( S, s.stab ); fi; od; Info( InfoIntNorm, 2, " "); return S; end; ############################################################################# ## #F ConjugacyCongruenceAction( G, linG, baseU, baseW, ser ) . . . . . U^g = W? ## ConjugacyCongruenceAction := function( G, linG, baseU, baseW, ser ) local V, S, g, i, d, linS, moveW, nath, indS, U, W, M, IU, IW, H, F, subh, actS, s, UH, WH, MH, j, fach, indG; # catch some trivial cases if baseU = baseW then return rec( norm := NormalizerCongruenceAction(G, linG, baseU, ser), conj := One(G) ); fi; if Length(baseU)<>Length(baseW) or ForAll( linG, x -> x = x^0 ) then return false; fi; # set up V := IdentityMat( Length(baseU[1]) ); S := G; g := One( G ); # use induction over the module series for i in [1..Length(ser)-1] do d := Length( ser[i] ) - Length( ser[i+1] ); Info( InfoIntNorm, 2, " "); Info( InfoIntNorm, 2, " consider layer ", i, " of dim ",d); # get action of S on the full space moveW := LatticeBasis( baseW * InducedByPcp( Pcp(G), g, linG )^-1 ); # do a check if Length(Pcp(S))=0 and baseU<>moveW then return false; fi; if Length(Pcp(S))=0 and baseU=moveW then return rec( norm := S, conj := g ); fi; # induce to the current layer V/ser[i+1]; Info( InfoIntNorm, 2, " induce to layer "); nath := NaturalHomomorphismByLattices( V, ser[i+1] ); indG := List( linG, x -> InducedActionFactorByNHLB( x, nath ) ); indS := InducedByPcp( Pcp(G), Pcp(S), indG ); U := LatticeBasis( List( baseU, x -> ImageByNHLB( x, nath ) ) ); W := LatticeBasis( List( moveW, x -> ImageByNHLB( x, nath ) ) ); M := LatticeBasis( List( ser[i], x -> ImageByNHLB( x, nath ) ) ); F := IdentityMat(Length(indG[1])); # get intersections IU := LatticeIntersection( U, M ); IW := LatticeIntersection( W, M ); H := PurifyRationalBase( IU ); # first, use action on the module M subh := NaturalHomomorphismByLattices( M, [] ); actS := List( indS, x -> InducedActionFactorByNHLB( x, subh ) ); IU := LatticeBasis( List( IU, x -> ImageByNHLB( x, subh ) ) ); IW := LatticeBasis( List( IW, x -> ImageByNHLB( x, subh ) ) ); Info( InfoIntNorm, 2, " conjugate intersections "); s := ConjugacyHomogeneousAction( S, actS, IU, IW ); if IsBool(s) then return false; fi; # reset action for next step g := g * s.conj; W := LatticeBasis( W * InducedByPcp( Pcp(G), s.conj, indG )^-1 ); if Index(S,s.norm)<>1 then indS := InducedByPcp(Pcp(G),Pcp(s.norm),indG); fi; S := s.norm; # next, consider factor modulo the intersection hull H if Length(F) > Length(H) then fach := NaturalHomomorphismByLattices( F, H ); UH := LatticeBasis( List( U, x -> ImageByNHLB( x, fach ) ) ); WH := LatticeBasis( List( W, x -> ImageByNHLB( x, fach ) ) ); MH := LatticeBasis( List( M, x -> ImageByNHLB( x, fach ) ) ); actS := List( indS, x -> InducedActionFactorByNHLB( x, fach ) ); Info( InfoIntNorm, 2, " conjugate complements "); s := ConjugacyComplements( S, actS, UH, WH, MH ); if IsBool(s) then return false; fi; # again, reset action g := g * s.conj; W := LatticeBasis( W * InducedByPcp( Pcp(G), s.conj, indG )^-1 ); if Index(S,s.norm)<>1 then indS := InducedByPcp(Pcp(G),Pcp(s.norm),indG); fi; S := s.norm; fi; # finally, add a finite orbit-stabilizer computation if H <> IU then Info( InfoIntNorm, 2, " add finite stabilizer computation"); s := PcpOrbitStabilizer( U, Pcp(S), indS, OnLatticeBases ); j := Position( s.orbit, W ); if IsBool(j) then return false; fi; g := g * TransversalElement( j, s, One(G) ); S := SubgroupByIgs( S, s.stab ); fi; od; Info( InfoIntNorm, 2, " "); return rec( norm := S, conj := g ); end; ############################################################################# ## #F NormalizerIntegralAction( G, linG, U ) . . . . . . . . . . . . . . .N_G(U) ## # FIXME: This function is documented and should be turned into a GlobalFunction NormalizerIntegralAction := function( G, linG, U ) local gensU, d, e, F, t, I, S, linS, K, linK, ser, T, orbf, N; # catch a trivial case if ForAll( linG, x -> x = x^0 ) then return G; fi; # do a check gensU := LatticeBasis( U ); if gensU <> U then Error("function needs lattice basis as input"); fi; # get generators and check for trivial case if Length( U ) = 0 then return G; fi; d := Length( U[1] ); e := Length( U ); # compute modulo 3 first Info( InfoIntNorm, 1, "reducing by orbit-stabilizer mod 3"); F := GF(3); t := InducedByField( linG, F ); I := VectorspaceBasis( U * One(F) ); S := PcpOrbitStabilizer( I, Pcp(G), t, OnSubspacesByCanonicalBasis ); S := SubgroupByIgs( G, S.stab ); linS := InducedByPcp( Pcp(G), Pcp(S), linG ); # use congruence kernel Info( InfoIntNorm, 1, "determining 3-congruence subgroup"); K := KernelOfFiniteMatrixAction( S, linS, F ); linK := InducedByPcp( Pcp(G), Pcp(K), linG ); # compute homogeneous series Info( InfoIntNorm, 1, "computing module series"); ser := HomogeneousSeriesOfRationalModule( linG, linK, d ); ser := List( ser, x -> PurifyRationalBase(x) ); # get N_K(U) Info( InfoIntNorm, 1, "adding stabilizer for congruence subgroup"); T := NormalizerCongruenceAction( K, linK, U, ser ); # set up orbit stabilizer function for K orbf := function( K, actK, a, b ) local o; o := ConjugacyCongruenceAction( K, actK, a, b, ser ); if IsBool(o) then return o; fi; return o.conj; end; # add remaining stabilizer Info( InfoIntNorm, 1, "constructing block orbit-stabilizer"); N := ExtendOrbitStabilizer( U, K, linK, S, linS, orbf, OnLatticeBases ); N := AddIgsToIgs( N.stab, Igs(T) ); N := SubgroupByIgs( G, N ); # do a temporary check if CHECK_INTNORM@ then Info( InfoIntNorm, 1, "checking results"); if not CheckNormalizer(G, N, linG, U) then Error("wrong norm in integral action"); fi; fi; # now return return N; end; ############################################################################# ## #F ConjugacyIntegralAction( G, linG, U, W ) . . . . . . . . . . . . .U^g = W? ## ## returns N_G(U) and g in G with U^g = W if g exists. ## returns false otherwise. ## # FIXME: This function is documented and should be turned into a GlobalFunction ConjugacyIntegralAction := function( G, linG, U, W ) local F, t, I, J, os, j, g, L, S, linS, K, linK, ser, orbf, h, T; # do a check if U <> LatticeBasis(U) or W <> LatticeBasis(W) then Error("function needs lattice bases as input"); fi; # catch some trivial cases if U = W then return rec( norm := NormalizerIntegralAction(G, linG, U), prei := One( G ) ); fi; if Length(U)<>Length(W) or ForAll( linG, x -> x = x^0 ) then return false; fi; # compute modulo 3 first Info( InfoIntNorm, 1, "reducing by orbit-stabilizer mod 3"); F := GF(3); t := InducedByField( linG, F ); I := VectorspaceBasis( U * One(F) ); J := VectorspaceBasis( W * One(F) ); os := PcpOrbitStabilizer( I, Pcp(G), t, OnSubspacesByCanonicalBasis ); j := Position( os.orbit, J ); if IsBool(j) then return false; fi; g := TransversalElement( j, os, One(G) ); L := LatticeBasis( W * InducedByPcp( Pcp(G), g, linG )^-1 ); S := SubgroupByIgs( G, os.stab ); linS := InducedByPcp( Pcp(G), Pcp(S), linG ); # use congruence kernel Info( InfoIntNorm, 1, "determining 3-congruence subgroup"); K := KernelOfFiniteMatrixAction( S, linS, F ); linK := InducedByPcp( Pcp(G), Pcp(K), linG ); # compute homogeneous series Info( InfoIntNorm, 1, "computing module series"); ser := HomogeneousSeriesOfRationalModule( linG, linK, Length(U[1]) ); ser := List( ser, x -> PurifyRationalBase(x) ); # set up orbit stabilizer function for K orbf := function( K, linK, a, b ) local o; o := ConjugacyCongruenceAction( K, linK, a, b, ser ); if IsBool(o) then return o; fi; return o.conj; end; # determine block orbit and stabilizer Info( InfoIntNorm, 1, "constructing block orbit-stabilizer"); os := ExtendOrbitStabilizer( U, K, linK, S, linS, orbf, OnRight ); # get orbit element and preimage j := FindPosition( os.orbit, L, K, linK, orbf ); if IsBool(j) then return false; fi; h := TransversalElement( j, os, One(G) ); L := LatticeBasis( L * InducedByPcp( Pcp(G), h, linG )^-1 ); g := orbf( K, linK, U, L ) * h * g; # get Stab_K(e) and thus Stab_G(e) Info( InfoIntNorm, 1, "adding stabilizer for congruence subgroup"); T := NormalizerCongruenceAction( K, linK, U, ser ); t := AddIgsToIgs( os.stab, Igs(T) ); T := SubgroupByIgs( T, t ); # do a temporary check if CHECK_INTNORM@ then Info( InfoIntNorm, 1, "checking results"); if not CheckNormalizer( G, T, linG, U) then Error("wrong norm in integral action"); elif not CheckConjugacy(G, g, linG, U, W) then Error("wrong conjugate in integral action"); fi; fi; # now return return rec( stab := T, prei := g ); end; polycyclic-2.16/gap/action/freegens.gi0000644000076600000240000000445313706672341017005 0ustar mhornstaff############################################################################# ## #W freegens.gi Polycyclic Pakage Bettina Eick ## ## Compute minimal generating sets for abelian mat groups in various ## situations. ## ############################################################################# ## #F FreeGensByRelationMat( gens, mat ) . . . . . . . . . use smith normal form ## FreeGensByRelationMat := function( gens, mat ) local S, H, Q, I, pos, i; # first try to simplify mat mat := ShallowCopy( mat ); Sort( mat, function( a, b ) return PositionNonZero(a) H[x][x] <> 1 ); return rec( gens := List( pos, x -> MappedVector( I[x], gens ) ), rels := List( pos, x -> H[x][x] ), imgs := I{pos}, prei := Q{[1..Length(gens)]}{pos} ); end; ############################################################################# ## #F FreeGensByRelsAndOrders( gens, mat, ords ) . . . . . additional rel orders ## FreeGensByRelsAndOrders := function( gens, mat, ords ) local idm, i; # append orders to relation mat mat := ShallowCopy( mat ); idm := IdentityMat( Length(gens) ); for i in [1..Length(ords)] do Add( mat, ords[i] * idm[i] ); od; # return return FreeGensByRelationMat( gens, mat ); end; ############################################################################# ## #F FreeGensByBasePcgs( pcgs ) ## FreeGensByBasePcgs := function( pcgs ) local pcss, rels, n, mat, i, e; # set up pcgs.revs := Reversed( pcgs.pcref ); pcss := PcSequenceBasePcgs( pcgs ); rels := RelativeOrdersBasePcgs( pcgs ); n := Length( pcss ); if n = 0 then return rec( gens := [], rels := [] ); fi; # get relation matrix mat := []; for i in [1..n] do e := ExponentsByBasePcgs( pcgs, pcss[i]^rels[i] ); e[i] := e[i] - rels[i]; Add( mat, e ); od; # return return FreeGensByRelationMat( pcss, mat ); end; polycyclic-2.16/gap/action/extend.gi0000644000076600000240000001200613706672341016467 0ustar mhornstaff############################################################################# ## #W extend.gi Bettina Eick ## #W Enlarge a base pcgs by normalizing elements. ## ############################################################################# ## #F SubsWord( w, list ) . . . . . . . . . . . . . . . . . . .substitute a word ## SubsWord := function( w, list ) local g, i; g := list[1]^0; for i in [1..Length(w)] do g := g * list[w[i][1]]^w[i][2]; od; return g; end; ############################################################################# ## #F SubsWordPlus( w, gens, invs, id ) . . . . . . . .use inverses and identity ## SubsWordPlus := function( w, gens, invs, id ) local g, v; g := id; for v in w do if v[2] = 1 then g := g * gens[v[1]]; elif v[2] = -1 then g := g * invs[v[1]]; elif v[2] > 1 then g := g * gens[v[1]] ^ v[2]; elif v[2] < -1 then g := g * invs[v[1]] ^ -v[2]; fi; od; return g; end; ############################################################################# ## #F SubsRecWord( w, list ) . . . . . . . . . . . . substitute a recursive word ## SubsRecWord := function( w, list ) local g, v; # catch the case of a single exponent if Length(w) = 2 and IsInt( w[1] ) and IsInt( w[2] ) then return list[w[1]]^w[2]; elif Length(w) = 2 and IsInt( w[2] ) and IsList( w[1] ) then return SubsRecWord( w[1], list )^w[2]; fi; # now deal with products g := list[1]^0; for v in w do g := g * SubsRecWord( v, list ); od; return g; end; ############################################################################# ## #F SubsAndInvertDefn( w, defns ) . . . . . . . . . . . .substitute and invert ## SubsAndInvertDefn := function( w, defns ) local v, l; v := []; for l in w do Add( v, [defns[l[1]], -l[2]] ); od; return Reversed( v ); end; ############################################################################# ## #F TransWord( j, trels ) . . . . . . . . . . . . . determine transversal word ## TransWord := function( j, trels ) local l, g, s, p, t, w; l := Product( trels ); j := j - 1; w := []; for s in Reversed( [1..Length( trels )] ) do p := trels[s]; l := l/p; t := QuoInt( j, l ); j := RemInt( j, l ); if t > 0 then Add( w, [s, t] ); fi; od; return Reversed( w ); end; ############################################################################# ## #F EnlargeOrbit( orbit, g, p, op ) . . . . enlarge orbit by p images under g ## EnlargeOrbit := function( orbit, g, p, op ) local l, s, k, t, h; l := Length( orbit ); orbit[p*l] := true; s := 0; for k in [ 1 .. p - 1 ] do t := s + l; for h in [ 1 .. l ] do orbit[h+t] := op( orbit[h+s], g ); od; s := t; od; end; ############################################################################# ## #F SmallOrbitPoint( pcgs, g ) ## SmallOrbitPoint := function( pcgs, g ) local b; repeat b := Random(pcgs.acton); until pcgs.oper( b, g ) <> b; return b; end; ############################################################################# ## #F ExtendedBasePcgs( pcgs, g, d ) . . . . . . . . . . . . extend a base pcgs ## ## g normalizes and we compute a new pcgs for . ## ExtendedBasePcgs := function( pcgs, g, d ) local h, e, i, o, b, m, c, l, w, j, k; # change in place - but unbind not updated information Unbind(pcgs.pcgs); Unbind(pcgs.rels); # set up h := g; e := ShallowCopy( d ); i := 0; # loop over base and divide off while not pcgs.trivl( h ) do i := i + 1; # take base point (if necessary, add new base point) if i > Length( pcgs.orbit ) then b := SmallOrbitPoint( pcgs, g ); Add( pcgs.orbit, [b] ); Add( pcgs.trans, [] ); Add( pcgs.defns, [] ); Add( pcgs.trels, [] ); else b := pcgs.orbit[i][1]; fi; # compute the relative orbit length of h m := 1; c := pcgs.oper( b, h ); while not c in pcgs.orbit[i] do m := m + 1; c := pcgs.oper( c, h ); od; # enlarge pcgs, if necessary if m > 1 then #Print(" enlarge basic orbit ",i," by ",m," copies \n"); Add( pcgs.trans[i], h ); Add( pcgs.defns[i], e ); Add( pcgs.trels[i], m ); EnlargeOrbit( pcgs.orbit[i], h, m, pcgs.oper ); Add( pcgs.pcref, [i, Length(pcgs.trans[i])] ); fi; # divide off j := Position( pcgs.orbit[i], c ); if j > 1 then w := TransWord( j, pcgs.trels[i] ); h := h^m * SubsWord( w, pcgs.trans[i] )^-1; e := [[e,m], SubsAndInvertDefn( w, pcgs.defns[i] ) ]; else h := h^m; e := [e,m]; fi; od; end; polycyclic-2.16/gap/action/README0000644000076600000240000000074313706672341015544 0ustar mhornstaff gap/action: Functions to compute with polycyclic groups acting on a set. extend.gi -- extending a base pcgs for finite actions basepcgs.gi -- computing a base pcgs for finite actions freegens.gi -- multiplicatively independent generating sets dixon.gi -- verify linear independence of abelian matrix gens kernels.gi -- kernel of finite/congruence actions orbstab.gi -- orbit stabilizer method for polycyclic groups acting on elements of Z^d polycyclic-2.16/gap/action/kernels.gi0000644000076600000240000002405113706672341016646 0ustar mhornstaff############################################################################# ## #W kernels.gi Polycyc Bettina Eick ## ############################################################################# ## #F InducedByPcp( pcpG, pcpU, actG ) ## InducedByPcp := function( pcpG, pcpU, actG ) if IsMultiplicativeElement( pcpU ) then return MappedVector( ExponentsByPcp( pcpG, pcpU ), actG ); fi; if AsList(pcpU) = AsList(pcpG) then return actG; else return List(pcpU, x-> MappedVector(ExponentsByPcp(pcpG,x),actG)); fi; end; ############################################################################# ## #W KernelOfFiniteMatrixAction( G, mats, f ) ## KernelOfFiniteMatrixAction := function( G, mats, f ) local d, I, U, i, actU, stab; if Length( mats ) = 0 then return G; fi; d := Length( mats[1] ); I := IdentityMat( d, f ); # loop over basis and stabilize each point U := G; for i in [1..d] do actU := InducedByPcp( Pcp(G), Pcp(U), mats ); stab := PcpOrbitStabilizer( I[i], Pcp(U), actU, OnRight ); U := SubgroupByIgs( G, stab.stab ); od; # that's it return U; end; ############################################################################# ## #W KernelOfFiniteAction( G, pcp ) ## ## If pcp defines an elementary abelian layer, then we compute the kernel ## of the action of G. If pcp is free abelian, then we compute the kernel ## of the action mod 3. ## KernelOfFiniteAction := function( G, pcp ) local rels, p, f, pcpG, actG; # get the char and the field rels := RelativeOrdersOfPcp( pcp ); p := rels[1]; if p = 0 then p := 3; fi; f := GF(p); # get the action of G on pcp pcpG := Pcp(G); actG := LinearActionOnPcp( pcpG, pcp ); actG := InducedByField( actG, f ); # centralize return KernelOfFiniteMatrixAction( G, actG, f ); end; ############################################################################# ## #F RelationLatticeMod( gens, f ) ## RelationLatticeMod := function( gens, f ) local mats, l, pcgs, free, r, defn, g, e, null, base, i; # induce to f mats := InducedByField( gens, f ); l := Length( mats ); # compute independent gens pcgs := BasePcgsByPcFFEMatrices( mats ); free := FreeGensByBasePcgs( pcgs ); r := Length( free.gens ); if r = 0 then return IdentityMat(l); fi; # set up relation system defn := []; for g in mats do e := ExponentsByBasePcgs( pcgs, g ); Add( defn, e * free.prei ); od; # solve it mod relative orders null := NullspaceMatMod( defn, free.rels ); # determine lattice basis base := NormalFormIntMat( null, 2 ).normal; base := Filtered( base, x -> PositionNonZero(x) <= l ); ## do a temporary check #for i in [1..Length(base)] do # if not MappedVector( base[i], mats ) = mats[1]^0 then # Error("found non-relation"); # fi; #od; return base; end; ############################################################################# ## #F IsRelation( mats, rel ) . . . . . . . .check if rel is a relation for mats ## IsRelation := function( mats, rel ) local M1, M2, i; M1 := mats[1]^0; M2 := mats[1]^0; for i in [1..Length(mats)] do if rel[i] > 0 then M1 := M1*mats[i]^rel[i]; elif rel[i] < 0 then M2 := M2*mats[i]^-rel[i]; fi; od; return M1 = M2; end; ############################################################################# ## #F ApproxRelationLattice( mats, k, p ). . . . . . . . . k step approximation ## ApproxRelationLattice := function( mats, k, p ) local lat, i, new, ind, len; # set up lat := IdentityMat( Length(mats) ); # compute new lattices and intersect for i in [1..k] do p := NextPrimeInt(p); new := RelationLatticeMod( mats, GF(p) ); lat := LatticeIntersection( lat, new ); od; # find short vectors lat := LLLReducedBasis( lat ).basis; # did we find any relations? for i in [1..Length(lat)] do if not IsRelation( mats, lat[i] ) then lat[i] := false; fi; od; return rec( rels := Filtered( lat, x -> not IsBool(x) ), prime := p ); end; ############################################################################# ## #F VerifyIndependence( mats ) ## VerifyIndependence := function( mats ) local base, prim, dixn, done, L, p, i, N, w, d; if Length( mats ) = 1 and mats[1] <> mats[1]^0 then return true; fi; Print(" verifying linear independence \n"); base := AlgebraBase( mats ); d := Length( base ); Print(" got ", Length( mats ), " generators and dimension ", d,"\n"); if Length( mats ) >= d then return false; fi; prim := PrimitiveAlgebraElement( mats, base ); Print(" computing dixon bound \n"); dixn := Length(mats[1]) * LogDixonBound( mats, prim )^2; Print(" found ", dixn, "\n"); done := false; # set up L := IdentityMat( Length(mats) ); p := 1; while not done do Print(" next step verification \n"); # compute new lattices and intersect for i in [1..d] do p := NextPrimeInt(p); N := RelationLatticeMod( mats, GF(p) ); L := LatticeIntersection( L, N ); od; # find short vectors L := LLLReducedBasis( L ).basis; w := Minimum( List( L, x -> x * x ) ); Print(" got shortest vector ", w, "\n"); # check dixon bound if w > dixn then return true; fi; # check rels for i in [1..Length(L)] do if IsRelation( mats, L[i] ) then return false; fi; od; od; end; ############################################################################# ## #W KernelOfCongruenceMatrixActionGAP( G, mats ) . . G acts as ss cong subgrp ## ## Warning: G must be integral! ## KernelOfCongruenceMatrixActionGAP := function( G, mats ) local p, U, pcp, K, gens, acts, rell, tmps; # set up p := 1; U := DerivedSubgroup(G); pcp := Pcp( G ); # now loop repeat K := U; gens := Pcp( G, K ); acts := InducedByPcp( pcp, gens, mats ); rell := ApproxRelationLattice( acts, Length(acts[1]), p ); tmps := List( rell.rels, x -> MappedVector( x, gens ) ); tmps := AddToIgs( DenominatorOfPcp( gens ), tmps ); U := SubgroupByIgs( G, tmps ); p := rell.prime; until Index( G, U ) = 1 or Index( U, K ) = 1; # verify if desired if Index( G, U ) > 1 and VERIFY@ then gens := Pcp( G, U ); acts := InducedByPcp( pcp, gens, mats ); if not VerifyIndependence( acts ) then Error(" generators are not linearly independent"); fi; fi; # that's it return U; end; ############################################################################# ## #F KernelOfCongruenceMatrixActionALNUTH( G, mats ) . G acts as ss cong subgrp ## KernelOfCongruenceMatrixActionALNUTH := function( G, mats ) local H, base, prim, fact, full, f, s, h, imats, F, rels, gens; # the trivial case if ForAll( mats, x -> x^0 = x ) then return G; fi; # split into irreducibles base := AlgebraBase( mats ); prim := PrimitiveAlgebraElement( base, List( base, Flat ) ); fact := Factors( prim.poly ); # catch the trivial case first - for increased efficiency if Length(fact) = 1 then F := FieldByMatricesNC( mats ); SetPrimitiveElement( F, prim.elem ); SetDefiningPolynomial( F, prim.poly ); rels := RelationLatticeOfTFUnits( F, mats ); return Subgroup( G, List( rels, x -> MappedVector( x, Pcp(G) ) ) ); fi; # loop over subspaces full := mats[1]^0; gens := AsList( Pcp(G) ); H := G; for f in fact do # induce matrices if necessary if Index( G, H ) > 1 then mats := List( rels, x -> MappedVector( x, mats ) ); G := H; fi; # get subspace s := NullspaceRatMat( Value( f, prim.elem ) ); h := NaturalHomomorphismBySemiEchelonBases( full, s ); # induce to factor imats := List( mats, x -> InducedActionSubspaceByNHSEB( x, h ) ); if ForAny( imats, x -> x <> x^0 ) then F := FieldByMatricesNC( mats ); SetPrimitiveElement( F, prim.elem ); SetDefiningPolynomial( F, prim.poly ); # compute kernel rels := RelationLatticeOfTFUnits( F, imats ); # set up for iteration gens := List( rels, x -> MappedVector( x, gens ) ); H := Subgroup( G, gens ); fi; od; # that's it return H; end; ############################################################################# ## #F KernelOfCongruenceMatrixAction( G, mats ) . . . . . . . . header function ## KernelOfCongruenceMatrixAction := function( G, mats ) if ForAll( mats, x -> x = x^0 ) then return G; fi; if USE_ALNUTH@ then return KernelOfCongruenceMatrixActionALNUTH( G, mats ); else return KernelOfCongruenceMatrixActionGAP( G, mats ); fi; end; ############################################################################# ## #F KernelOfCongruenceAction( G, pcp ) . . . . . . . .G acts as ss cong subgrp ## KernelOfCongruenceAction := function( G, pcp ) local mats; mats := LinearActionOnPcp( Pcp(G), pcp ); return KernelOfCongruenceMatrixAction( G, mats ); end; ############################################################################# ## #F MemberByCongruenceMatrixAction( G, mats, m ) . . G acts as irr cong subgrp ## ## So far, this works only if G is an integral group. ## MemberByCongruenceMatrixAction := function( G, mats, m ) local F, r, e; # get field F := FieldByMatricesNC( mats ); # check whether m is a unit in F if not IsUnitOfNumberField( F, m ) then return false; fi; # check if m is in G r := RelationLatticeOfTFUnits( F, Concatenation( [m], mats ) )[1]; if PositionNonZero( r ) > 1 or AbsInt( r[1] ) <> 1 then return false; fi; # now translate to G e := -r{[2..Length(r)]} * r[1]; return MappedVector( e, Pcp(G) ); end; polycyclic-2.16/gap/action/basepcgs.gi0000644000076600000240000001460613706672341016777 0ustar mhornstaff############################################################################# ## #W basepcgs.gi Bettina Eick ## #W A base pcgs is a pcgs with attached base and strong generating set. #W It is a record consisting of: #W .orbit and .trans and .trels - the base and strong gen set #W .acton and .oper and .trivl - the domain to act on and the action #W .pcref - the reference to a pcgs ## ############################################################################# ## #F BasePcgsByPcSequence( pcs, dom, trv, oper ) ## ## pcs is a sequence of normalizing elements, dom is the domain they act ## on, trv is a function to decide when an element is trivial, oper is the ## operation of pcs on dom. (If trv is boolean, then a standard trv function ## is used.) ## BasePcgsByPcSequence := function( pcs, dom, trv, oper ) local pcgs, i; if IsBool( trv ) then trv := function( x ) return x = x^0; end; fi; pcgs := rec( orbit := [], trans := [], trels := [], defns := [], pcref := [], acton := dom, oper := oper, trivl := trv ); for i in Reversed( [1..Length(pcs)] ) do ExtendedBasePcgs( pcgs, pcs[i], [i,1] ); od; return pcgs; end; ############################################################################# ## #F BasePcgsByPcFFEMatrices( gens ) ## BasePcgsByPcFFEMatrices := function( gens ) local f, d, pcgs; # triviality check if Length(gens) = 0 then return BasePcgsByPcSequence( gens, false, false, OnRight ); fi; # set up f := Field( gens[1][1][1] ); d := Length( gens[1] ); # compute pcgs, add preimages and return pcgs := BasePcgsByPcSequence( gens, f^d, false, OnRight ); pcgs.gens := gens; return pcgs; end; ############################################################################# ## #F BasePcgsByPcIntMatrices( gens, f ) ## BasePcgsByPcIntMatrices := function( gens, f ) local d, news, pcgs; # triviality check if Length(gens) = 0 then return BasePcgsByPcSequence( gens, false, false, OnRight ); fi; # change field and compute d := Length( gens[1] ); news := InducedByField( gens, f ); pcgs := BasePcgsByPcSequence( news, f^d, false, OnRight ); pcgs.gens := gens; pcgs.field := f; return pcgs; end; ############################################################################# ## #F RelativeOrdersBasePcgs( pcgs ) ## RelativeOrdersBasePcgs := function( pcgs ) local t; if IsBound( pcgs.rels ) then return pcgs.rels; fi; pcgs.rels := []; for t in Reversed( pcgs.pcref ) do Add( pcgs.rels, pcgs.trels[t[1]][t[2]] ); od; return pcgs.rels; end; ############################################################################# ## #F PcSequenceBasePcgs( pcgs ) ## PcSequenceBasePcgs := function( pcgs ) local t; if IsBound( pcgs.pcgs ) then return pcgs.pcgs; fi; pcgs.pcgs := []; for t in Reversed( pcgs.pcref ) do Add( pcgs.pcgs, pcgs.trans[t[1]][t[2]] ); od; return pcgs.pcgs; end; ############################################################################# ## #F DefinitionsBasePcgs( pcgs ) ## DefinitionsBasePcgs := function( pcgs ) local defn, t; defn := []; for t in Reversed( pcgs.pcref ) do Add( defn, pcgs.defns[t[1]][t[2]] ); od; return defn; end; ############################################################################# ## #F GeneratorsBasePcgs( pcgs ) ## GeneratorsBasePcgs := function( pcgs ) return pcgs.gens; end; ############################################################################# ## #F SiftByBasePcgs( pcgs, g ) ## SiftByBasePcgs := function( pcgs, g ) local h, w, i, j; h := g; for i in [1..Length(pcgs.orbit)] do j := Position( pcgs.orbit[i], pcgs.oper( pcgs.orbit[i][1], h ) ); if IsBool( j ) then return h; fi; if j > 1 then w := TransWord( j, pcgs.trels[i] ); h := h * SubsWord( w, pcgs.trans[i] )^-1; fi; od; return h; end; ############################################################################# ## #F SiftExponentsByBasePcgs( pcgs, g ) ## SiftExponentsByBasePcgs := function( pcgs, g ) local h, w, e, i, j; h := g; e := List( pcgs.orbit, x -> 0 ); for i in [1..Length(pcgs.orbit)] do if pcgs.trivl( h ) then return e; fi; j := Position( pcgs.orbit[i], pcgs.oper( pcgs.orbit[i][1], h ) ); if IsBool( j ) then return false; fi; if j > 1 then w := TransWord( j, pcgs.trels[i] ); h := h * SubsWord( w, pcgs.trans[i] )^-1; fi; e[i] := j-1; od; if pcgs.trivl( h ) then return e; fi; return false; end; ############################################################################# ## #F BasePcgsElementBySiftExponents( pcgs, exp ) ## BasePcgsElementBySiftExponents := function( pcgs, exp ) local g, w, i; g := pcgs.trans[1][1]^0; for i in Reversed( [1..Length(exp)] ) do if exp[i] > 0 then w := TransWord( exp[i]+1, pcgs.trels[i] ); g := SubsWord( w, pcgs.trans[i] ) * g; fi; od; return g; end; ############################################################################# ## #F MemberTestByBasePcgs( pcgs, g ) ## MemberTestByBasePcgs := function( pcgs, g ) return pcgs.trivl( SiftByBasePcgs( pcgs,g ) ); end; ############################################################################# ## #F WordByBasePcgs( pcgs, g ) ## WordByBasePcgs := function( pcgs, g ) local w, h, i, j, t; w := List( pcgs.orbit, x -> [] ); h := g; for i in [1..Length(pcgs.orbit)] do j := Position( pcgs.orbit[i], pcgs.orbit[i][1] * h ); if j > 1 then t := TransWord( j, pcgs.trels[i] ); t := List( t, x -> [Position( pcgs.revs, [i,x[1]] ), x[2]] ); h := h * SubsWord( t, pcgs.pcgs )^-1; w[i] := t; fi; od; return Concatenation( Reversed( w ) ); end; ############################################################################# ## #F ExponentsByBasePcgs( pcgs, g ) ## ## This function gives useful results for abelian groups only. ## ExponentsByBasePcgs := function( pcgs, g ) local n, w, e, s; n := Length( PcSequenceBasePcgs( pcgs ) ); w := WordByBasePcgs( pcgs, g ); e := List( [1..n], x -> 0 ); for s in w do e[s[1]] := e[s[1]] + s[2]; od; return e; end; polycyclic-2.16/gap/action/orbstab.gi0000644000076600000240000005305313706672341016643 0ustar mhornstaff############################################################################# ## #W orbstab.gi Polycyc Bettina Eick ## ## The orbit-stabilizer algorithm for elements of Z^d. ## ############################################################################# ## #F CheckStabilizer( G, S, mats, v ) ## CheckStabilizer := function( G, S, mats, v ) local actS, m, R; # first check that S is stabilizing actS := InducedByPcp( Pcp(G), Pcp(S), mats ); for m in actS do if v*m <> v then return false; fi; od; # now consider the random stabilizer R := RandomPcpOrbitStabilizer( v, Pcp(G), mats, OnRight ); if ForAny( R.stab, x -> not x in S ) then return false; fi; return true; end; ############################################################################# ## #F CheckOrbit( G, g, mats, e, f ) ## CheckOrbit := function( G, g, mats, e, f ) return e * InducedByPcp( Pcp(G), g, mats ) = f; end; ############################################################################# ## #F OrbitStabilizerTranslationAction( K, derK ) . . . . . for transl. subgroup ## OrbitStabilizerTranslationAction := function( K, derK ) local base, gens, orbit, trans, stabl; # the first case is that image is trivial if ForAll( derK, x -> x = 0*x ) then return rec( stabl := K, trans := [], orbit := [] ); fi; # now compute orbit in standart form gens := AsList( Pcp(K) ); base := FreeGensAndKernel( derK ); if Length( base.kern ) > 0 then base.kern := NormalFormIntMat( base.kern, 2 ).normal; fi; # set up result orbit := base.free; trans := List( base.trsf, x -> MappedVector( x, gens ) ); stabl := List( base.kern, x -> MappedVector( x, gens ) ); return rec( stabl := stabl, trans := trans, orbit := orbit ); end; ############################################################################# ## #F InducedDerivation( g, G, linG, derG ) . . . . . . . . . value of derG on g ## InducedDerivation := function( g, G, linG, derG ) local pcp, exp, der, i, e, j, inv; pcp := Pcp( G ); exp := ExponentsByPcp( pcp, g ); der := 0 * derG[1]; for i in [1..Length(exp)] do e := exp[i]; if linG[i] = linG[i]^0 then der := der + e*derG[i]; elif e > 0 then for j in [1..e] do der := der * linG[i] + derG[i]; od; elif e < 0 then inv := linG[i]^-1; for j in [1..-e] do der := (der - derG[i]) * inv; od; fi; od; return der; end; ############################################################################# ## #F StabilizerIrreducibleAction( G, K, linG, derG ) . . . . . . kernel of derG ## StabilizerIrreducibleAction := function( G, K, linG, derG ) local derK, stabK, OnAffMod, affG, e, h, H, gens, i, f, k; # catch the trivial case first if ForAll( derG, x -> x = 0 * x ) then return G; fi; # now we are in a non-trivial case - compute derivations of K derK := List( Pcp(K), x -> InducedDerivation( x, G, linG, derG ) ); # compute orbit and stabilizer under K stabK := OrbitStabilizerTranslationAction( K, derK ); Info( InfoIntStab, 3, " translation orbit: ", stabK.orbit); # if derK = 0, then K is the kernel if Length( stabK.orbit ) = 0 then return K; fi; # define affine action OnAffMod := function( pt, aff ) local im; im := pt * aff[1] + aff[2]; return VectorModLattice( im, stabK.orbit ); end; # use finite orbit stabilizer to determine block-stab affG := List( [1..Length(linG)], x -> [linG[x], derG[x]] ); e := derG[1] * 0; h := PcpOrbitStabilizer( e, Pcp(G), affG, OnAffMod ).stab; H := SubgroupByIgs( G, h ); Info( InfoIntStab, 3, " finite orbit has length ", Index(G,H)); # now we have to compute the complement gens := ShallowCopy( AsList( Pcp( H, K ) ) ); for i in [1..Length( gens )] do f := InducedDerivation( gens[i], G, linG, derG ); e := MemberBySemiEchelonBase( f, stabK.orbit ); k := MappedVector( e, stabK.trans ); gens[i] := gens[i] * k^-1; od; Info( InfoIntStab, 3, " determined complement "); gens := AddIgsToIgs( gens, stabK.stabl ); return SubgroupByIgs( G, gens ); end; ############################################################################# ## #F OrbitIrreducibleActionTrivialKernel( G, K, linG, derG, v ) . v^g in derG? ## ## returns an element g in G with v^g in derG and ker(derG) if g exists. ## returns false otherwise. ## OrbitIrreducibleActionTrivialKernel := function( G, K, linG, derG, v ) local I, d, lin, der, g, t, a, m; # set up I := linG[1]^0; d := Length(I); # compute basis of Q[G] and corresponding derivations lin := StructuralCopy(linG); der := StructuralCopy(derG); while RankMat(der) < d do g := Random(G); t := InducedDerivation( g, G, linG, derG ); if t <> 0 * t and IsBool( SolutionMat( der, t ) ) then Add( der, t ); Add( lin, InducedByPcp( Pcp(G), g, linG ) ); fi; od; # find linear combination a := SolutionMat( der, v ); if IsBool( a ) then Error("derivations do not span"); fi; # translate combination m := Sum(List( [1..Length(a)], x -> a[x] * (lin[x] - I))) + I; # check if a preimage of m is in g g := MemberByCongruenceMatrixAction( G, linG, m ); # now return if IsBool( g ) then return false; fi; return rec( stab := K, prei := g ); end; ############################################################################# ## #F OrbitIrreducibleAction( G, K, linG, derG, v ) . . . . . . . . v^g in derG? ## ## returns an element g in G with v^g in derG and ker(derG) if g exists. ## returns false otherwise. ## OrbitIrreducibleAction := function( G, K, linG, derG, v ) local derK, stabK, I, a, m, g, OnAffMod, affG, e, h, i, c, k, H, f, gens, found, w; # catch some trivial cases first if v = 0 * v then return rec( stab := StabilizerIrreducibleAction( G, K, linG, derG ), prei := One(G) ); fi; if ForAll( derG, x -> x = 0 * x ) then return false; fi; # now we are in a non-trivial case - compute derivations of K derK := List( Pcp(K), x -> InducedDerivation( x, G, linG, derG ) ); # compute orbit and stabilizer under K stabK := OrbitStabilizerTranslationAction( K, derK ); Info( InfoIntStab, 3, " translation orbit: ", stabK.orbit); # if derK = 0, then K is the kernel and g is a linear combination if Length( stabK.orbit ) = 0 then return OrbitIrreducibleActionTrivialKernel( G, K, linG, derG, v ); fi; # define affine action OnAffMod := function( pt, aff ) local im; im := pt * aff[1] + aff[2]; return VectorModLattice( im, stabK.orbit ); end; # use finite orbit stabilizer to determine block-stab affG := List( [1..Length(linG)], x -> [linG[x], derG[x]] ); e := derG[1] * 0; h := PcpOrbitStabilizer( e, Pcp(G), affG, OnAffMod ); H := SubgroupByIgs( G, h.stab ); # get preimage found := false; i := 0; while not found and i < Length( h.orbit ) do i := i + 1; c := PcpSolutionIntMat( stabK.orbit, v-h.orbit[i] ); if not IsBool( c ) then g := TransversalElement( i, h, One(G) ); w := InducedDerivation( g, G, linG, derG ); c := PcpSolutionIntMat( stabK.orbit, v-w); k := MappedVector( c, stabK.trans ); g := g * k; found := true; fi; od; if not found then return false; fi; # get stabilizer as complement gens := ShallowCopy( AsList( Pcp( H, K ) ) ); for i in [1..Length( gens )] do f := InducedDerivation( gens[i], G, linG, derG ); e := MemberBySemiEchelonBase( f, stabK.orbit ); k := MappedVector( e, stabK.trans ); gens[i] := gens[i] * k^-1; od; gens := AddToIgs( stabK.stabl, gens ); return rec( stab := SubgroupByIgs( G, gens ), prei := g); end; ############################################################################# ## #F StabilizerCongruenceAction( G, mats, e, ser ) ## StabilizerCongruenceAction := function( G, mats, e, ser ) local S, d, actS, derS, nath, K, T, actT, derT, full, subs, bas, tak, act, der, U, f, comp, i, inv; # catch the trivial case if ForAll( mats, x -> e * x = e ) then return G; fi; # set up S := G; # now use induction on this series for i in [1..Length(ser)-1] do d := Length( ser[i] ) - Length( ser[i+1] ); Info( InfoIntStab, 2, " consider layer ", i, " of dim ",d); # reset actS := InducedByPcp( Pcp(G), Pcp(S), mats ); derS := List( actS, x -> e*x - e ); # get layer nath := NaturalHomomorphismByLattices( ser[i], ser[i+1] ); actS := List( actS, x -> InducedActionFactorByNHLB( x, nath ) ); derS := List( derS, x -> ImageByNHLB( x, nath ) ); # the current layer is a semisimple S-module -- get kernel Info( InfoIntStab, 2, " computing kernel of linear action"); K := KernelOfCongruenceMatrixAction( S, actS ); # set up for iteration T := S; actT := actS; derT := derS; full := IdentityMat(d); subs := RefineSplitting( actT, [full] ); subs := List( subs, PurifyRationalBase ); comp := []; # now loop over irreducible submodules and compute stab T while Length(subs)>0 do Info( InfoIntStab, 2, " layer: ", List(subs,Length)); bas := Concatenation(subs); Append(bas, comp); inv := bas^-1; tak := Remove(subs, 1); f := Length(tak); Append(comp, tak); act := List(actT, x -> bas*x*inv); act := List(act, x -> x{[1..f]}{[1..f]}); der := List(derT, x -> x*inv); der := List(der, x -> x{[1..f]}); # stabilize U := StabilizerIrreducibleAction( T, K, act, der ); # reset if Index(T,U) > 1 then T := SubgroupByIgs( G, Cgs(U) ); K := NormalIntersection( K, T ); actT := InducedByPcp( Pcp(S), Pcp(T), actS ); derT := List(Pcp(T),x->InducedDerivation(x, S, actS, derS)); if Length(subs) > 0 then subs := RefineSplitting( actT, subs ); subs := List( subs, PurifyRationalBase ); fi; fi; # do a check if Length( Pcp( T ) ) = 0 then return T; fi; od; S := T; od; return S; end; ############################################################################# ## #F OrbitCongruenceAction := function( G, mats, e, f, ser ) ## ## returns Stab_G(e) and g in G with e^g = f if g exists. ## returns false otherwise. ## OrbitCongruenceAction := function( G, mats, e, f, ser ) local S, d, actS, derS, nath, K, T, actT, derT, full, subs, bas, tak, act, der, U, j, comp, g, t, o, u, inv, i; # catch some trivial cases if e = f then return rec( stab := StabilizerCongruenceAction(G, mats, e, ser), prei := One( G ) ); fi; if RankMat( [e,f] ) = 1 or ForAll( mats, x -> e*x = e) then return false; fi; # set up S := G; g := One( G ); # now use induction on this series for i in [1..Length(ser)-1] do d := Length( ser[i] ) - Length( ser[i+1] ); Info( InfoIntStab, 2, " consider layer ", i, " with dim ",d); # reset actS := InducedByPcp( Pcp(G), Pcp(S), mats ); derS := List( actS, x -> e*x - e ); # get layer nath := NaturalHomomorphismBySemiEchelonBases( ser[i], ser[i+1] ); actS := List( actS, x -> InducedActionFactorByNHSEB( x, nath ) ); derS := List( derS, x -> ImageByNHSEB( x, nath ) ); # the current layer is a semisimple S-module -- get kernel Info( InfoIntStab, 2, " computing kernel of linear action"); K := KernelOfCongruenceMatrixAction( S, actS ); # set up for iteration T := S; actT := actS; derT := derS; full := IdentityMat( Length(actS[1]) ); subs := RefineSplitting( actT, [full] ); subs := List(subs, PurifyRationalBase ); comp := []; # now loop over irreducible submodules and compute stab T while Length( subs ) > 0 do Info( InfoIntStab, 2, " layer: ", List(subs,Length)); bas := Concatenation(subs); Append(bas, comp); inv := bas^-1; tak := Remove(subs, 1); j := Length(tak); Append(comp, tak); act := List(actT, x -> bas*x*inv); act := List(act, x -> x{[1..j]}{[1..j]}); der := List(derT, x -> x*inv); der := List(der, x -> x{[1..j]}); # set up element and do a check t := InducedByPcp(Pcp(G), g, mats)^-1; u := f*t-e; if Length(Pcp(T)) = 0 and u = 0*u then return rec( stab := T, prei := g ); elif Length(Pcp(T)) = 0 then return false; fi; # induce to layer u := ImageByNHSEB( u, nath ) * inv; u := u{[1..j]}; # find preimage h with u = h^der if it exists o := OrbitIrreducibleAction( T, K, act, der, u ); if IsBool(o) then return false; fi; g := o.prei * g; U := o.stab; # reset if Index(T, U) > 1 then T := SubgroupByIgs(G, Cgs(U)); K := NormalIntersection( K, T ); actT := InducedByPcp( Pcp(S), Pcp(T), actS ); derT := List(Pcp(T), x->InducedDerivation(x, S, actS, derS)); if Length(subs) > 0 then subs := RefineSplitting( actT, subs ); subs := List( subs, PurifyRationalBase ); fi; fi;; od; S := T; od; return rec( stab := S, prei := g ); end; ############################################################################# ## #F FindPosition( orbit, pt, K, actK, orbfun ) ## FindPosition := function( orbit, pt, K, actK, orbfun ) local j, k; for j in [1..Length(orbit)] do k := orbfun( K, actK, pt, orbit[j] ); if not IsBool( k ) then return j; fi; od; return false; end; ############################################################################# ## #F ExtendOrbitStabilizer( e, K, actK, S, actS, orbfun, op ) ## ## K has finite index in S and and orbfun solves the orbit problem for K. ## ExtendOrbitStabilizer := function( e, K, actK, S, actS, orbfun, op ) local gens, rels, mats, orbit, trans, trels, stab, i, f, j, n, t, s, g; # get action gens := Pcp(S,K); rels := RelativeOrdersOfPcp( gens ); mats := InducedByPcp( Pcp(S), gens, actS ); # set up orbit := [e]; trans := []; trels := []; stab := []; # construct orbit and stabilizer for i in Reversed( [1..Length(gens)] ) do # get new point f := op( e, mats[i] ); j := FindPosition( orbit, f, K, actK, orbfun ); # if it is new, add all blocks n := orbit; t := []; s := 1; while IsBool( j ) do n := List( n, x -> op( x, mats[i] ) ); Append( t, n ); j := FindPosition( orbit, op( n[1], mats[i]), K, actK, orbfun ); s := s + 1; od; # add to orbit Append( orbit, t ); # add to transversal if s > 1 then Add( trans, gens[i]^-1 ); Add( trels, s ); fi; # compute stabiliser element if rels[i] = 0 or s < rels[i] then g := gens[i]^s; if j > 1 then t := TransversalInverse(j, trels); g := g * SubsWord( t, trans ); fi; f := op( e, InducedByPcp( Pcp(S), g, actS ) ); g := g * orbfun( K, actK, f, e ); Add( stab, g ); fi; od; return rec( stab := Reversed( stab ), orbit := orbit, trels := trels, trans := trans ); end; ############################################################################# ## #F StabilizerModPrime( G, mats, e, p ) ## StabilizerModPrime := function( G, mats, e, p ) local F, t, S; F := GF(p); t := InducedByField( mats, F ); S := PcpOrbitStabilizer( e*One(F), Pcp(G), t, OnRight ); return SubgroupByIgs( G, S.stab ); end; ############################################################################# ## #F StabilizerIntegralAction( G, mats, e ) . . . . . . . . . . . . . Stab_G(e) ## # FIXME: This function is documented and should be turned into a GlobalFunction StabilizerIntegralAction := function( G, mats, e ) local p, S, actS, K, actK, T, stab, ser, orbf; # reduce e e := e / Gcd( e ); # catch the trivial case if ForAll( mats, x -> e*x = e ) then return G; fi; # compute modulo 3 first S := G; actS := mats; for p in USED_PRIMES@ do Info( InfoIntStab, 1, "reducing by stabilizer mod ",p); T := StabilizerModPrime( S, actS, e, p ); Info( InfoIntStab, 1, " obtained reduction by ",Index(S,T)); S := T; actS := InducedByPcp( Pcp(G), Pcp(S), mats ); od; # use congruence kernel Info( InfoIntStab, 1, "determining 3-congruence subgroup"); K := KernelOfFiniteMatrixAction( S, actS, GF(3) ); actK := InducedByPcp( Pcp(G), Pcp(K), mats ); Info( InfoIntStab, 1, " obtained subgroup of index ",Index(S,K)); # compute homogeneous series Info( InfoIntStab, 1, "computing module series"); ser := HomogeneousSeriesOfRationalModule( mats, actK, Length(e) ); ser := List( ser, x -> PurifyRationalBase(x) ); # get Stab_K(e) Info( InfoIntStab, 1, "adding stabilizer for congruence subgroup"); T := StabilizerCongruenceAction( K, actK, e, ser ); # set up orbit stabilizer function for K orbf := function( K, actK, a, b ) local o; o := OrbitCongruenceAction( K, actK, a, b, ser ); if IsBool(o) then return o; fi; return o.prei; end; # compute block stabilizer Info( InfoIntStab, 1, "constructing block orbit-stabilizer"); stab := ExtendOrbitStabilizer( e, K, actK, S, actS, orbf, OnRight ); Info( InfoIntStab, 1, " obtained ",Length(stab.orbit)," blocks"); stab := AddIgsToIgs( stab.stab, Igs(T) ); stab := SubgroupByIgs( G, stab ); # do a temporary check if CHECK_INTSTAB@ then Info( InfoIntStab, 1, "checking results"); if not CheckStabilizer(G, stab, mats, e) then Error("wrong stab in integral action"); fi; fi; # now return return stab; end; ############################################################################# ## #F OrbitIntegralAction( G, mats, e, f ) . . . . . . . . . . . . . . .e^g = f? ## ## returns Stab_G(e) and g in G with e^g = f if g exists. ## returns false otherwise. ## # FIXME: This function is documented and should be turned into a GlobalFunction OrbitIntegralAction := function( G, mats, e, f ) local c, F, t, os, j, g, S, actS, K, actK, ser, orbf, h, T, l; # reduce e and f c := Gcd(e); e := e/c; f := f/c; if not ForAll( f, IsInt ) or AbsInt(Gcd(f)) <> 1 then return false; fi; # catch some trivial cases if e = f then return rec( stab := StabilizerIntegralAction(G, mats, e), prei := One( G ) ); fi; if RankMat( [e,f] ) = 1 or ForAll( mats, x -> e*x = e) then return false; fi; # compute modulo 3 first Info( InfoIntStab, 1, "reducing by orbit-stabilizer mod 3"); F := GF(3); t := InducedByField( mats, F ); os := PcpOrbitStabilizer( e*One(F), Pcp(G), t, OnRight ); j := Position( os.orbit, f*One(F) ); if IsBool(j) then return false; fi; # extract infos g := TransversalElement( j, os, One(G) ); l := f * InducedByPcp( Pcp(G), g, mats )^-1; S := SubgroupByIgs( G, os.stab ); actS := InducedByPcp( Pcp(G), Pcp(S), mats ); # use congruence kernel Info( InfoIntStab, 1, "determining 3-congruence subgroup"); K := KernelOfFiniteMatrixAction( S, actS, F ); actK := InducedByPcp( Pcp(G), Pcp(K), mats ); # compute homogeneous series Info( InfoIntStab, 1, "computing module series"); ser := HomogeneousSeriesOfRationalModule( mats, actK, Length(e) ); ser := List( ser, x -> PurifyRationalBase(x) ); # set up orbit stabilizer function for K orbf := function( K, actK, a, b ) local o; o := OrbitCongruenceAction( K, actK, a, b, ser ); if IsBool(o) then return o; fi; return o.prei; end; # determine block orbit and stabilizer Info( InfoIntStab, 1, "constructing block orbit-stabilizer"); os := ExtendOrbitStabilizer( e, K, actK, S, actS, orbf, OnRight ); # get orbit element and preimage j := FindPosition( os.orbit, l, K, actK, orbf ); if IsBool(j) then return false; fi; h := TransversalElement( j, os, One(G) ); l := l * InducedByPcp( Pcp(S), h, actS )^-1; g := orbf( K, actK, e, l ) * h * g; # get Stab_K(e) and thus Stab_G(e) Info( InfoIntStab, 1, "adding stabilizer for congruence subgroup"); T := StabilizerCongruenceAction( K, actK, e, ser ); t := AddIgsToIgs( os.stab, Igs(T) ); T := SubgroupByIgs( T, t ); # do a temporary check if CHECK_INTSTAB@ then Info( InfoIntStab, 1, "checking results"); if not CheckStabilizer(G, T, mats, e) then Error("wrong stab in integral action"); elif not CheckOrbit(G, g, mats, e, f) then Error("wrong orbit in integral action"); fi; fi; # now return return rec( stab := T, prei := g ); end; polycyclic-2.16/gap/action/dixon.gi0000644000076600000240000001225213706672341016324 0ustar mhornstaff############################################################################# ## #W dixon.gi Bettina Eick ## ## Determine Dixon's Bound for torsion free semisimple matrix groups. ## ############################################################################# ## #F PadicValue( rat, p ) ## PadicValue := function( rat, p ) local a1, a2; a1 := AbsInt( NumeratorRat(rat) ); a2 := DenominatorRat(rat); a1 := Length( Filtered( FactorsInt(a1), x -> x = p ) ); a2 := Length( Filtered( FactorsInt(a2), x -> x = p ) ); return a1 - a2; end; ############################################################################# ## #F LogAbsValueBound( rat ) ## LogAbsValueBound := function( rat ) local a1, a2, a; a1 := LogInt( AbsInt( NumeratorRat(rat) ), 2 ); a2 := LogInt( DenominatorRat(rat), 2 ); a := Maximum( AbsInt( a1 - a2 + 1 ), AbsInt( a1 - a2 - 1) ); return QuoInt( a * 3, 4 ); end; ############################################################################# ## #F ConsideredPrimes( rats ) ## ConsideredPrimes := function( rats ) local pr, r, a1, a2, tmp; pr := []; for r in rats do a1 := AbsInt( NumeratorRat(r) ); a2 := DenominatorRat(r); if a1 <> 1 then tmp := FactorsInt( a1: RhoTrials := 1000000 ); pr := Union( pr, tmp ); fi; if a2 <> 1 then tmp := FactorsInt( a2: RhoTrials := 1000000 ); pr := Union( pr, tmp ); fi; od; return pr; end; ############################################################################# ## #F CoefficientsByBase( base, vec ) ## CoefficientsByBase := function( base, vec ) local sol; sol := MemberBySemiEchelonBase( vec, base.vectors ); if IsBool( sol ) then return fail; fi; return sol * base.coeffs; end; ############################################################################# ## #F FullDixonBound( gens, prim ) ## FullDixonBound := function( gens, prim ) local c, f, j, n, d, minp, sub, max, cof, deg, base, cofs, dofs, g, pr, t1, p, s, i, a, b, t2, t; # set up c := prim.elem; f := prim.poly; n := Length( gens ); d := Degree(f); cof := CoefficientsOfUnivariatePolynomial( f ); if cof[1] <> 1 or cof[d+1] <> 1 then return fail; fi; # get prim-basis # Print("compute prim-base \n"); base := List([0..d-1], x -> Flat(c^x)); base := SemiEchelonMatTransformation( base ); # get coeffs of gens in prim-base Print("compute coefficients \n"); cofs := []; dofs := []; for g in gens do Add( cofs, CoefficientsByBase( base, Flat( g ) ) ); Add( dofs, CoefficientsByBase( base, Flat( g^-1 ) ) ); od; Print("compute relevant primes \n"); pr := ConsideredPrimes( Flat( Concatenation( cofs, dofs ) ) ); # first consider p-adic case Print("p-adic valuations \n"); t1 := 0; for p in pr do s := 0; for i in [1..n] do a := AbsInt( Minimum( List( cofs[i], x -> PadicValue(x,p) ) ) ); b := AbsInt( Minimum( List( dofs[i], x -> PadicValue(x,p) ) ) ); s := s + Maximum( a, b ); od; t1 := Maximum( t1, s ); od; t1 := d * t1; Print("non-archimedian: ", t1,"\n"); # then the log-value Print("logarithmic valuations \n"); t := Maximum( List( cof, x -> LogAbsValueBound( 1+AbsInt(x) ) ) ); t2 := 0; for i in [1..n] do if gens[i] = c then t2 := t2 + t; else a := LogAbsValueBound( Sum( AbsInt( cofs[i] ) ) ); b := LogAbsValueBound( Sum( AbsInt( dofs[i] ) ) ); t2 := t2 + (d-1) * t + Maximum( a, b ); fi; od; t2 := QuoInt( 3 * 7 * d^2 * t2, 2 * LogInt(d,2) ); Print("archimedian: ", t2,"\n"); t := Maximum( t1, t2 ); return QuoInt( t^n + 1, t ); end; ############################################################################# ## #F LogDixonBound( gens, prim ) ## LogDixonBound := function( gens, prim ) local c, f, d, base, cofs, dofs, g, t, s, i, a, b; # set up c := prim.elem; f := CoefficientsOfUnivariatePolynomial( prim.poly ); d := Length( f ) - 1; if f[1] <> 1 or f[d+1] <> 1 then return fail; fi; # get prim-basis # Print("compute prim-base \n"); base := List([0..d-1], x -> Flat(c^x)); base := SemiEchelonMatTransformation( base ); # get coeffs of gens in prim-base # Print("compute coefficients \n"); cofs := []; dofs := []; for g in gens do Add( cofs, CoefficientsByBase( base, Flat( g ) ) ); Add( dofs, CoefficientsByBase( base, Flat( g^-1 ) ) ); od; # get log-value # Print("logarithmic valuation \n"); t := Maximum( List( f, x -> LogAbsValueBound( 1+AbsInt(x) ) ) ); s := 0; for i in [1..Length(gens)] do if gens[i] = c then s := s + t; else a := LogAbsValueBound( Sum( AbsInt( cofs[i] ) ) ); b := LogAbsValueBound( Sum( AbsInt( dofs[i] ) ) ); s := s + (d-1) * t + Maximum( a, b ); fi; od; # now determine final value t := 7 * d^2 * s / QuoInt( 2 * LogInt(d,2), 3 ); return QuoInt( t^Length(gens) + 1, t ); end; polycyclic-2.16/gap/exam/0000755000076600000240000000000013706672341014335 5ustar mhornstaffpolycyclic-2.16/gap/exam/nqlib.gi0000644000076600000240000007630713706672341016000 0ustar mhornstaff############################################################################# ## #W nqlib.gi Polycyc Werner Nickel ## ############################################################################# ## #W NqExamples( n ) ## InstallGlobalFunction( NqExamples, function( n ) local NqF, NqColl; if n = 1 then NqF := FreeGroup( 24 ); NqColl := FromTheLeftCollector( NqF ); SetRelativeOrder( NqColl, 11, 5 ); SetRelativeOrder( NqColl, 12, 4 ); SetRelativeOrder( NqColl, 14, 5 ); SetRelativeOrder( NqColl, 15, 5 ); SetRelativeOrder( NqColl, 16, 4 ); SetRelativeOrder( NqColl, 18, 6 ); SetRelativeOrder( NqColl, 19, 5 ); SetRelativeOrder( NqColl, 20, 5 ); SetRelativeOrder( NqColl, 21, 4 ); SetRelativeOrder( NqColl, 23, 10 ); SetRelativeOrder( NqColl, 24, 6 ); SetPower( NqColl, 11, NqF.19^2*NqF.20^4*NqF.21^2*NqF.22^4*NqF.24^4 ); SetPower( NqColl, 12, NqF.13^2*NqF.15*NqF.16^3*NqF.17^-6*NqF.18^4*\ NqF.19^3*NqF.21^3*NqF.22^12*NqF.23^8*NqF.24^4 ); SetPower( NqColl, 16, NqF.17^2*NqF.20*NqF.21^3*NqF.22^-6*NqF.24^4 ); SetPower( NqColl, 18, NqF.23*NqF.24^4 ); SetPower( NqColl, 21, NqF.22^2 ); SetPower( NqColl, 23, NqF.24^2 ); SetConjugate( NqColl, 2, 1, NqF.2*NqF.3 ); SetConjugate( NqColl, 2, -1, NqF.2*NqF.3^-1*NqF.4*NqF.5^-1*NqF.6*NqF.7*\ NqF.8^-3*NqF.9^10*NqF.10^-1*NqF.11*NqF.13^5*NqF.14^4*NqF.15*NqF.16^2*NqF.17^-23*\ NqF.18^5*NqF.19^2*NqF.20^2*NqF.21*NqF.22^54*NqF.23^5*NqF.24 ); SetConjugate( NqColl, -2, 1, NqF.2^-1*NqF.3^-1 ); SetConjugate( NqColl, -2, -1, NqF.2^-1*NqF.3*NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*\ NqF.11^2*NqF.12^2*NqF.13^-1*NqF.14^2*NqF.18^5*NqF.19*NqF.20^3*NqF.21^3*\ NqF.22^-2 ); SetConjugate( NqColl, 3, 1, NqF.3*NqF.4 ); SetConjugate( NqColl, 3, -1, NqF.3*NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*NqF.11^2*\ NqF.12^2*NqF.13^-1*NqF.14^2*NqF.18^5*NqF.19*NqF.20^3*NqF.21^3*NqF.22^-2 ); SetConjugate( NqColl, -3, 1, NqF.3^-1*NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*\ NqF.10*NqF.11^4*NqF.12*NqF.13^-6*NqF.14^3*NqF.16^2*NqF.17^19*NqF.19*NqF.20^3*\ NqF.22^-50*NqF.23^4 ); SetConjugate( NqColl, -3, -1, NqF.3^-1*NqF.4*NqF.5^-1*NqF.6*NqF.7*NqF.8^-3*\ NqF.9^10*NqF.10^-1*NqF.11*NqF.13^5*NqF.14^4*NqF.15*NqF.16^2*NqF.17^-23*\ NqF.18^5*NqF.19^2*NqF.20^2*NqF.21*NqF.22^54*NqF.23^5*NqF.24 ); SetConjugate( NqColl, 3, 2, NqF.3 ); SetConjugate( NqColl, 3, -2, NqF.3 ); SetConjugate( NqColl, -3, 2, NqF.3^-1 ); SetConjugate( NqColl, -3, -2, NqF.3^-1 ); SetConjugate( NqColl, 4, 1, NqF.4*NqF.5 ); SetConjugate( NqColl, 4, -1, NqF.4*NqF.5^-1*NqF.6*NqF.14^3*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, -4, 1, NqF.4^-1*NqF.5^-1*NqF.9^-3*NqF.12^2*NqF.13^-1*\ NqF.14*NqF.15^4*NqF.16^3*NqF.17^3*NqF.18^3*NqF.19^4*NqF.20*NqF.21^2*NqF.22^-10*\ NqF.24^5 ); SetConjugate( NqColl, -4, -1, NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*NqF.11^2*\ NqF.12^2*NqF.13^-1*NqF.14^2*NqF.18^5*NqF.19*NqF.20^3*NqF.21^3*NqF.22^-2 ); SetConjugate( NqColl, 4, 2, NqF.4*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4*NqF.14^2*NqF.15*NqF.16^2*NqF.17^-18*NqF.18^5*NqF.19*NqF.20*NqF.21*\ NqF.22^42*NqF.23^5*NqF.24^2 ); SetConjugate( NqColl, 4, -2, NqF.4*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10^2*\ NqF.11^4*NqF.12*NqF.13^-6*NqF.14^3*NqF.15^3*NqF.16^3*NqF.17^20*NqF.18*NqF.20^3*\ NqF.22^-52*NqF.24^4 ); SetConjugate( NqColl, -4, 2, NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10*\ NqF.11^4*NqF.12*NqF.13^-6*NqF.14^3*NqF.16^2*NqF.17^19*NqF.19*NqF.20^3*NqF.22^-50*\ NqF.23^4 ); SetConjugate( NqColl, -4, -2, NqF.4^-1*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-2*\ NqF.11*NqF.12^2*NqF.13^6*NqF.14^2*NqF.16^2*NqF.17^-21*NqF.18^2*NqF.19*NqF.20^2*\ NqF.21^3*NqF.22^48*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, 4, 3, NqF.4*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10^2*\ NqF.11^4*NqF.12*NqF.13^-6*NqF.14^3*NqF.15^3*NqF.16^3*NqF.17^20*NqF.18*NqF.20^3*\ NqF.22^-52*NqF.24^4 ); SetConjugate( NqColl, 4, -3, NqF.4*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4*NqF.14^2*NqF.15*NqF.16^2*NqF.17^-18*NqF.18^5*NqF.19*NqF.20*NqF.21*\ NqF.22^42*NqF.23^5*NqF.24^2 ); SetConjugate( NqColl, -4, 3, NqF.4^-1*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-2*\ NqF.11*NqF.12^2*NqF.13^6*NqF.14^2*NqF.16^2*NqF.17^-21*NqF.18^2*NqF.19*NqF.20^2*\ NqF.21^3*NqF.22^48*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, -4, -3, NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10*\ NqF.11^4*NqF.12*NqF.13^-6*NqF.14^3*NqF.16^2*NqF.17^19*NqF.19*NqF.20^3*NqF.22^-50*\ NqF.23^4 ); SetConjugate( NqColl, 5, 1, NqF.5*NqF.6 ); SetConjugate( NqColl, 5, -1, NqF.5*NqF.6^-1 ); SetConjugate( NqColl, -5, 1, NqF.5^-1*NqF.6^-1*NqF.14^2*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, -5, -1, NqF.5^-1*NqF.6*NqF.14^3*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, 5, 2, NqF.5*NqF.7 ); SetConjugate( NqColl, 5, -2, NqF.5*NqF.7^-1*NqF.10^3*NqF.12^2*NqF.13^-6*\ NqF.15*NqF.16^3*NqF.17^12*NqF.18*NqF.20^2*NqF.21^2*NqF.22^-28*NqF.23^9 ); SetConjugate( NqColl, -5, 2, NqF.5^-1*NqF.7^-1*NqF.15^4*NqF.16^3*NqF.17^-3*\ NqF.19^3*NqF.20*NqF.21*NqF.22^4*NqF.23^3 ); SetConjugate( NqColl, -5, -2, NqF.5^-1*NqF.7*NqF.10^-3*NqF.12^2*NqF.13^4*\ NqF.15^4*NqF.16^3*NqF.17^-9*NqF.18*NqF.19^4*NqF.20^4*NqF.21*NqF.22^22*NqF.23^9*\ NqF.24^4 ); SetConjugate( NqColl, 5, 3, NqF.5*NqF.8^-1*NqF.9^5*NqF.11^3*NqF.12^3*\ NqF.13^-1*NqF.14^3*NqF.15*NqF.17^-4*NqF.18^4*NqF.19^4*NqF.22^8*NqF.23^9*\ NqF.24^3 ); SetConjugate( NqColl, 5, -3, NqF.5*NqF.8*NqF.9^-5*NqF.11^2*NqF.12^2*NqF.13^-2*\ NqF.14^2*NqF.15^2*NqF.16^3*NqF.17^10*NqF.18^3*NqF.19^4*NqF.20^3*NqF.21^3*\ NqF.22^-28*NqF.23*NqF.24^3 ); SetConjugate( NqColl, -5, 3, NqF.5^-1*NqF.8*NqF.9^-5*NqF.11^2*NqF.12*NqF.13^-1*\ NqF.14^2*NqF.15^3*NqF.16*NqF.17^8*NqF.18^4*NqF.19^2*NqF.20^2*NqF.21^3*NqF.22^-23*\ NqF.23*NqF.24^3 ); SetConjugate( NqColl, -5, -3, NqF.5^-1*NqF.8^-1*NqF.9^5*NqF.11^3*NqF.12^2*\ NqF.14^3*NqF.15^2*NqF.16^2*NqF.17^-8*NqF.18^5*NqF.20^4*NqF.21^3*NqF.22^15*\ NqF.23^9*NqF.24^5 ); SetConjugate( NqColl, 5, 4, NqF.5*NqF.9^-3*NqF.12^2*NqF.13^-1*NqF.14*\ NqF.15^4*NqF.16^3*NqF.17^3*NqF.18^3*NqF.19*NqF.20^2*NqF.21^3*NqF.22^-12*\ NqF.24^5 ); SetConjugate( NqColl, 5, -4, NqF.5*NqF.9^3*NqF.12^2*NqF.13^-1*NqF.14^4*\ NqF.16^2*NqF.17^-1*NqF.18^5*NqF.19^3*NqF.20^2*NqF.21*NqF.22^4*NqF.24^3 ); SetConjugate( NqColl, -5, 4, NqF.5^-1*NqF.9^3*NqF.12^2*NqF.13^-1*NqF.14^4*\ NqF.16^2*NqF.17^-1*NqF.18^5*NqF.19*NqF.20*NqF.22^6*NqF.24^3 ); SetConjugate( NqColl, -5, -4, NqF.5^-1*NqF.9^-3*NqF.12^2*NqF.13^-1*NqF.14*\ NqF.15^4*NqF.16^3*NqF.17^3*NqF.18^3*NqF.19^4*NqF.20*NqF.21^2*NqF.22^-10*\ NqF.24^5 ); SetConjugate( NqColl, 6, 1, NqF.6 ); SetConjugate( NqColl, 6, -1, NqF.6 ); SetConjugate( NqColl, -6, 1, NqF.6^-1 ); SetConjugate( NqColl, -6, -1, NqF.6^-1 ); SetConjugate( NqColl, 6, 2, NqF.6*NqF.8^2*NqF.9^-7*NqF.10*NqF.11^4*NqF.13^-4*\ NqF.14^3*NqF.15^4*NqF.16^2*NqF.17^16*NqF.18*NqF.19*NqF.20^3*NqF.22^-42*\ NqF.23^4*NqF.24^2 ); SetConjugate( NqColl, 6, -2, NqF.6*NqF.8^-2*NqF.9^7*NqF.10*NqF.11*NqF.12*\ NqF.14^2*NqF.15^3*NqF.17^-8*NqF.19^4*NqF.20*NqF.21^2*NqF.22^18*NqF.23^9*\ NqF.24^4 ); SetConjugate( NqColl, -6, 2, NqF.6^-1*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4*NqF.14^2*NqF.15*NqF.16^2*NqF.17^-18*NqF.18^5*NqF.19^2*NqF.20^2*\ NqF.21^3*NqF.22^40*NqF.23^5*NqF.24^2 ); SetConjugate( NqColl, -6, -2, NqF.6^-1*NqF.8^2*NqF.9^-7*NqF.10^-1*NqF.11^4*\ NqF.12^3*NqF.13^-2*NqF.14^3*NqF.15*NqF.16*NqF.17^12*NqF.18^2*NqF.19*NqF.20^4*\ NqF.21^2*NqF.22^-34*NqF.23^2 ); SetConjugate( NqColl, 6, 3, NqF.6*NqF.9^2*NqF.11^3*NqF.12^2*NqF.13^-1*\ NqF.14^4*NqF.15*NqF.16*NqF.18^5*NqF.19^4*NqF.22^-2*NqF.23^7*NqF.24^5 ); SetConjugate( NqColl, 6, -3, NqF.6*NqF.9^-2*NqF.11^2*NqF.12^2*NqF.13^-1*\ NqF.14*NqF.15*NqF.16^2*NqF.17^4*NqF.18^3*NqF.20*NqF.22^-12*NqF.23^4 ); SetConjugate( NqColl, -6, 3, NqF.6^-1*NqF.9^-2*NqF.11^2*NqF.12^2*NqF.13^-1*\ NqF.14*NqF.15^3*NqF.17^4*NqF.18^3*NqF.19*NqF.22^-12*NqF.23^3*NqF.24 ); SetConjugate( NqColl, -6, -3, NqF.6^-1*NqF.9^2*NqF.11^3*NqF.12^2*NqF.13^-1*\ NqF.14^4*NqF.15^3*NqF.16^3*NqF.17^-2*NqF.18^5*NqF.20^3*NqF.21*NqF.22^2*\ NqF.23^6*NqF.24^2 ); SetConjugate( NqColl, 6, 4, NqF.6*NqF.11^2*NqF.14^3*NqF.16^2*NqF.17^-1*\ NqF.19^3*NqF.21^3*NqF.22^-2*NqF.24^5 ); SetConjugate( NqColl, 6, -4, NqF.6*NqF.11^3*NqF.14^2*NqF.16^2*NqF.17^-1*\ NqF.24^5 ); SetConjugate( NqColl, -6, 4, NqF.6^-1*NqF.11^3*NqF.14^2*NqF.16^2*NqF.17^-1*\ NqF.24^5 ); SetConjugate( NqColl, -6, -4, NqF.6^-1*NqF.11^2*NqF.14^3*NqF.16^2*NqF.17^-1*\ NqF.19^3*NqF.21^3*NqF.22^-2*NqF.24^5 ); SetConjugate( NqColl, 6, 5, NqF.6*NqF.14^2*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, 6, -5, NqF.6*NqF.14^3*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, -6, 5, NqF.6^-1*NqF.14^3*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, -6, -5, NqF.6^-1*NqF.14^2*NqF.21^2*NqF.22^-1 ); SetConjugate( NqColl, 7, 1, NqF.7*NqF.8 ); SetConjugate( NqColl, 7, -1, NqF.7*NqF.8^-1*NqF.9*NqF.11^4*NqF.14*NqF.19^3*\ NqF.20*NqF.21^2*NqF.22^-6*NqF.24^2 ); SetConjugate( NqColl, -7, 1, NqF.7^-1*NqF.8^-1 ); SetConjugate( NqColl, -7, -1, NqF.7^-1*NqF.8*NqF.9^-1*NqF.11*NqF.14^4 ); SetConjugate( NqColl, 7, 2, NqF.7*NqF.10^3*NqF.12^2*NqF.13^-6*NqF.15*\ NqF.16^3*NqF.17^12*NqF.18^2*NqF.20^2*NqF.21^2*NqF.22^-28*NqF.23*NqF.24^4 ); SetConjugate( NqColl, 7, -2, NqF.7*NqF.10^-3*NqF.12^2*NqF.13^4*NqF.15^3*\ NqF.16^2*NqF.17^-10*NqF.18*NqF.19^2*NqF.20*NqF.21*NqF.22^22*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, -7, 2, NqF.7^-1*NqF.10^-3*NqF.12^2*NqF.13^4*NqF.15^3*\ NqF.16^2*NqF.17^-10*NqF.19^2*NqF.20*NqF.21*NqF.22^22*NqF.24^2 ); SetConjugate( NqColl, -7, -2, NqF.7^-1*NqF.10^3*NqF.12^2*NqF.13^-6*NqF.15*\ NqF.16^3*NqF.17^12*NqF.18*NqF.20^2*NqF.21^2*NqF.22^-28*NqF.23^9 ); SetConjugate( NqColl, 7, 3, NqF.7*NqF.10^-1*NqF.12*NqF.13^2*NqF.15^4*\ NqF.16^3*NqF.17^-6*NqF.18^3*NqF.19*NqF.21^3*NqF.22^12*NqF.23^7*NqF.24^4 ); SetConjugate( NqColl, 7, -3, NqF.7*NqF.10*NqF.12^3*NqF.13^-4*NqF.16^2*\ NqF.17^8*NqF.19*NqF.20^3*NqF.22^-18*NqF.23^8*NqF.24^4 ); SetConjugate( NqColl, -7, 3, NqF.7^-1*NqF.10*NqF.12^3*NqF.13^-4*NqF.16^2*\ NqF.17^8*NqF.18^5*NqF.19*NqF.20^3*NqF.22^-18*NqF.23^3*NqF.24^2 ); SetConjugate( NqColl, -7, -3, NqF.7^-1*NqF.10^-1*NqF.12*NqF.13^2*NqF.15^4*\ NqF.16^3*NqF.17^-6*NqF.18^2*NqF.19*NqF.21^3*NqF.22^12*NqF.23^3 ); SetConjugate( NqColl, 7, 4, NqF.7*NqF.12*NqF.13^-2*NqF.15^3*NqF.17^3*\ NqF.18^5*NqF.19^3*NqF.22^-8*NqF.23^4*NqF.24^2 ); SetConjugate( NqColl, 7, -4, NqF.7*NqF.12^3*NqF.15*NqF.16*NqF.17*NqF.18^3*\ NqF.19^4*NqF.20^4*NqF.21^2*NqF.22^-2*NqF.23^6*NqF.24^2 ); SetConjugate( NqColl, -7, 4, NqF.7^-1*NqF.12^3*NqF.15*NqF.16*NqF.17*NqF.18^3*\ NqF.19^4*NqF.20^4*NqF.21^2*NqF.22^-2*NqF.23^6*NqF.24^2 ); SetConjugate( NqColl, -7, -4, NqF.7^-1*NqF.12*NqF.13^-2*NqF.15^3*NqF.17^3*\ NqF.18^5*NqF.19^3*NqF.22^-8*NqF.23^4*NqF.24^2 ); SetConjugate( NqColl, 7, 5, NqF.7*NqF.15^4*NqF.16^3*NqF.17^-3*NqF.19^3*\ NqF.20*NqF.21*NqF.22^4*NqF.23^3 ); SetConjugate( NqColl, 7, -5, NqF.7*NqF.15*NqF.16*NqF.17*NqF.19^2*NqF.20^3*\ NqF.23^7 ); SetConjugate( NqColl, -7, 5, NqF.7^-1*NqF.15*NqF.16*NqF.17*NqF.19^2*NqF.20^3*\ NqF.23^7 ); SetConjugate( NqColl, -7, -5, NqF.7^-1*NqF.15^4*NqF.16^3*NqF.17^-3*NqF.19^3*\ NqF.20*NqF.21*NqF.22^4*NqF.23^3 ); SetConjugate( NqColl, 7, 6, NqF.7*NqF.19*NqF.20*NqF.21^2*NqF.22^-2 ); SetConjugate( NqColl, 7, -6, NqF.7*NqF.19^4*NqF.20^4*NqF.21^2 ); SetConjugate( NqColl, -7, 6, NqF.7^-1*NqF.19^4*NqF.20^4*NqF.21^2 ); SetConjugate( NqColl, -7, -6, NqF.7^-1*NqF.19*NqF.20*NqF.21^2*NqF.22^-2 ); SetConjugate( NqColl, 8, 1, NqF.8*NqF.9 ); SetConjugate( NqColl, 8, -1, NqF.8*NqF.9^-1*NqF.11*NqF.14^4 ); SetConjugate( NqColl, -8, 1, NqF.8^-1*NqF.9^-1 ); SetConjugate( NqColl, -8, -1, NqF.8^-1*NqF.9*NqF.11^4*NqF.14*NqF.19^3*NqF.20*\ NqF.21^2*NqF.22^-6*NqF.24^2 ); SetConjugate( NqColl, 8, 2, NqF.8*NqF.10 ); SetConjugate( NqColl, 8, -2, NqF.8*NqF.10^-1*NqF.18*NqF.23^7*NqF.24^2 ); SetConjugate( NqColl, -8, 2, NqF.8^-1*NqF.10^-1 ); SetConjugate( NqColl, -8, -2, NqF.8^-1*NqF.10*NqF.18^5*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, 8, 3, NqF.8*NqF.12^3*NqF.13^-1*NqF.17^4*NqF.18^3*\ NqF.19*NqF.21^2*NqF.22^-10*NqF.24^4 ); SetConjugate( NqColl, 8, -3, NqF.8*NqF.12*NqF.13^-1*NqF.15^4*NqF.16*NqF.18^5*\ NqF.19*NqF.20^4*NqF.23^2*NqF.24^5 ); SetConjugate( NqColl, -8, 3, NqF.8^-1*NqF.12*NqF.13^-1*NqF.15^4*NqF.16*\ NqF.18^5*NqF.19*NqF.20^4*NqF.24^2 ); SetConjugate( NqColl, -8, -3, NqF.8^-1*NqF.12^3*NqF.13^-1*NqF.17^4*NqF.18^3*\ NqF.19*NqF.21^2*NqF.22^-10*NqF.23^8*NqF.24^5 ); SetConjugate( NqColl, 8, 4, NqF.8*NqF.15*NqF.16^2*NqF.17^-1*NqF.19^3*\ NqF.20^3*NqF.21^3*NqF.22^2*NqF.23^7*NqF.24^5 ); SetConjugate( NqColl, 8, -4, NqF.8*NqF.15^4*NqF.16^2*NqF.17^-1*NqF.19^2*\ NqF.20*NqF.21^2*NqF.23^3*NqF.24 ); SetConjugate( NqColl, -8, 4, NqF.8^-1*NqF.15^4*NqF.16^2*NqF.17^-1*NqF.19^2*\ NqF.20*NqF.21^2*NqF.23^3*NqF.24 ); SetConjugate( NqColl, -8, -4, NqF.8^-1*NqF.15*NqF.16^2*NqF.17^-1*NqF.19^3*\ NqF.20^3*NqF.21^3*NqF.22^2*NqF.23^7*NqF.24^5 ); SetConjugate( NqColl, 8, 5, NqF.8*NqF.19^4*NqF.20^3*NqF.21*NqF.22^-1 ); SetConjugate( NqColl, 8, -5, NqF.8*NqF.19*NqF.20^2*NqF.21^3*NqF.22^-1 ); SetConjugate( NqColl, -8, 5, NqF.8^-1*NqF.19*NqF.20^2*NqF.21^3*NqF.22^-1 ); SetConjugate( NqColl, -8, -5, NqF.8^-1*NqF.19^4*NqF.20^3*NqF.21*NqF.22^-1 ); SetConjugate( NqColl, 9, 1, NqF.9*NqF.11 ); SetConjugate( NqColl, 9, -1, NqF.9*NqF.11^4*NqF.14*NqF.19^3*NqF.20*NqF.21^2*\ NqF.22^-6*NqF.24^2 ); SetConjugate( NqColl, -9, 1, NqF.9^-1*NqF.11^4*NqF.19^3*NqF.20*NqF.21^2*\ NqF.22^-6*NqF.24^2 ); SetConjugate( NqColl, -9, -1, NqF.9^-1*NqF.11*NqF.14^4 ); SetConjugate( NqColl, 9, 2, NqF.9*NqF.12 ); SetConjugate( NqColl, 9, -2, NqF.9*NqF.12^3*NqF.13^-2*NqF.15^4*NqF.16*\ NqF.17^4*NqF.18^4*NqF.19^2*NqF.20^4*NqF.21^2*NqF.22^-10*NqF.23^3 ); SetConjugate( NqColl, -9, 2, NqF.9^-1*NqF.12^3*NqF.13^-2*NqF.15^4*NqF.16*\ NqF.17^4*NqF.18^2*NqF.19^2*NqF.20^4*NqF.21^2*NqF.22^-10*NqF.23*NqF.24^4 ); SetConjugate( NqColl, -9, -2, NqF.9^-1*NqF.12*NqF.18^4*NqF.23^7*NqF.24^4 ); SetConjugate( NqColl, 9, 3, NqF.9*NqF.15^4*NqF.16*NqF.19*NqF.20^4*NqF.23^3*\ NqF.24^2 ); SetConjugate( NqColl, 9, -3, NqF.9*NqF.15*NqF.16^3*NqF.17^-2*NqF.19^4*\ NqF.21*NqF.22^4*NqF.23^7*NqF.24^4 ); SetConjugate( NqColl, -9, 3, NqF.9^-1*NqF.15*NqF.16^3*NqF.17^-2*NqF.19^4*\ NqF.21*NqF.22^4*NqF.23^7*NqF.24^4 ); SetConjugate( NqColl, -9, -3, NqF.9^-1*NqF.15^4*NqF.16*NqF.19*NqF.20^4*\ NqF.23^3*NqF.24^2 ); SetConjugate( NqColl, 9, 4, NqF.9*NqF.19*NqF.20^3*NqF.21 ); SetConjugate( NqColl, 9, -4, NqF.9*NqF.19^4*NqF.20^2*NqF.21^3*NqF.22^-2 ); SetConjugate( NqColl, -9, 4, NqF.9^-1*NqF.19^4*NqF.20^2*NqF.21^3*NqF.22^-2 ); SetConjugate( NqColl, -9, -4, NqF.9^-1*NqF.19*NqF.20^3*NqF.21 ); SetConjugate( NqColl, 10, 1, NqF.10*NqF.13 ); SetConjugate( NqColl, 10, -1, NqF.10*NqF.13^-1*NqF.17*NqF.22^-1 ); SetConjugate( NqColl, -10, 1, NqF.10^-1*NqF.13^-1 ); SetConjugate( NqColl, -10, -1, NqF.10^-1*NqF.13*NqF.17^-1*NqF.22 ); SetConjugate( NqColl, 10, 2, NqF.10*NqF.18*NqF.23^7*NqF.24^2 ); SetConjugate( NqColl, 10, -2, NqF.10*NqF.18^5*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, -10, 2, NqF.10^-1*NqF.18^5*NqF.23^2*NqF.24^4 ); SetConjugate( NqColl, -10, -2, NqF.10^-1*NqF.18*NqF.23^7*NqF.24^2 ); SetConjugate( NqColl, 10, 3, NqF.10*NqF.18^5*NqF.24^2 ); SetConjugate( NqColl, 10, -3, NqF.10*NqF.18*NqF.23^9*NqF.24^4 ); SetConjugate( NqColl, -10, 3, NqF.10^-1*NqF.18*NqF.23^9*NqF.24^4 ); SetConjugate( NqColl, -10, -3, NqF.10^-1*NqF.18^5*NqF.24^2 ); SetConjugate( NqColl, 10, 4, NqF.10*NqF.23*NqF.24^4 ); SetConjugate( NqColl, 10, -4, NqF.10*NqF.23^9 ); SetConjugate( NqColl, -10, 4, NqF.10^-1*NqF.23^9 ); SetConjugate( NqColl, -10, -4, NqF.10^-1*NqF.23*NqF.24^4 ); SetConjugate( NqColl, 11, 1, NqF.11*NqF.14 ); SetConjugate( NqColl, 11, -1, NqF.11*NqF.14^4 ); SetConjugate( NqColl, 11, 2, NqF.11*NqF.15 ); SetConjugate( NqColl, 11, -2, NqF.11*NqF.15^4*NqF.23^6 ); SetConjugate( NqColl, 11, 3, NqF.11*NqF.19^4*NqF.20 ); SetConjugate( NqColl, 11, -3, NqF.11*NqF.19*NqF.20^4 ); SetConjugate( NqColl, 12, 1, NqF.12*NqF.16 ); SetConjugate( NqColl, 12, -1, NqF.12*NqF.16^3*NqF.17^-2*NqF.20^4*NqF.21^2*\ NqF.22^4*NqF.24^2 ); SetConjugate( NqColl, 12, 2, NqF.12*NqF.18^2*NqF.23^2*NqF.24^2 ); SetConjugate( NqColl, 12, -2, NqF.12*NqF.18^4*NqF.23^7*NqF.24^4 ); SetConjugate( NqColl, 12, 3, NqF.12*NqF.23^7*NqF.24^2 ); SetConjugate( NqColl, 12, -3, NqF.12*NqF.23^3*NqF.24^2 ); SetConjugate( NqColl, 13, 1, NqF.13*NqF.17 ); SetConjugate( NqColl, 13, -1, NqF.13*NqF.17^-1*NqF.22 ); SetConjugate( NqColl, -13, 1, NqF.13^-1*NqF.17^-1 ); SetConjugate( NqColl, -13, -1, NqF.13^-1*NqF.17*NqF.22^-1 ); SetConjugate( NqColl, 13, 2, NqF.13*NqF.18 ); SetConjugate( NqColl, 13, -2, NqF.13*NqF.18^5*NqF.23^9 ); SetConjugate( NqColl, -13, 2, NqF.13^-1*NqF.18^5*NqF.23^9 ); SetConjugate( NqColl, -13, -2, NqF.13^-1*NqF.18 ); SetConjugate( NqColl, 13, 3, NqF.13*NqF.23^9*NqF.24^5 ); SetConjugate( NqColl, 13, -3, NqF.13*NqF.23*NqF.24^5 ); SetConjugate( NqColl, -13, 3, NqF.13^-1*NqF.23*NqF.24^5 ); SetConjugate( NqColl, -13, -3, NqF.13^-1*NqF.23^9*NqF.24^5 ); SetConjugate( NqColl, 14, 1, NqF.14 ); SetConjugate( NqColl, 14, -1, NqF.14 ); SetConjugate( NqColl, 14, 2, NqF.14*NqF.19 ); SetConjugate( NqColl, 14, -2, NqF.14*NqF.19^4 ); SetConjugate( NqColl, 15, 1, NqF.15*NqF.20 ); SetConjugate( NqColl, 15, -1, NqF.15*NqF.20^4 ); SetConjugate( NqColl, 15, 2, NqF.15*NqF.23^6 ); SetConjugate( NqColl, 15, -2, NqF.15*NqF.23^4*NqF.24^4 ); SetConjugate( NqColl, 16, 1, NqF.16*NqF.21 ); SetConjugate( NqColl, 16, -1, NqF.16*NqF.21^3*NqF.22^-2 ); SetConjugate( NqColl, 16, 2, NqF.16*NqF.23^3*NqF.24^4 ); SetConjugate( NqColl, 16, -2, NqF.16*NqF.23^7 ); SetConjugate( NqColl, 17, 1, NqF.17*NqF.22 ); SetConjugate( NqColl, 17, -1, NqF.17*NqF.22^-1 ); SetConjugate( NqColl, -17, 1, NqF.17^-1*NqF.22^-1 ); SetConjugate( NqColl, -17, -1, NqF.17^-1*NqF.22 ); SetConjugate( NqColl, 17, 2, NqF.17*NqF.23 ); SetConjugate( NqColl, 17, -2, NqF.17*NqF.23^9*NqF.24^4 ); SetConjugate( NqColl, -17, 2, NqF.17^-1*NqF.23^9*NqF.24^4 ); SetConjugate( NqColl, -17, -2, NqF.17^-1*NqF.23 ); SetConjugate( NqColl, 18, 1, NqF.18*NqF.24 ); SetConjugate( NqColl, 18, -1, NqF.18*NqF.24^5 ); SetConjugate( NqColl, 18, 2, NqF.18 ); SetConjugate( NqColl, 18, -2, NqF.18 ); return PcpGroupByCollector( NqColl ); elif n = 2 then NqF := FreeGroup( 13 ); NqColl := FromTheLeftCollector( NqF ); SetRelativeOrder( NqColl, 11, 5 ); SetRelativeOrder( NqColl, 12, 4 ); SetPower( NqColl, 12, NqF.13^2 ); SetConjugate( NqColl, 2, 1, NqF.2*NqF.3 ); SetConjugate( NqColl, 2, -1, NqF.2*NqF.3^-1*NqF.4*NqF.5^-1*NqF.6*NqF.7*\ NqF.8^-3*NqF.9^10*NqF.10^-1*NqF.11*NqF.13^5 ); SetConjugate( NqColl, -2, 1, NqF.2^-1*NqF.3^-1 ); SetConjugate( NqColl, -2, -1, NqF.2^-1*NqF.3*NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*\ NqF.11^2*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 3, 1, NqF.3*NqF.4 ); SetConjugate( NqColl, 3, -1, NqF.3*NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*NqF.11^2*\ NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -3, 1, NqF.3^-1*NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*\ NqF.10*NqF.11^4*NqF.12*NqF.13^-6 ); SetConjugate( NqColl, -3, -1, NqF.3^-1*NqF.4*NqF.5^-1*NqF.6*NqF.7*NqF.8^-3*\ NqF.9^10*NqF.10^-1*NqF.11*NqF.13^5 ); SetConjugate( NqColl, 3, 2, NqF.3 ); SetConjugate( NqColl, 3, -2, NqF.3 ); SetConjugate( NqColl, -3, 2, NqF.3^-1 ); SetConjugate( NqColl, -3, -2, NqF.3^-1 ); SetConjugate( NqColl, 4, 1, NqF.4*NqF.5 ); SetConjugate( NqColl, 4, -1, NqF.4*NqF.5^-1*NqF.6 ); SetConjugate( NqColl, -4, 1, NqF.4^-1*NqF.5^-1*NqF.9^-3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -4, -1, NqF.4^-1*NqF.5*NqF.6^-1*NqF.9^3*NqF.11^2*\ NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 4, 2, NqF.4*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4 ); SetConjugate( NqColl, 4, -2, NqF.4*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10^2*\ NqF.11^4*NqF.12*NqF.13^-6 ); SetConjugate( NqColl, -4, 2, NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10*\ NqF.11^4*NqF.12*NqF.13^-6 ); SetConjugate( NqColl, -4, -2, NqF.4^-1*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-2*\ NqF.11*NqF.12^2*NqF.13^6 ); SetConjugate( NqColl, 4, 3, NqF.4*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10^2*\ NqF.11^4*NqF.12*NqF.13^-6 ); SetConjugate( NqColl, 4, -3, NqF.4*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4 ); SetConjugate( NqColl, -4, 3, NqF.4^-1*NqF.7*NqF.8^-2*NqF.9^7*NqF.10^-2*\ NqF.11*NqF.12^2*NqF.13^6 ); SetConjugate( NqColl, -4, -3, NqF.4^-1*NqF.7^-1*NqF.8^2*NqF.9^-7*NqF.10*\ NqF.11^4*NqF.12*NqF.13^-6 ); SetConjugate( NqColl, 5, 1, NqF.5*NqF.6 ); SetConjugate( NqColl, 5, -1, NqF.5*NqF.6^-1 ); SetConjugate( NqColl, -5, 1, NqF.5^-1*NqF.6^-1 ); SetConjugate( NqColl, -5, -1, NqF.5^-1*NqF.6 ); SetConjugate( NqColl, 5, 2, NqF.5*NqF.7 ); SetConjugate( NqColl, 5, -2, NqF.5*NqF.7^-1*NqF.10^3*NqF.12^2*NqF.13^-6 ); SetConjugate( NqColl, -5, 2, NqF.5^-1*NqF.7^-1 ); SetConjugate( NqColl, -5, -2, NqF.5^-1*NqF.7*NqF.10^-3*NqF.12^2*NqF.13^4 ); SetConjugate( NqColl, 5, 3, NqF.5*NqF.8^-1*NqF.9^5*NqF.11^3*NqF.12^3*\ NqF.13^-1 ); SetConjugate( NqColl, 5, -3, NqF.5*NqF.8*NqF.9^-5*NqF.11^2*NqF.12^2*NqF.13^-2 ); SetConjugate( NqColl, -5, 3, NqF.5^-1*NqF.8*NqF.9^-5*NqF.11^2*NqF.12*NqF.13^-1 ); SetConjugate( NqColl, -5, -3, NqF.5^-1*NqF.8^-1*NqF.9^5*NqF.11^3*NqF.12^2 ); SetConjugate( NqColl, 5, 4, NqF.5*NqF.9^-3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 5, -4, NqF.5*NqF.9^3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -5, 4, NqF.5^-1*NqF.9^3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -5, -4, NqF.5^-1*NqF.9^-3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 6, 1, NqF.6 ); SetConjugate( NqColl, 6, -1, NqF.6 ); SetConjugate( NqColl, -6, 1, NqF.6^-1 ); SetConjugate( NqColl, -6, -1, NqF.6^-1 ); SetConjugate( NqColl, 6, 2, NqF.6*NqF.8^2*NqF.9^-7*NqF.10*NqF.11^4*NqF.13^-4 ); SetConjugate( NqColl, 6, -2, NqF.6*NqF.8^-2*NqF.9^7*NqF.10*NqF.11*NqF.12 ); SetConjugate( NqColl, -6, 2, NqF.6^-1*NqF.8^-2*NqF.9^7*NqF.10^-1*NqF.11*\ NqF.13^4 ); SetConjugate( NqColl, -6, -2, NqF.6^-1*NqF.8^2*NqF.9^-7*NqF.10^-1*NqF.11^4*\ NqF.12^3*NqF.13^-2 ); SetConjugate( NqColl, 6, 3, NqF.6*NqF.9^2*NqF.11^3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 6, -3, NqF.6*NqF.9^-2*NqF.11^2*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -6, 3, NqF.6^-1*NqF.9^-2*NqF.11^2*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, -6, -3, NqF.6^-1*NqF.9^2*NqF.11^3*NqF.12^2*NqF.13^-1 ); SetConjugate( NqColl, 6, 4, NqF.6*NqF.11^2 ); SetConjugate( NqColl, 6, -4, NqF.6*NqF.11^3 ); SetConjugate( NqColl, -6, 4, NqF.6^-1*NqF.11^3 ); SetConjugate( NqColl, -6, -4, NqF.6^-1*NqF.11^2 ); SetConjugate( NqColl, 7, 1, NqF.7*NqF.8 ); SetConjugate( NqColl, 7, -1, NqF.7*NqF.8^-1*NqF.9*NqF.11^4 ); SetConjugate( NqColl, -7, 1, NqF.7^-1*NqF.8^-1 ); SetConjugate( NqColl, -7, -1, NqF.7^-1*NqF.8*NqF.9^-1*NqF.11 ); SetConjugate( NqColl, 7, 2, NqF.7*NqF.10^3*NqF.12^2*NqF.13^-6 ); SetConjugate( NqColl, 7, -2, NqF.7*NqF.10^-3*NqF.12^2*NqF.13^4 ); SetConjugate( NqColl, -7, 2, NqF.7^-1*NqF.10^-3*NqF.12^2*NqF.13^4 ); SetConjugate( NqColl, -7, -2, NqF.7^-1*NqF.10^3*NqF.12^2*NqF.13^-6 ); SetConjugate( NqColl, 7, 3, NqF.7*NqF.10^-1*NqF.12*NqF.13^2 ); SetConjugate( NqColl, 7, -3, NqF.7*NqF.10*NqF.12^3*NqF.13^-4 ); SetConjugate( NqColl, -7, 3, NqF.7^-1*NqF.10*NqF.12^3*NqF.13^-4 ); SetConjugate( NqColl, -7, -3, NqF.7^-1*NqF.10^-1*NqF.12*NqF.13^2 ); SetConjugate( NqColl, 7, 4, NqF.7*NqF.12*NqF.13^-2 ); SetConjugate( NqColl, 7, -4, NqF.7*NqF.12^3 ); SetConjugate( NqColl, -7, 4, NqF.7^-1*NqF.12^3 ); SetConjugate( NqColl, -7, -4, NqF.7^-1*NqF.12*NqF.13^-2 ); SetConjugate( NqColl, 8, 1, NqF.8*NqF.9 ); SetConjugate( NqColl, 8, -1, NqF.8*NqF.9^-1*NqF.11 ); SetConjugate( NqColl, -8, 1, NqF.8^-1*NqF.9^-1 ); SetConjugate( NqColl, -8, -1, NqF.8^-1*NqF.9*NqF.11^4 ); SetConjugate( NqColl, 8, 2, NqF.8*NqF.10 ); SetConjugate( NqColl, 8, -2, NqF.8*NqF.10^-1 ); SetConjugate( NqColl, -8, 2, NqF.8^-1*NqF.10^-1 ); SetConjugate( NqColl, -8, -2, NqF.8^-1*NqF.10 ); SetConjugate( NqColl, 8, 3, NqF.8*NqF.12^3*NqF.13^-1 ); SetConjugate( NqColl, 8, -3, NqF.8*NqF.12*NqF.13^-1 ); SetConjugate( NqColl, -8, 3, NqF.8^-1*NqF.12*NqF.13^-1 ); SetConjugate( NqColl, -8, -3, NqF.8^-1*NqF.12^3*NqF.13^-1 ); SetConjugate( NqColl, 9, 1, NqF.9*NqF.11 ); SetConjugate( NqColl, 9, -1, NqF.9*NqF.11^4 ); SetConjugate( NqColl, -9, 1, NqF.9^-1*NqF.11^4 ); SetConjugate( NqColl, -9, -1, NqF.9^-1*NqF.11 ); SetConjugate( NqColl, 9, 2, NqF.9*NqF.12 ); SetConjugate( NqColl, 9, -2, NqF.9*NqF.12^3*NqF.13^-2 ); SetConjugate( NqColl, -9, 2, NqF.9^-1*NqF.12^3*NqF.13^-2 ); SetConjugate( NqColl, -9, -2, NqF.9^-1*NqF.12 ); SetConjugate( NqColl, 10, 1, NqF.10*NqF.13 ); SetConjugate( NqColl, 10, -1, NqF.10*NqF.13^-1 ); SetConjugate( NqColl, -10, 1, NqF.10^-1*NqF.13^-1 ); SetConjugate( NqColl, -10, -1, NqF.10^-1*NqF.13 ); SetConjugate( NqColl, 10, 2, NqF.10 ); SetConjugate( NqColl, 10, -2, NqF.10 ); SetConjugate( NqColl, -10, 2, NqF.10^-1 ); SetConjugate( NqColl, -10, -2, NqF.10^-1 ); return PcpGroupByCollector( NqColl ); elif n = 3 then NqF := FreeGroup( 17 ); NqColl := FromTheLeftCollector( NqF ); SetRelativeOrder( NqColl, 8, 2 ); SetRelativeOrder( NqColl, 10, 2 ); SetRelativeOrder( NqColl, 11, 2 ); SetRelativeOrder( NqColl, 14, 2 ); SetRelativeOrder( NqColl, 15, 2 ); SetRelativeOrder( NqColl, 17, 5 ); SetPower( NqColl, 8, NqF.9*NqF.10*NqF.11*NqF.12^-1*NqF.13^3*NqF.14*\ NqF.16^-2*NqF.17 ); SetPower( NqColl, 10, NqF.12*NqF.15*NqF.16^3 ); SetPower( NqColl, 11, NqF.13*NqF.14*NqF.16^-2*NqF.17 ); SetPower( NqColl, 14, NqF.16^2 ); SetPower( NqColl, 15, NqF.16 ); SetConjugate( NqColl, 2, 1, NqF.2*NqF.3 ); SetConjugate( NqColl, 2, -1, NqF.2*NqF.3^-1 ); SetConjugate( NqColl, -2, 1, NqF.2^-1*NqF.3^-1*NqF.4*NqF.5^-1*NqF.6^-1*\ NqF.7*NqF.8*NqF.9*NqF.10*NqF.11*NqF.13^-2*NqF.15*NqF.16^-2*NqF.17^3 ); SetConjugate( NqColl, -2, -1, NqF.2^-1*NqF.3*NqF.4^-1*NqF.5*NqF.7^-1*NqF.11*\ NqF.13^-2*NqF.14*NqF.16*NqF.17^2 ); SetConjugate( NqColl, 3, 1, NqF.3 ); SetConjugate( NqColl, 3, -1, NqF.3 ); SetConjugate( NqColl, -3, 1, NqF.3^-1 ); SetConjugate( NqColl, -3, -1, NqF.3^-1 ); SetConjugate( NqColl, 3, 2, NqF.3*NqF.4 ); SetConjugate( NqColl, 3, -2, NqF.3*NqF.4^-1*NqF.5*NqF.7^-1*NqF.11*NqF.13^-2*\ NqF.14*NqF.16*NqF.17^2 ); SetConjugate( NqColl, -3, 2, NqF.3^-1*NqF.4^-1*NqF.6*NqF.9^-1*NqF.10*NqF.12^-1*\ NqF.15*NqF.16^-4 ); SetConjugate( NqColl, -3, -2, NqF.3^-1*NqF.4*NqF.5^-1*NqF.6^-1*NqF.7*NqF.8*\ NqF.9*NqF.10*NqF.11*NqF.13^-2*NqF.15*NqF.16^-2*NqF.17^3 ); SetConjugate( NqColl, 4, 1, NqF.4*NqF.6*NqF.9^-1 ); SetConjugate( NqColl, 4, -1, NqF.4*NqF.6^-1*NqF.9*NqF.10*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, -4, 1, NqF.4^-1*NqF.6^-1*NqF.9*NqF.15*NqF.16 ); SetConjugate( NqColl, -4, -1, NqF.4^-1*NqF.6*NqF.9^-1*NqF.10*NqF.12^-1*\ NqF.15*NqF.16^-4 ); SetConjugate( NqColl, 4, 2, NqF.4*NqF.5 ); SetConjugate( NqColl, 4, -2, NqF.4*NqF.5^-1*NqF.7 ); SetConjugate( NqColl, -4, 2, NqF.4^-1*NqF.5^-1*NqF.11*NqF.13*NqF.16^-1*\ NqF.17 ); SetConjugate( NqColl, -4, -2, NqF.4^-1*NqF.5*NqF.7^-1*NqF.11*NqF.13^-2*\ NqF.14*NqF.16*NqF.17^2 ); SetConjugate( NqColl, 4, 3, NqF.4*NqF.6*NqF.9^-1 ); SetConjugate( NqColl, 4, -3, NqF.4*NqF.6^-1*NqF.9*NqF.10*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, -4, 3, NqF.4^-1*NqF.6^-1*NqF.9*NqF.15*NqF.16 ); SetConjugate( NqColl, -4, -3, NqF.4^-1*NqF.6*NqF.9^-1*NqF.10*NqF.12^-1*\ NqF.15*NqF.16^-4 ); SetConjugate( NqColl, 5, 1, NqF.5*NqF.6 ); SetConjugate( NqColl, 5, -1, NqF.5*NqF.6^-1*NqF.10*NqF.12*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, -5, 1, NqF.5^-1*NqF.6^-1 ); SetConjugate( NqColl, -5, -1, NqF.5^-1*NqF.6*NqF.10*NqF.12^-2*NqF.16^-2 ); SetConjugate( NqColl, 5, 2, NqF.5*NqF.7 ); SetConjugate( NqColl, 5, -2, NqF.5*NqF.7^-1 ); SetConjugate( NqColl, -5, 2, NqF.5^-1*NqF.7^-1 ); SetConjugate( NqColl, -5, -2, NqF.5^-1*NqF.7 ); SetConjugate( NqColl, 5, 3, NqF.5*NqF.8*NqF.11*NqF.13^-3*NqF.14*NqF.15*\ NqF.16^-1*NqF.17^3 ); SetConjugate( NqColl, 5, -3, NqF.5*NqF.8*NqF.9^-1*NqF.10*NqF.13^-1*NqF.15*\ NqF.16^-3 ); SetConjugate( NqColl, -5, 3, NqF.5^-1*NqF.8*NqF.9^-1*NqF.10*NqF.13^-1*\ NqF.14*NqF.16^-3 ); SetConjugate( NqColl, -5, -3, NqF.5^-1*NqF.8*NqF.11*NqF.13^-3*NqF.16*NqF.17^3 ); SetConjugate( NqColl, 5, 4, NqF.5*NqF.11*NqF.13*NqF.16^-1*NqF.17 ); SetConjugate( NqColl, 5, -4, NqF.5*NqF.11*NqF.13^-2*NqF.14*NqF.16*NqF.17^3 ); SetConjugate( NqColl, -5, 4, NqF.5^-1*NqF.11*NqF.13^-2*NqF.14*NqF.16*NqF.17^3 ); SetConjugate( NqColl, -5, -4, NqF.5^-1*NqF.11*NqF.13*NqF.16^-1*NqF.17 ); SetConjugate( NqColl, 6, 1, NqF.6*NqF.10*NqF.12*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, 6, -1, NqF.6*NqF.10*NqF.12^-2*NqF.16^-2 ); SetConjugate( NqColl, -6, 1, NqF.6^-1*NqF.10*NqF.12^-2*NqF.16^-2 ); SetConjugate( NqColl, -6, -1, NqF.6^-1*NqF.10*NqF.12*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, 6, 2, NqF.6*NqF.8 ); SetConjugate( NqColl, 6, -2, NqF.6*NqF.8*NqF.9^-1*NqF.10*NqF.13^-3*NqF.14*\ NqF.15*NqF.16^-4*NqF.17 ); SetConjugate( NqColl, -6, 2, NqF.6^-1*NqF.8*NqF.9^-1*NqF.10*NqF.11*NqF.13^-4*\ NqF.15*NqF.16^-2*NqF.17^3 ); SetConjugate( NqColl, -6, -2, NqF.6^-1*NqF.8*NqF.11*NqF.13^-1*NqF.14*NqF.17^2 ); SetConjugate( NqColl, 6, 3, NqF.6*NqF.10*NqF.15*NqF.16^-3 ); SetConjugate( NqColl, 6, -3, NqF.6*NqF.10*NqF.12^-1*NqF.16^-1 ); SetConjugate( NqColl, -6, 3, NqF.6^-1*NqF.10*NqF.12^-1*NqF.16^-1 ); SetConjugate( NqColl, -6, -3, NqF.6^-1*NqF.10*NqF.15*NqF.16^-3 ); SetConjugate( NqColl, 6, 4, NqF.6*NqF.15*NqF.16 ); SetConjugate( NqColl, 6, -4, NqF.6*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, -6, 4, NqF.6^-1*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, -6, -4, NqF.6^-1*NqF.15*NqF.16 ); SetConjugate( NqColl, 7, 1, NqF.7*NqF.9 ); SetConjugate( NqColl, 7, -1, NqF.7*NqF.9^-1*NqF.12 ); SetConjugate( NqColl, -7, 1, NqF.7^-1*NqF.9^-1 ); SetConjugate( NqColl, -7, -1, NqF.7^-1*NqF.9*NqF.12^-1 ); SetConjugate( NqColl, 7, 2, NqF.7 ); SetConjugate( NqColl, 7, -2, NqF.7 ); SetConjugate( NqColl, -7, 2, NqF.7^-1 ); SetConjugate( NqColl, -7, -2, NqF.7^-1 ); SetConjugate( NqColl, 7, 3, NqF.7*NqF.13^-1*NqF.16 ); SetConjugate( NqColl, 7, -3, NqF.7*NqF.13*NqF.16^-1 ); SetConjugate( NqColl, -7, 3, NqF.7^-1*NqF.13*NqF.16^-1 ); SetConjugate( NqColl, -7, -3, NqF.7^-1*NqF.13^-1*NqF.16 ); SetConjugate( NqColl, 7, 4, NqF.7*NqF.17^4 ); SetConjugate( NqColl, 7, -4, NqF.7*NqF.17 ); SetConjugate( NqColl, -7, 4, NqF.7^-1*NqF.17 ); SetConjugate( NqColl, -7, -4, NqF.7^-1*NqF.17^4 ); SetConjugate( NqColl, 8, 1, NqF.8*NqF.10 ); SetConjugate( NqColl, 8, -1, NqF.8*NqF.10*NqF.12^-1*NqF.15*NqF.16^-4 ); SetConjugate( NqColl, 8, 2, NqF.8*NqF.11 ); SetConjugate( NqColl, 8, -2, NqF.8*NqF.11*NqF.13^-1*NqF.14*NqF.17^2 ); SetConjugate( NqColl, 8, 3, NqF.8*NqF.14*NqF.15*NqF.16^-2 ); SetConjugate( NqColl, 8, -3, NqF.8*NqF.14*NqF.15*NqF.16^-1 ); SetConjugate( NqColl, 9, 1, NqF.9*NqF.12 ); SetConjugate( NqColl, 9, -1, NqF.9*NqF.12^-1 ); SetConjugate( NqColl, -9, 1, NqF.9^-1*NqF.12^-1 ); SetConjugate( NqColl, -9, -1, NqF.9^-1*NqF.12 ); SetConjugate( NqColl, 9, 2, NqF.9*NqF.13 ); SetConjugate( NqColl, 9, -2, NqF.9*NqF.13^-1*NqF.17 ); SetConjugate( NqColl, -9, 2, NqF.9^-1*NqF.13^-1 ); SetConjugate( NqColl, -9, -2, NqF.9^-1*NqF.13*NqF.17^4 ); SetConjugate( NqColl, 9, 3, NqF.9*NqF.16^-1 ); SetConjugate( NqColl, 9, -3, NqF.9*NqF.16 ); SetConjugate( NqColl, -9, 3, NqF.9^-1*NqF.16 ); SetConjugate( NqColl, -9, -3, NqF.9^-1*NqF.16^-1 ); SetConjugate( NqColl, 10, 1, NqF.10 ); SetConjugate( NqColl, 10, -1, NqF.10 ); SetConjugate( NqColl, 10, 2, NqF.10*NqF.14 ); SetConjugate( NqColl, 10, -2, NqF.10*NqF.14*NqF.16^-2 ); SetConjugate( NqColl, 11, 1, NqF.11*NqF.15 ); SetConjugate( NqColl, 11, -1, NqF.11*NqF.15*NqF.16^-1 ); SetConjugate( NqColl, 11, 2, NqF.11*NqF.17^3 ); SetConjugate( NqColl, 11, -2, NqF.11*NqF.17^2 ); SetConjugate( NqColl, 12, 1, NqF.12 ); SetConjugate( NqColl, 12, -1, NqF.12 ); SetConjugate( NqColl, -12, 1, NqF.12^-1 ); SetConjugate( NqColl, -12, -1, NqF.12^-1 ); SetConjugate( NqColl, 12, 2, NqF.12*NqF.16^2 ); SetConjugate( NqColl, 12, -2, NqF.12*NqF.16^-2 ); SetConjugate( NqColl, -12, 2, NqF.12^-1*NqF.16^-2 ); SetConjugate( NqColl, -12, -2, NqF.12^-1*NqF.16^2 ); SetConjugate( NqColl, 13, 1, NqF.13*NqF.16 ); SetConjugate( NqColl, 13, -1, NqF.13*NqF.16^-1 ); SetConjugate( NqColl, -13, 1, NqF.13^-1*NqF.16^-1 ); SetConjugate( NqColl, -13, -1, NqF.13^-1*NqF.16 ); SetConjugate( NqColl, 13, 2, NqF.13*NqF.17 ); SetConjugate( NqColl, 13, -2, NqF.13*NqF.17^4 ); SetConjugate( NqColl, -13, 2, NqF.13^-1*NqF.17^4 ); SetConjugate( NqColl, -13, -2, NqF.13^-1*NqF.17 ); return PcpGroupByCollector( NqColl ); fi; return fail; end); polycyclic-2.16/gap/exam/generic.gi0000644000076600000240000001500113706672341016267 0ustar mhornstaff############################################################################# ## #W generic.gi Polycyc Bettina Eick ## ############################################################################# ## #M AbelianPcpGroup ## InstallGlobalFunction( AbelianPcpGroup, function( arg ) local coll, i, n, r, grp; # catch arguments if Length(arg) = 1 and IsInt(arg[1]) then n := arg[1]; r := List([1..n], x -> 0); elif Length(arg) = 1 and IsList(arg[1]) then n := Length(arg[1]); r := arg[1]; elif Length(arg) = 2 then n := arg[1]; r := arg[2]; fi; # construct group coll := FromTheLeftCollector( n ); for i in [1..n] do if IsBound( r[i] ) and r[i] > 0 then SetRelativeOrder( coll, i, r[i] ); fi; od; UpdatePolycyclicCollector(coll); grp := PcpGroupByCollectorNC( coll ); SetIsAbelian( grp, true ); return grp; end ); ############################################################################# ## #M DihedralPcpGroup ## InstallGlobalFunction( DihedralPcpGroup, function( n ) local coll, m; coll := FromTheLeftCollector( 2 ); SetRelativeOrder( coll, 1, 2 ); if IsInt( n ) then m := n/2; if not IsInt( m ) then return fail; fi; SetRelativeOrder( coll, 2, m ); SetConjugate( coll, 2, 1, [2,m-1] ); else SetConjugate( coll, 2, 1, [2,-1] ); SetConjugate( coll, 2, -1, [2,-1] ); fi; UpdatePolycyclicCollector(coll); return PcpGroupByCollectorNC( coll ); end ); ############################################################################# ## #M UnitriangularPcpGroup( n, p ) . . . . . . . . for p = 0 we take UT( n, Z ) ## InstallGlobalFunction( UnitriangularPcpGroup, function( n, p ) local F, l, c, e, g, r, pairs, i, j, k, o, G; if not IsPosInt(n) then return fail; fi; if p = 0 then F := Rationals; elif IsPrimeInt(p) then F := GF(p); else return fail; fi; l := n*(n-1)/2; c := FromTheLeftCollector( l ); # compute matrix generators g := []; e := One(F); for i in [1..n-1] do for j in [1..n-i] do r := IdentityMat( n, F ); r[j][i+j] := e; Add( g, r ); od; od; # read of pc presentation pairs := ListX([1..n-1], i -> [1..n-i], function(i,j) return [j, i+j]; end); for i in [1..l] do # commutators for j in [i+1..l] do if pairs[i][1] = pairs[j][2] then k := Position(pairs, [pairs[j][1], pairs[i][2]]); o := [j,1,k,1]; SetConjugate( c, j, i, o ); elif pairs[i][2] = pairs[j][1] then k := Position(pairs, [pairs[i][1], pairs[j][2]]); o := [j,1,k,-1]; if p > 0 then o[4] := o[4] mod p; fi; SetConjugate( c, j, i, o ); else # commutator is trivial fi; od; # powers if p > 0 then SetRelativeOrder( c, i, p ); fi; od; # translate from collector to group UpdatePolycyclicCollector( c ); G := PcpGroupByCollectorNC( c ); G!.mats := g; # check # IsConfluent(c); return G; end ); ############################################################################# ## #M SubgroupUnitriangularPcpGroup( mats ) ## InstallGlobalFunction( SubgroupUnitriangularPcpGroup, function( mats ) local n, p, G, g, i, j, r, h, m, e, v, c; # get the dimension, the char and the full unitriangluar group n := Length( mats[1] ); p := Characteristic( mats[1][1][1] ); G := UnitriangularPcpGroup( n, p ); # compute corresponding generators g := []; for i in [1..n-1] do for j in [1..n-i] do r := IdentityMat( n ); r[j][i+j] := 1; Add( g, r ); od; od; # get exponents for each matrix h := []; for m in mats do e := []; c := 0; for i in [1..n-1] do v := List( [1..n-i], x -> m[x][x+i] ); r := MappedVector( v, g{[c+1..c+n-i]} ); m := r^-1 * m; c := c + n-i; Append( e, v ); od; Add( h, MappedVector( e, Pcp(G) ) ); od; return Subgroup( G, h ); end ); ############################################################################# ## #M HeisenbergPcpGroup( m ) ## InstallGlobalFunction( HeisenbergPcpGroup, function( m ) local FLT, i; FLT := FromTheLeftCollector( 2*m+1 ); for i in [1..m] do SetConjugate( FLT, m+i, i, [m+i, 1, 2*m+1, 1] ); od; UpdatePolycyclicCollector( FLT ); return PcpGroupByCollectorNC( FLT ); end ); ############################################################################# ## #M MaximalOrderByUnitsPcpGroup(f) ## InstallGlobalFunction( MaximalOrderByUnitsPcpGroup, function(f) local m, F, O, U, i, G, u, a; # check if Length(Factors(f)) > 1 then return fail; fi; # create field m := CompanionMat(f); F := FieldByMatricesNC([m]); # get order and units O := MaximalOrderBasis(F); U := UnitGroup(F); # get pcp groups i := IsomorphismPcpGroup(U); G := Image(i); # get action of U on O u := List( Pcp(G), x -> PreImagesRepresentative(i,x) ); a := List( u, x -> List( O, y -> Coefficients(O, y*x))); # return split extension return SplitExtensionPcpGroup( G, a ); end); ############################################################################# ## #F PDepth(G, e) ## PDepth := function(G, e) local l, i; l := PCentralSeries(G); for i in Reversed([1..Length(l)]) do if e in l[i] then return i; fi; od; end; ############################################################################# ## #F BlowUpPcpPGroup(G) ## BlowUpPcpPGroup := function(G) local p, e, f, c, i, j, k; # set up p := PrimePGroup(G); e := ShallowCopy(AsList(G)); f := function(a,b) return PDepth(G,a) 16 then return fail; fi; if n <= 13 then return PcpExamples(n); fi; return NqExamples(n-13); end ); ############################################################################# ## #F PcpExamples(n) ## InstallGlobalFunction( PcpExamples, function( n ) local FTL; ## ## [ 0 1 ] [ -1 0 ] ## The semidirect product of the matrices [ 1 1 ], [ 0 -1 ] ## ## and Z^2. We let the generator corresponding to the second matrix ## have infinite order. ## if n = 1 then return SplitExtensionPcpGroup( AbelianPcpGroup( 2, [] ), [ [[0,1],[1,1]], [[-1,0],[0,-1]] ] ); fi; ## ## The following matrices are a basis of the fundamental units of the ## order defined by the polynomials x^4 - x - 1 ## if n = 2 then return SplitExtensionPcpGroup( AbelianPcpGroup( 2, [] ), [ [ [ 0,1,0,0 ], [ 0,0,1,0 ], [ 0,0,0,1 ], [ 1,1,0,0 ] ], [ [ 1,1,0,-1 ], [ -1,0,1,0 ], [ 0,-1,0,1 ], [ 1,1,-1,0 ] ] ] ); fi; ## ## Z split Z ## if n = 3 then FTL := FromTheLeftCollector( 2 ); SetConjugate( FTL, 2, 1, [2,-1] ); SetConjugate( FTL, 2, -1, [2,-1] ); return PcpGroupByCollector(FTL); fi; ## ## A gr oup of Hirsch length 3. Interesting because the exponents in ## words can become large very quickly. ## if n = 4 then FTL := FromTheLeftCollector( 3 ); SetConjugate( FTL, 2, 1, [3, 1] ); SetConjugate( FTL, 3, 1, [2, 1, 3, 7] ); return PcpGroupByCollector(FTL); fi; ## ## A torsion free polycyclic group which is not nilpotent. It is ## taken vom Robinson's book, page 158. ## if n = 5 then FTL := FromTheLeftCollector( 4 ); SetRelativeOrder( FTL, 1, 2 ); SetPower( FTL, 1, [4,1] ); SetConjugate( FTL, 2,1, [2,-1] ); SetConjugate( FTL, 3,1, [3,-1] ); SetConjugate( FTL, 3,2, [3,1,4,2] ); return PcpGroupByCollector(FTL); fi; ## ## The next 4 groups are from Lo/Ostheimer paper on finding matrix reps ## for pc groups. They are all non-nilpotent, but poly-Z ## if n = 6 then FTL := FromTheLeftCollector( 3 ); SetConjugate( FTL, 2, 1, [2,2,3,1]); SetConjugate( FTL, 3, 1, [2,1,3,1]); return PcpGroupByCollector(FTL); fi; if n = 7 then FTL := FromTheLeftCollector( 4 ); SetConjugate( FTL, 2, 1, [3,1] ); SetConjugate( FTL, 3, 1, [2,-1, 3,3, 4,1] ); SetConjugate( FTL, 3, 2, [3,1,4,-1]); return PcpGroupByCollector(FTL); fi; if n = 8 then FTL := FromTheLeftCollector( 5 ); SetConjugate( FTL, 2, 1, [2,1,4,-1]); SetConjugate( FTL, 3, 2, [5,1]); SetConjugate( FTL, 4, 2, [3,1,4,-1,5,1]); SetConjugate( FTL, 5, 2, [4,1]); return PcpGroupByCollector(FTL); fi; if n = 9 then FTL := FromTheLeftCollector( 3 ); SetConjugate( FTL, 2, 1, [2,1,3,-3] ); SetConjugate( FTL, 3, 1, [3,-1] ); SetConjugate( FTL, 3, 2, [3,-1] ); return PcpGroupByCollector(FTL); fi; ## ## A pc group from Eddie's preprint on `low index for pc groups' ## if n = 10 then FTL := FromTheLeftCollector( 4 ); SetConjugate( FTL, 2, 1, [2,-1] ); SetConjugate( FTL, 4, 1, [4,-1] ); SetConjugate( FTL, 3, 2, [3,2,4,1]); SetConjugate( FTL, 4, 2, [3,3,4,2]); return PcpGroupByCollector(FTL); fi; ## ## The free nilpotent group of rank 2 and class 3. ## if n = 11 then FTL := FromTheLeftCollector( 5 ); SetConjugate( FTL, 2, 1, [2,1,3, 1] ); SetConjugate( FTL, 3, 1, [3,1,4, 1] ); SetConjugate( FTL, 3, 2, [3,1,5, 1] ); return PcpGroupByCollector( FTL ); fi; ## ## The free nilpotent group of rank 3 and class 2. ## if n = 12 then FTL := FromTheLeftCollector( 6 ); SetConjugate( FTL, 2, 1, [2,1,4, 1] ); SetConjugate( FTL, 3, 1, [3,1,5, 1] ); SetConjugate( FTL, 3, 2, [3,1,6, 1] ); return PcpGroupByCollector( FTL ); fi; if n = 13 then FTL := FromTheLeftCollector( 4 ); SetConjugate( FTL, 2, 1, [2,-1] ); SetConjugate( FTL, 4, 1, [4,-1] ); SetConjugate( FTL, 3, 2, [3,2,4,1]); SetConjugate( FTL, 4, 2, [3,3,4,2]); return PcpGroupByCollector( FTL ); fi; return fail; end ); polycyclic-2.16/gap/exam/metacyc.gi0000644000076600000240000000410313706672341016301 0ustar mhornstaff############################################################################# ## #W metacyc.gi Polycyclic Werner Nickel ## ############################################################################# ## #F InfiniteMetacyclicPcpGroup . . . . . . . . the metacyclic group G(m,n,r) ## ## In ## J.R. Beuerle & L.-C. Kappe (2000), Infinite Metacyclic Groups and ## Their Non-Abelian Tensor Square, Proc. Edin. Math. Soc., 43, ## 651--662 ## the infinite metacyclic groups are classified up to isomorphism. This ## function implements their classification. The groups are given by the ## following family of presentations: ## < a,b | a^m, b^n, [a,b] = a^(1-r) > ## where [a,b] = a b a^-1 b^-1. ## ## For this function we use the presentation ## < x,y | x^n, y^m, y^x = y^r > ## which is isomorphic to the one above via x --> b^-1, y --> a. ## ## It would be nice if this function could also return representatives for ## the isomorphism classes of finite metacyclic groups. ## InstallGlobalFunction( InfiniteMetacyclicPcpGroup, function( n, m, r ) local coll; ## or must be zero for the group to be infinite. if m*n <> 0 then return Error( "at least one of or must be zero" ); fi; if m < 0 or m = 1 or n < 0 or n = 1 then return Error( " and must not be negative and not be 1" ); fi; if r = 0 then return Error( " must not be zero" ); fi; if m = 0 and AbsInt(r) <> 1 then return Error( " = 0 implies = 1 or = -1" ); fi; ## If r = -1 mod m, then n must be even. if IsOddInt(n) and (r = -1 or (m <> 0 and r mod m = -1)) then return Error( " = -1 implies that n is even" ); fi; coll := FromTheLeftCollector( 2 ); SetRelativeOrder( coll, 1, n ); SetRelativeOrder( coll, 2, m ); if m <> 0 then r := r mod m; SetConjugate( coll, 2, 1, [2,r] ); fi; UpdatePolycyclicCollector( coll ); return PcpGroupByCollector( coll ); end ); polycyclic-2.16/gap/exam/bgnilp.gi0000644000076600000240000001015113706672341016127 0ustar mhornstaff############################################################################# ## #W bgnilp.gi Polycyc Bettina Eick ## ############################################################################# ## #F This is a set of nilpotent groups defined by Burde und Grunewald. ## InstallGlobalFunction( BurdeGrunewaldPcpGroup, function( s, t ) local F, k3, k4, k5, k6, k7, k8, k9, k10, k11, G; F := FromTheLeftCollector( 11 ); k11 := 2*(203230225 - 12930435*s + 677376*s^2 + 4372200*t); k10 := -2267555 + 151088*s - 126000*t; k9 := 6*(-1108 - 525*s - 1400*t); k8 := 4*(79 - 60*s); k7 := 109; k6 := -5; k5 := 2; k4 := 1; k3 := -1; SetConjugate( F, 2, 1, [2,1,3,k3,4,k4,5,k5,6,k6,7,k7,8,k8,9,k9, 10,k10,11,k11] ); k11 := 180*(-5202055 + 135870*s + 18816*s^2 - 190050*t); k10 := 80*(45515 - 3066*s + 3150*t); k9 := 35*(829 + 180*s - 360*t); k8 := 6*(-91 - 60*s); k7 := 185; k6 := -4; k5 := 3; k4 := -2; SetConjugate( F, 3, 1, [3,1,4,k4,5,k5,6,k6,7,k7,8,k8,9,k9, 10,k10,11,k11] ); k11 := 3780*(-41975 - 13035*s - 2688*s^2 + 5150*t); k10 := 6720*(-585 - 28*s - 75*t); k9 := 840*(-26 - 15*s + 15*t); k8 := 360*(3 + s); k7 := -120; k5 := -6; SetConjugate( F, 3, 2, [3,1,5,k5,7,k7,8,k8,9,k9, 10,k10,11,k11] ); k11 := 90*(836225 + 60480*s + 28224*s^2 - 222075*t); k10 := 140*(-2425 - 864*s); k9 := 16905; k8 := 15; k7 := -10; k6 :=6; k5 :=-3; SetConjugate( F, 4, 1, [4,1,5,k5,6,k6,7,k7,8,k8,9,k9, 10,k10,11,k11] ); k11 := 13494600*s; k10 := 8400*(341 + 60*t); k9 := 12600*s; k8 := -900; k6 := -12; SetConjugate( F, 4, 2, [4,1,6,k6,8,k8,9,k9,10,k10,11,k11] ); k11 := -85050*(4725 + 334*s); k10 := -5896800; k9 := 12600*t; k8 := 360*s; k7 := -180; SetConjugate( F, 4, 3, [4,1,7,k7,8,k8,9,k9,10,k10,11,k11] ); k11 := 21708750; k10 := -1400; k9 := 175; k8 := -20; k7 := 10; k6 := -4; SetConjugate( F, 5, 1, [5,1,6,k6,7,k7,8,k8,9,k9,10,k10,11,k11] ); k11 := 1260*(-12350 + 4032*s^2 - 7725*t); k10 := 94080*s; k9 := 840*(13 - 5*t); k8 := -120*s; k7 := 40; SetConjugate( F, 5, 2, [5,1,7,k7,8,k8,9,k9,10,k10,11,k11] ); k11 := 91003500; k10 := 168000*t; k9 := 4200*s; k8 := -360; SetConjugate( F, 5, 3, [5,1,8,k8,9,k9,10,k10,11,k11] ); k11 := 3780*(-448*s^2 + 3525*t); k10 := 80640*s; k9 := -11340 ; SetConjugate( F, 5, 4, [5,1,9,k9,10,k10,11,k11] ); k11 := -31500; k10 := 1750; k9 := -175; k8 := 15; k7 := -5; SetConjugate( F, 6, 1, [6,1,7,k7,8,k8,9,k9,10,k10,11,k11] ); k11 := -6095250*s; k10 := 42000*(-13 - 2*t); k9 := -2100*s; k8 := 150; SetConjugate( F, 6, 2, [6,1,8,k8,9,k9,10,k10,11,k11] ); k11 := 1890*(448*s^2 - 1525*t); k10 := 1680*s; k9 := 2520; SetConjugate( F, 6, 3, [6,1,9,k9,10,k10,11,k11] ); k11 := 1814400*s; k10 := -113400; SetConjugate( F, 6, 4, [6,1,10,k10,11,k11] ); k11 := -10843875; SetConjugate( F, 6, 5, [6,1,11,k11] ); k11 := 31500; k10 := -1400; k9 := 105; k8 := -6; SetConjugate( F, 7, 1, [7,1,8,k8,9,k9,10,k10,11,k11] ); k11 := 189*(-6175 - 896*s^2 - 4950*t); k10 := -17136*s; k9 := 546; SetConjugate( F, 7, 2, [7,1,9,k9,10,k10,11,k11] ); k11 := -695520*s; k10 := 65520; SetConjugate( F, 7, 3, [7,1,10,k10,11,k11] ); k11 := 4465125; SetConjugate( F, 7, 4, [7,1,11,k11] ); k11 := -21000; k10 := 700; k9 := -35; SetConjugate( F, 8, 1, [8,1,9,k9,10,k10,11,k11] ); k11 := -141120*s; k10 := -7280; SetConjugate( F, 8, 2, [8,1,10,k10,11,k11] ); k11 := -505575; SetConjugate( F, 8, 3, [8,1,11,k11] ); k11 := 1800; k10 := -40; SetConjugate( F, 9, 1, [9,1,10,k10,11,k11] ); k11 := -4275; SetConjugate( F, 9, 2, [9,1,11,k11] ); k11 := -90; SetConjugate( F, 10, 1, [10,1,11,k11] ); G := PcpGroupByCollector( F ); return G; end ); polycyclic-2.16/gap/exam/metagrp.gi0000644000076600000240000000463113706672341016321 0ustar mhornstaff############################################################################# ## #W metagrp.gi Polycyclic Werner Nickel ## ############################################################################# ## #F ExampleOfMetabelianPcpGroup . . . . a special type of metabelian group ## ## This function takes the regular matrix representation of two units in the ## ring of algebraic integers defined by the polynomial x(x-a)(x+a)-1. It ## forms the semidirect product with the natural modul and changes the ## cocycle such that the resulting group is non-split. ## InstallGlobalFunction( ExampleOfMetabelianPcpGroup, function( a, k ) local i, x, y, coeffs, pol, M1, M2, ext, ftl; if not (IsInt(a) and IsInt(k)) or a < 2 then return Error( "arguments should be integers > 1" ); fi; ## k should be in the range [0..a-1] k := k mod a; ## ## The ring of algebraic integers defined by the following ## polynomial has the obvious units x, x-a and x+a. ## x := Indeterminate( Rationals, "x" : new ); pol := x * (x-a) * (x+a) - 1; ## ## Now we construct the regular matrix representation of x and x+a on ## the algebraic number field with respect to the basis 1,x,x^2. ## M1 := NullMat( Degree(pol), Degree(pol) ); for i in [0..Degree(pol)-1] do y := QuotientRemainder( x^i * x, pol )[2]; coeffs := CoefficientsOfUnivariatePolynomial( y ); M1[i+1]{[1..Length(coeffs)]} := coeffs; od; M2 := NullMat( Degree(pol), Degree(pol) ); for i in [0..Degree(pol)-1] do y := QuotientRemainder( x^i * (x+a), pol )[2]; coeffs := CoefficientsOfUnivariatePolynomial( y ); M2[i+1]{[1..Length(coeffs)]} := coeffs; od; ## ## a bit clumsy to construct the group first and then recover the ## collector. There should be a function that constructs the ## collector. ## ## one could also use CRRecordByMats( ) and then ExtensionCR() ## to construct the non-split extension directly. ## ext := SplitExtensionPcpGroup( AbelianPcpGroup( 2, [] ), [ M1, M2 ] ); ftl := Collector( One( ext ) ); ## The commutator of the two top generators is made non-trivial. SetConjugate( ftl, 2, 1, [2,1,5,k] ); UpdatePolycyclicCollector( ftl ); return PcpGroupByCollector( ftl ); end ); polycyclic-2.16/gap/cohom/0000755000076600000240000000000013706672341014510 5ustar mhornstaffpolycyclic-2.16/gap/cohom/onecohom.gi0000644000076600000240000001441213706672341016642 0ustar mhornstaff############################################################################# ## #W onecohom.gi Polycyc Bettina Eick ## ############################################################################# ## #F OneCocyclesEX( A ) #F OneCocyclesCR( A ) ## InstallGlobalFunction( OneCocyclesEX, function( A ) local sys, c, w; # add equations for relators sys := CRSystem( A.dim, Length(A.mats), A.char ); for c in A.enumrels do w := CollectedRelatorCR( A, c[1], c[2] ); if IsBound( A.extension) then AddEquationsCR( sys, w[1], w[2], false ); else AddEquationsCR( sys, w[1], w[2], true ); fi; od; # solve system return rec( basis := KernelCR( A, sys ), transl := SpecialSolutionCR( A, sys ) ); end ); InstallGlobalFunction( OneCocyclesCR, function( A ) return OneCocyclesEX( A ).basis; end ); ############################################################################# ## #F OneCoboundariesEX( A ) . . . . . . . . . . one cobounds and transformation #F OneCoboundariesCR( A ) ## InstallGlobalFunction( OneCoboundariesEX, function( A ) local n, mat, i, v, j; # create a matrix mat := []; if not IsBound( A.central ) or not A.central then n := Length( A.mats ); for i in [1..A.dim] do v := []; for j in [1..n] do Append( v, A.mats[j][i] - A.one[i] ); od; Add( mat, v ); od; fi; # compute the space spanned by the matrix return ImageCR( A, rec( base := mat ) ); end ); InstallGlobalFunction( OneCoboundariesCR, function( A ) return OneCoboundariesEX( A ).basis; end ); ############################################################################# ## #F OneCohomologyCR( C ) . . . . . . . . . . . . . . . . . . .extended version ## InstallGlobalFunction( OneCohomologyCR, function( C ) local cc, cb; cc := OneCocyclesCR( C ); cb := OneCoboundariesCR( C ); return rec( gcc := cc, gcb := cb, factor := AdditiveFactorPcp( cc, cb, C.char ) ); end ); ############################################################################# ## #F InverseCohMapping( coh, base ) ## InverseCohMapping := function( coh, base ) local l, mat, new, dep, i; # for the empty space we do not need to do this if Length( base ) = 0 then return false; fi; # compute full basis l := Length( coh.sol ); mat := IdentityMat( l ); if not IsBool( coh.fld ) then mat := mat * One( coh.fld ); fi; # extend base to full lattice dep := List( base, PositionNonZero ); new := MutableCopyMat( base ); for i in [1..l] do if not i in dep then Add( new, mat[i] ); fi; od; # return inverse return new^-1; end; ############################################################################# ## #F OneCohomologyEX( C ) . . . . . . . . . . . . . . . . . . .extended version ## InstallGlobalFunction( OneCohomologyEX, function( C ) local cc, cb, coh; # compute cocycles and cobounds cc := OneCocyclesEX( C ); if IsBool( cc.transl ) then return fail; fi; cb := OneCoboundariesEX( C ); # set up cohomology record coh := rec( gcc := cc.basis, # 1-cocycles gcb := cb.basis, # 1-coboundaries sol := cc.transl, # special solution trf := cb.transf, # convertion A -> cb rls := cb.fixpts ); # the fixed points # add the field if C.char > 0 then coh.fld := GF( C.char ); fi; if C.char = 0 then coh.fld := true; fi; # add decription of the factor gcc/gcb coh.factor := AdditiveFactorPcp( coh.gcc, coh.gcb, C.char ); # compute linear mapping extend coh.gcc to an full basis coh.invgcc := InverseCohMapping( coh, coh.gcc ); # add conversion functions coh.CocToCCElement := function( coh, coc ) local new; if Length( coh.gcc ) = 0 then return []; fi; new := coc * coh.invgcc; return new{[ 1..Length(coh.gcc)]}; end; # add conversion functions coh.CocToCBElement := function( coh, coc ) local new; if Length( coh.gcb ) = 0 then return []; fi; if IsBool( coh.fld ) then return PcpSolutionIntMat( coh.gcb, coc ); else return SolutionMat( coh.gcb, coc ); fi; new := coc * coh.invgcb; return new{[ 1..Length(coh.gcb)]}; end; coh.ElementToCoc := function( gc, elm ) return IntVector( elm * gc ); end; coh.CocToFactor := function( coh, coc ) local elm, i; elm := coh.CocToCCElement( coh, coc ); elm := elm * coh.factor.imgs; if IsBool( coh.fld ) then for i in [1..Length(elm)] do if coh.factor.rels[i] > 0 then elm[i] := elm[i] mod coh.factor.rels[i]; fi; od; fi; return elm; end; coh.FactorToCoc := function( coh, elm ) return elm * coh.factor.prei; end; # return return coh; end ); ############################################################################# ## #F ComplementCR( C, c ) . . . . . . . . . . . . . . . .for c an affine vector ## # FIXME: This function is documented and should be turned into a GlobalFunction ComplementCR := function( A, c ) local pcpK, l, vec, K, all; # if A has no group, then we want the split extension if not IsBound( A.group ) then A.group := ExtensionCR( A, false ); A.factor := Pcp( A.group, A.group!.module ); A.normal := Pcp( A.group!.module, "snf" ); fi; # compute complement corresponding to c l := Length( A.factor ); vec := CutVector( IntVector( c ), l ); pcpK := List([1..l], i -> A.factor[i] * MappedVector(vec[i], A.normal)); all := AddIgsToIgs( pcpK, DenominatorOfPcp( A.normal ) ); #K := SubgroupByIgs( A.group, all ); K := Subgroup( A.group, all ); K!.compgens := pcpK; K!.cocycle := vec; return K; end; ############################################################################# ## #F ComplementByH1Element( A, coh, elm ) ## ComplementByH1Element := function( A, coh, elm ) local coc; coc := coh.FactorToCoc( coh, elm ) + coh.sol; return ComplementCR( A, coc ); end; polycyclic-2.16/gap/cohom/solabel.gi0000644000076600000240000001546513706672341016465 0ustar mhornstaff############################################################################# ## #W solabel.gi Polycyc Bettina Eick ## # Input: # n integer, m integer, p prime, # e = [e_1, ..., e_k] list of p-powers e_1 <= ... <= e_k # A = nk x mk integer matrix # b = integer vector of lenth mk SeriesSteps := function(e) local l, f, p, k, s, i; l := Length(e); f := Factors(e[l]); p := f[1]; k := Length(f); s := []; for i in [1..k] do s[i] := Length(Filtered(e, x -> x < p^i)); od; return s; end; StripIt := function( mat, l ) local n, d, k; n := Length( mat ); d := List( mat, PositionNonZero ); k := First( [1..n], x -> d[x] >= l ); if IsBool(k) then return Length(mat)+1; fi; return k; end; Strip := function( mat, l ) return mat{[StripIt(mat,l)..Length(mat)]}; end; DivideVec := function(t,p) local i; for i in [1..Length(t)] do if t[i] <> 0 then t[i] := t[i]/p; fi; od; return t; end; TransversalMat := function( M, n ) local d; if Length(M) = 0 then return IdentityMat(n); fi; d := List(M, PositionNonZero); d := Difference([1..Length(M[1])], d); return IdentityMat(n){d}; end; KernelSystemGauss := function( A, e, p ) local k, n, m, q, F, s, AA, SS, KK, II, TT, K, I, i, dW, dV, rT, B, J, W, S, U; # catch arguments k := Length(e); n := Length(A)/k; if not IsInt(n) then return fail; fi; m := Length(A[1])/k; if not IsInt(m) then return fail; fi; F := GF(p); # get steps in series s := SeriesSteps(e); # solve mod p AA := A*One(F); ConvertToMatrixRepNC(AA, F); SS := TriangulizedNullspaceMat(AA); # extract info KK := List(SS, IntVecFFE); TT := TransversalMat( KK, n*k ); II := List(TT, x -> x*A ); # init for induction K := ShallowCopy(KK); # use induction for i in [2..Length(s)] do q := p^(i-1); # catch ranges dW := [m*s[i]+1..m*k]; dV := n*s[i]+1; rT := [StripIt( TT, dV )..Length(TT)]; # image of K B := List( A, x -> x{dW} ); J := List( K, x -> DivideVec( x*B, q ) ); # extend kernel and image Append( K, q * TT{rT} ); Append( J, List( rT, x -> II[x]{dW} )); # apply gauss W := J*One(F); ConvertToMatrixRepNC(W, F); S := TriangulizedNullspaceMat(W); # convert K := List(S, x -> IntVecFFE(x)*K); Append(K, q*Strip( KK, dV ) ); od; return K; end; ReduceVecMod := function( vec, e ) local i, m; m := Length(vec)/Length(e); for i in [1..Length(vec)] do vec[i] := vec[i] mod e[Int((i-1)/m)+1]; od; return vec; end; CheckKernelSpecial := function( A, e ) local W, I, w, v; W := ExponentsByRels( e ); I := []; for w in W do v := ReduceVecMod( w*A, e ); if v = 0*v then Add(I, w); fi; od; return I; end; TransversalSystemGauss := function( A, K, e, p ) local k, n, m, s, d, l, I, T, i, q, t, J, u, r; # catch arguments k := Length(e); n := Length(A)/k; if not IsInt(n) then return fail; fi; m := Length(A[1])/k; if not IsInt(m) then return fail; fi; s := SeriesSteps(e); d := List(K, PositionNonZero); l := List([1..Length(d)], x -> K[x][d[x]]); I := IdentityMat(n*k); T := []; for i in [1..Length(s)] do # general q := p^(i-1); t := n*s[i]+1; # kernel u := Filtered([1..Length(d)], x -> l[x] = q); r := Difference( [t..n*k], d{u} ); # transversal Append(T, q*I{r}); od; return T; end; ImageSystemGauss := function( A, K, e, p ) local k, n, m, s, d, l, I, T, i, q, t, J, u, r; # catch arguments k := Length(e); n := Length(A)/k; if not IsInt(n) then return fail; fi; m := Length(A[1])/k; if not IsInt(m) then return fail; fi; s := SeriesSteps(e); d := List(K, PositionNonZero); l := List([1..Length(d)], x -> K[x][d[x]]); I := IdentityMat(n*k); T := []; for i in [1..Length(s)] do # general q := p^(i-1); t := n*s[i]+1; # kernel u := Filtered([1..Length(d)], x -> l[x] = q); r := Difference( [t..n*k], d{u} ); # image J := I{r}*A; # add Append(T, List( q*J, x -> ReduceVecMod( x, e ))); od; return T; end; FindSpecialSolution := function( S, vec ) local m, n, z, sol, i, vno, x; m := Length(vec); n := Length(S.coeffs[1]); z := Zero(vec[1]); sol := ListWithIdenticalEntries(n,z); ConvertToVectorRepNC(sol); for i in [1..m] do vno := S.heads[i]; if vno <> 0 then x := vec[i]; if x <> z then AddRowVector(vec, S.vectors[vno], -x); AddRowVector(sol, S.coeffs[vno], x); fi; fi; od; if IsZero(vec) then return sol; else return fail; fi; end; SolveSystemGauss := function( A, e, p, b ) local k, n, m, q, F, s, AA, SE, SS, KK, II, TT, sl, ss, h, K, I, i, dW, dV, rT, B, J, W, S, U, v, u, f, M; # catch arguments k := Length(e); n := Length(A)/k; if not IsInt(n) then return fail; fi; m := Length(A[1])/k; if not IsInt(m) then return fail; fi; F := GF(p); f := (b <> 0*b); # get steps in series s := SeriesSteps(e); # solve mod p AA := A*One(F); ConvertToMatrixRepNC(AA, F); SE := SemiEchelonMatTransformation(AA); SS := MutableCopyMat(SE.relations); TriangulizeMat(SS); if f then sl := FindSpecialSolution(SE, b*One(F)); fi; # extract info KK := List(SS, IntVecFFE); TT := TransversalMat( KK, n*k ); II := List(TT, x -> x*A ); if f then ss := IntVecFFE(sl); fi; # init for induction K := ShallowCopy(KK); if f then h := ShallowCopy(ss); fi; # use induction for i in [2..Length(s)] do q := p^(i-1); # catch ranges dW := [m*s[i]+1..m*k]; dV := n*s[i]+1; rT := [StripIt( TT, dV )..Length(TT)]; # image of K B := List( A, x -> x{dW} ); J := List( K, x -> DivideVec( x*B, q ) ); # extend kernel and image Append( K, q * TT{rT} ); Append( J, List( rT, x -> II[x]{dW} )); # apply gauss W := J*One(F); ConvertToMatrixRepNC(W, F); M := SemiEchelonMatTransformation(W); S := MutableCopyMat(M.relations); TriangulizeMat(S); # consider special solution if f then v := DivideVec( h*B - b{dW}, q ); u := IntVecFFE(FindSpecialSolution(M, v*One(F))); h := h - u*K; fi; # convert K := List(S, x -> IntVecFFE(x)*K); Append(K, q*Strip( KK, dV ) ); od; if f then return rec( kernel := K, sol := h ); else return K; fi; end; polycyclic-2.16/gap/cohom/abelaut.gi0000644000076600000240000000660713706672341016457 0ustar mhornstaff############################################################################# ## #W abelaut.gi Polycyc Bettina Eick ## ReduceMatMod := function( mat, exp ) local i, j; for i in [1..Length(mat)] do for j in [1..Length(mat)] do mat[i][j] := mat[i][j] mod exp[j]; od; od; end; InstallGlobalFunction( APEndoNC, function( mat, exp, p ) local elm, type; elm := rec( mat := mat, dim := Length(mat), exp := exp, prime := p); type := NewType( APEndoFamily, IsAPEndoRep ); return Objectify(type, elm ); end ); InstallGlobalFunction( APEndo, function( mat, exp, p ) if Length(mat) <> Length(mat[1]) then return fail; fi; if Length(mat) <> Length(exp) then return fail; fi; ReduceMatMod( mat, exp ); return APEndoNC( mat, exp, p); end ); IdentityAPEndo := function( exp, p ) return APEndoNC( IdentityMat(Length(exp)), exp, p ); end; ZeroAPEndo := function(exp, p) return APEndoNC( NullMat(Length(exp), Length(exp)), exp, p ); end; InstallMethod( PrintObj, "", true, [IsAPEndo], SUM_FLAGS, function(auto) local i; for i in [1..auto!.dim] do Print(auto!.mat[i], "\n"); od; Print(Concatenation(List([1..auto!.dim], x -> "----")),"\n"); Print(auto!.exp); end); InstallMethod( ViewObj, "", true, [IsAPEndo], SUM_FLAGS, function(auto) Print("APEndo of dim ",auto!.dim," mod ",auto!.exp); end); InstallMethod( \=, "", IsIdenticalObj, [IsAPEndo, IsAPEndo], 0, function( auto1, auto2 ) return auto1!.prime = auto2!.prime and auto1!.exp = auto2!.exp and auto1!.mat = auto2!.mat; end ); InstallMethod( \+, "", IsIdenticalObj, [IsAPEndo, IsAPEndo], 0, function( auto1, auto2 ) local mat; if auto1!.exp <> auto2!.exp then TryNextMethod(); fi; if auto1!.prime <> auto2!.prime then TryNextMethod(); fi; mat := auto1!.mat + auto2!.mat; ReduceMatMod( mat, auto1!.exp); return APEndoNC( mat, auto1!.exp, auto1!.prime ); end); InstallMethod( \-, "", IsIdenticalObj, [IsAPEndo, IsAPEndo], 0, function( auto1, auto2 ) local mat; if auto1!.exp <> auto2!.exp then TryNextMethod(); fi; if auto1!.prime <> auto2!.prime then TryNextMethod(); fi; mat := auto1!.mat - auto2!.mat; ReduceMatMod( mat, auto1!.exp); return APEndoNC( mat, auto1!.exp, auto1!.prime ); end); InstallMethod( ZeroOp, "", [IsAPEndo], 0, function( auto ) return ZeroAPEndo( auto!.exp, auto!.prime); end); InstallMethod( AdditiveInverseOp, "", [IsAPEndo], 0, function( auto ) local mat; mat := -auto!.mat; ReduceMatMod( mat, auto!.exp); return APEndoNC( mat, auto!.exp, auto!.prime ); end); InstallMethod( OneOp, "", [IsAPEndo], 0, function( auto ) return IdentityAPEndo( auto!.exp, auto!.prime); end); InstallMethod( \*, "", IsIdenticalObj, [IsAPEndo, IsAPEndo], 0, function( auto1, auto2 ) local mat; if auto1!.exp <> auto2!.exp then TryNextMethod(); fi; if auto1!.prime <> auto2!.prime then TryNextMethod(); fi; mat := auto1!.mat * auto2!.mat; ReduceMatMod( mat, auto1!.exp); return APEndoNC( mat, auto1!.exp, auto1!.prime ); end); InstallOtherMethod( \[\], "", true, [IsAPEndo, IsPosInt], 0, function( auto, i ) return auto!.mat[i]; end ); InstallOtherMethod( ELMS_LIST, "", true, [IsAPEndo, IsDenseList], 0, function( auto, l ) return auto!.mat{l}; end ); #InstallMethod( InverseOp, "", [IsAPEndo], 0, #function( auto ) #end); polycyclic-2.16/gap/cohom/cohom.gi0000644000076600000240000002147213706672341016144 0ustar mhornstaff############################################################################# ## #W cohom.gi Polycyc Bettina Eick ## ## Defining a module for the cohomology functions. ## ############################################################################# ## #F WordOfVectorCR( v ) ## WordOfVectorCR := function( v ) local w, i; w := []; for i in [1..Length(v)] do if v[i] <> 0 then Add( w, [i, v[i]] ); fi; od; return w; end; ############################################################################# ## #F VectorOfWordCR( w, n ) ## VectorOfWordCR := function( w, n ) local v, t; v := List( [1..n], x -> 0 ); for t in w do v[t[1]] := t[2]; od; return v; end; ############################################################################# ## #F MappedWordCR( w, gens, invs ) ## MappedWordCR := function( w, gens, invs ) local e, v; e := gens[1]^0; for v in w do if v[2] > 0 then e := e * gens[v[1]]^v[2]; elif v[2] < 0 then e := e * invs[v[1]]^-v[2]; fi; od; return e; end; ############################################################################# ## #F ExtVectorByRel( A, g, rel ) ## ## A is a G-module and this function determines the extension of G by A. ## ExtVectorByRel := function( A, g, rel ) local b; # the following is buggy # # check if we can read it off # if Depth( A.factor[Length(A.factor)] ) < Depth( A.normal[1] ) and # IsList( A.factor!.tail ) and IsList( A.normal!.tail ) then # return ExponentsByPcp( A.normal, g ); # fi; # otherwise compute b := MappedWordCR( rel, A.factor, List(A.factor, x -> x^-1) ); return ExponentsByPcp( A.normal, b^-1 * g ); end; ############################################################################# ## #F AddRelatorsCR( A ) ## AddRelatorsCR := function( A ) local pcp, rels, n, r, c, e, i, j, a, b; # if they are known return if IsBound( A.relators ) then return; fi; # add relators pcp := A.factor; rels := RelativeOrdersOfPcp( pcp ); n := Length( pcp ); r := []; c := []; e := []; for i in [1..n] do r[i] := []; if rels[i] > 0 then a := pcp[i]^rels[i]; r[i][i] := ExponentsByPcp( pcp, a ); r[i][i] := WordOfVectorCR( r[i][i] ); Add( c, [i,i] ); if IsBound( A.normal ) then Add( e, ExtVectorByRel( A, a, r[i][i] ) ); fi; fi; for j in [1..i-1] do a := pcp[i] ^ pcp[j]; r[i][j] := ExponentsByPcp( pcp, a ); r[i][j] := WordOfVectorCR( r[i][j] ); Add( c, [i,j] ); if IsBound( A.normal ) then Add( e, ExtVectorByRel( A, a, r[i][j] ) ); fi; a := pcp[i] ^ (pcp[j]^-1); r[i][i+j] := ExponentsByPcp( pcp, a ); r[i][i+j] := WordOfVectorCR( r[i][i+j] ); Add( c, [i, i+j] ); if IsBound( A.normal ) then Add( e, ExtVectorByRel( A, a, r[i][i+j] ) ); fi; od; od; A.enumrels := c; A.relators := r; if IsBound( A.normal ) and A.char > 0 then A.extension := e * One( A.field ); elif IsBound( A.normal ) then A.extension := e; fi; end; ############################################################################# ## #F InvertWord( r ) ## InvertWord := function( r ) local l, i; l := Reversed( r ); for i in [1..Length(l)] do l[i] := ShallowCopy( l[i] ); l[i][2] := -l[i][2]; od; return l; end; ############################################################################# ## #F PowerWord( A, r, e ) ## PowerWord := function( A, r, e ) local l; if Length( r ) = 1 then return [[ r[1][1], e * r[1][2]] ]; elif e = 1 then return ShallowCopy(r); elif e > 0 then return Concatenation( List( [1..e], x -> r ) ); elif e = -1 then return InvertWord( r ); elif e < 0 then l := InvertWord( r ); return Concatenation( List( [1..-e], x -> l ) ); fi; end; ############################################################################# ## #F PowerTail( A, r, e ) ## PowerTail := function( A, r, e ) local t, m, i; # catch special case if e = 1 then return A.one; fi; # derivative of r^e if e > 1 then m := MappedWordCR( r, A.mats, A.invs ); t := A.one; for i in [1..e-1] do t := t * m + A.one; od; elif e < 0 then m := MappedWordCR( InvertWord(r), A.mats, A.invs ); t := -m; for i in [1..-e-1] do t := (t - A.one)*m; od; fi; return t; end; ############################################################################# ## #F AddOperationCR( A ) ## AddOperationCR := function( A ) # add operation of factor on normal if not IsBound( A.mats ) then A.mats := List( A.factor, x -> List( A.normal, y -> ExponentsByPcp( A.normal, y^x ))); if A.char > 0 then A.mats := A.mats * One( A.field ); fi; fi; # add operation of oper on normal if IsBound( A.super ) then if not IsBound( A.smats ) then A.smats := List( A.super, x -> List( A.normal, y -> ExponentsByPcp( A.normal, y^x ))); if A.char > 0 then A.smats := A.smats * One( A.field ); fi; fi; fi; end; ############################################################################# ## #F AddInversesCR( A ) ## ## Invert A.mats and A.smats. Additionally check centrality. ## AddInversesCR := function( A ) local cent, i; cent := true; A.invs := List( A.mats, x -> A.one ); for i in [1..Length(A.mats)] do if A.mats[i] <> A.one then cent := false; A.invs[i] := A.mats[i]^-1; fi; od; A.central := cent; if IsBound( A.super ) then A.sinvs := List( A.smats, x -> A.one ); for i in [1..Length(A.smats)] do if A.smats[i] <> A.one then A.sinvs[i] := A.smats[i]^-1; fi; od; fi; end; ############################################################################# ## #F AddFieldCR( A ) ## AddFieldCR := function( A ) local ro; ro := Set( RelativeOrdersOfPcp( A.normal ) ); # Verify that A.normal is free or elementary abelian. if not (IsAbelian( PcpGroupByPcp( A.normal ) ) and Length(ro) = 1 and (ro[1] = 0 or IsPrimeInt( ro[1] )) ) then Error("module must be free abelian or elementary abelian"); fi; A.char := ro[1]; A.dim := Length( A.normal ); A.one := IdentityMat( A.dim ); if A.char > 0 then A.field := GF( A.char ); A.one := A.one * One( A.field ); fi; end; ############################################################################# ## #F CRRecordByMats( G, mats ) ## InstallGlobalFunction( CRRecordByMats, function( G, mats ) local p, cr; if Length( mats ) <> Length(Pcp(G)) then Error("wrong input in CRRecord"); fi; if IsInt(mats[1][1][1]) then p := 0; else p := Characteristic( Field( mats[1][1][1] ) ); fi; cr := rec( factor := Pcp( G ), mats := mats, dim := Length( mats[1] ), one := mats[1]^0, char := p ); if cr.char > 0 then cr.field := GF( cr.char ); fi; AddRelatorsCR( cr ); AddInversesCR( cr ); return cr; end ); ############################################################################# ## #F CRRecordBySubgroup( G, N ) ## # FIXME: This function is documented and should be turned into a GlobalFunction CRRecordBySubgroup := function( G, N ) local A; # set up record A := rec( group := G, factor := Pcp( G, N ), normal := Pcp( N, "snf" ) ); AddFieldCR( A ); AddRelatorsCR( A ); AddOperationCR( A ); AddInversesCR( A ); return A; end; ############################################################################# ## #F CRRecordByPcp( G, pcp ) ## # FIXME: This function is documented and should be turned into a GlobalFunction CRRecordByPcp := function( G, pcp ) local A; # set up record A := rec( group := G, factor := Pcp( G, GroupOfPcp( pcp ) ), normal := pcp ); AddFieldCR( A ); AddRelatorsCR( A ); AddOperationCR( A ); AddInversesCR( A ); return A; end; ############################################################################# ## #F CRRecordWithAction( G, U, pcp ) ## CRRecordWithAction := function( G, U, pcp ) local A; # set up record A := rec( group := U, super := Pcp( G, U ), factor := Pcp( U, GroupOfPcp(pcp) ), normal := pcp ); AddFieldCR( A ); AddRelatorsCR( A ); AddOperationCR( A ); AddInversesCR( A ); return A; end; polycyclic-2.16/gap/cohom/norcom.gi0000644000076600000240000001603313706672341016331 0ustar mhornstaff############################################################################# ## #W norcom.gi Polycyc Bettina Eick ## ## ## computing normal complements ## ############################################################################# ## #F ComplementsCR( C ) . . . . . . . . . . . . . . . . . . . . all complements ## # FIXME: This function is documented and should be turned into a GlobalFunction ComplementsCR := function( C ) local B, cc, elm, rel, new; if Length( C.factor ) = 0 then B := SubgroupByIgs( C.group, DenominatorOfPcp( C.normal ) ); return [B]; fi; cc := OneCocyclesEX( C ); if IsBool( cc.transl ) then return []; fi; # if there are infinitely many complements if C.char = 0 and Length( cc.basis ) > 0 then Print("infinitely many complements \n"); return fail; fi; # otherwise compute all elements new := []; if Length( cc.basis ) = 0 then elm := ComplementCR( C, cc.transl ); Add( new, elm ); else rel := ExponentsByRels( List( cc.basis, x -> C.char ) ); elm := List( rel, x -> IntVector( x * cc.basis + cc.transl ) ); elm := List( elm, x -> ComplementCR( C, x ) ); Append( new, elm ); fi; return new; end; ############################################################################# ## #F Complements( U, N ) . . . . . . . . . . . . . . . .compute all complements ## Complements := function( U, N ) local pcps, com, pcp, new, L, C; # catch the trivial case if U = N then return [TrivialSubgroup(U)]; fi; # compute complements along a series pcps := PcpsOfEfaSeries( N ); com := [ U ]; for pcp in pcps do new := []; for L in com do # set up CR record C := rec(); C.group := U; C.factor := Pcp( L, GroupOfPcp( pcp ) ); C.normal := pcp; AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); AddInversesCR( C ); Append( new, ComplementsCR( C ) ); od; com := ShallowCopy( new ); od; return com; end; ############################################################################# ## #F OperationOnZ1( C, cc ) . . . . . . . . . . . . . . . C.super on cocycles ## OperationOnZ1 := function( C, cc ) local l, m, s, lin, trl, i, j, g, h, ms, coc, img, add, act; # catch some trivial cases if Length( C.super ) = 0 then return []; elif Length( cc.basis ) = 0 then return List( C.super, x -> 1 ); fi; l := Length( C.factor ); # compute the linear action lin := List( C.super, x -> [] ); trl := List( C.super, x -> 0 ); for i in [1..Length(C.super)] do g := C.super[i]^-1; h := C.super[i]; m := C.smats[i]; s := List( C.factor, x -> ExponentsByPcp( C.factor, x^g ) ); # the linear part for j in [1..Length( cc.basis )] do coc := CutVector( cc.basis[j], l ); img := List( s, x -> EvaluateCocycle( C, coc, x ) ); img := List( img, x -> x * m ); lin[i][j] := Flat( img ); od; # translation part coc := CutVector( cc.transl, l ); img := List( s, x -> EvaluateCocycle( C, coc, x ) ); img := List( img, x -> x * m ); add := List( [1..l], x -> C.factor[x]^-1 * MappedVector(s[x], C.factor)^h); add := List( add, x -> ExponentsByPcp( C.normal, x ) ); trl[i] := Flat( img ) + Flat( add ) - cc.transl; od; # combine linear and translation action act := []; for i in [1..Length( C.super )] do if lin[i] = cc.basis and trl[i] = 0*trl[i] then act[i] := 1; else act[i] := rec( lin := lin[i], trl := trl[i] ); fi; od; return act; end; ############################################################################# ## #F FixedPoints( pts, gens, oper ) ## FixedPoints := function( pts, gens, oper ) return Filtered( pts, x -> ForAll( gens, y -> oper( x, y ) = x ) ); end; ############################################################################# ## #F InvariantComplementsCR( C ) . . . . . . . . . . .invariant under operation ## InvariantComplementsCR := function( C ) local cc, f, rels, elms, act, sub; # compute H^1( U, A/B ) and return if there is no complement if not C.central then return []; fi; cc := OneCocyclesEX( C ); if IsBool( cc.transl ) then return []; fi; # check the finiteness of H^1 if C.char = 0 and Length( cc.basis ) > 0 then Print("infinitely many complements \n"); return fail; fi; # catch the case of a trivial H1 if Length( cc.basis ) = 0 then return [ComplementCR( C, cc.transl )]; fi; # the operation of G on H1 f := function( pt, act ) local im; if act = 1 then return pt; fi; im := pt * act.lin + act.trl; return SolutionMat( cc.basis, im ); end; # create elements of cc.factor rels := List( cc.basis, x -> C.char ); elms := ExponentsByRels( rels ) * One( C.field ); # compute action and fixed points act := OperationOnZ1( C, cc ); sub := FixedPoints( elms, act, f ); # catch trivial case and translate result if Length(sub) = 0 then return sub; fi; sub := sub * cc.basis; return List(sub, x -> ComplementCR( C, IntVector(x+cc.transl))); end; ############################################################################# ## #F InvariantComplementsEfaPcps( G, U, pcps ). . . . . ## compute invariant complements in U along series. Series must ## be an efa-series and each subgroup in series must be normal ## under G. ## InvariantComplementsEfaPcps := function( G, U, pcps ) local cls, pcp, new, L, C; cls := [ U ]; for pcp in pcps do if Length( pcp ) > 0 then new := []; for L in cls do # set up class record C := rec( group := L, super := Pcp( G, L ), factor := Pcp( L, GroupOfPcp( pcp ) ), normal := pcp ); AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); AddInversesCR( C ); Append( new, InvariantComplementsCR( C ) ); od; cls := ShallowCopy(new); fi; od; return cls; end; ############################################################################# ## #F InvariantComplements( [G,] U, N ). . . . . invariant complements to N in U ## InvariantComplements := function( arg ) local G, U, N, pcps; # the arguments G := arg[1]; if Length( arg ) = 3 then U := arg[2]; N := arg[3]; else U := arg[1]; N := arg[2]; fi; # catch a trivial case if U = N then return [ TrivialSubgroup(N) ]; fi; # otherwise compute series and all next function pcps := PcpsOfEfaSeries( N ); return InvariantComplementsEfaPcps( G, U, pcps ); end; polycyclic-2.16/gap/cohom/README0000644000076600000240000000074313706672341015374 0ustar mhornstaff gap/cohom: compute with cohomology of pcp groups. general.gi -- general stuff cohom.gi/gd -- basic setup solcohom.gi -- solve integer equations twocohom.gi -- 2-cohomology grpext.gi -- group extensions onecohom.gi -- 1-cohomology orbstab.gi -- orbit stabilizer for pcp groups grpcom.gi -- group complements norcom.gi -- normal complements addgrp.gi -- additive abelian groups (needed for cohomgrps) polycyclic-2.16/gap/cohom/solcohom.gi0000644000076600000240000001674213706672341016666 0ustar mhornstaff############################################################################# ## #W solcohom.gi Polycyc Bettina Eick ## ############################################################################# ## #F CRSystem( d, l, c ) ## CRSystem := function( d, l, c ) local null, zero; null := List( [1..d*l], x -> 0 ); if c <> 0 then null := null * One(GF(c)); fi; zero := List( [1..d], x -> 0 ); if c <> 0 then zero := zero * One(GF(c)); fi; return rec( null := null, zero := zero, dim := d, len := l, base := [] ); end; ############################################################################# ## #F AddToCRSystem( sys, mat ) ## AddToCRSystem := function( sys, mat ) local v; for v in mat do if IsBound( sys.full ) and sys.full then Add( sys.base, v ); elif not v = sys.null and not v in sys.base then Add( sys.base, v ); fi; od; end; ############################################################################# ## #F SubtractTailVectors( t1, t2 ) ## SubtractTailVectors := function( t1, t2 ) local i; for i in [ 1 .. Length(t2) ] do if IsBound(t2[i]) then if IsBound(t1[i]) then t1[i] := t1[i] - t2[i]; else t1[i] := - t2[i]; fi; fi; od; end; ############################################################################# ## #F IsZeroTail( t ) ## IsZeroTail := function( t ) local i; for i in [ 1 .. Length(t) ] do if IsBound(t[i]) and t[i] <> 0 * t[i] then return false; fi; od; return true; end; ############################################################################# ## #F AddEquationsCR( sys, t1, t2, flag ) ## AddEquationsCRNorm := function( sys, t, flag ) local i, j, v, mat; # create a matrix mat := []; for j in [1..sys.dim] do v := []; for i in [1..sys.len] do if IsBound( t[i] ) then Append( v, t[i]{[1..sys.dim]}[j] ); else Append( v, sys.zero ); fi; od; Add( mat, v ); od; # finally add it if flag then AddToCRSystem( sys, mat ); else Append( sys.base, mat ); fi; end; AddEquationsCREndo := function( sys, t ) local i, l; for i in [1..Length(sys)] do l := List(t, x -> x[i]); AddEquationsCRNorm( sys[i], l, true ); od; end; AddEquationsCR := function( sys, t1, t2, flag ) local t; # the trivial case if t1 = t2 and flag then return; fi; # subtract t1 - t2 into t t := ShallowCopy(t1); SubtractTailVectors( t, t2 ); # check case if IsList(sys) then AddEquationsCREndo( sys, t ); else AddEquationsCRNorm( sys, t, flag ); fi; end; ############################################################################# ## ## Some small helpers ## MatPerm := function( d, e ) local k, t, l, i, f, n, r; if d = 1 then return (); fi; k := Length(e); t := Set(SeriesSteps(e)); Add(t, k); l := []; for i in [1..Length(t)-1] do f := t[i]+1; n := t[i+1]; r := List([1..d], x -> (x-1)*k+[f..n]); Append(l, Concatenation(r)); od; return PermListList([1..d*k], l)^-1; end; PermuteMat := function( M, rho, sig ) local N, i, j; N := MutableCopyMat(M); for i in [1..Length(M)] do for j in [1..Length(M[1])] do N[i][j] := M[i^sig][j^rho]; od; od; return N; end; PermuteVec := function( v, rho ) return List([1..Length(v)], i -> v[i^rho]); end; ############################################################################# ## ## ImageCR( A, sys ) ## ## returns a basis of the image of sys. Additionally, it returns the ## transformation from the given generating set and the nullspace of the ## given generating set. ## ImageCRNorm := function( A, sys ) local mat, new, tmp; mat := sys.base; # if mat is empty if mat = 0 * mat then tmp := rec( basis := [], transformation := [], relations := A.one ); # if mat is integer elif A.char = 0 then tmp := LLLReducedBasis( mat, "linearcomb" ); # if mat is ffe elif A.char > 0 then new := SemiEchelonMatTransformation( mat ); tmp := rec( basis := new.vectors, transformation := new.coeffs, relations := ShallowCopy(new.relations) ); TriangulizeMat(tmp.relations); fi; # return return rec( basis := tmp.basis, transf := tmp.transformation, fixpts := tmp.relations ); end; ImageCREndo := function( A, sys ) local i, mat, K, e, p, n, m, rho, sig; K := []; for i in [1..Length(sys)] do mat := sys[i].base; p := A.endosys[i][1]; e := A.mats[1][i]!.exp; n := Length(mat)/Length(e); m := Length(mat[1])/Length(e); rho := MatPerm(m, e)^-1; sig := MatPerm(n, e)^-1; mat := PermuteMat( mat, rho, sig ); K[i] := KernelSystemGauss( mat, e, p ); K[i] := ImageSystemGauss( mat, K[i], e, p ); K[i] := List(K[i], x -> PermuteVec( x, rho^-1)); od; return K; end; ImageCR := function( A, sys ) if IsList(sys) then return ImageCREndo( A, sys ); else return ImageCRNorm( A, sys ); fi; end; ############################################################################# ## ## KernelCR( A, sys ) ## ## returns the kernel of the system ## KernelCRNorm := function( A, sys ) local mat, null; if sys.len = 0 then return []; fi; # we want the kernel of the transposed mat := TransposedMat( sys.base ); # the nullspace if Length( mat ) = 0 then null := IdentityMat( sys.dim * sys.len ); if A.char > 0 then null := null * One( A.field ); fi; elif A.char > 0 then null := TriangulizedNullspaceMat( mat ); else null := PcpNullspaceIntMat( mat ); null := TriangulizedIntegerMat( null ); fi; return null; end; KernelCREndo := function( A, sys ) local i, mat, K, e, p, n, m, rho, sig; K := []; for i in [1..Length(sys)] do mat := TransposedMat( sys[i].base ); p := A.endosys[i][1]; e := A.mats[1][i]!.exp; n := Length(mat)/Length(e); m := Length(mat[1])/Length(e); rho := MatPerm(m, e); sig := MatPerm(n, e); mat := PermuteMat( mat, rho, sig ); K[i] := KernelSystemGauss( mat, e, p ); K[i] := List(K[i], x -> PermuteVec( x, rho^-1)); od; return K; end; KernelCR := function( A, sys ) if IsList(sys) then return KernelCREndo( A, sys ); else return KernelCRNorm( A, sys ); fi; end; ############################################################################# ## ## SpecialSolutionCR( A, sys ) ## ## returns a special solution of the system corresponding to A.extension ## SpecialSolutionCR := function( A, sys ) local mat, sol, vec; if sys.len = 0 then return []; fi; if Length( sys.base ) = 0 or not IsBound( A.extension ) then sol := List( [1..sys.dim * sys.len], x -> 0 ); if A.char > 0 then sol := sol * One( A.field ); fi; else mat := TransposedMat( sys.base ); vec := Concatenation( A.extension ); if A.char > 0 then sol := SolutionMat( mat, vec ); else sol := PcpSolutionIntMat( mat, vec ); fi; fi; # return with special solution return sol; end; polycyclic-2.16/gap/cohom/grpcom.gi0000644000076600000240000002327713706672341016333 0ustar mhornstaff############################################################################# ## #W grpcom.gi Polycyc Bettina Eick ## ## ## computing conjugacy classes of complements ## ############################################################################# ## #F PushVector( mats, invs, one, coc, exp ) ## PushVector := function( mats, invs, one, coc, exp ) local n, m, i, e, j; n := 0 * coc[1]; m := one; # parse coc trough exp under action of matrixes for i in Reversed( [1..Length(exp)] ) do e := exp[i]; if e > 0 then for j in [1..e] do n := n + coc[i] * m; m := mats[i] * m; od; elif e < 0 then for j in [1..-e] do m := invs[i] * m; n := n - coc[i] * m; od; fi; od; return n; end; ############################################################################# ## #F EvaluateCocycle( C, coc, exp ) ## EvaluateCocycle := function( C, coc, exp ) if IsBound( C.central ) and C.central then return exp * coc; fi; return PushVector( C.mats, C.invs, C.one, coc, exp ); end; ############################################################################# ## #F CocycleConjugateComplement( C, cc, coc, w, h ) ## CocycleConjugateComplement := function( C, cc, coc, w, h ) local l, g, m, s, c, a, b, v; # first catch a special cases if Length( w ) = 1 and w[1][2] = 1 and cc.factor.gens <> [] then v := cc.action[w[1][1]]; if v = 1 then return 0 * cc.sol; else return coc * v.lin + v.trl - coc * cc.factor.prei; fi; fi; # now compute if Length( coc ) = 0 then coc := cc.sol; else coc := coc * cc.factor.prei + cc.sol; fi; l := Length( C.factor ); g := h^-1; m := SubsWord( w, C.smats ); s := List( C.factor, x -> ExponentsByPcp( C.factor, x^g ) ); # the linear part c := CutVector( coc, l ); a := Flat( List( s, x -> EvaluateCocycle( C, c, x )*m ) ); # the translation part b := List( [1..l], x -> C.factor[x]^-1 * MappedVector(s[x], C.factor)^h); b := List( b, x -> ExponentsByPcp( C.normal, x ) ); return Flat(a) + Flat(b) - coc; end; ############################################################################# ## #F OperationOnH1( C, cc ) . . . .affine action of C.super on cohomology group ## OperationOnH1 := function( C, cc ) local lin, sub, i, j, g, m, l, coc, img, trl, act, s, h, add; # catch some trivial cases if Length( C.super ) = 0 then return []; elif Length( cc.factor.gens ) = 0 then return List( C.super, x -> 1 ); fi; l := Length( C.factor ); # compute action - linear and translation lin := List( C.super, x -> [] ); trl := List( C.super, x -> 0 ); for i in [1..Length(C.super)] do g := C.super[i]^-1; h := C.super[i]; m := C.smats[i]; s := List( C.factor, x -> ExponentsByPcp( C.factor, x^g ) ); # the linear part for j in [1..Length( cc.factor.prei )] do coc := CutVector( cc.factor.prei[j], l ); img := List( s, x -> EvaluateCocycle(C, coc, x)); img := List( img, x -> x * m ); lin[i][j] := Flat( img ); od; # translation part coc := CutVector( cc.sol, l ); img := List( s, x -> EvaluateCocycle( C, coc, x ) ); img := List( img, x -> x * m ); add := List( [1..l], x -> C.factor[x]^-1 * MappedVector(s[x], C.factor)^h); add := List( add, x -> ExponentsByPcp( C.normal, x ) ); trl[i] := Flat( img ) + Flat( add ) - cc.sol; od; # combine linear and translation action act := []; for i in [1..Length( C.super )] do if lin[i] = cc.gcc and trl[i] = 0*trl[i] then act[i] := 1; else act[i] := rec( lin := lin[i], trl := trl[i] ); fi; od; return act; end; ############################################################################# ## #F ComplementClassesCR( C ) ## InstallGlobalFunction( ComplementClassesCR, function( C ) local cc, elms, supr, mats, oper, os, cent, comp, e, d, K, gens, w, g, c, S; # first catch a trivial case if Length(C.normal) = 0 then return [rec( repr := GroupOfPcp( C.factor ), norm := GroupOfPcp( C.super ) )]; fi; # compute H^1( U, A/B ) and return if there is no complement cc := OneCohomologyEX( C ); if IsBool( cc ) then return []; fi; # check the finiteness of H^1 if ForAny( cc.factor.rels, x -> x = 0 ) then Print("infinitely many complements to lift \n"); return fail; fi; # create elements of H1 elms := ExponentsByRels( cc.factor.rels ); if C.char > 0 then elms := elms * One( C.field ); fi; # get acting matrices of G on H1 if not IsBound( C.super ) then supr := []; mats := []; else supr := C.super; mats := OperationOnH1( C, cc ); fi; cc.action := mats; # the operation function of G on H1 oper := function( pt, act ) local im; if act = 1 then return pt; fi; im := pt * act.lin + act.trl; return cc.CocToFactor( cc, im ); end; # orbits of G on elements of H1 os := PcpOrbitsStabilizers( elms, supr, mats, oper ); # compute centralizer of complements cent := List( cc.rls, x -> MappedVector( IntVector( x ), C.normal ) ); # loop over orbit and extract information comp := []; for e in os do # the complement if Length( e.repr ) > 0 then d := e.repr * cc.factor.prei + cc.sol; else d := cc.sol; fi; K := ComplementCR( C, d ); # add centralizer to complement gens := AddIgsToIgs( cent, Igs( K ) ); # add normalizer to centralizer and complement for w in e.word do g := SubsWord( w, supr ); if g <> g^0 and Length( cc.gcb ) > 0 and Length( w ) > 0 then c := CocycleConjugateComplement( C, cc, e.repr, w, g ); c := cc.CocToCBElement(cc, c) * cc.trf; g := g * MappedVector( IntVector( c ), C.normal ); gens := AddIgsToIgs( [g], gens ); elif g <> g^0 then gens := AddIgsToIgs( [g], gens ); fi; od; # the normalizer S := SubgroupByIgs( C.group, gens ); #if not CheckComplement( C, S, K ) then # Error("complement wrong"); #fi; Add( comp, rec( repr := K, norm := S ) ); od; return comp; end ); ############################################################################# ## #F CheckComplement( C, S, K ) ## CheckComplement := function( C, S, K ) local G, A, B, L, I, g; # check that it is a complement G := C.group; A := SubgroupByIgs( G, NumeratorOfPcp( C.normal ) ); B := SubgroupByIgs( G, DenominatorOfPcp( C.normal ) ); L := SubgroupByIgs( G, Igs(A), Igs(K) ); I := NormalIntersection( A, K ); if not L = G then Print("intersection wrong\n"); return false; elif not I = B then Print("cover wrong\n"); return false; elif ForAny( Igs(S), x -> x = One(K) ) then Print("igs of normalizer is incorrect\n"); return false; elif not IsSubgroup(S,K) then Print("normalizer does not contain complement\n"); return false; elif not IsNormal(S, K) then Print("normalizer does not normalize \n"); return false; fi; # now its o.k. return true; end; ############################################################################# ## #F ComplementClassesEfaPcps( G, U, pcps ). . . . . ## compute G-classes of complements in U along series. Series must ## be an efa-series and each subgroup in series must be normal ## under G. ## InstallGlobalFunction( ComplementClassesEfaPcps, function( G, U, pcps ) local cls, pcp, new, cl, tmp, C; cls := [ rec( repr := U, norm := G )]; for pcp in pcps do if Length( pcp ) > 0 then new := []; for cl in cls do # set up class record C := rec( group := cl.repr, super := Pcp( cl.norm, cl.repr ), factor := Pcp( cl.repr, GroupOfPcp( pcp ) ), normal := pcp ); AddFieldCR( C ); AddRelatorsCR( C ); AddOperationCR( C ); AddInversesCR( C ); tmp := ComplementClassesCR( C ); Append( new, tmp ); od; cls := ShallowCopy(new); fi; od; return cls; end ); ############################################################################# ## #F ComplementClasses( [G,] U, N ). . . . . G-classes of complements to N in U ## ## Note that N and U must be normalized by G. ## InstallGlobalFunction( ComplementClasses, function( arg ) local G, U, N, pcps; # the arguments G := arg[1]; if Length( arg ) = 3 then U := arg[2]; N := arg[3]; else U := arg[1]; N := arg[2]; fi; # catch a trivial case if U = N then return [rec( repr := TrivialSubgroup( N ), norm := G )]; fi; # otherwise compute series and all next function pcps := PcpsOfEfaSeries( N ); return ComplementClassesEfaPcps( G, U, pcps ); end ); InstallMethod( ComplementClassesRepresentatives, "for pcp groups", IsIdenticalObj, [IsPcpGroup,IsPcpGroup], function( G, N ) if not IsNormal(G, N) then Error("N must be normal in G"); fi; return List(ComplementClasses(G, N), r -> r.repr); end ); polycyclic-2.16/gap/cohom/abelaut.gd0000644000076600000240000000127013706672341016441 0ustar mhornstaff############################################################################# ## #W abelaut.gd Polycyc Bettina Eick ## # APEndos form a ring. In order to allow the operations One and Inverse, # we make them Scalars. DeclareCategory( "IsAPEndo", IsScalar ); # IsMultiplicativeElement and IsAdditiveElementWithInverse ); DeclareCategoryFamily( "IsAPEndo" ); DeclareCategoryCollections( "IsAPEndo" ); APEndoFamily := NewFamily( "APEndoFam", IsAPEndo, IsAPEndo ); DeclareRepresentation( "IsAPEndoRep", IsComponentObjectRep, ["mat", "dim", "exp", "prime"] ); DeclareGlobalFunction( "APEndoNC" ); DeclareGlobalFunction( "APEndo" ); polycyclic-2.16/gap/cohom/intcohom.gi0000644000076600000240000001610413706672341016653 0ustar mhornstaff############################################################################# ## #W intcohom.gi Polycyc Bettina Eick ## ############################################################################# ## ## IntKernelCR( A, sys, lat, bat ) ## IntKernelCR := function( A, sys, lat, bat ) local l, n, d, mat, null; # get sizes l := Length(sys.base)/sys.dim; n := sys.len; d := sys.dim; # catch two trivial cases if n = 0 or l = 0 then return IdentityMat(d*n); fi; # transpose and blow up system mat := MutableTransposedMat( sys.base ); Append( mat, DirectSumMat( List( [1..l], x -> lat ) ) ); # compute kernel # Print(" solve system ",Length(mat)," by ",Length(mat[1]),"\n"); null := PcpNullspaceIntMat( mat ); # cut and add null := List( null, x -> x{[1..d*n]} ); null := Concatenation( null, bat ); # find basis # Print(" reduce system ",Length(null)," by ",Length(null[1]),"\n"); return BaseIntMat( null ); end; ############################################################################# ## #F IntTwoCocycleSystemCR( A ) ## IntTwoCocycleSystemCR := function( A ) local C, n, e, id, l, gn, gp, gi, eq, pairs, i, j, k, w1, w2, d, sys, h; # set up system of length d n := Length( A.mats ); e := RelativeOrdersOfPcp( A.factor ); l := Length( A.enumrels ); d := A.dim; sys := CRSystem( d, l, A.char ); sys.full := true; # check if not A.char = 0 then return fail; fi; # set up for equations id := IdentityMat(n); gn := List( id, x -> rec( word := x, tail := [] ) ); # precompute (ij) for i > j #Print(" precompute \n"); pairs := List( [1..n], x -> [] ); for i in [1..n] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); pairs[i][i] := CollectedTwoCR( A, h, gn[i] ); fi; for j in [1..i-1] do pairs[i][j] := CollectedTwoCR( A, gn[i], gn[j] ); od; od; # consistency 1: k(ji) = (kj)i #Print(" consistency 1 \n"); for i in [ n, n-1 .. 1 ] do for j in [ n, n-1 .. i+1 ] do for k in [ n, n-1 .. j+1 ] do w1 := CollectedTwoCR( A, gn[k], pairs[j][i] ); w2 := CollectedTwoCR( A, pairs[k][j], gn[i] ); if w1.word <> w2.word then Error( "k(ji) <> (kj)i" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; od; od; od; # consistency 2: j^(p-1) (ji) = j^p i #Print(" consistency 2 \n"); for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[j] > 0 then h := rec( word := (e[j] - 1) * id[j], tail := [] ); w1 := CollectedTwoCR( A, h, pairs[j][i]); w2 := CollectedTwoCR( A, pairs[j][j], gn[i]); if w1.word <> w2.word then Error( "j^(p-1) (ji) <> j^p i" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; fi; od; od; # consistency 3: k (i i^(p-1)) = (ki) i^p-1 #Print(" consistency 3 \n"); for i in [n,n-1..1] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); l := CollectedTwoCR( A, gn[i], h ); for k in [n,n-1..i+1] do w1 := CollectedTwoCR( A, gn[k], l ); w2 := CollectedTwoCR( A, pairs[k][i], h ); if w1.word <> w2.word then Error( "k i^p <> (ki) i^(p-1)" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; od; fi; od; # consistency 4: (i i^(p-1)) i = i (i^(p-1) i) #Print(" consistency 4 \n"); for i in [ n, n-1 .. 1 ] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); l := CollectedTwoCR( A, gn[i], h ); w1 := CollectedTwoCR( A, l, gn[i] ); w2 := CollectedTwoCR( A, gn[i], pairs[i][i] ); if w1.word <> w2.word then Error( "i i^p-1 <> i^p" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; fi; od; # consistency 5: j = (j -i) i #Print(" consistency 5 \n"); gi := List( id, x -> rec( word := -x, tail := [] ) ); for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[i] = 0 then w1 := CollectedTwoCR( A, gn[j], gi[i] ); w2 := CollectedTwoCR( A, w1, gn[i] ); if w2.word <> id[j] then Error( "j <> (j -i) i" ); else AddEquationsCR( sys, w2.tail, [], true ); fi; fi; od; od; # consistency 6: i = -j (j i) #Print(" consistency 6 \n"); for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[j] = 0 then w1 := CollectedTwoCR( A, gi[j], pairs[j][i] ); if w1.word <> id[i] then Error( "i <> -j (j i)" ); else AddEquationsCR( sys, w1.tail, [], true ); fi; fi; od; od; # consistency 7: -i = -j (j -i) #Print(" consistency 7 \n"); for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[i] = 0 and e[j] = 0 then w1 := CollectedTwoCR( A, gn[j], gi[i] ); w1 := CollectedTwoCR( A, gi[j], w1 ); if w1.word <> -id[i] then Error( "-i <> -j (j -i)" ); else AddEquationsCR( sys, w1.tail, [], true ); fi; fi; od; od; # add a check ((j ^ i) ^-i ) = j #Print(" consistency 8 \n"); for i in [1..n] do for j in [1..i-1] do w1 := CollectedTwoCR( A, gi[j], pairs[i][j] ); w1 := CollectedTwoCR( A, gn[j], w1 ); w1 := CollectedTwoCR( A, w1, gi[j] ); if w1.word <> id[i] then Error("in rel check "); elif not IsZeroTail( w2.tail ) then # Error("relations bug"); AddEquationsCR( sys, w1.tail, [], true ); fi; od; od; # return system return sys; end; ############################################################################# ## #F TwoCohomologyModCR( A, lat ) ## # FIXME: This function is documented and should be turned into a GlobalFunction TwoCohomologyModCR := function( A, lat ) local cb, cc, bat; if A.char <> 0 then return fail; fi; # two cobounds cb := TwoCoboundariesCR( A ); # two cocycle system cc := IntTwoCocycleSystemCR( A ); # big lattice bat := DirectSumMat( List( [1..cc.len], y -> lat ) ); # add lattice to cb and cc cb := BaseIntMat( Concatenation( cb, bat ) ); cc := IntKernelCR( A, cc, lat, bat ); return rec( gcc := cc, gcb := cb, factor := AdditiveFactorPcp( cc, cb, 0 ) ); end; polycyclic-2.16/gap/cohom/addgrp.gi0000644000076600000240000001732113706672341016276 0ustar mhornstaff############################################################################# ## #W addgrp.gi Polycyc Bettina Eick ## ## In our case cohomology groups are factors Z / B where Z and B are either ## free or elementary abelian. In the elementary abelian case we can repr. ## such factors by vector spaces. In the free abelian case we need machery ## for f.g. abelian groups in additive notation. ## ############################################################################# ## #F AdditiveIgsParallel ## AdditiveIgsParallel := function( gens, imgs ) local n, zero, ind, indd, todo, tododo, g, gg, d, h, hh, k, eg, eh, e, c, is; if Length( gens ) = 0 then return [gens, imgs]; fi; # get information n := Length( gens[1] ); zero := 0 * gens[1]; # create new list from pcs/ppcs ind := List( [1..n], x -> false ); indd := List( [1..n], x -> false ); # create a to-do list from gens/pgens todo := ShallowCopy( gens ); tododo:= ShallowCopy( imgs ); # loop over to-do list until it is empty # c := []; while Length( todo ) > 0 do g := Remove(todo); gg := Remove(tododo); d := PositionNonZero( g ); # shift g into ind while d < n+1 do h := ind[d]; hh := indd[d]; if not IsBool( h ) then # reduce g with h eg := g[d]; if IsFFE( eg ) then eg := IntFFE(eg); fi; eh := h[d]; if IsFFE( eh ) then eh := IntFFE(eh); fi; e := Gcdex( eg, eh ); # adjust ind[d] by gcd ind[d] := (e.coeff1 * g) + (e.coeff2 * h); indd[d] := (e.coeff1 * gg) + (e.coeff2 * hh); # if e.coeff1 <> 0 then Add( c, d ); fi; # adjust g g := (e.coeff3 * g) + (e.coeff4 * h); gg := (e.coeff3 * gg) + (e.coeff4 * hh); else # just add g into ind ind[d] := g; indd[d] := gg; g := 0 * g; gg := 0 * gg; # Add( c, d ); fi; d := PositionNonZero( g ); od; od; # return resulting list return [Filtered( ind, x -> not IsBool( x ) ), Filtered( indd, x -> not IsBool( x ) ) ]; end; ############################################################################# ## #F AbelianExponents( g, gens, rels, pcpN ) ## AbelianExponents := function( g, gens, rels, pcpN ) local dept, depN, exp, d, j, e, n; # get depths and set up dept := List( gens, PositionNonZero ); depN := List( pcpN, PositionNonZero ); exp := List( gens, x -> 0 ); n := Length( gens[1] ); if Length( gens ) = 0 then return exp; fi; # go through and reduce g d := PositionNonZero( g ); g := ShallowCopy( g ); while d <= n do # get exponent in pcpF if d in dept then j := Position( dept, d ); e := ReducingCoefficient( g[d], gens[j][d], 0 ); if IsBool( e ) then return fail; fi; if rels[j] > 0 then e := e mod rels[j]; fi; exp[j] := e; g := -e * gens[j] + g; fi; # reduce with pcpN if d in depN and PositionNonZero( g ) = d then j := Position( depN, d ); e := ReducingCoefficient( g[d], pcpN[j][d], 0 ); if IsBool( e ) then return fail; fi; g := -e * pcpN[j] + g; fi; # if g has still depth d then there is something wrong if PositionNonZero(g) <= d then Error("wrong reduction in ExponentsByPcp"); fi; d := PositionNonZero( g ); od; # finally return return exp; end; ############################################################################# ## #F AdditiveFactorPcp( base, sub, r ) ## ## To describe factors of additive abelian groups. r = 0 or r = p. ## We assume that base is in upper triangular form, but sub can be an ## arbitrary basis. ## AdditiveFactorPcp := function( base, sub, r ) local denom, deps, prei, rels, h, d, j, e, gens, imgs, zero, new, k, full, n, fimg, news, exp, chng, mat, i, g, l, rimg, newr, newp, oldg, t, invs, tmps; # triangulise sub if r = 0 then denom := TriangulizedIntegerMat( sub ); else denom := ShallowCopy( sub ); TriangulizeMat( denom ); fi; deps := List( denom, PositionNonZero ); prei := []; rels := []; # get modulo generators and their relative orders for h in base do d := PositionNonZero( h ); j := Position( deps, d ); if IsBool( j ) then Add( rels, r ); Add( prei, h ); elif r = 0 then e := AbsInt( denom[j][d] / h[d] ); if e > 1 then Add( rels, e ); Add( prei, h ); fi; fi; od; l := Length( rels ); # catch a special case if l = 0 then return rec( gens := [], rels := [], imgs := List( base, x -> [] ), prei := prei, denom := denom ); fi; # first the case that r = p if r > 0 then gens := IdentityMat( l ) * One( GF(r) ); zero := 0 * gens[1]; full := Concatenation( prei, denom ); fimg := Concatenation( gens, List( denom, x -> zero ) ); news := AdditiveIgsParallel( full, fimg ); imgs := []; # get images for base elements for h in base do j := Position( prei, h ); if IsInt( j ) then Add( imgs, gens[j] ); else t := SolutionMat( news[1], h ); t := t * news[2]; Add( imgs, t ); fi; od; return rec( gens := gens, rels := rels, imgs := imgs, prei := prei, denom := denom ); fi; # now we are in case r = 0 - get isomorphism type of image mat := []; for i in [1..l] do g := rels[i] * prei[i]; exp := AbelianExponents( g, prei, rels, denom ); exp[i] := exp[i] - rels[i]; Add( mat, exp ); od; #new := SmithNormalFormSQ( mat ); new := NormalFormIntMat( mat, 13 ); # rewrite rels and prei tmps := TransposedMat( new.coltrans ); invs := Inverse( new.coltrans ); newr := []; newp := []; oldg := []; for i in [1..Length(new.normal)] do if new.normal[i][i] <> 1 then Add( newr, new.normal[i][i] ); Add( newp, invs[i] * prei ); Add( oldg, tmps[i] ); fi; od; oldg := TransposedMat( oldg ); # get images of gcc zero := 0 * oldg[1]; full := Concatenation( prei, denom ); fimg := Concatenation( oldg, List( denom, x -> zero ) ); news := AdditiveIgsParallel( full, fimg ); imgs := []; for h in base do j := Position( prei, h ); if IsInt( j ) then t := oldg[j]; else t := PcpSolutionIntMat( news[1], h ); t := t * news[2]; fi; t := ShallowCopy(t); for i in [1..Length(t)] do if newr[i] > 0 then t[i] := t[i] mod newr[i]; fi; od; Add( imgs, t ); od; return rec( gens := IdentityMat( Length( newr )), rels := newr, imgs := imgs, prei := newp, denom := denom ); end; SizeAddFactor := function( fact ) if ForAny( fact.rels, x -> x = 0 ) then return infinity; else return Product( fact.rels ); fi; end; ElementsAddFactor := function( fact ) return ExponentsByRels( fact.rels ); end; polycyclic-2.16/gap/cohom/general.gi0000644000076600000240000001234613706672341016454 0ustar mhornstaff############################################################################# ## #W general.gi Polycyc Bettina Eick ## ## General stuff for cohomology computations. ## ############################################################################# ## #F CollectedOneCR( A, w ) . . . . . . . . . . . . . . . . . . . . . comb word ## CollectedOneCR := function( A, w ) local tail, t, i, j, mat; tail := []; t := A.one; for i in Reversed( [1..Length(w)] ) do if w[i][2] > 0 then for j in [1..w[i][2]] do # first add tail if IsBound( tail[w[i][1]] ) then tail[w[i][1]] := tail[w[i][1]] + t; else tail[w[i][1]] := t; fi; # push next generator if not IsBound( A.central) or not A.central then t := A.mats[w[i][1]] * t; fi; od; else for j in [1..-w[i][2]] do # push next generator if not IsBound( A.central) or not A.central then t := A.invs[w[i][1]] * t; fi; # first add tail if IsBound( tail[w[i][1]] ) then tail[w[i][1]] := tail[w[i][1]] - t; else tail[w[i][1]] := -t; fi; od; fi; od; return tail; end; ############################################################################# ## #F BinaryPowering( A, m, e ) . . . . . . . . . .compute 1 + m + ... + m^(e-1) ## and m^e ## BinaryPowering := function( A, m, e ) local l, p, i, r, c; if IsBound(A.central) and A.central then return [e * A.one, A.one]; fi; # set up for binary powers approach l := Log( e, 2 ); p := [m]; for i in [1..l] do Add( p, p[i]^2 ); od; # compute binary powers r := ShallowCopy( A.one ); for i in [1..e-1] do c := CoefficientsQadic( i, 2 ); c := MappedVector( c, p{[1..Length(c)]} ); r := r + c; od; # compute final power c := CoefficientsQadic( e, 2 ); c := MappedVector( c, p ); return [r, c]; end; ############################################################################# ## #F CollectedOneCR( A, w ) . . . . . . . . . . . . . . . . . . . . . comb word ## CollectedOneCRNew := function( A, w ) local tail, t, i, j, r; tail := []; t := A.one; for i in Reversed( [1..Length(w)] ) do if w[i][2] > 0 then # compute 1 + m + ... + m^(e-1) r := BinaryPowering( A, A.mats[w[i][1]], w[i][2] ); # add derivation to tail if IsBound( tail[w[i][1]] ) then tail[w[i][1]] := tail[w[i][1]] + r[1] * t; else tail[w[i][1]] := r[1] * t; fi; # adjust tail t := r[2] * t; else # compute l + l^2 + ... + l^e r := BinaryPowering( A, A.invs[w[i][1]], -w[i][2] ); r[1] := A.invs[w[i][1]] * r[1]; # add derivation to tail if IsBound( tail[w[i][1]] ) then tail[w[i][1]] := tail[w[i][1]] - r[1] * t; else tail[w[i][1]] := - r[1] * t; fi; # adjust tail t := r[2] * t; fi; od; if tail <> CollectedOneCR( A, w ) then Error("tails"); fi; return tail; end; ############################################################################# ## #F CollectedRelatorCR( A, i, j ) ## CollectedRelatorCR := function( A, i, j ) local a, b, e, taila, tailb; # get the word e := RelativeOrdersOfPcp( A.factor )[i]; a := A.relators[i][j]; if i = j then b := [[i,e]]; elif j < i then b := [[i,1], [j,1]]; a := Concatenation( [[j,1]], a ); else b := [[i,1], [j-i,-1]]; a := Concatenation( [[j-i,-1]], a ); fi; # create tails taila := CollectedOneCR( A, a ); tailb := CollectedOneCR( A, b ); return [taila, tailb]; end; ############################################################################# ## #F AddTailVectorsCR( t1, t2 ) ## AddTailVectorsCR := function( t1, t2 ) local i; for i in [ 1 .. Length(t2) ] do if IsBound(t2[i]) then if IsBound(t1[i]) then t1[i] := t1[i] + t2[i]; else t1[i] := t2[i]; fi; fi; od; end; ############################################################################# ## #F CutVector( vec, l ) . . . . . . . . . . . . . . . . cut vector in l pieces ## CutVector := function( vec, l ) local d, new, i; if Length( vec ) = 0 then return []; fi; d := Length(vec)/l; new := []; for i in [1..l] do Add( new, vec{[d*(i-1)+1..d*i]} ); od; return new; end; ############################################################################# ## #F IntVector( vec ) ## IntVector := function( vec ) local i; if Length( vec ) = 0 then return []; fi; vec := ShallowCopy( vec ); for i in [1..Length(vec)] do if IsFFE( vec[i] ) then vec[i] := IntFFE( vec[i] ); fi; od; return vec; end; polycyclic-2.16/gap/cohom/cohom.gd0000644000076600000240000000156613706672341016141 0ustar mhornstaff############################################################################# ## #W cohom.gd Polycyc Bettina Eick ## DeclareGlobalFunction("OneCoboundariesCR"); DeclareGlobalFunction("OneCoboundariesEX"); DeclareGlobalFunction("OneCocyclesCR"); DeclareGlobalFunction("OneCocyclesEX"); DeclareGlobalFunction("OneCohomologyCR"); DeclareGlobalFunction("OneCohomologyEX"); DeclareGlobalFunction("TwoCoboundariesCR"); DeclareGlobalFunction("TwoCocyclesCR"); DeclareGlobalFunction("TwoCohomologyCR"); DeclareGlobalFunction("ComplementClassesCR"); DeclareGlobalFunction("ComplementClassesEfaPcps"); DeclareGlobalFunction("ComplementClasses"); DeclareGlobalFunction("ExtensionCR"); DeclareGlobalFunction("CRRecordByMats"); DeclareGlobalFunction("CollectedTwoCR"); DeclareOperation( "SplitExtensionByAutomorphisms", [ IsGroup, IsGroup, IsList ] ); polycyclic-2.16/gap/cohom/twocohom.gi0000644000076600000240000003104413706672341016672 0ustar mhornstaff############################################################################# ## #F CollectedTwoCR( A, u, v ) ## InstallGlobalFunction( CollectedTwoCR, function( A, u, v ) local n, word, tail, rels, wstack, tstack, p, c, l, g, e, mat, s, t, r, i; # set up and push u into result n := Length( A.mats ); word := ShallowCopy( u.word ); tail := ShallowCopy( u.tail ); rels := RelativeOrdersOfPcp( A.factor ); # catch a trivial case if v.word = 0 * v.word then AddTailVectorsCR( tail, v.tail ); return rec( word := word, tail := tail ); fi; # create stacks and put v onto stack wstack := [WordOfVectorCR( v.word )]; tstack := [v.tail]; p := [1]; c := [1]; # run until stacks are empty l := 1; while l > 0 do # take a generator and its exponent g := wstack[l][p[l]][1]; e := wstack[l][p[l]][2]; # take operation mat if e < 0 then mat := A.invs[g]; else mat := A.mats[g]; fi; # push g through module tail for i in [1..Length(tail)] do if IsBound( tail[i] ) then tail[i] := tail[i] * mat; fi; od; # correct the stacks c[l] := c[l] + 1; if AbsInt(e) < c[l] then # exponent overflow c[l] := 1; p[l] := p[l] + 1; if Length(wstack[l]) < p[l] then # word overflow - add tail AddTailVectorsCR( tail, tstack[l] ); tstack[l] := 0; l := l - 1; fi; fi; # push g through word for i in [ n, n-1 .. g+1 ] do if word[i] <> 0 then # get relator and tail t := []; if e > 0 then s := Position( A.enumrels, [i, g] ); r := PowerWord( A, A.relators[i][g], word[i] ); t[s] := PowerTail( A, A.relators[i][g], word[i] ); elif e < 0 then s := Position( A.enumrels, [i, i+g] ); r := PowerWord( A, A.relators[i][i+g], word[i] ); t[s] := PowerTail( A, A.relators[i][i+g], word[i] ); fi; # add to stacks AddTailVectorsCR( tail, t ); l := l+1; wstack[l] := r; tstack[l] := tail; tail := []; c[l] := 1; p[l] := 1; fi; # reset word[i] := 0; od; # increase exponent if e < 0 then word[g] := word[g] - 1; else word[g] := word[g] + 1; fi; # insert power relators if exponent has reached rel order if rels[g] > 0 and word[g] = rels[g] then word[g] := 0; r := A.relators[g][g]; s := Position( A.enumrels, [g, g] ); for i in [1..Length(r)] do word[r[i][1]] := r[i][2]; od; t := []; t[s] := A.one; AddTailVectorsCR( tail, t ); # insert power relators if exponent is negative elif rels[g] > 0 and word[g] < 0 then word[g] := rels[g] + word[g]; if Length(A.relators[g][g]) <= 1 then r := A.relators[g][g]; for i in [1..Length(r)] do word[r[i][1]] := -r[i][2]; od; s := Position( A.enumrels, [g, g] ); t := []; t[s] := - MappedWordCR( r, A.mats, A.invs ); AddTailVectorsCR( tail, t ); else r := InvertWord( A.relators[g][g] ); s := Position( A.enumrels, [g, g] ); t := []; t[s] := - MappedWordCR( r, A.mats, A.invs ); AddTailVectorsCR( tail, t ); l := l+1; wstack[l] := r; tstack[l] := tail; tail := []; c[l] := 1; p[l] := 1; fi; fi; od; return rec( word := word, tail := tail ); end ); ############################################################################# ## #F TwoCocyclesCR( A ) ## InstallGlobalFunction( TwoCocyclesCR, function( A ) local C, n, e, id, l, gn, gp, gi, eq, pairs, i, j, k, w1, w2, d, sys, h; # set up system of length d n := Length( A.mats ); e := RelativeOrdersOfPcp( A.factor ); l := Length( A.enumrels ); if IsBound(A.endosys) then sys := List( A.endosys, x -> CRSystem( x[2], l, 0 ) ); for i in [1..Length(sys)] do sys[i].full := true; od; else sys := CRSystem( A.dim, l, A.char ); fi; # set up for equations id := IdentityMat(n); gn := List( id, x -> rec( word := x, tail := [] ) ); # precompute (ij) for i > j pairs := List( [1..n], x -> [] ); for i in [1..n] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); pairs[i][i] := CollectedTwoCR( A, h, gn[i] ); fi; for j in [1..i-1] do pairs[i][j] := CollectedTwoCR( A, gn[i], gn[j] ); od; od; # consistency 1: k(ji) = (kj)i for i in [ n, n-1 .. 1 ] do for j in [ n, n-1 .. i+1 ] do for k in [ n, n-1 .. j+1 ] do w1 := CollectedTwoCR( A, gn[k], pairs[j][i] ); w2 := CollectedTwoCR( A, pairs[k][j], gn[i] ); if w1.word <> w2.word then Error( "k(ji) <> (kj)i" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; od; od; od; # consistency 2: j^(p-1) (ji) = j^p i for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[j] > 0 then h := rec( word := (e[j] - 1) * id[j], tail := [] ); w1 := CollectedTwoCR( A, h, pairs[j][i]); w2 := CollectedTwoCR( A, pairs[j][j], gn[i]); if w1.word <> w2.word then Error( "j^(p-1) (ji) <> j^p i" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; fi; od; od; # consistency 3: k (i i^(p-1)) = (ki) i^p-1 for i in [n,n-1..1] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); l := CollectedTwoCR( A, gn[i], h ); for k in [n,n-1..i+1] do w1 := CollectedTwoCR( A, gn[k], l ); w2 := CollectedTwoCR( A, pairs[k][i], h ); if w1.word <> w2.word then Error( "k i^p <> (ki) i^(p-1)" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; od; fi; od; # consistency 4: (i i^(p-1)) i = i (i^(p-1) i) for i in [ n, n-1 .. 1 ] do if e[i] > 0 then h := rec( word := (e[i] - 1) * id[i], tail := [] ); l := CollectedTwoCR( A, gn[i], h ); w1 := CollectedTwoCR( A, l, gn[i] ); w2 := CollectedTwoCR( A, gn[i], pairs[i][i] ); if w1.word <> w2.word then Error( "i i^p-1 <> i^p" ); else AddEquationsCR( sys, w1.tail, w2.tail, true ); fi; fi; od; # consistency 5: j = (j -i) i gi := List( id, x -> rec( word := -x, tail := [] ) ); for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[i] = 0 then w1 := CollectedTwoCR( A, gn[j], gi[i] ); w2 := CollectedTwoCR( A, w1, gn[i] ); if w2.word <> id[j] then Error( "j <> (j -i) i" ); else AddEquationsCR( sys, w2.tail, [], true ); fi; fi; od; od; # consistency 6: i = -j (j i) for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[j] = 0 then w1 := CollectedTwoCR( A, gi[j], pairs[j][i] ); if w1.word <> id[i] then Error( "i <> -j (j i)" ); else AddEquationsCR( sys, w1.tail, [], true ); fi; fi; od; od; # consistency 7: -i = -j (j -i) for i in [n,n-1..1] do for j in [n,n-1..i+1] do if e[i] = 0 and e[j] = 0 then w1 := CollectedTwoCR( A, gn[j], gi[i] ); w1 := CollectedTwoCR( A, gi[j], w1 ); if w1.word <> -id[i] then Error( "-i <> -j (j -i)" ); else AddEquationsCR( sys, w1.tail, [], true ); fi; fi; od; od; # add a check ((j ^ i) ^-i ) = j for i in [1..n] do for j in [1..i-1] do w1 := CollectedTwoCR( A, gi[j], pairs[i][j] ); w1 := CollectedTwoCR( A, gn[j], w1 ); w1 := CollectedTwoCR( A, w1, gi[j] ); if w1.word <> id[i] then Error("in rel check "); elif not IsZeroTail( w2.tail ) then # Error("relations bug"); AddEquationsCR( sys, w1.tail, [], true ); fi; od; od; # and return solution return KernelCR( A, sys ); end ); ############################################################################# ## #F TwoCoboundariesCR( A ) ## InstallGlobalFunction( TwoCoboundariesCR, function( A ) local n, e, l, sys, R, c, tail, i, t, j; # set up system of length d n := Length( A.mats ); e := RelativeOrdersOfPcp( A.factor ); l := Length( A.enumrels ); if IsBound(A.endosys) then sys := List( A.endosys, x -> CRSystem( x[2], l, 0 ) ); for i in [1..Length(sys)] do sys[i].full := true; od; else sys := CRSystem( A.dim, l, A.char ); fi; # loop over relators R := []; for c in A.enumrels do tail := CollectedRelatorCR( A, c[1], c[2] ); SubtractTailVectors( tail[1], tail[2] ); Add( R, tail[1] ); od; # shift into system for i in [1..n] do t := []; for j in [1..l] do if IsBound(R[j][i]) then t[j] := TransposedMat(R[j][i]); fi; od; if IsList(sys) then AddEquationsCREndo( sys, t ); else AddEquationsCRNorm( sys, t, true ); fi; od; # return return ImageCR( A, sys ).basis; end ); ############################################################################# ## #F TwoCohomologyCR( A ) ## InstallGlobalFunction( TwoCohomologyCR, function( A ) local cc, cb, exp, l, B, b, Q, U, V, i; cc := TwoCocyclesCR( A ); cb := TwoCoboundariesCR( A ); if not IsBound(A.endosys) then return rec( gcc := cc, gcb := cb, factor := AdditiveFactorPcp( cc, cb, A.char )); fi; Q := []; for i in [1..Length(cc)] do if Length(cc[i]) = 0 then Add( Q, AbelianPcpGroup([])); fi; exp := A.mats[1][i]!.exp; l := Length(cc[i][1])/Length(exp); B := AbelianPcpGroup( Concatenation(List([1..l], x -> exp)) ); b := Igs(B); U := Subgroup( B, List(cc[i], x -> MappedVector(x,b))); V := Subgroup( B, List(cb[i], x -> MappedVector(x,b))); Add(Q, U/V); od; return Q; end ); ############################################################################# ## #F TwoCohomologyTrivialModule( G, d[, p] ) ## TwoCohomologyTrivialModule := function(arg) local G, d, m, C, c; # catch arguments G := arg[1]; d := arg[2]; if Length(arg)=2 then m := List(Igs(G), x -> IdentityMat(d)); elif Length(arg)=3 then m := List(Igs(G), x -> IdentityMat(d,arg[3])); fi; # construct H^2 C := CRRecordByMats(G, m); c := TwoCohomologyCR(C); return c.factor.rels; end; ############################################################################# ## #F CheckTrivialCohom( G ) ## CheckTrivialCohom := function(G) local mats, C, cb, cc, c, E; # compute cohom Print("compute cohomology \n"); mats := List( Pcp(G), x -> IdentityMat( 1 ) ); C := CRRecordByMats( G, mats ); cb := TwoCoboundariesCR( C ); Print("cb has length ", Length(cb)," \n"); cc := TwoCocyclesCR( C ); Print("cc has length ", Length(cc)," \n"); # first check Print("check cb in cc \n"); c := First( cb, x -> IsBool( SolutionMat( cc,x ) ) ); if not IsBool( c ) then Print(" coboundary is not contained in cc \n"); return c; fi; # second check Print("check cc \n"); for c in cc do E := ExtensionCR( C, c ); od; end; polycyclic-2.16/gap/cohom/grpext.gi0000644000076600000240000002133613706672341016347 0ustar mhornstaff############################################################################# ## #F ExtensionCR( A, c ) . . . . . . . . . . . . . . . . . . . . .one extension ## InstallGlobalFunction( ExtensionCR, function( A, c ) local n, m, coll, i, j, g, e, r, o, x, k, v, G, rels; # get dimensions n := Length( A.mats ); m := A.dim; rels := RelativeOrdersOfPcp( A.factor ); # in case c is ffe if IsBool( c ) then c := List( [1..m*Length(A.enumrels)], x -> 0 ); fi; if Length( c ) > 0 and IsFFE( c[1] ) then c := IntVecFFE(c); fi; # the free group coll := FromTheLeftCollector( n+m ); # the relators of G for i in [1..Length(A.enumrels)] do e := A.enumrels[i]; r := VectorOfWordCR( A.relators[e[1]][e[2]], n ); v := c{[(i-1)*m+1..i*m]}; Append(r, v); o := ObjByExponents( coll, r ); if e[1] = e[2] then SetRelativeOrder( coll, e[1], rels[e[1]] ); SetPower( coll, e[1], o ); elif e[1] > e[2] then SetConjugate( coll, e[1], e[2], o ); else SetConjugate( coll, e[1], -e[2]+e[1], o ); fi; od; # power relators of A if A.char > 0 then for i in [n+1..n+m] do SetRelativeOrder( coll, i, A.char ); od; fi; # conjugate relators - G acts on A for i in [1..n] do for j in [n+1..n+m] do x := List( [1..n], x -> 0 ); if A.char = 0 then Append( x, A.mats[i][j-n] ); else Append( x, IntVecFFE( A.mats[i][j-n] ) ); fi; SetConjugate( coll, j, i, ObjByExponents( coll, x ) ); od; od; UpdatePolycyclicCollector( coll ); G := PcpGroupByCollectorNC( coll ); G!.module := Subgroup( G, Igs(G){[n+1..n+m]} ); return G; end ); ############################################################################# ## #F ExtensionsCR( C ) . . . . . . . . . . . . . . . . . . . . . all extensions ## # FIXME: This function is documented and should be turned into a GlobalFunction ExtensionsCR := function( C ) local cc, new, elm, rel; # compute cocycles cc := TwoCocyclesCR( C ); # if there are infinitely many extensions if C.char = 0 and Length( cc ) > 0 then Print("infinitely many extensions \n"); return fail; fi; # otherwise compute all elements new := []; if Length( cc ) = 0 then elm := ExtensionCR( C, false ); Add( new, elm ); else rel := ExponentsByRels( List( cc, x -> C.char ) ); elm := List( rel, x -> IntVector( x * cc ) ); elm := List( elm, x -> ExtensionCR( C, x ) ); Append( new, elm ); fi; return new; end; ############################################################################# ## #F ExtensionClassesCR( C ) . . . . . . . . . . . . . . all up to equivalence ## # FIXME: This function is documented and should be turned into a GlobalFunction ExtensionClassesCR := function( C ) local cc, elms; # compute H^2( U, A/B ) and return if there is no complement cc := TwoCohomologyCR( C ); if IsBool( cc ) then return []; fi; # check the finiteness of H^1 if ForAny( cc.factor.rels, x -> x = 0 ) then Print("infinitely many extensions \n"); return fail; fi; # catch a trivial case if Length( cc.factor.rels ) = 0 then return [ ExtensionCR( C, false ) ]; fi; # create elements of cc.factor elms := ExponentsByRels( cc.factor.rels ); if C.char > 0 then elms := elms * One( C.field ); fi; # loop over orbit and extract information return List( elms, x -> ExtensionCR( C, IntVector( x * cc.factor.prei ))); end; ############################################################################# ## #F SplitExtensionPcpGroup( G, mats ) . . . . . . . . . . . . . . .G split Z^n ## # FIXME: This function is documented and should be turned into a GlobalFunction SplitExtensionPcpGroup := function( G, mats ) return ExtensionCR( CRRecordByMats( G, mats ), false ); end; ############################################################################# ## #F SplitExtensionByAutomorphisms( G, H, auts ) ## InstallMethod( SplitExtensionByAutomorphisms, "for a PcpGroup, a PcpGroup, and a list of automorphisms", true, [ IsPcpGroup, IsPcpGroup, IsList ], 0, function( G, H, auts ) local g, h, n, m, rg, rh, zn, zm, coll, o, i, j, k; # get dimensions g := Igs(G); h := Igs(H); n := Length( g ); m := Length( h ); rg := List( g, x -> RelativeOrderPcp(x) ); rh := List( h, x -> RelativeOrderPcp(x) ); zn := ListWithIdenticalEntries( n, 0 ); zm := ListWithIdenticalEntries( m, 0 ); # the free group coll := FromTheLeftCollector( n+m ); # the relators of G for i in [1..n] do if rg[i] > 0 then o := ExponentsByIgs( g, g[i]^rg[i] ); o := ObjByExponents( coll, Concatenation( zm, o ) ); SetRelativeOrder( coll, m+i, rg[i] ); SetPower( coll, m+i, o ); fi; for j in [i+1..n] do o := ExponentsByIgs( g, g[j]^g[i] ); o := ObjByExponents( coll, Concatenation( zm, o ) ); SetConjugate( coll, m+j, m+i, o ); od; od; # the relators of H for i in [1..m] do if rh[i] > 0 then o := ExponentsByIgs( h, h[i]^rh[i] ); o := ObjByExponents( coll, Concatenation( o, zn ) ); SetRelativeOrder( coll, i, rh[i] ); SetPower( coll, i, o ); fi; for j in [i+1..m] do o := ExponentsByIgs( h, h[j]^h[i] ); o := ObjByExponents( coll, Concatenation( o, zn ) ); SetConjugate( coll, j, i, o ); od; od; # the action of H on G for i in [1..m] do k := List( g, x -> Image( auts[i], x ) ); for j in [1..n] do o := ExponentsByIgs( g, k[j] ); o := ObjByExponents( coll, Concatenation( zm, o ) ); SetConjugate( coll, m+j, i, o ); od; od; UpdatePolycyclicCollector(coll); G := PcpGroupByCollectorNC( coll ); return G; end); ############################################################################# ## #M DirectProductOp( , ) . . . . . . . . . for pcp groups ## InstallMethod( DirectProductOp, "for pcp groups", [ IsList, IsPcpGroup ], function( groups, onegroup ) local D, info, f, a, i; if IsEmpty(groups) or not ForAll(groups,IsPcpGroup) then TryNextMethod(); fi; D := groups[1]; f := [1,Length(Igs(D))+1]; for i in [2..Length(groups)] do a := List(Igs(D), x -> IdentityMapping(groups[i])); D := SplitExtensionByAutomorphisms(groups[i],D,a); Add(f,Length(Igs(D))+1); od; info := rec(groups := groups, first := f, embeddings := [ ], projections := [ ]); SetDirectProductInfo(D,info); if ForAny(groups,grp->HasIsFinite(grp) and not IsFinite(grp)) then SetSize(D,infinity); elif ForAll(groups,HasSize) then SetSize(D,Product(List(groups,Size))); fi; return D; end ); ############################################################################# ## #A Embedding ## InstallMethod( Embedding, true, [ IsPcpGroup and HasDirectProductInfo, IsPosInt ], 0, function( D, i ) local info, G, imgs, hom, gens; # check info := DirectProductInfo( D ); if IsBound( info.embeddings[i] ) then return info.embeddings[i]; fi; # compute embedding G := info.groups[i]; gens := Igs( G ); imgs := Igs( D ){[info.first[i] .. info.first[i+1]-1]}; hom := GroupHomomorphismByImagesNC( G, D, gens, imgs ); SetIsInjective( hom, true ); # store information info.embeddings[i] := hom; return hom; end ); ############################################################################# ## #A Projection ## InstallMethod( Projection, true, [ IsPcpGroup and HasDirectProductInfo, IsPosInt ], 0, function( D, i ) local info, G, imgs, hom, N, gens; # check info := DirectProductInfo( D ); if IsBound( info.projections[i] ) then return info.projections[i]; fi; # compute projection G := info.groups[i]; gens := Igs( D ); imgs := Concatenation( List( [1..info.first[i]-1], x -> One( G ) ), Igs( G ), List( [info.first[i+1]..Length(gens)], x -> One(G))); hom := GroupHomomorphismByImagesNC( D, G, gens, imgs ); SetIsSurjective( hom, true ); # add kernel N := SubgroupNC( D, gens{Concatenation( [1..info.first[i]-1], [info.first[i+1]..Length(gens)])}); SetKernelOfMultiplicativeGeneralMapping( hom, N ); # store information info.projections[i] := hom; return hom; end ); polycyclic-2.16/gap/matrix/0000755000076600000240000000000013706672341014707 5ustar mhornstaffpolycyclic-2.16/gap/matrix/lattices.gi0000644000076600000240000001410413706672341017040 0ustar mhornstaff############################################################################# ## #W lattices.gi Polycyclic Bettina Eick ## ## Methods to compute with integral lattices. ## ############################################################################# ## #F InducedByField( mats, f ) ## InducedByField := function( mats, f ) local i; mats := ShallowCopy( mats ); for i in [1..Length(mats)] do mats[i] := Immutable( mats[i] * One(f) ); ConvertToMatrixRep( mats[i], f ); od; return mats; end; ############################################################################# ## #F PcpNullspaceIntMat( arg ) ## InstallGlobalFunction( PcpNullspaceIntMat, function( arg ) local A, d, hnfm, rels, j; A := arg[1]; if Length( arg ) = 2 then d := arg[2]; fi; # catch a trivial case if Length(A) = 0 and Length( arg ) = 2 then return IdentityMat(d); fi; if Length(A) = 0 and Length( arg ) = 1 then Error("trivial matrix"); fi; # compute hnf hnfm := NormalFormIntMat( A, 4 ); rels := hnfm.rowtrans; hnfm := hnfm.normal; # get relations j := Position( hnfm, 0 * hnfm[1] ); if IsBool( j ) then return []; fi; return NormalFormIntMat( rels{[j..Length(rels)]}, 0 ).normal; end ); InstallGlobalFunction( NullspaceRatMat, function( arg ) local A, d, hnfm, rels, j; A := arg[1]; if Length( arg ) = 2 then d := arg[2]; fi; # catch a trivial case if Length(A) = 0 and Length( arg ) = 2 then return IdentityMat(d); fi; if Length(A) = 0 and Length( arg ) = 1 then Error("trivial matrix"); fi; # compute nullspace return TriangulizedNullspaceMat( A ); end ); ############################################################################# ## #F NullspaceMatMod( mat, rels ) ## NullspaceMatMod := function( mat, rels ) local l, idm, i, null; # set up l := Length( mat ); # append relative orders mat := ShallowCopy( mat ); idm := IdentityMat( Length(rels) ); for i in [1..Length(rels)] do Add( mat, rels[i] * idm[i] ); od; # solve null := PcpNullspaceIntMat( mat, l ); if Length( null ) = 0 then return null; fi; # cut out the solutions for i in [1..Length(null)] do null[i] := null[i]{[1..l]}; if null[i] = 0 * null[i] then null[i] := false; fi; od; return Filtered( null, x -> not IsBool(x) ); end; ############################################################################# ## #F PcpBaseIntMat( mat ) ## PcpBaseIntMat := function( A ) local hnfm, zero, j; hnfm := NormalFormIntMat( A, 0 ).normal; zero := hnfm[1] * 0; j := Position( hnfm, zero ); if not IsBool( j ) then hnfm := hnfm{[1..j-1]}; fi; return hnfm; end; ############################################################################# ## #F FreeGensAndKernel( mat ) ## FreeGensAndKernel := function( mat ) local norm, j; norm := NormalFormIntMat( mat, 6 ); j := Position( norm.normal, 0 * mat[1] ); if IsBool( j ) then j := Length(norm.normal)+1; fi; return rec( free := norm.normal{[1..j-1]}, trsf := norm.rowtrans{[1..j-1]}, kern := norm.rowtrans{[j..Length(norm.rowtrans)]} ); end; ############################################################################# ## #F PcpSolutionIntMat( A, s ) ## InstallGlobalFunction( PcpSolutionIntMat, function( A, s ) local B, N, H; B := Concatenation( [s], A ); N := PcpNullspaceIntMat( B ); if Length(N) = 0 then return fail; fi; H := NormalFormIntMat( N, 2 ).normal; if H[1][1] = 1 then return -H[1]{[2..Length(H[1])]}; else return fail; fi; end ); ############################################################################# ## #F LatticeIntersection( base1, base2 ) ## InstallGlobalFunction( LatticeIntersection, function( base1, base2 ) local n, l, m, id, zr, A, i, H, I, h; # set up and catch the trivial cases if Length( base1 ) = 0 or Length( base2 ) = 0 then return []; fi; n := Length( base1[1] ); l := Length( base1 ); m := Length( base2 ); id := IdentityMat( n ); if base1 = id then return base2; fi; if base2 = id then return base1; fi; zr := List( [1..n], x -> 0 ); # determine matrix A := List( [1..l+m], x -> [] ); for i in [1..l] do A[i] := Concatenation( base1[i], base1[i] ); od; for i in [1..m] do A[l+i] := Concatenation( base2[i], zr ); od; # compute normal form H := NormalFormIntMat( A, 0 ).normal; # read off intersection I := []; for h in H do if h{[1..n]} = zr then Add( I, h{[n+1..2*n]} ); fi; od; return I; end ); ############################################################################# ## #F VectorModLattice( vec, base ) ## VectorModLattice := function( vec, base ) local i, q; vec := ShallowCopy(vec); for i in [1..Length(vec)] do if vec[i] <> 0 then q := QuotientRemainder( vec[i], base[i][i] ); if q[2] < 0 then q[1] := q[1] - 1; fi; AddRowVector( vec, base[i], -q[1] ); if vec[i] < 0 or vec[i] >= base[i][i] then Error("bloody quotient"); fi; fi; od; return vec; end; ############################################################################# ## #F PurifyRationalBase( base ) . . . . . . . . . . . . .this is too expensive ## PurifyRationalBase := function( base ) local i, dual; if Length(base) = 0 then return base; fi; if Length(base) = Length(base[1]) then return IdentityMat( Length(base[1]) ); fi; base := ShallowCopy( base ); for i in [1..Length(base)] do base[i] := Lcm( List( base[i], DenominatorRat ) ) * base[i]; base[i] := base[i] / Gcd( base[i] ); od; for i in [Length(base)+1..Length(base[1])] do Add( base, 0*base[1] ); od; base := PcpNullspaceIntMat( TransposedMat( base ) ); for i in [Length(base)+1..Length(base[1])] do Add( base, 0*base[1] ); od; base := PcpNullspaceIntMat( TransposedMat( base ) ); return NormalFormIntMat(base, 2).normal; end; polycyclic-2.16/gap/matrix/latbases.gi0000644000076600000240000001543113706672341017032 0ustar mhornstaff############################################################################# ## #W latbases.gi Bettina Eick ## ## Methods to compute with lattice bases. ## ############################################################################# ## #F LatticeBasis( gens ) ## LatticeBasis := function( gens ) local b, j; if Length(gens) = 0 or ForAll(gens, x -> Length(x)=0 ) then return []; fi; b := NormalFormIntMat( gens, 2 ).normal; if Length(b) = 0 then return b; fi; j := Position( b, 0*b[1] ); if not IsBool( j ) then b := b{[1..j-1]}; fi; return b; end; ############################################################################# ## #F FactorLattice( V, U ) ## FactorLattice := function( V, U ) local d, g, r, i, j, e; d := List( U, PositionNonZero ); g := []; r := []; for i in [1..Length(V)] do j := Position( d, PositionNonZero(V[i]) ); if IsBool(j) then Add( g, V[i] ); Add( r, 0 ); else e := AbsInt( U[j][d[j]] / V[i][d[j]] ); if e > 1 then Add( g, V[i] ); Add( r, e ); fi; fi; od; return rec( gens := g, rels := r, kern := U ); end; ############################################################################# ## #F CoefficientsByFactorLattice( F, v ) ## CoefficientsByFactorLattice := function( F, v ) local df, dk, cf, ck, z, l, j, e; v := ShallowCopy(v); df := List( F.gens, PositionNonZero ); dk := List( F.kern, PositionNonZero ); cf := List( df, x -> 0 ); ck := List( dk, x -> 0 ); z := 0 * v; while v <> z do l := PositionNonZero(v); # reduce with factor j := Position( df, l ); if not IsBool( j ) then if F.rels[j] > 0 then e := Gcdex( F.rels[j], F.gens[j][l] ); cf[j] := (v[l]/e.gcd * e.coeff2) mod F.rels[j]; else cf[j] := v[l]/F.gens[j][l]; fi; AddRowVector( v, F.gens[j], -cf[j] ); fi; # reduce with kernel if PositionNonZero(v) = l then j := Position( dk, l ); ck[j] := v[l]/F.kern[j][l]; AddRowVector( v, F.kern[j], -ck[j] ); fi; od; return rec( fcoeff := cf, kcoeff := ck ); end; ############################################################################# ## #F NaturalHomomorphismByLattices( V, U ). . . . . . . includes normalisation ## NaturalHomomorphismByLattices := function( V, U ) local F, n, mat, i, row, new, cyc, ord, chg, inv, imgs, prei; # get the factor F := FactorLattice( V, U ); n := Length(F.gens); # normalize the factor mat := []; for i in [1..n] do if F.rels[i] > 0 then row := CoefficientsByFactorLattice(F,F.rels[i]*F.gens[i]).fcoeff; row[i] := row[i] - F.rels[i]; Add( mat, row ); else Add( mat, List( [1..n], x -> 0 ) ); fi; od; # solve matrix new := NormalFormIntMat( mat, 9 ); # get new generators, relators and the basechange cyc := []; ord := []; chg := []; inv := []; imgs := TransposedMat( new.coltrans ); prei := Inverse( new.coltrans ); for i in [1..n] do if new.normal[i][i] <> 1 then Add( cyc, LinearCombination( F.gens, prei[i] ) ); Add( ord, new.normal[i][i] ); Add( chg, prei[i] ); if new.normal[i][i] > 0 then Add( inv, List( imgs[i], x -> x mod new.normal[i][i] ) ); else Add( inv, imgs[i] ); fi; fi; od; cyc := rec( gens := cyc, rels := ord, base := chg, inv := TransposedMat( inv ) ); return rec( fac := F, cyc := cyc ); end; ############################################################################# ## #F TranslateExp( cyc, exp ) . . . . . . . . . translate to decomposed factor ## TranslateExp := function( cyc, exp ) local new, i; new := exp * cyc.inv; for i in [1..Length(new)] do if cyc.rels[i] > 0 then new[i] := new[i] mod cyc.rels[i]; fi; od; return new; end; ############################################################################# ## #F CoefficientsByNHLB( v, hom ) ## CoefficientsByNHLB := function( v, hom ) local cf; cf := CoefficientsByFactorLattice( hom.fac, v ); cf.fcoeff := TranslateExp( hom.cyc, cf.fcoeff ); return cf; end; ############################################################################# ## #F ProjectionByNHLB( vec, hom ) ## ProjectionByNHLB := function( vec, hom ) return CoefficientsByNHLB( vec, hom ).kcoeff; end; ############################################################################# ## #F ImageByNHLB( vec, hom ) ## ImageByNHLB := function( vec, hom ) return CoefficientsByNHLB( vec, hom ).fcoeff; end; ############################################################################# ## #F ImageOfNHLB( hom ) ## ImageOfNHLB := function( hom ) return hom.cyc.base; end; ############################################################################# ## #F KernelOfNHLB( hom ) ## KernelOfNHLB := function( hom ) return hom.fac.kern; end; ############################################################################# ## #F PreimagesBasisOfNHLB( hom ) ## PreimagesBasisOfNHLB := function( hom ) return hom.cyc.gens; end; ############################################################################# ## #F PreimagesRepresentativeByNHLB( vec, hom ) ## PreimagesRepresentativeByNHLB := function( vec, hom ) return vec * hom.cyc.gens; end; ############################################################################# ## #F PreimageByNHLB( base, hom ) ## PreimageByNHLB := function( base, hom ) local new; new := List( base, x -> x * hom.cyc.gens ); Append( new, hom.fac.kern ); return LatticeBasis( new ); end; ############################################################################# ## #F InducedActionByNHLB( mat, hom ) ## InducedActionByNHLB := function( mat, hom ) local fac, sub; fac := List( hom.cyc.gens, x -> CoefficientsByNHLB(x*mat, hom).fcoeff ); sub := List( hom.fac.kern, x -> CoefficientsByNHLB(x*mat, hom).kcoeff ); return rec( factor := fac, subsp := sub ); end; ############################################################################# ## #F InducedActionFactorByNHLB( mat, hom ) ## InducedActionFactorByNHLB := function( mat, hom ) return List( hom.cyc.gens, x -> CoefficientsByNHLB(x*mat, hom).fcoeff ); end; ############################################################################# ## #F InducedActionSubspaceByNHLB( mat, hom ) ## InducedActionSubspaceByNHLB := function( mat, hom ) return List( hom.fac.kern, x -> CoefficientsByNHLB(x*mat, hom).kcoeff ); end; polycyclic-2.16/gap/matrix/README0000644000076600000240000000050613706672341015570 0ustar mhornstaff gap/matrix: Functions to compute with rational and integral matrices and modules. rowbases.gi -- computing with rational spaces lattices.gi -- computing with lattices modules.gi -- radical and composition series of efa modules intmat.gi -- integer matrices inversion triangle.gi -- upper triangular matrices polycyclic-2.16/gap/matrix/triangle.gi0000644000076600000240000001574513706672341017051 0ustar mhornstaff############################################################################# ## #W triangle.gi Polycyclic Werner Nickel ## ############################################################################# ## #F PreImageSubspaceIntMat( , ) ## ## Find all v such that v * in . ## PreImageSubspaceIntMat := function( M, D ) local nsp, d; ## [ M ] ## Find the nullspace of [ D ] nsp := PcpNullspaceIntMat( Concatenation( M, D ) ); ## Cut off the relevant bit. return List( nsp, v->v{[1..Length(M)]} ); end; ############################################################################# ## #F PreImageSubspaceIntMats( , ) ## ## Find all v such that v * M in for all matrices M in ## . ## PreImageSubspaceIntMats := function( mats, D ) local E, M, N; if Length(mats[1]) <> Length(mats[1][1]) then Error( "square matrices expected" ); fi; E := mats[1]^0; for M in mats do N := PreImageSubspaceIntMat( E * M, D ); if N = [] then break; fi; E := N * E; #if E = [] then break; fi; od; return E; end; ############################################################################# ## #F RowsWithLeadingIndexHNF( ) ## ## Given an integer matrix in Hermite Normal Form, return a list that ## indicates which row of the matrix has its leading entry in a given ## column. ## RowsWithLeadingIndexHNF := function( hnf ) local indices, i, j; indices := [1..Length(hnf[1])] * 0; i := 1; for j in [1..Length(hnf)] do while i < Length(hnf[j]) and hnf[j][i] = 0 do i := i+1; od; if i > Length( hnf[j]) then break; fi; indices[i] := j; od; return indices; end; ############################################################################# ## #F CoefficientsVectorHNF( , ) ## ## Decompose the integer vector into the rows of the integer matrix ## given in Hermite Normal Form and return the respective ## coefficients. ## CoefficientsVectorHNF := function( v, hnf ) local reduce, coeffs, i, k, c; reduce := RowsWithLeadingIndexHNF( hnf ); coeffs := [1..Length(hnf)] * 0; for i in [1..Length(v)] do if v[i] <> 0 then k := reduce[i]; if k = 0 or v[i] mod hnf[k][i] <> 0 then return fail; fi; c := v[i] / hnf[k][i]; v := v - c * hnf[k]; coeffs[k] := c; fi; od; return coeffs; end; ############################################################################# ## #F CompletionToUnimodularMat( ) ## ## Complete the integer matrix to a unimodular matrix if possible ## and produces an error message otherwise. ## CompletionToUnimodularMat := function( M ) local nf, D, i, d, n, P, compl; nf := NormalFormIntMat( M, 13 ); ## ## Check that there are only 1s on the diagonal. ## D := nf.normal; for i in [1..Length(D)] do if D[i][i] <> 1 then return Error( "\n\n\tSmith Normal Form contains diagonal", "entries different from 1\n\n" ); fi; od; d := Length( M ); n := Length( M[1] ); ## ## Extend the left transforming matrix to the identity. ## P := List( nf.rowtrans, ShallowCopy ); P{[1..d]}{[d+1..n]} := NullMat( d, n-d ); P{[d+1..n]} := IdentityMat( n ){[d+1..n]}; compl := P^-1 * Inverse( nf.coltrans ); if compl{[1..d]} <> M then return Error( "\n\n\tCompletion to unimodular matrix failed\n\n" ); fi; return compl{[d+1..n]}; end; ############################################################################# ## #F TriangularForm( ) ## ## Transform the unimodular integer matrices to lower ## block-triangular form. Each block corresponds to a common eigenvalue ## (possibly in a suitable extension field) of the matrices. ## TriangularForm := function( mats ) local d, comms, i, j, subs, dims, flag, newflag, T, M, C; d := Length( mats[1] ); # Print( "Computing commutators\n" ); comms := []; for i in [1..Length(mats)] do for j in [1..i-1] do Add( comms, mats[i]*mats[j] - mats[j]*mats[i] ); od; od; # Print( "Computing flag: " ); subs := []; dims := []; flag := []; while Length( flag ) < d do newflag := PreImageSubspaceIntMats( comms, flag ); newflag := HermiteNormalFormIntegerMat( newflag ); Add( subs, newflag ); Add( dims, Length(newflag) - Length(flag) ); flag := newflag; od; # Print( dims, "\n" ); # Print( "Computing transforming matrix\n" ); T := ShallowCopy( subs[1] ); for i in [2..Length(subs)] do M := List( T, v->CoefficientsVectorHNF( v, subs[i] ) ); C := CompletionToUnimodularMat( M ); Append( T, C * subs[i] ); od; return T * mats * T^-1; end; ############################################################################# ## #F LowerUnitriangularForm( ) ## ## Transform the unimodular integer matrices to lower ## unitriangular form, i.e. to lower triangular matrices with ones on the ## diagonal. ## LowerUnitriangularForm := function( mats ) local d, nilpmats, i, j, subs, dims, flag, newflag, T, M, C, I; d := Length( mats[1] ); I := IdentityMat( d ); ## Subtract the identity, this makes each matrix nilpotent. nilpmats := List( mats, M->M - I ); ## Compute an ascending chain of subspaces with the property that ## each space is mapped by the nilpotent matrices into the ## previous one. subs := []; dims := []; flag := []; while Length( flag ) < d do newflag := PreImageSubspaceIntMats( nilpmats, flag ); newflag := HermiteNormalFormIntegerMat( newflag ); Add( subs, newflag ); Add( dims, Length(newflag) - Length(flag) ); flag := newflag; od; T := ShallowCopy( subs[1] ); for i in [2..Length(subs)] do ## How does T embed into subs[i] C := List( T, v->CoefficientsVectorHNF( v, subs[i] ) ); ## Now extend to a basis of subs[i], the coefficients are ## with respect to the basis of subs[i] C := CompletionToUnimodularMat( C ); ## Add the additional basis vectors to T Append( T, C * subs[i] ); od; return T * mats * T^-1; end; ############################################################################# ## #F IsLowerUnitriangular( ) ## ## Test if the matrix is lower unitriangular. ## IsLowerUnitriangular := function( M ) return ForAll( M, v->Length(v) = Length(M) ) ## Is M quadratic? and ForAll( [1..Length(M)], i->M[i][i] = 1 ) ## Does M have ones ## on the diagonal? and IsLowerTriangularMat( M ); ## Is M lower triangular? end; polycyclic-2.16/gap/matrix/matrix.gd0000644000076600000240000000113013706672341016522 0ustar mhornstaffDeclareGlobalFunction("PcpNullspaceIntMat"); DeclareGlobalFunction("PcpSolutionIntMat"); DeclareGlobalFunction("LatticeIntersection"); DeclareGlobalFunction("AlgebraBase"); DeclareGlobalFunction("PrimitiveAlgebraElement"); DeclareGlobalFunction("NullspaceRatMat"); DeclareGlobalFunction("NaturalHomomorphismBySemiEchelonBases"); DeclareGlobalFunction("SpinnUpEchelonBase"); DeclareGlobalFunction("MatByVector"); DeclareGlobalFunction("OnMatVector"); DeclareGlobalFunction("PreimageByNHSEB"); DeclareGlobalFunction("InducedActionFactorByNHSEB"); DeclareGlobalFunction("InducedActionSubspaceByNHSEB"); polycyclic-2.16/gap/matrix/hnf.gi0000644000076600000240000002431113706672341016004 0ustar mhornstaff ############################################################################# ## #F FindNiceRowOneNorm #F FindNiceRowTwoNorm #F FindNiceRowInfinityNorm ## ## Functions that select during an HNF computation a row from a matrix ## such that the row is minimal with respect to a chosen norm and can ## function as pivot entry in position i,j. ## #F FindNiceRowInfinityNormRowOps ## ## Does the same as FindNiceRowInfinityNorm() but records the row ## operations. ## FindNiceRowOneNorm := function( M, i, j ) local m, n, k, a, r; m := Length( M ); n := Length( M[1] ); for k in [i+1..m] do a := AbsInt( M[k][j] ); if a <> 0 and (a < AbsInt( M[i][j] ) or (a = AbsInt( M[i][j] ) and Sum( M[k], AbsInt ) < Sum( M[i], AbsInt ) ) ) then r := M[i]; M[i] := M[k]; M[k] := r; fi; od; return; end; FindNiceRowTwoNorm := function( M, i, j ) local m, n, k, a, r; m := Length( M ); n := Length( M[1] ); for k in [i+1..m] do a := AbsInt( M[k][j] ); if a <> 0 and (a < AbsInt( M[i][j] ) or (a = AbsInt( M[i][j] ) and M[k]*M[k] < M[i]*M[i] ) ) then r := M[i]; M[i] := M[k]; M[k] := r; fi; od; return; end; FindNiceRowInfinityNorm := function( M, i, j ) local m, n, k, a, r; m := Length( M ); n := Length( M[1] ); for k in [i+1..m] do a := AbsInt( M[k][j] ); if a <> 0 and (a < AbsInt( M[i][j] ) or (a = AbsInt( M[i][j] ) and Number( M[k], x->x<>0 ) < Number( M[i], x->x<>0 ) ) ) then r := M[i]; M[i] := M[k]; M[k] := r; fi; od; return; end; FindNiceRowInfinityNormRowOps := function( M, Q, i, j ) local m, n, k, a, r; m := Length( M ); n := Length( M[1] ); for k in [i+1..m] do a := AbsInt( M[k][j] ); if a <> 0 and (a < AbsInt( M[i][j] ) or (a = AbsInt( M[i][j] ) and Number( M[k], x->x<>0 ) < Number( M[i], x->x<>0 ) ) ) then r := M[i]; M[i] := M[k]; M[k] := r; r := Q[i]; Q[i] := Q[k]; Q[k] := r; fi; od; return; end; ############################################################################# ## #F HNFIntMat . . . . . . . . . . . . Hermite Normalform of an integer matrix ## HNFIntMat := function( M ) local MM, m, n, i, j, k, r, Cleared, a; if M = [] then return []; fi; MM := M; M := List( M, ShallowCopy ); m := Length( M ); n := Length( M[1] ); i := 1; j := 1; while i <= m and j <= n do # find first k with M[k][j] non-zero k := i; while k <= m and M[k][j] = 0 do k := k+1; od; if k <= m then # swap rows r := M[i]; M[i] := M[k]; M[k] := r; # find nicest row with M[k][j] non-zero FindNiceRowInfinityNorm( M, i, j ); if M[i][j] < 0 then M[i] := -1 * M[i]; fi; # reduce all other entries in this columns with the pivot entry Cleared := true; for k in [i+1..m] do a := QuoInt(M[k][j],M[i][j]); if a <> 0 then AddRowVector( M[k], M[i], -a, i, n ); fi; if M[k][j] <> 0 then Cleared := false; fi; od; # if all entries below the pivot are zero, reduce above the # pivot and then move on along the diagonal if Cleared then for k in [1..i-1] do a := QuoInt(M[k][j],M[i][j]); if M[k][j] < 0 and M[k][j] mod M[i][j] <> 0 then a := a-1; fi; if a <> 0 then AddRowVector( M[k], M[i], -a, 1, n ); fi; od; i := i+1; j := j+1; fi; else # increase column counter if column has only zeroes j := j+1; fi; od; return M{[1..i-1]}; end; ############################################################################# ## #F HNFIntMat . . . . . . . . . . . . Hermite Normal Form plus row operations ## HNFIntMatRowOps := function( M ) local MM, m, n, Q, i, j, k, r, Cleared, a; if M = [] then return []; fi; MM := M; M := List( M, ShallowCopy ); m := Length( M ); n := Length( M[1] ); Q := IdentityMat( Length(M) ); i := 1; j := 1; while i <= m and j <= n do # find first k with M[k][j] non-zero k := i; while k <= m and M[k][j] = 0 do k := k+1; od; if k <= m then # swap rows r := M[i]; M[i] := M[k]; M[k] := r; r := Q[i]; Q[i] := Q[k]; Q[k] := r; # find nicest row with M[k][j] non-zero FindNiceRowInfinityNormRowOps( M, Q, i, j ); if M[i][j] < 0 then M[i] := -1 * M[i]; Q[i] := -1 * Q[i]; fi; # reduce all other entries in this columns with the pivot entry Cleared := true; for k in [i+1..m] do a := QuoInt(M[k][j],M[i][j]); if a <> 0 then AddRowVector( M[k], M[i], -a, i, n ); AddRowVector( Q[k], Q[i], -a, 1, m ); fi; if M[k][j] <> 0 then Cleared := false; fi; od; # if all entries below the pivot are zero, reduce above the # pivot and then move on along the diagonal if Cleared then for k in [1..i-1] do a := QuoInt(M[k][j],M[i][j]); if M[k][j] < 0 and M[k][j] mod M[i][j] <> 0 then a := a-1; fi; if a <> 0 then AddRowVector( M[k], M[i], -a, 1, n ); AddRowVector( Q[k], Q[i], -a, 1, m ); fi; od; i := i+1; j := j+1; fi; else # increase column counter if column has only zeroes j := j+1; fi; od; return [ M, Q ]; end; ############################################################################# ## #F DiagonalFormIntMat . . . . diagonal form of an integer matrix plus column #F operations ## DiagonalFormIntMat := function( M ) local Q, pair; M := HNFIntMat( M ); Q := IdentityMat( Length(M[1]) ); while not IsDiagonalMat( M ) do M := TransposedMat( M ); pair := HNFIntMatRowOps( M ); Q := Q * TransposedMat( pair[2] ); M := TransposedMat( pair[1] ); if not IsDiagonalMat( M ) then M := HNFIntMat( M ); fi; od; return [ M, Q ]; end; ## ## This function takes a matrix M in HNF and eliminates for each row whose ## leading entry is 1 the remaining entries of the row. This corresponds ## to a sequence of column operations. Note that all entries above and ## below the 1 are 0 since the matrix is in HNF. ## ## The function returns the transformed matrix M' together with the ## transforming matrix Q such that ## M * Q = M' ## ClearOutWithOnes := function( M ) local Q, i, k, j, l; M := List( M, ShallowCopy ); Q := IdentityMat( Length(M[1]) ); for i in [1..Length(M)] do k := First( [1..Length(M[i])], e -> M[i][e] <> 0 ); if M[i][k] = 1 then for j in [k+1..Length(M[i])] do if M[i][j] <> 0 then Q[j] := Q[j] - M[i][j] * Q[k]; M[i][j] := 0; fi; od; fi; od; return [M, TransposedMat(Q)]; end; ## ## After we have cleared out those rows of the HNF whose leading entry is 1, ## we need to compute a diagonal form of the rest of the matrix. This ## routines cuts out the relevant part, computes a diagonal form of it, puts ## that back into the matrix and returns the performed columns operations. ## CutOutNonOnes := function( M ) local rows, cols, nf, Q, i; # Find all rows whose leading entry is 1 rows := Filtered( [1..Length(M)], i->First( M[i], e->e <> 0 ) = 1 ); if rows = [1..Length(M)] then return IdentityMat( Length(M[1]) ); fi; # Find those colums where the leading entry is cols := List( rows, i->Position( M[i], 1 ) ); # The complement are those rows whose leading entry is not one and those # colums that do not have a 1 in a leading position. rows := Difference( [1..Length(M)], rows ); cols := Difference( [1..Length(M[1])], cols ); # skip leading zeroes i := 1; while M[rows[1]][cols[i]] = 0 do i := i+1; od; cols := cols{[i..Length(cols)]}; nf := DiagonalFormIntMat( M{rows}{cols} ); Q := IdentityMat( Length(M[1]) ); for i in cols do Q[i][i] := 0; od; Q{cols}{cols} := nf[2]; M{rows}{cols} := nf[1]; return Q; end; ## ## The HNF of a matrix that comes out of the consistency test for a ## central extension tends to have a lot of rows whose leading entry is 1. ## In particular, if we do not have an efficient strategy for computing ## tails, we have many generators which can be expressed by others. ## ## This is a simple consequence of the fact that we add about n^2/2 new ## generators to the polycyclic presentation if the the group has n ## generators. But it is clear that the rank of R/[R,F] is bounded from ## above by n. Therefore, about n^2/2 generators will be expressed by ## others. ## ## We return a diagonal form of M and the matrix of column operations in ## the same format as NormalFormIntMat() ## ## An example where this performs much better than NormalFormIntMat is ## given by ## G:=HeisenbergPcpGroup(2); ## NonAbelianTensorSquarePlusEpimorphism(G); ## Timing the call to NormalFormConsistencyRelations and comparing it to ## an equivalent NormalFormIntMat call yielded 50 msec vs. 1000 msec, ## i.e. a speedup by factor 20. ## NormalFormConsistencyRelations := function( M ) local nf, Q, rows, cols, small, nfim, QQ; M := HNFIntMat( M ); nf := ClearOutWithOnes( M ); M := nf[1]; Q := nf[2]; Q := Q * CutOutNonOnes( M ); return rec( normal := M, coltrans := Q ); end; polycyclic-2.16/gap/matrix/modules.gi0000644000076600000240000002464713706672341016715 0ustar mhornstaff############################################################################# ## #W modules.gi Polycyc Bettina Eick ## ############################################################################# ## #F RadicalSeriesOfFiniteModule( mats, d, f ) ## RadicalSeriesOfFiniteModule := function( mats, d, f ) local base, sers, modu, radb; if d = 0 then return []; fi; base := IdentityMat( d, f ); sers := [base]; modu := GModuleByMats( mats, d, f ); repeat radb := SMTX.BasisRadical( modu ); if Length( radb ) > 0 then base := radb * base; else base := []; fi; Add( sers, base ); if Length( base ) > 0 then modu := SMTX.InducedActionSubmodule( modu, radb ); fi; until Length( base ) = 0; return sers; end; ############################################################################# ## #F RadicalOfCongruenceModule( mats, d ) . . . . . . .for congruence subgroups ## RadicalOfCongruenceModule := function( mats, d ) local coms, i, j, new, base, full, nath, indm, l, algb, newv, tmpb, subb, f, g, h, mat; # get commutators coms := []; for i in [1..Length( mats )] do for j in [i+1..Length( mats )] do new := mats[i] * mats[j] - mats[j] * mats[i]; Append(coms, new ); od; od; base := SpinnUpEchelonBase( [], coms, mats, OnRight ); full := IdentityMat( d ); nath := NaturalHomomorphismBySemiEchelonBases( full, base ); indm := List( mats, x -> InducedActionFactorByNHSEB( x, nath ) ); #Print("found derived submodule of dimension ",Length(base),"\n"); # start spinning up basis and look for nilpotent elements i := 1; algb := []; while i <= Length( indm ) do # add next element to algebra basis l := Length( algb ); newv := Flat( indm[i] ); tmpb := SpinnUpEchelonBase( algb, [newv], indm{[1..i]}, OnMatVector ); # check whether we have added a non-semi-simple element subb := []; for j in [l+1..Length(tmpb)] do mat := MatByVector( tmpb[j], Length(indm[i]) ); f := MinimalPolynomial( Rationals, mat ); g := Collected( Factors( f ) ); if ForAny( g, x -> x[2] > 1 ) then h := Product( List( g, x -> Value( x[1], mat ) ) ); Append( subb, List( h, x -> ShallowCopy(x) ) ); fi; od; #Print("found nilpotent submodule of dimension ", Length(subb),"\n"); # spinn up new subspace of radical subb := SpinnUpEchelonBase( [], subb, indm, OnRight ); if Length( subb ) > 0 then base := PreimageByNHSEB( subb, nath ); nath := NaturalHomomorphismBySemiEchelonBases( full, base ); indm := List( mats, x -> InducedActionFactorByNHSEB( x, nath ) ); algb := []; i := 1; else i := i + 1; fi; od; return rec( radical := base, nathom := nath, algebra := algb ); end; ############################################################################# ## #F TraceMatProd( m1, m2, d ) ## TraceMatProd := function( m1, m2, d ) local t, i, j; t := 0; for i in [1..d] do for j in [1..d] do t := t + m1[i][j] * m2[j][i]; od; od; return t; end; ############################################################################# ## #F AlgebraBase( mats ) ## InstallGlobalFunction( AlgebraBase, function( mats ) local base, flat; if Length( mats ) = 0 then return []; fi; flat := List( mats, Flat ); base := SpinnUpEchelonBase( [], flat, mats, OnMatVector); return List( base, x -> MatByVector( x, Length(mats[1]) ) ); end ); ############################################################################# ## #F RadicalOfRationalModule( mats, d ) . . . . . . . . . . . .general approach ## RadicalOfRationalModule := function( mats, d ) local base, trac, null, j; # get base base := AlgebraBase( mats ); if Length(base) = 0 then return rec( radical := [] ); fi; # set up system of linear equations ( Tr( ai * aj ) ) trac := List( base, b -> List( base, c -> TraceMatProd( b, c, d ) ) ); # compute nullspace null := NullspaceMat( trac ); if Length(null) = 0 then return rec( radical := [] ); fi; # translate null := List( null, x -> LinearCombination( x, base ) ); null := Concatenation( null ); TriangulizeMat( null ); j := Position( null, 0 * null[1] ); return rec( radical := null{[1..j-1]} ); end; ############################################################################# ## #F RadicalSeriesOfRationalModule( mats, d ) . . . . . .compute radical series ## RadicalSeriesOfRationalModule := function( mats, d ) local acts, full, base, sers, radb, nath; if d = 0 then return []; fi; full := IdentityMat( d ); sers := [full]; base := full; acts := mats; repeat radb := RadicalOfRationalModule( acts, d ).radical; if Length(radb) > 0 then base := radb * base; nath := NaturalHomomorphismBySemiEchelonBases( full, base ); acts := List( mats, x -> InducedActionSubspaceByNHSEB( x, nath ) ); else base := []; fi; d := Length( base ); Add( sers, base ); until d = 0; return sers; end; ############################################################################# ## #F PrimitiveAlgebraElement( mats, base ) ## InstallGlobalFunction( PrimitiveAlgebraElement, function( mats, base ) local d, mat, f, c, b, l; # set up d := Length( base ); # first try one of mats for mat in mats do f := MinimalPolynomial( Rationals, mat ); if Degree( f ) = d then return rec( elem := mat, poly := f ); fi; od; # otherwise try random elements l := Sqrt( Length( base[1] ) ); repeat c := List( [1..d], x -> Random( Integers ) ); b := MatByVector( c * base, l ); f := MinimalPolynomial( Rationals, b ); if Degree( f ) = d then return rec( elem := b, poly := f ); fi; until false; end ); ############################################################################# ## #F SplitSemisimple( base ) ## SplitSemisimple := function( base ) local d, b, f, s, i; d := Length( base ); b := PrimitiveAlgebraElement( [], base ); f := Factors( b.poly ); # the trivial case if Length( f ) = 1 then return [rec( basis := IdentityMat(Length(base[1])), poly := f )]; fi; # the non-trivial case s := List( f, x -> NullspaceRatMat( Value( x, b.elem ) ) ); s := List( [1..Length(f)], x -> rec( basis := s[x], poly := f[x] ) ); return s; end; ############################################################################# ## #F HomogeneousSeriesOfCongruenceModule( mats, d ). . for congruence subgroups ## HomogeneousSeriesOfCongruenceModule := function( mats, d ) local radb, splt, nath, l, sers, i, sub, full, acts, rads; # catch the trivial case and set up if d = 0 then return []; fi; full := IdentityMat( d ); if Length( mats ) = 0 then return [full, []]; fi; sers := [full]; # get the radical radb := RadicalOfCongruenceModule( mats, d ); splt := SplitSemisimple( radb.algebra ); nath := radb.nathom; # refine radical factor and initialize series l := Length( splt ); for i in [2..l] do sub := Concatenation( List( [i..l], x -> splt[x].basis ) ); TriangulizeMat( sub ); Add( sers, PreimageByNHSEB( sub, nath ) ); od; Add( sers, radb.radical ); # induce action to radical nath := NaturalHomomorphismBySemiEchelonBases( full, radb.radical ); acts := List( mats, x -> InducedActionSubspaceByNHSEB( x, nath ) ); # use recursive call to refine radical rads := HomogeneousSeriesOfCongruenceModule( acts, Length(radb.radical) ); for i in [2..Length(rads)] do if Length(rads[i]) > 0 then rads[i] := rads[i] * radb.radical; fi; od; Append( sers, rads{[2..Length(rads)]} ); return sers; end; ############################################################################# ## #F HomogeneousSeriesOfRationalModule( mats, cong, d ). . . . . use full space ## HomogeneousSeriesOfRationalModule := function( mats, cong, d ) local full, sers, radb, nath, fact, base, splt, l, i, sub, acts, subs, rads; # catch the trivial case and set up if d = 0 then return []; fi; full := IdentityMat( d ); sers := [full]; # other trivial case if Length( cong ) = 0 then return [full, []]; fi; # get the radical and split its factor radb := RadicalOfRationalModule( mats, d ); nath := NaturalHomomorphismBySemiEchelonBases( full, radb.radical ); fact := List( cong, x -> InducedActionFactorByNHSEB( x, nath ) ); base := AlgebraBase( fact ); splt := SplitSemisimple( List( base, Flat ) ); # refine radical factor and initialize series l := Length( splt ); for i in [2..l] do sub := Concatenation( List( [i..l], x -> splt[x].basis ) ); TriangulizeMat( sub ); Add( sers, PreimageByNHSEB( sub, nath ) ); od; Add( sers, radb.radical ); # use recursive call to refine radical l := Length( radb.radical ); acts := List( mats, x -> InducedActionSubspaceByNHSEB( x, nath ) ); subs := List( cong, x -> InducedActionSubspaceByNHSEB( x, nath ) ); rads := HomogeneousSeriesOfRationalModule( acts, subs, l ); for i in [2..Length(rads)] do if Length(rads[i]) > 0 then rads[i] := rads[i] * radb.radical; fi; od; Append( sers, rads{[2..Length(rads)]} ); return sers; end; ############################################################################# ## #F RefineSplitting( mats, subs ) . . . . . . . . . . for congruence subgroups ## RefineSplitting := function( mats, subs ) local i, full, news, dims, j, d, e, tmp; # refine each of the subspaces subs in turn by spinning for i in [1..Length(subs)] do full := subs[i]; news := []; dims := 0; j := 1; d := Length( subs[i] ); while dims < d do e := full[j]; if ForAll( news, x -> MemberBySemiEchelonBase(e, x) = false ) then tmp := SpinnUpEchelonBase( [], [e], mats, OnRight ); Add( news, tmp ); dims := dims + Length( tmp ); fi; j := j + 1; od; subs[i] := news; od; return Concatenation( subs ); end; polycyclic-2.16/gap/matrix/rowbases.gi0000644000076600000240000001611113706672341017055 0ustar mhornstaff############################################################################# ## #W rowbases.gi Bettina Eick ## ## Methods to compute with rational vector spaces. ## ############################################################################# ## #F VectorspaceBasis( gens ) ## VectorspaceBasis := function( gens ) local j; TriangulizeMat( gens ); if Length(gens) = 0 then return gens; fi; j := Position( gens, 0*gens[1] ); if not IsBool( j ) then gens := gens{[1..j-1]}; fi; return gens; end; ############################################################################# ## #F SemiEchelonFactorBase( V, U ) ## SemiEchelonFactorBase := function( V, U ) local L1, L2; L1 := List( V, PositionNonZero ); L2 := List( U, PositionNonZero ); return V{Filtered( [1..Length(V)], i -> not L1[i] in L2 )}; end; ############################################################################# ## #F MemberBySemiEchelonBase( v, U ) ## MemberBySemiEchelonBase := function( v, U ) local d, c, z, l, j; v := ShallowCopy(v); d := List( U, PositionNonZero ); c := List( d, x -> 0 ); z := 0 * v; while v <> z do l := PositionNonZero(v); j := Position( d, l ); if IsBool( j ) then return false; fi; c[j] := v[l]; if U[j][l] <> 1 then c[j] := c[j]/U[j][l]; fi; AddRowVector( v, U[j], -c[j] ); od; return c; end; ############################################################################# ## #F NaturalHomomorphismBySemiEchelonBases ( V, U ) ## InstallGlobalFunction( NaturalHomomorphismBySemiEchelonBases, function( V, U ) local F, A; F := SemiEchelonFactorBase( V, U ); A := Concatenation( F, U ); return rec( source := A, kernel := U, factor := F ); end ); ############################################################################# ## #F CoefficientsByNHSEB( v, hom ) ## CoefficientsByNHSEB := function( v, hom ) local df, dk, cf, ck, z, l, j; v := ShallowCopy(v); df := List( hom.factor, PositionNonZero ); dk := List( hom.kernel, PositionNonZero ); cf := List( df, x -> 0 ); ck := List( dk, x -> 0 ); z := 0 * v; while v <> z do l := PositionNonZero(v); j := Position( df, l ); if not IsBool( j ) then cf[j] := v[l]; if hom.factor[j][l] <> 1 then cf[j] := cf[j]/hom.factor[j][l]; fi; AddRowVector( v, hom.factor[j], -cf[j] ); else j := Position( dk, l ); ck[j] := v[l]; if hom.kernel[j][l] <> 1 then ck[j] := ck[j]/hom.kernel[j][l]; fi; AddRowVector( v, hom.kernel[j], -ck[j] ); fi; od; return rec( coeff1 := cf, coeff2 := ck ); end; ############################################################################# ## #F ProjectionByNHSEB( vec, hom ) ## ProjectionByNHSEB := function( vec, hom ) return CoefficientsByNHSEB( vec, hom ).coeff2; end; ############################################################################# ## #F ImageByNHSEB( vec, hom ) ## ImageByNHSEB := function( vec, hom ) return CoefficientsByNHSEB( vec, hom ).coeff1; end; ############################################################################# ## #F PreimagesRepresentativeByNHSEB( vec, hom ) ## PreimagesRepresentativeByNHSEB := function( vec, hom ) return vec * hom.factor; end; ############################################################################# ## #F PreimageByNHSEB( base, hom ) ## InstallGlobalFunction( PreimageByNHSEB, function( base, hom ) local new; new := List( base, x -> x * hom.factor ); Append( new, hom.kernel ); return new; end ); ############################################################################# ## #F InducedActionByNHSEB( mat, hom ) ## InducedActionByNHSEB := function( mat, hom ) local fac, sub; fac := List( hom.factor, x -> CoefficientsByNHSEB( x*mat, hom ).coeff1 ); sub := List( hom.kernel, x -> CoefficientsByNHSEB( x*mat, hom ).coeff2 ); return rec( factor := fac, subsp := sub ); end; ############################################################################# ## #F InducedActionFactorByNHSEB( mat, hom ) ## InstallGlobalFunction( InducedActionFactorByNHSEB, function( mat, hom ) return List( hom.factor, x -> CoefficientsByNHSEB( x*mat, hom ).coeff1 ); end ); ############################################################################# ## #F InducedActionSubspaceByNHSEB( mat, hom ) ## InstallGlobalFunction( InducedActionSubspaceByNHSEB, function( mat, hom ) return List( hom.kernel, x -> CoefficientsByNHSEB( x*mat, hom ).coeff2 ); end ); ############################################################################# ## #F AddVectorEchelonBase( base, vec ) ## AddVectorEchelonBase := function( base, vec ) local d, l, j, i; # reduce vec d := List( base, PositionNonZero ); repeat l := PositionNonZero( vec ); j := Position( d, l ); if not IsBool( j ) then AddRowVector( vec, base[j], -vec[l] ); fi; until IsBool( j ); # if vec is completely reduced if l = Length(vec)+1 then return; fi; # norm vector MultVector( vec, vec[l]^-1 ); # finally add vector to base base[Length(base)+1] := vec; end; ############################################################################# ## #F SpinnUpEchelonBase( base, vecs, gens, oper ) ## InstallGlobalFunction( SpinnUpEchelonBase, function( base, vecs, gens, oper ) local todo, i, v, l; todo := ShallowCopy( vecs ); i := 1; l := Length( base ); while i <= Length( todo ) do v := todo[i]; AddVectorEchelonBase( base, v ); if Length( base ) > l then Append( todo, List( gens, x -> oper( v, x ) ) ); l := l + 1; fi; i := i + 1; od; TriangulizeMat( base ); return base; end ); ############################################################################# ## #F OnMatVector( vec, mat ) . . . . . . . .operation by matrix on flat matrix ## InstallGlobalFunction( OnMatVector, function( vec, mat ) local new, d, i; d := Length( mat ); new := List( mat, x -> 0 ); for i in [1..d] do new[i] := vec{[(i-1)*d+1..i*d]} * mat; od; return Flat( new ); end ); ############################################################################# ## #F MatByVector( vec, d ) . . . . . . . . . . . . . . reconstruct flat matrix ## InstallGlobalFunction( MatByVector, function( vec, d ) return List( [1..d], x -> vec{[(x-1)*d+1..x*d]} ); end ); ############################################################################# ## #F IsSemiEchelonBase( base ) ## IsSemiEchelonBase := function( base ) return IsSSortedList( List( base, PositionNonZero ) ); end; ############################################################################# ## #F IsEchelonBase( base ) ## IsEchelonBase := function( base ) local d, i; d := List( base, PositionNonZero ); if not IsSSortedList( List( base, PositionNonZero ) ) then return false; fi; for i in [1..Length(d)] do if base[i][d[i]] <> 1 then return false; fi; od; return true; end; polycyclic-2.16/gap/cover/0000755000076600000240000000000013706672341014521 5ustar mhornstaffpolycyclic-2.16/gap/cover/trees/0000755000076600000240000000000013706672341015643 5ustar mhornstaffpolycyclic-2.16/gap/cover/trees/xtree.gi0000644000076600000240000001720713706672341017322 0ustar mhornstaffWIDTH := 25; DEPTH := 40; SSIZE := [1000, 700]; PrintScale := function( Sheet, p, s, r, l ) local y, i, t; # the top line Text( Sheet, FONTS.normal, 20, 30, "|G|" ); Text( Sheet, FONTS.normal, 100, 30, "tree" ); # the first columns y := 30; for i in [s..l] do y := y + 40; t := Concatenation( String(p), "^", String(i+r)); Text( Sheet, FONTS.normal, 20, y, t ); #Text( Sheet, FONTS.normal, 60, y, String(i) ); od; end; StringIt := function(t) if IsString(t) then return t; fi; if IsInt(t) then return String(t); fi; if IsList(t) then return Concatenation(List(t, x->Concatenation("-",String(x)))); fi; end; PrintVertex := function( Sheet, cl, nr, tx, pa, ty ) local x, y; # set up x and y values x := 100 + WIDTH * (nr-1); y := 70 + DEPTH * cl; # print vertex if ty=1 then Disc( Sheet, x, y, 3 ); elif ty = 2 then Circle( Sheet, x, y, 3 ); elif ty = 3 then Box( Sheet, x, y, 3, 3 ); elif ty = 4 then Rectangle( Sheet, x, y, 3, 3 ); fi; # print line Line( Sheet, pa[1], pa[2], x-pa[1], y-pa[2] ); # add text if not IsBool(tx) then Text( Sheet, FONTS.small, x+2, y, StringIt(tx) ); fi; # return place return [x, y]; end; CoclassPGroup := function(G) local n,c; n := Length(Factors(Size(G))); c := Length(LowerCentralSeriesOfGroup(G))-1; return n-c; end; DrawCoverTree := function( G, r ) local Title, Sheet, grp, res, nex, des, sub, p, i, j, c, m, d, g, v, H; # set up graphic sheet Title := Concatenation( "Cover tree for ", StringIt(AbelianInvariants(G))); Sheet := GraphicSheet( Title, SSIZE[1], SSIZE[2] ); # set up for iteration grp := [[G,[100, 70]]]; res := [[G]]; AddSExtension(G); p := PrimePGroup(G); i := 0; # init tree if CoclassPGroup(G) < r then Circle( Sheet, 100, 70, 3 ); else Disc( Sheet, 100, 70, 3 ); fi; # compute iterated descendants repeat nex := []; j := 1; i := i+1; for H in grp do # get invars c := CoclassPGroup(H[1]); m := H[1]!.mord; d := c+Length(Factors(m))-1; # check if m > 1 and d <= r then # compute covers des := SchurCovers(H[1]); for g in des do AddSExtension(g); od; # filter if d < r then des := Filtered(des, x -> x!.mord > 1); des := Filtered(des, x -> d+Length(Factors(x!.mord))-1<=r); for g in des do repeat v := PrintVertex( Sheet, i, j, false, H[2],2); j := j + 1; until not IsBool(v); Add( nex, [g, v] ); od; fi; if d = r then # non-terminal sub := Filtered(des, x -> x!.mord = p); for g in sub do repeat v := PrintVertex( Sheet, i, j, false, H[2],1); j := j + 1; until not IsBool(v); Add( nex, [g, v] ); od; # terminal with non-triv. Schu-Mu sub := Filtered(des, x -> x!.mord > p); if Length(sub)>0 then repeat v := PrintVertex(Sheet,i,j,Length(sub),H[2],3); j := j + 1; until not IsBool(v); fi; # terminal with triv. Schu-Mu sub := Filtered(des, x -> x!.mord = 1); if Length(sub)>0 then repeat v := PrintVertex(Sheet,i,j,Length(sub),H[2],1); j := j + 1; until not IsBool(v); fi; fi; fi; od; # reset for next level grp := nex; until Length(grp)=0; end; DrawSubtree := function( Sheet, root, v, tree ) local x, y, w, j; # get x and y x := v; y := root[2]+40; # draw vertex if tree[1] = 1 then Disc( Sheet, x, y, 3 ); elif tree[1] = 2 then Circle( Sheet, x, y, 3 ); elif tree[1] = 3 then Diamond( Sheet, x, y, 3, 3 ); fi; # draw line Line( Sheet, root[1], root[2], x-root[1], y-root[2] ); # add text if tree[3] > 1 then Text( Sheet, FONTS.small, x+2, y, StringIt(tree[3]) ); fi; # init recursion w := v; if Length(tree[2]) > 0 then for j in [1..Length(tree[2])] do if IsBound(tree[2][j]) then w := DrawSubtree( Sheet, [x,y], w, tree[2][j]) + 25; fi; od; if j = Length(tree[2]) then w := w-25; fi; fi; # return maximal x-value return w; end; DrawRootedTree := function( grps ) local Sheet, v, d, j; Sheet := GraphicSheet( "Tree", 1000, 700 ); # draw root if grps[1] = 1 then Disc( Sheet, 70, 100, 3 ); elif grps[1] = 2 then Circle( Sheet, 70, 100, 3 ); elif grps[1] = 3 then Diamond( Sheet, 70, 100, 3, 3 ); fi; v := 70; for j in [1..Length(grps[2])] do if IsBound(grps[2][j]) then v := DrawSubtree( Sheet, [70,100], v, grps[2][j] )+25; fi; od; end; CollectedTree := function(tree) local i, j, des; Print("collect tree ",tree,"\n"); # catch the trivial case if Length(tree)=0 then return []; fi; des := List(tree[2], x -> x{[1,2]}); # loop over descendants and collect for i in [1..Length(des)] do j := Position(des, des[i]); if j < i then tree[2][j][3] := tree[2][j][3] + 1; tree[2][i] := false; fi; od; tree[2] := Filtered(tree[2], x -> not IsBool(x)); Print(" and got ",tree,"\n\n"); # recurse for i in [1..Length(tree[2])] do tree[2][i] := CollectedTree(tree[2][i]); od; return tree; end; ConstCoverTree := function( G, r ) local p, o, grps, t, H, c, m, d, new, des, i, j; # set up p := PrimePGroup(G); o := CoverCode(G); o[4] := CoclassPGroup(G); t := 0; # init list grps := [o]; # compute iterated descendants while t < Length(grps) do t := t+1; # get invars c := grps[t][4]; m := grps[t][2]; d := c+Length(Factors(m))-1; # compute covers if m > 1 and d<=r then H := CodeCover(grps[t]); new := SchurCovers(H); for i in [1..Length(new)] do new[i] := CoverCode(new[i]); new[i][4] := d; od; Append(grps, new); des := [Length(grps)-Length(new)+1 .. Length(grps)]; else des := []; fi; # replace grps[t] if c < r then grps[t] := [1, des]; fi; if c = r then if m = 1 then grps[t] := [3, []]; else grps[t] := [2, des]; fi; fi; od; # reverse tree structure for t in Reversed([1..Length(grps)]) do if Length(grps[t][2])>0 then des := List(grps[t][2], x -> grps[x]); for i in grps[t][2] do Unbind(grps[i]); od; grps[t][2] := des; fi; grps[t][3] := 1; od; # collect tree structure grps := CollectedTree(grps[1]); # draw tree DrawRootedTree( grps ); end; polycyclic-2.16/gap/cover/const/0000755000076600000240000000000013706672341015647 5ustar mhornstaffpolycyclic-2.16/gap/cover/const/ccc.gi0000644000076600000240000000376513706672341016733 0ustar mhornstaff ## ## List iterated Schur covers of coclass r and rank d. ## SchurCoversByCoclass := function(ccpsfile, p,r,d) local file, res, i, j, G, l, new, H, c, q; if not d in [2..r+1] then return fail; fi; # get file file := Concatenation(ccpsfile, "_",String(p), "_",String(r), "_",String(d), ".gi"); # init file PrintTo(file,"CCover[",p,"][",r,"][",d,"] := [\n"); # set up res := []; # 1. step : smaller coclass Print("extend smaller coclass \n"); for i in [1..r-1] do for j in [1..NrCcCovers(p,i,d)] do # get group G := CcCover(p,i,d,j); l := Length(Factors(G!.mord)); # check relevance if G!.mord > 1 and r = i+l-1 then Print("start group ",j," of ",NrCcCovers(p,i,d),"\n"); new := SchurCovers(G); for H in new do c := CoverCode(H); AppendTo(file, c,", \n"); if c[2] = p then Add(res, c); fi; od; fi; od; od; # 2. step : abelian groups of rank d and coclass r Print("abelian groups \n"); for q in Partitions(r+1) do if Length(q) = d then G := AbelianPcpGroup(Length(q), List(q, x -> p^x)); c := CoverCode(G); AppendTo(file, c,", \n"); if c[2] = p then Add(res, c); fi; fi; od; # 3. step : iterated extensions j := 1; Print("extend same coclass \n"); while j <= Length(res) do Print("start group ",j," of ",Length(res),"\n"); G := CodeCover(res[j]); new := SchurCovers(G); for H in new do c := CoverCode(H); AppendTo(file, c,", \n"); if c[2] = p then Add(res, c); fi; od; j := j+1; od; AppendTo(file,"];\n"); AddSet(availc[p],[r,d]); ReadCcFile(p,r,d); end; polycyclic-2.16/gap/cover/const/orb.gi0000644000076600000240000000536013706672341016756 0ustar mhornstaffAgOrbitCover := function( A, pt, act ) local pcgs, rels, orbit, dict, i, y, j, p, l, s, k, t, h; pcgs := A.agAutos; rels := A.agOrder; # initialise orbit orbit := [pt]; dict := NewDictionary( pt, true ); AddDictionary( dict, pt, 1 ); # Start constructing orbit. i := Length( pcgs ); while i >= 1 do y := act( pt, pcgs[i] ); j := LookupDictionary( dict, y ); if IsBool( j ) then p := rels[i]; l := Length( orbit ); orbit[p*l] := true; s := 0; for k in [1 .. p-1] do t := s + l; for h in [1..l] do orbit[h+t] := act( orbit[h+s], pcgs[i] ); AddDictionary( dict, orbit[h+t], h+t ); od; s := t; od; fi; i := i-1; od; return orbit; end; HybridOrbitCover := function( A, pt, act ) local block, l, orbit, dict, new, k, i, j, y; # get block block := AgOrbitCover( A, pt, act ); l := Length( block ); # set up orbit orbit := [block]; dict := NewDictionary( block[1], true ); for j in [1..l] do AddDictionary( dict, block[j], [1, j] ); od; # loop k := 1; while k <= Length( orbit ) do for i in [ 1..Length( A.glAutos ) ] do y := act( orbit[k][1], A.glAutos[i] ); j := LookupDictionary( dict, y ); if IsBool( j ) then new := List( orbit[k], x -> act(x, A.glAutos[i]) ); Add( orbit, new ); for j in [1..l] do AddDictionary( dict, new[j], [Length(orbit), j] ); od; fi; od; k := k + 1; #Print(" OS: sub length ",Length(block)," * ",Length(orbit),"\n"); od; return Concatenation( orbit ); end; MyOrbits := function( A, len, act ) local todo, reps, i, c, a, o, j; Print(" OS: compute orbits \n"); # set up boolean list of length len todo := []; todo[len] := true; for i in [1..len] do todo[i] := true; od; # c is the number of entries true, # a+1 is the position of the first true c := len; a := 1; # set up orbit reps reps := []; # determine orbits while IsInt(a) do # store Add(reps, a-1); # get orbit o := HybridOrbitCover( A, a-1, act ); # cancel in todo-list for j in o do if j < len then if todo[j+1] = false then Error("orb problem"); fi; todo[j+1] := false; c := c-1; fi; od; a := Position(todo, true, a); Print(" OS: orbit length ",Length(o), " -- ",c," to go \n"); od; return reps; end; polycyclic-2.16/gap/cover/const/cov.gi0000644000076600000240000000443313706672341016763 0ustar mhornstaff## ## Covers ## # FIXME: This function is documented and should be turned into a GlobalFunction SchurCovers := function(G) local p, GG, K, R, M, D, Z, k, H, C, T, P, e, O, bij, f, t, m, A, c, l, n, i; if not IsPGroup(G) then return fail; fi; # set up p := Factors(Size(G))[1]; # move to Pcp groups if necessary if IsPcGroup(G) then GG := PcGroupToPcpGroup(G); else GG := G; fi; # cover and subgroups AddSExtension(GG); K := GG!.scov; R := GG!.modu; M := GG!.mult; # info Print(" Schur Mult has type ",AbelianInvariants(M),"\n"); # catch a trival case if GG!.mord = 1 then return G; fi; # determine Z = Z(K) cap RK' D := ProductPcpGroups(K, R, DerivedSubgroup(K)); Z := Intersection(Centre(K), D); # determine phi(G) in K P := Subgroup(K, Concatenation(Igs(D), List(Pcp(K,D), x -> x^p))); # get small cover of K/Z H := Subgroup(K, GeneratorsOfPcp(Pcp(K,P))); # reduce into H and obtain H > C > T > M > 1 C := Intersection( Z, H ); T := TorsionSubgroup(C); # add in powers e := ExponentAbelianPcpGroup(T); O := OmegaAbelianPcpGroup(C, e); T := ProductPcpGroups(H, T, O); M := ProductPcpGroups(H, M, O); # change presentation k := Pcp(H,C); c := Pcp(C,T,"snf"); t := Pcp(T,M,"snf"); m := Pcp(M,O,"snf"); H := PcpFactorByPcps(H, [k,c,t,m]); # move n := Length(Cgs(H)); C := SubgroupByIgs(H, Cgs(H){[Length(k)+1..n]}); T := SubgroupByIgs(H, Cgs(H){[Length(k)+Length(c)+1..n]}); M := SubgroupByIgs(H, Cgs(H){[Length(k)+Length(c)+Length(t)+1..n]}); f := Pcp(C,T); t := Pcp(T); m := Pcp(M); # info Print(" Schur Mult new type ",AbelianInvariants(T),"\n"); # the acting automorphisms A := AutomorphismActionCover( H, C ); # induce to desired action A.agAutos := List( A.agAutos, x -> InducedAutCover(x, f,t,e) ); A.glAutos := List( A.glAutos, x -> InducedAutCover(x, f,t,e) ); # determine complement classes under action of A c := FactorsComplementClasses( A, H, f, t, m ); # adjust if necessary if IsPcGroup(G) and not CODEONLY@ then for i in [1..Length(c)] do c[i] := PcpGroupToPcGroup(RefinedPcpGroup(c[i])); od; fi; return c; end; polycyclic-2.16/gap/cover/const/bas.gi0000644000076600000240000000274013706672341016740 0ustar mhornstaff IsIsomBySP := function(G, H) local g,h,r,s; g := GeneratorsOfGroup(FreeGroupOfFpGroup(G)); h := GeneratorsOfGroup(FreeGroupOfFpGroup(H)); r := RelatorsOfFpGroup(G); s := List(RelatorsOfFpGroup(H), x -> MappedWord(x,h,g)); return r=s; end; IdPcpGroup := function(G) return IdGroup(PcpGroupToPcGroup(RefinedPcpGroup(G))); end; ExtensionModule := function(K,H) return SubgroupByIgs(K,Igs(K){[Length(Igs(H))+1..Length(Igs(K))]}); end; ReduceMod := function(vec, rels) return List([1..Length(vec)], i -> vec[i] mod rels[i]); end; ProductPcpGroups := function(G, U, V) return Subgroup(G, Concatenation(Igs(U), Igs(V))); end; ExponentAbelianPcpGroup := function( G ) return Maximum(RelativeOrdersOfPcp(Pcp(G,"snf"))); end; OmegaAbelianPcpGroup := function(G, e) return Subgroup(G, List(Igs(G), x -> x^e)); end; AddSExtension := function(G) if IsBound(G!.scov) then return; fi; G!.scov := SchurExtension(G); G!.modu := ExtensionModule(G!.scov, G); G!.mult := TorsionSubgroup(G!.modu); G!.mord := Size(G!.mult); end; AddMOrder := function(G) if IsBound(G!.mord) then return; fi; AddSExtension(G); end; CoverCode := function(G) if IsList(G) then return G; fi; AddMOrder(G); return [Size(G), G!.mord, CodePcGroup(PcpGroupToPcGroup(RefinedPcpGroup(G)))]; end; CodeCover := function(c) local G; G := PcGroupCode(c[3], c[1]); G := PcGroupToPcpGroup(G); G!.mord := c[2]; return G; end; polycyclic-2.16/gap/cover/const/com.gi0000644000076600000240000000471413706672341016754 0ustar mhornstaffNrToElm := function(rels, nr, n) local q, elm, i; elm := []; elm[n] := 0; for i in Reversed([1..n]) do q := QuotientRemainder(nr, rels[i]); nr := q[1]; elm[i] := q[2]; od; return elm; end; ElmToNr := function(rels, elm, n) local nr, i; nr := elm[1]; for i in [2..n] do nr := nr*rels[i] + elm[i]; od; return nr; end; ComplementCover := function(H, n, f, t, coc) local d, e; d := CutVector(coc, n); e := List([1..n], x -> f[x] * MappedVector( d[x], t)); return Subgroup(H, e); end; ConstructPerm := function(n, r, s) local l, m, g, i; # catch a trivial case if r=s then return (); fi; # set up l := Length(s); m := Length(r)-Length(s); # get list g := []; for i in [1..n] do Append(g, (i-1)*m+[1..m]); Append(g, n*m + (i-1)*l + [1..l]); od; return PermList(g); end; GetExponents := function(pcp, x) return ExponentsByPcp(pcp, MappedVector(x,pcp)); end; FactorsComplementClasses := function(A, H, f, t, m) local n, nn, r, s, rr, oper, elms, os, i, p, q; # set up n := Length(f); r := RelativeOrdersOfPcp(t); s := RelativeOrdersOfPcp(m); # construct perms p := ConstructPerm(n,r,s); q := p^-1; # long rels rr := Permuted(Flat(List([1..n], x -> r)),p); nn := Length(rr); # the action oper := function(nr, aut) local coc, cut, new; coc := Permuted(NrToElm(rr, nr, nn),q); cut := CutVector(coc, n); new := List([1..n],x->(aut[2][x]*cut)*aut[1]+aut[3][x]); new := List(new, x -> GetExponents(t,x)); coc := Concatenation(new); return ElmToNr(rr, Permuted(coc,p), nn); end; # orbits os := MyOrbits(A, Product(s)^n, oper); # translate for i in [1..Length(os)] do os[i] := Permuted(NrToElm(rr, os[i], nn),q); os[i] := ComplementCover(H,n,f,t,os[i]); os[i] := H/os[i]; if CODEONLY@ then AddMOrder(os[i]); os[i] := [Size(os[i]), os[i]!.mord, CodePcGroup(PcpGroupToPcGroup(RefinedPcpGroup(os[i])))]; fi; od; return os; end; AllComplementsCover := function(K, f, m) local n, r, s, elms; # set up n := Length(f); s := RelativeOrdersOfPcp(m); # the points elms := ExponentsByRels(s); elms := List( Tuples(elms,n), Flat ); # translate return List(elms, x -> ComplementCover(K, n, f, m, x)); end; polycyclic-2.16/gap/cover/const/aut.gi0000644000076600000240000001152413706672341016764 0ustar mhornstaffAddPermOper := function(A) local G, r, p, base, V, norm, f, M, iso, P; # set up G := A.group; r := RankPGroup( G ); p := PrimePGroup( G ); # points base := IdentityMat( r, GF(p) ); V := GF(p)^r; norm := NormedRowVectors( V ); # oper f := function( pt, a ) return NormedRowVector( pt * a ); end; M := Group( A.glOper, base ); iso := ActionHomomorphism( M, norm, f ); P := Image( iso ); # reset A.glOper := GeneratorsOfGroup( P ); end; ReduceAuto := function( auto, C, isom, gens, imgs ) local news; news := List(imgs, x -> Image(auto, x)); news := List(news, x -> PreImagesRepresentative(isom, x)); news := GroupHomomorphismByImagesNC( C, C, gens, news ); SetIsBijective( news, true ); return news; end; AutomorphismActionCover := function( G, C ) local pcgs, first, p, n, r, f, i, chars, bases, S, H, kern, A, F, Q, s, t, P, M, N, U, baseN, baseU, OnSubs, gens, imgs, isom, Cimg; # start off pcgs := SpecialPcgs( G ); first := LGFirst( SpecialPcgs(G) ); p := PrimePGroup( G ); n := Length(pcgs); r := RankPGroup( G ); f := GF(p); # init automorphism group - compute Aut(G/G_1) Print(" AG: step 1: ",p,"^", first[2]-1, "\n"); # compute characteristic subgroups chars := TwoStepCentralizersByLcs(G); Add(chars, C); bases := List( chars, x -> FrattiniQuotientBase( pcgs, x ) ) * One(f); # compute the matrixgroup stabilising all subspaces in chain S := StabilizingMatrixGroup( bases, r, p ); # the Frattini Quotient H := FrattiniQuotientPGroup( G ); kern := InitAgAutos( H, p ); # set up aut group A := rec( ); A.glAutos := InitGlAutos( H, GeneratorsOfGroup(S) ); A.glOrder := Size(S) / Product( kern.rels ); A.glOper := GeneratorsOfGroup(S); A.agAutos := kern.auts; A.agOrder := kern.rels; A.one := IdentityPGAutomorphism( H ); A.group := H; A.size := A.glOrder * Product( A.agOrder ); # add perm rep AddPermOper(A); # check for large solvable subgroups TrySolvableSubgroup(A); # loop over remaining steps F := Range( IsomorphismFpGroupByPcgs( pcgs, "f" ) ); Q := PQuotient( F, p, 1 ); for i in [2..Length(first)-1] do # print info s := first[i]; t := first[i+1]; Print(" AG: step ",i ,": ",p,"^", t-s, " -- size ", A.size,"\n" ); # the cover P := PCover( Q ); M := PMultiplicator( Q, P ); N := Nucleus( Q, P ); U := AllowableSubgroup( Q, P ); AddInfoCover( Q, P, M, U ); # induced action of A on M LinearActionAutGrp( A, P, M ); # compute stabilizer baseN := GeneratorsOfGroup(N); baseU := GeneratorsOfGroup(U); baseN := List(baseN, x -> ExponentsOfPcElement(Pcgs(M), x)) * One(f); baseU := List(baseU, x -> ExponentsOfPcElement(Pcgs(M), x)) * One(f); baseU := EcheloniseMat( baseU ); PGOrbitStabilizer( A, baseU, baseN, false ); # next step of p-quotient IncorporateCentralRelations( Q ); RenumberHighestWeightGenerators( Q ); # induce to next factor A := InduceAutGroup( A, Q, P, M, U ); od; # now get a real automorphism group Print(" AG: full has type ", A.glOrder, " by ",A.agOrder,"\n" ); # translate isom := CgsParallel( pcgs{[1..r]}, Pcgs(A.group){[1..r]}); gens := Cgs(C); imgs := List(gens, x->MappedVector(ExponentsByIgs(isom[1],x),isom[2])); Cimg := Subgroup(A.group, imgs); # stabilise C OnSubs := function( U, auto, info ) return Image(auto, U); end; PGHybridOrbitStabilizer(A,A.glAutos,A.agAutos,Cimg,OnSubs,true); Print(" AG: stab has type ", A.glOrder, " by ",A.agOrder,"\n" ); # convert A isom := GroupHomomorphismByImagesNC(C, Cimg, gens, imgs); A.agAutos := List(A.agAutos, x -> ReduceAuto(x, C, isom, gens, imgs)); A.glAutos := List(A.glAutos, x -> ReduceAuto(x, C, isom, gens, imgs)); A.one := IdentityMapping(C); A.group := C; return A; end; InducedAutCover := function(aut, f, t, e) local actT, invF, trs, AsMat, InvertMod; AsMat := function(aut, m) return List(m, x -> ExponentsByPcp(m,Image(aut,x))); end; InvertMod := function(mat, e) mat := (mat * One(ZmodnZ(e)))^-1; if IsPrime(e) then return List(mat, IntVecFFE); else return List(mat, x -> List(x, ExtRepOfObj)); fi; end; # construct linear actions on t and f/f^e actT := AsMat(aut,t); invF := InvertMod(AsMat(aut,f), e); # construct translation trs := List([1..Length(f)], x -> MappedVector(invF[x],f)); trs := List([1..Length(f)], x -> f[x]^-1 * Image(aut,trs[x])); trs := List(trs, x -> ExponentsByPcp(t,x)); # return all return [actT, invF, trs]; end; polycyclic-2.16/gap/cover/const/ord.gi0000644000076600000240000000271613706672341016762 0ustar mhornstaff ## ## List iterated Schur covers of order p^n and rank d and print to file. ## SchurCoversByOrder := function(grpsfile, p,n,d) local file, i, j, G, new, q, H; if not d in [2..n] then return fail; fi; # get file file := Concatenation(grpsfile, "_",String(p), "_",String(n), "_",String(d), ".gi"); PrintTo(file,"SCover[",p,"][",n,"][",d,"] := [\n"); # extend gps Print("compute covers \n"); for i in [1..n-1] do for j in [1..NrItCovers(p,i,d)] do # get group G := ItCover(p,i,d,j); # check relevance and extend if necessary if Size(G)*G!.mord = p^n then # info Print("start group ",j," of ",NrItCovers(p,i,d)); Print(" with order p^",i," \n"); # get covers new := SchurCovers(G); # print to file for H in new do AppendTo(file, CoverCode(H),", \n"); od; fi; od; od; # add abelians Print("add abelian groups \n"); for q in Partitions(n) do if Length(q) = d then # get group G := AbelianPcpGroup(d, List(q, x -> p^x)); # print AppendTo(file, CoverCode(G),", \n"); fi; od; AppendTo(file,"];\n"); AddSet(availb[p],[n,d]); ReadDBFile(p,n,d); end; polycyclic-2.16/makedoc.g0000644000076600000240000000056313706672341014413 0ustar mhornstaff## this creates the documentation, needs: GAPDoc and AutoDoc packages, pdflatex ## ## Call this with GAP from within the package directory. ## if fail = LoadPackage("AutoDoc", ">= 2016.01.21") then Error("AutoDoc 2016.01.21 or newer is required"); fi; AutoDoc(rec( scaffold := rec( MainPage := false ), gapdoc := rec( main := "polycyclic.xml" ))); polycyclic-2.16/CHANGES.md0000644000076600000240000002405713706672341014236 0ustar mhornstaffThis file describes changes in the GAP package 'polycyclic'. 2.16 (2020-07-25) - Fix a bug in `NormalIntersection` which could lead to wrong results; this also affected other operations, such `Core`, `Intersection` - Fix `PreImagesRepresentative` for trivial homomorphisms (it used to return the identity fo the source as preimage for all elements in the range, instead of returning fail for all but the identity of the range) - Fix some bugs in `AddToIgs` and `AddTailInfo` - Some janitorial changes 2.15.1 (2019-10-03) - Fix a regression that could lead to an infinite loop in IsomorphismPcGroup 2.15 (2019-09-27) - Added license information to package metadata - Add support for random sources to Random method for pcp-groups - Documented IsPcpGroup and IsPcpElementCollection - Increased rank for IsomorphismPcGroup and IsomorphismFpGroup methods for pcp-groups, to ensure they are still used when all GAP packages are loaded - Some janitorial changes 2.14 (2018-05-12) * Fixed a bug in OneCoboundariesCR which lead to an error in OneCohomologyCR * Fixed a bug where the normal closure of an abelian subgroup could end up being flagged as abelian, even though it was not * Restored compatibility with GAP versions before 4.9 2.13.1 (2018-04-27) * Removed a regression test case which failed if no other packages are loaded 2.13 (2018-04-26) * Fixed bug in IsConjugate * Fixed building the manual via makedoc.g on case-sensitive file systems * Replaced immediate methods for IsTorsionFree and IsFreeAbelian by implications, which have zero overhead, while immediate methods can slow down GAP * Improved performance of UnitriangularPcpGroup for large n 2.12 (2018-03-18) * Improved performance of some orbit algorithms by using dictionaries * Improved performance of AddToIgs for some examples where it previously performed very badly * Added custom IsSingleValued method for group homomorphisms whose Source is an polycyclic groups, which can avoid an endless loop when the range is an infinite group * Fixed bug in NormalizerPcpGroup which could result in a break loop * Fixed bug in ComplementClassesCR which could result in a break loop * Fixed bug in OrbitIntegralAction which could result in a break loop * Fixed bug in StabilizerIntegralAction which could result in a break loop * Fixed bug in AddToIgs for infinite groups which could result in an invalid output leading to strange results * Fixed IsConjugate for pcp group elements to always return true and false (instead of an element which conjugates the inputs to each other) * Corrected documentation for HeisenbergPcpGroup to give correct number of generators, an correct Hirsch length * Corrected and clarified InfiniteMetacyclicPcpGroup documentation * Deprecated NaturalHomomorphism, use NaturalHomomorphismByNormalSubgroup instead (which is a standard GAP operation) * Removed left-over traces of Schur towers in the manual and elsewhere * Added more tests cases * Changed tests to using TestDirectory * Various minor tweaks 2.11 (2013-03-07) * Added a fast SylowSubgroup method (via IsomorphismPcGroup) * Add FreeAbelianGroup constructor method (feature will only be available in a future GAP release) * Replaced some internal code dealing with integer matrices with calls to equivalent GAP functions; for some things (e.g. inverting a matrix), this can be a lot faster * Fixed regressions in 2-cohomology code (introduced in 2.9), which caused TwoCoboundariesCR and TwoCohomologyCR to produce errors or wrong results * Fixed infinite recursion in LowerCentralSeriesOfGroup for non-nilpotent pcp groups (thanks to Andreas Distler for noticing and fixing this) * Removed support for GAP 4.4, now GAP 4.5 or newer is required * Removed some obsolete code * Removed or hid multiple undocumented internal functions (such as AsMat, IntMat, OnVectorspace, VERIFY, ...) to reduce the pollution of the global namespace * Various minor tweaks 2.10.1 (2012-06-01) * Fixed generic IsFreeAbelian method to only apply to finitely generated groups * Removed "name strings" from two InstallImmediateMethod calls; this should have no effect on any user, and is done to silence some pedantic warnings in the GAP test suite 2.10 (2012-05-31) * Added methods for GAP's Epicentre and EpimorphismSchurCover attribute * Added group constructors that allow construction extraspecial groups as well as alternating and symmetric groups of degree <= 4 as pcp groups * Changed SchurExtension and SchurExtensionEpimorphism into attributes * Changed IsomorphismPcpGroup for pcp groups, now returns identity map * Changed SchurCovering to be a synonym for GAP's SchurCover attribute * Fixed regression in AddFieldCR which caused incorrect errors (e.g. when testing whether a pcp group is torsion free) * Fixed some warnings by adding a IsGeneratorsOfMagmaWithInverses method for pcp element collections * Fixed several bugs resulting in errors when computing Schur extensions, nonabelian exterior and tensor squares, and so on, but only if the argument was a subgroup of a pcp group * Fixed computing Schur extensions, nonabelian exterior and tensor squares etc. of the infinite cyclic group * Fixed bug in direct products of pcp groups that could result in wrong embedding and projection maps * Fixed error triggered when calling NormalizerOp on two groups that have differing Parent() groups, yet still are subgroups of a common overgroup * Removed some dead obsolete code 2.9 (2012-01-12) * Updated README * Added GPL license text * Added this CHANGES file * Added Max Horn to authors / maintainer list * Removed Werner Nickel from maintainer list * Removed compatibility with GAP versions before 4.4 * Removed redundant IsomorphismPermGroup method * Removed redundant IsPcpGHBI group mapping representation * Added various group constructors (TrivialGroupCons etc.), so that it is now possible to construct Pcp groups with e.g. TrivialGroup(IsPcpGroup) or DihedralGroup(IsPcpGroup, infinity). Specifically, this works now for cyclic, (elementary) abelian, dihedral, and quaternion groups * Added implementations of IndependentGeneratorsOfAbelianGroup and IndependentGeneratorExponents for pcp groups * Improved handling of homomorphisms between pcp groups and non-pcp groups * Improved validation of input for various functions / methods * Improved AbelianPcpGroup to flag the constructed group as abelian * Fixed AbelianInvariants to return values that match what the GAP documentation promises the user * Fixed a bug that caused TwoCohomologyCR and many related operations to error out if the cohomology record was obtained using CRRecordBySubgroup or CRRecordByPcp * Fixed bug comparing homomorphisms between pcp groups by removing the (incorrect) method for this; the default method provided by GAP is now used and returns correct results * Fixed ClosureGroup method to not make invalid assumptions about a group's Parent (and thus no longer return incorrect results) * Fixed bug causing general mappings from/to pcp groups to be always marked as total, even if they were in fact not * Added IsNilpotentByFinite methods for finite and nilpotent groups * Added immediate IsTorsionFree method for finite groups * Added IsFreeAbelian method for arbitrary groups, turned it into a property * Converted documentation to GAPDoc format * Replaced internal function DepthOfVec by GAP's PositionNonZero * Added (trivial) IsomorphismPcpGroup method for pcp groups * Added a String method for pcp elements 2.8.1 (2011-05-24) * Use Calcreps2 instead of calcreps2 for compatibility with GAP 4.5 * Updated homepage URLs 2.8 (2011-01-26) * Improved and corrected parts of the manual * Removed IsomorphismPcpGroup method for fpgroups, and instead provide and document it as a regular function under the name IsomorphismPcpGroupFromFpGroupWithPcPres * Use "-u" option when creating the HTML manual to produce unicode output * Removed SchurMultiplicator method and instead install a method for AbelianInvariantsMultiplier 2.7: Never released 2.6 (2009-02-18) * Disabled (and removed any mention from the documentation) some code dealing with Schur towers of p-groups of fixed coclass. * Fixed email address of Bettina Eick 2.5 (2008-11-25) * Added SchurCovers * Added dependency on autpgrp package * Added GroupHomomorphismByImagesNC implementation for when the source is a Pcp group, but the range is possibly not. * Compute size of newly constructed group in PcpGroupByCollectorNC * Various other fixes and improvements 2.4 (2008-11-12) * Fixed a bug in DirectProduct for PcpGroups 2.3 (2008-11-09) * Removed compatibility with GAP versions before 4.3 * Added WhiteheadQuadraticFunctor * Added IsPolycyclicPresentation * Added IsomorphismPermGroup * Renamed DepthVector -> DepthOfVec * Renamed PrintFullPresentation -> PrintPcpPresentation * Renamed Tail -> TailOfElm * Replaced many uses of BindGlobal by InstallGlobalFunction * Improved group homomorphism code * Implemented Embedding and Projection for DirectProduct * Implemented Embedding for WreathProduct * Extended AbelianPcpGroup to accept types of arguments (undocumented) * Various other fixes and improvements 2.2 (2007-06-22) * Added support for non-abelian tensor and exterior squares * TODO: Schur extensions code was also touched? * Various other fixes and improvements 2.1 (2006-11-07) * Declare in PackageInfo.g that nq is a suggested (but not required) external package, and try harder to work when nq is missing. * Rewrote SchurExtension * Changed IsomorphismPcGroup to first convert the group to a refined pcp group; also, the resulting homomorphism is now marked as being a group homomorphism. * Several existing functions now are "properly" installed via InstallMethod or InstallGlobalFunction * Various other fixes and improvements 2.0 (2006-10-23) 1.1 (2003-10-15) 1.0 (???) polycyclic-2.16/doc/0000755000076600000240000000000013706672374013407 5ustar mhornstaffpolycyclic-2.16/doc/chap9_mj.html0000644000076600000240000002675713706672374016010 0ustar mhornstaff GAP (polycyclic) - Chapter 9: Matrix Representations

9 Matrix Representations

This chapter describes functions which compute with matrix representations for pcp-groups. So far the routines in this package are only able to compute matrix representations for torsion-free nilpotent groups.

9.1 Unitriangular matrix groups

9.1-1 UnitriangularMatrixRepresentation
‣ UnitriangularMatrixRepresentation( G )( operation )

computes a faithful representation of a torsion-free nilpotent group G as unipotent lower triangular matrices over the integers. The pc-presentation for G must not contain any power relations. The algorithm is described in [dGN02].

9.1-2 IsMatrixRepresentation
‣ IsMatrixRepresentation( G, matrices )( function )

checks if the map defined by mapping the \(i\)-th generator of the pcp-group G to the \(i\)-th matrix of matrices defines a homomorphism.

9.2 Upper unitriangular matrix groups

We call a matrix upper unitriangular if it is an upper triangular matrix with ones on the main diagonal. The weight of an upper unitriangular matrix is the number of diagonals above the main diagonal that contain zeroes only.

The subgroup of all upper unitriangular matrices of \(GL(n,ℤ)\) is torsion-free nilpotent. The \(k\)-th term of its lower central series is the set of all matrices of weight \(k-1\). The \(ℤ\)-rank of the \(k\)-th term of the lower central series modulo the \((k+1)\)-th term is \(n-k\).

9.2-1 IsomorphismUpperUnitriMatGroupPcpGroup
‣ IsomorphismUpperUnitriMatGroupPcpGroup( G )( function )

takes a group G generated by upper unitriangular matrices over the integers and computes a polycylic presentation for the group. The function returns an isomorphism from the matrix group to the pcp group. Note that a group generated by upper unitriangular matrices is necessarily torsion-free nilpotent.

9.2-2 SiftUpperUnitriMatGroup
‣ SiftUpperUnitriMatGroup( G )( function )

takes a group G generated by upper unitriangular matrices over the integers and returns a recursive data structure L with the following properties: L contains a polycyclic generating sequence for G, using L one can decide if a given upper unitriangular matrix is contained in G, a given element of G can be written as a word in the polycyclic generating sequence. L is a representation of a chain of subgroups of G refining the lower centrals series of G.. It contains for each subgroup in the chain a minimal generating set.

9.2-3 RanksLevels
‣ RanksLevels( L )( function )

takes the data structure returned by SiftUpperUnitriMat and prints the \(ℤ\)-rank of each the subgroup in L.

9.2-4 MakeNewLevel
‣ MakeNewLevel( m )( function )

creates one level of the data structure returned by SiftUpperUnitriMat and initialises it with weight m.

9.2-5 SiftUpperUnitriMat
‣ SiftUpperUnitriMat( gens, level, M )( function )

takes the generators gens of an upper unitriangular group, the data structure returned level by SiftUpperUnitriMat and another upper unitriangular matrix M. It sift M through level and adds M at the appropriate place if M is not contained in the subgroup represented by level.

The function SiftUpperUnitriMatGroup illustrates the use of SiftUpperUnitriMat.

InstallGlobalFunction( "SiftUpperUnitriMatGroup", function( G )
    local   firstlevel,  g;

    firstlevel := MakeNewLevel( 0 );
    for g in GeneratorsOfGroup(G) do
        SiftUpperUnitriMat( GeneratorsOfGroup(G), firstlevel, g );
    od;
    return firstlevel;
end );

9.2-6 DecomposeUpperUnitriMat
‣ DecomposeUpperUnitriMat( level, M )( function )

takes the data structure level returned by SiftUpperUnitriMatGroup and a upper unitriangular matrix M and decomposes M into a word in the polycyclic generating sequence of level.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/manual.six0000644000076600000240000010325113706672374015413 0ustar mhornstaff#SIXFORMAT GapDocGAP HELPBOOKINFOSIXTMP := rec( encoding := "UTF-8", bookname := "polycyclic", entries := [ [ "Title page", ".", [ 0, 0, 0 ], 1, 1, "title page", "X7D2C85EC87DD46E5" ], [ "Copyright", ".-1", [ 0, 0, 1 ], 47, 2, "copyright", "X81488B807F2A1CF1" ] , [ "Acknowledgements", ".-2", [ 0, 0, 2 ], 58, 2, "acknowledgements", "X82A988D47DFAFCFA" ], [ "Table of Contents", ".-3", [ 0, 0, 3 ], 65, 3, "table of contents", "X8537FEB07AF2BEC8" ], [ "\033[1X\033[33X\033[0;-2YPreface\033[133X\033[101X", "1", [ 1, 0, 0 ], 1, 5, "preface", "X874E1D45845007FE" ], [ "\033[1X\033[33X\033[0;-2YIntroduction to polycyclic presentations\033[133X\ \033[101X", "2", [ 2, 0, 0 ], 1, 6, "introduction to polycyclic presentations" , "X792561B378D95B23" ], [ "\033[1X\033[33X\033[0;-2YCollectors\033[133X\033[101X", "3", [ 3, 0, 0 ], 1, 8, "collectors", "X792305CC81E8606A" ], [ "\033[1X\033[33X\033[0;-2YConstructing a Collector\033[133X\033[101X", "3.1", [ 3, 1, 0 ], 33, 8, "constructing a collector", "X800FD91386C08CD8" ], [ "\033[1X\033[33X\033[0;-2YAccessing Parts of a Collector\033[133X\033[101X" , "3.2", [ 3, 2, 0 ], 223, 11, "accessing parts of a collector", "X818484817C3BAAE6" ], [ "\033[1X\033[33X\033[0;-2YSpecial Features\033[133X\033[101X", "3.3", [ 3, 3, 0 ], 291, 13, "special features", "X79AEB3477800DC16" ], [ "\033[1X\033[33X\033[0;-2YPcp-groups - polycyclically presented groups\033[\ 133X\033[101X", "4", [ 4, 0, 0 ], 1, 15, "pcp-groups - polycyclically presented groups", "X7E2AF25881CF7307" ], [ "\033[1X\033[33X\033[0;-2YPcp-elements -- elements of a pc-presented group\ \033[133X\033[101X", "4.1", [ 4, 1, 0 ], 4, 15, "pcp-elements -- elements of a pc-presented group", "X7882F0F57ABEB680" ], [ "\033[1X\033[33X\033[0;-2YMethods for pcp-elements\033[133X\033[101X", "4.2", [ 4, 2, 0 ], 66, 16, "methods for pcp-elements", "X790471D07A953E12" ], [ "\033[1X\033[33X\033[0;-2YPcp-groups - groups of pcp-elements\033[133X\033[\ 101X", "4.3", [ 4, 3, 0 ], 168, 18, "pcp-groups - groups of pcp-elements", "X7A4EF7C68151905A" ], [ "\033[1X\033[33X\033[0;-2YBasic methods and functions for pcp-groups\033[13\ 3X\033[101X", "5", [ 5, 0, 0 ], 1, 20, "basic methods and functions for pcp-groups", "X7B9B85AE7C9B13EE" ], [ "\033[1X\033[33X\033[0;-2YElementary methods for pcp-groups\033[133X\033[10\ 1X", "5.1", [ 5, 1, 0 ], 10, 20, "elementary methods for pcp-groups", "X821360107E355B88" ], [ "\033[1X\033[33X\033[0;-2YElementary properties of pcp-groups\033[133X\033[\ 101X", "5.2", [ 5, 2, 0 ], 94, 22, "elementary properties of pcp-groups", "X80E88168866D54F3" ], [ "\033[1X\033[33X\033[0;-2YSubgroups of pcp-groups\033[133X\033[101X", "5.3", [ 5, 3, 0 ], 133, 23, "subgroups of pcp-groups", "X85A7E26C7E14AFBA" ], [ "\033[1X\033[33X\033[0;-2YPolycyclic presentation sequences for subfactors\\ 033[133X\033[101X", "5.4", [ 5, 4, 0 ], 211, 24, "polycyclic presentation sequences for subfactors", "X803D62BC86EF07D0" ], [ "\033[1X\033[33X\033[0;-2YFactor groups of pcp-groups\033[133X\033[101X", "5.5", [ 5, 5, 0 ], 359, 27, "factor groups of pcp-groups", "X845D29B478CA7656" ], [ "\033[1X\033[33X\033[0;-2YHomomorphisms for pcp-groups\033[133X\033[101X", "5.6", [ 5, 6, 0 ], 383, 27, "homomorphisms for pcp-groups", "X82E643F178E765EA" ], [ "\033[1X\033[33X\033[0;-2YChanging the defining pc-presentation\033[133X\\ 033[101X", "5.7", [ 5, 7, 0 ], 434, 28, "changing the defining pc-presentation", "X7C873F807D4F3A3C" ], [ "\033[1X\033[33X\033[0;-2YPrinting a pc-presentation\033[133X\033[101X", "5.8", [ 5, 8, 0 ], 475, 29, "printing a pc-presentation", "X85E681027AF19B1E" ], [ "\033[1X\033[33X\033[0;-2YConverting to and from a presentation\033[133X\\ 033[101X", "5.9", [ 5, 9, 0 ], 496, 29, "converting to and from a presentation", "X826ACBBB7A977206" ], [ "\033[1X\033[33X\033[0;-2YLibraries and examples of pcp-groups\033[133X\\ 033[101X", "6", [ 6, 0, 0 ], 1, 31, "libraries and examples of pcp-groups", "X78CEF1F27ED8D7BB" ], [ "\033[1X\033[33X\033[0;-2YLibraries of various types of polycyclic groups\\ 033[133X\033[101X", "6.1", [ 6, 1, 0 ], 4, 31, "libraries of various types of polycyclic groups", "X84A48FAB83934263" ] , [ "\033[1X\033[33X\033[0;-2YSome assorted example groups\033[133X\033[101X", "6.2", [ 6, 2, 0 ], 86, 32, "some assorted example groups", "X806FBA4A7CB8FB71" ], [ "\033[1X\033[33X\033[0;-2YHigher level methods for pcp-groups\033[133X\033[\ 101X", "7", [ 7, 0, 0 ], 1, 34, "higher level methods for pcp-groups", "X85BB6FE078679DAF" ], [ "\033[1X\033[33X\033[0;-2YSubgroup series in pcp-groups\033[133X\033[101X" , "7.1", [ 7, 1, 0 ], 9, 34, "subgroup series in pcp-groups", "X8266A0A2821D98A1" ], [ "\033[1X\033[33X\033[0;-2YOrbit stabilizer methods for pcp-groups\033[133X\\ 033[101X", "7.2", [ 7, 2, 0 ], 146, 37, "orbit stabilizer methods for pcp-groups", "X7CE2DA437FD2B383" ], [ "\033[1X\033[33X\033[0;-2YCentralizers, Normalizers and Intersections\033[1\ 33X\033[101X", "7.3", [ 7, 3, 0 ], 258, 39, "centralizers normalizers and intersections", "X80E3B42E792532B3" ], [ "\033[1X\033[33X\033[0;-2YFinite subgroups\033[133X\033[101X", "7.4", [ 7, 4, 0 ], 302, 39, "finite subgroups", "X7CF015E87A2B2388" ], [ "\033[1X\033[33X\033[0;-2YSubgroups of finite index and maximal subgroups\\ 033[133X\033[101X", "7.5", [ 7, 5, 0 ], 374, 41, "subgroups of finite index and maximal subgroups", "X7D9F737F80F6E396" ] , [ "\033[1X\033[33X\033[0;-2YFurther attributes for pcp-groups based on the Fi\ tting subgroup\033[133X\033[101X", "7.6", [ 7, 6, 0 ], 435, 42, "further attributes for pcp-groups based on the fitting subgroup", "X785E0E877AB1D549" ], [ "\033[1X\033[33X\033[0;-2YFunctions for nilpotent groups\033[133X\033[101X" , "7.7", [ 7, 7, 0 ], 484, 43, "functions for nilpotent groups", "X878DBDC77CCA4F7E" ], [ "\033[1X\033[33X\033[0;-2YRandom methods for pcp-groups\033[133X\033[101X" , "7.8", [ 7, 8, 0 ], 532, 44, "random methods for pcp-groups", "X8640F9D47A1F7434" ], [ "\033[1X\033[33X\033[0;-2YNon-abelian tensor product and Schur extensions\\ 033[133X\033[101X", "7.9", [ 7, 9, 0 ], 569, 44, "non-abelian tensor product and schur extensions", "X824142B784453DB9" ] , [ "\033[1X\033[33X\033[0;-2YSchur covers\033[133X\033[101X", "7.10", [ 7, 10, 0 ], 828, 49, "schur covers", "X7D3023697BA5CE5A" ], [ "\033[1X\033[33X\033[0;-2YCohomology for pcp-groups\033[133X\033[101X", "8", [ 8, 0, 0 ], 1, 50, "cohomology for pcp-groups", "X796AB9787E2A752C" ], [ "\033[1X\033[33X\033[0;-2YCohomology records\033[133X\033[101X", "8.1", [ 8, 1, 0 ], 13, 50, "cohomology records", "X875758FA7C6F5CE1" ], [ "\033[1X\033[33X\033[0;-2YCohomology groups\033[133X\033[101X", "8.2", [ 8, 2, 0 ], 71, 51, "cohomology groups", "X874759D582393441" ], [ "\033[1X\033[33X\033[0;-2YExtended 1-cohomology\033[133X\033[101X", "8.3", [ 8, 3, 0 ], 156, 52, "extended 1-cohomology", "X79610E9178BD0C54" ], [ "\033[1X\033[33X\033[0;-2YExtensions and Complements\033[133X\033[101X", "8.4", [ 8, 4, 0 ], 203, 53, "extensions and complements", "X853E51787A24AE00" ], [ "\033[1X\033[33X\033[0;-2YConstructing pcp groups as extensions\033[133X\\ 033[101X", "8.5", [ 8, 5, 0 ], 280, 55, "constructing pcp groups as extensions", "X823771527DBD857D" ], [ "\033[1X\033[33X\033[0;-2YMatrix Representations\033[133X\033[101X", "9", [ 9, 0, 0 ], 1, 57, "matrix representations", "X858D1BB07A8FBF87" ], [ "\033[1X\033[33X\033[0;-2YUnitriangular matrix groups\033[133X\033[101X", "9.1", [ 9, 1, 0 ], 8, 57, "unitriangular matrix groups", "X7D0ED06C7E6A457D" ], [ "\033[1X\033[33X\033[0;-2YUpper unitriangular matrix groups\033[133X\033[10\ 1X", "9.2", [ 9, 2, 0 ], 27, 57, "upper unitriangular matrix groups", "X79A8A51B84E4BF8C" ], [ "\033[1X\033[33X\033[0;-2YObsolete Functions and Name Changes\033[133X\033[\ 101X", "a", [ "A", 0, 0 ], 1, 60, "obsolete functions and name changes", "X874ECE907CAF380D" ], [ "Bibliography", "bib", [ "Bib", 0, 0 ], 1, 61, "bibliography", "X7A6F98FD85F02BFE" ], [ "References", "bib", [ "Bib", 0, 0 ], 1, 61, "references", "X7A6F98FD85F02BFE" ], [ "Index", "ind", [ "Ind", 0, 0 ], 1, 63, "index", "X83A0356F839C696F" ], [ "License", ".-1", [ 0, 0, 1 ], 47, 2, "license", "X81488B807F2A1CF1" ], [ "\033[2XFromTheLeftCollector\033[102X", "3.1-1", [ 3, 1, 1 ], 43, 8, "fromtheleftcollector", "X8382A4E78706DE65" ], [ "\033[2XSetRelativeOrder\033[102X", "3.1-2", [ 3, 1, 2 ], 66, 9, "setrelativeorder", "X79A308B28183493B" ], [ "\033[2XSetRelativeOrderNC\033[102X", "3.1-2", [ 3, 1, 2 ], 66, 9, "setrelativeordernc", "X79A308B28183493B" ], [ "\033[2XSetPower\033[102X", "3.1-3", [ 3, 1, 3 ], 96, 9, "setpower", "X7BC319BA8698420C" ], [ "\033[2XSetPowerNC\033[102X", "3.1-3", [ 3, 1, 3 ], 96, 9, "setpowernc", "X7BC319BA8698420C" ], [ "\033[2XSetConjugate\033[102X", "3.1-4", [ 3, 1, 4 ], 113, 10, "setconjugate", "X86A08D887E049347" ], [ "\033[2XSetConjugateNC\033[102X", "3.1-4", [ 3, 1, 4 ], 113, 10, "setconjugatenc", "X86A08D887E049347" ], [ "\033[2XSetCommutator\033[102X", "3.1-5", [ 3, 1, 5 ], 129, 10, "setcommutator", "X7B25997C7DF92B6D" ], [ "\033[2XUpdatePolycyclicCollector\033[102X", "3.1-6", [ 3, 1, 6 ], 142, 10, "updatepolycycliccollector", "X7E9903F57BC5CC24" ], [ "\033[2XIsConfluent\033[102X", "3.1-7", [ 3, 1, 7 ], 155, 10, "isconfluent", "X8006790B86328CE8" ], [ "\033[2XRelativeOrders\033[102X", "3.2-1", [ 3, 2, 1 ], 226, 11, "relativeorders", "X7DD0DF677AC1CF10" ], [ "\033[2XGetPower\033[102X", "3.2-2", [ 3, 2, 2 ], 232, 11, "getpower", "X844C0A478735EF4B" ], [ "\033[2XGetPowerNC\033[102X", "3.2-2", [ 3, 2, 2 ], 232, 11, "getpowernc", "X844C0A478735EF4B" ], [ "\033[2XGetConjugate\033[102X", "3.2-3", [ 3, 2, 3 ], 244, 12, "getconjugate", "X865160E07FA93E00" ], [ "\033[2XGetConjugateNC\033[102X", "3.2-3", [ 3, 2, 3 ], 244, 12, "getconjugatenc", "X865160E07FA93E00" ], [ "\033[2XNumberOfGenerators\033[102X", "3.2-4", [ 3, 2, 4 ], 257, 12, "numberofgenerators", "X7D6A26A4871FF51A" ], [ "\033[2XObjByExponents\033[102X", "3.2-5", [ 3, 2, 5 ], 263, 12, "objbyexponents", "X873ECF388503E5DE" ], [ "\033[2XExponentsByObj\033[102X", "3.2-6", [ 3, 2, 6 ], 271, 12, "exponentsbyobj", "X85BCB97B8021EAD6" ], [ "\033[2XIsWeightedCollector\033[102X", "3.3-1", [ 3, 3, 1 ], 297, 13, "isweightedcollector", "X82EE2ACD7B8C178B" ], [ "\033[2XAddHallPolynomials\033[102X", "3.3-2", [ 3, 3, 2 ], 308, 13, "addhallpolynomials", "X7A1D7ED68334282C" ], [ "\033[2XString\033[102X", "3.3-3", [ 3, 3, 3 ], 317, 13, "string", "X81FB5BE27903EC32" ], [ "\033[2XFTLCollectorPrintTo\033[102X", "3.3-4", [ 3, 3, 4 ], 323, 13, "ftlcollectorprintto", "X7ED466B6807D16FE" ], [ "\033[2XFTLCollectorAppendTo\033[102X", "3.3-5", [ 3, 3, 5 ], 331, 13, "ftlcollectorappendto", "X789D9EB37ECFA9D7" ], [ "\033[2XUseLibraryCollector\033[102X", "3.3-6", [ 3, 3, 6 ], 338, 13, "uselibrarycollector", "X808A26FB873A354F" ], [ "\033[2XUSE_LIBRARY_COLLECTOR\033[102X", "3.3-7", [ 3, 3, 7 ], 346, 14, "use_library_collector", "X844E195C7D55F8BD" ], [ "\033[2XDEBUG_COMBINATORIAL_COLLECTOR\033[102X", "3.3-8", [ 3, 3, 8 ], 354, 14, "debug_combinatorial_collector", "X7945C6B97BECCDA8" ], [ "\033[2XUSE_COMBINATORIAL_COLLECTOR\033[102X", "3.3-9", [ 3, 3, 9 ], 363, 14, "use_combinatorial_collector", "X7BDFB55D7CB33543" ], [ "\033[2XPcpElementByExponentsNC\033[102X", "4.1-1", [ 4, 1, 1 ], 16, 15, "pcpelementbyexponentsnc", "X786DB93F7862D903" ], [ "\033[2XPcpElementByExponents\033[102X", "4.1-1", [ 4, 1, 1 ], 16, 15, "pcpelementbyexponents", "X786DB93F7862D903" ], [ "\033[2XPcpElementByGenExpListNC\033[102X", "4.1-2", [ 4, 1, 2 ], 24, 15, "pcpelementbygenexplistnc", "X7BBB358C7AA64135" ], [ "\033[2XPcpElementByGenExpList\033[102X", "4.1-2", [ 4, 1, 2 ], 24, 15, "pcpelementbygenexplist", "X7BBB358C7AA64135" ], [ "\033[2XIsPcpElement\033[102X", "4.1-3", [ 4, 1, 3 ], 42, 16, "ispcpelement", "X86083E297D68733B" ], [ "\033[2XIsPcpElementCollection\033[102X", "4.1-4", [ 4, 1, 4 ], 48, 16, "ispcpelementcollection", "X8695069A7D5073B7" ], [ "\033[2XIsPcpElementRep\033[102X", "4.1-5", [ 4, 1, 5 ], 54, 16, "ispcpelementrep", "X7F2C83AD862910B9" ], [ "\033[2XIsPcpGroup\033[102X", "4.1-6", [ 4, 1, 6 ], 60, 16, "ispcpgroup", "X8470284A78A6C41B" ], [ "\033[2XCollector\033[102X", "4.2-1", [ 4, 2, 1 ], 94, 16, "collector", "X7E2D258B7DCE8AC9" ], [ "\033[2XExponents\033[102X", "4.2-2", [ 4, 2, 2 ], 100, 17, "exponents", "X85C672E78630C507" ], [ "\033[2XGenExpList\033[102X", "4.2-3", [ 4, 2, 3 ], 107, 17, "genexplist", "X8571F6FB7E74346C" ], [ "\033[2XNameTag\033[102X", "4.2-4", [ 4, 2, 4 ], 114, 17, "nametag", "X82252C5E7B011559" ], [ "\033[2XDepth\033[102X", "4.2-5", [ 4, 2, 5 ], 121, 17, "depth", "X840D32D9837E99F5" ], [ "\033[2XLeadingExponent\033[102X", "4.2-6", [ 4, 2, 6 ], 127, 17, "leadingexponent", "X874F1EC178721833" ], [ "\033[2XRelativeOrder\033[102X", "4.2-7", [ 4, 2, 7 ], 134, 17, "relativeorder", "X8008AB61823A76B7" ], [ "\033[2XRelativeIndex\033[102X", "4.2-8", [ 4, 2, 8 ], 141, 17, "relativeindex", "X875D04288577015B" ], [ "\033[2XFactorOrder\033[102X", "4.2-9", [ 4, 2, 9 ], 148, 18, "factororder", "X87E070747955F2C1" ], [ "\033[2XNormingExponent\033[102X", "4.2-10", [ 4, 2, 10 ], 155, 18, "normingexponent", "X79A247797F0A8583" ], [ "\033[2XNormedPcpElement\033[102X", "4.2-11", [ 4, 2, 11 ], 162, 18, "normedpcpelement", "X798BB22B80833441" ], [ "\033[2XPcpGroupByCollector\033[102X", "4.3-1", [ 4, 3, 1 ], 175, 18, "pcpgroupbycollector", "X7C8FBCAB7F63FACB" ], [ "\033[2XPcpGroupByCollectorNC\033[102X", "4.3-1", [ 4, 3, 1 ], 175, 18, "pcpgroupbycollectornc", "X7C8FBCAB7F63FACB" ], [ "\033[2XGroup\033[102X", "4.3-2", [ 4, 3, 2 ], 187, 18, "group", "X7D7B075385435151" ], [ "\033[2XSubgroup\033[102X", "4.3-3", [ 4, 3, 3 ], 193, 18, "subgroup", "X7C82AA387A42DCA0" ], [ "\033[2X\\=\033[102X", "5.1-1", [ 5, 1, 1 ], 19, 20, "=", "X806A4814806A4814" ], [ "\033[2XSize\033[102X", "5.1-2", [ 5, 1, 2 ], 25, 20, "size", "X858ADA3B7A684421" ], [ "\033[2XRandom\033[102X", "5.1-3", [ 5, 1, 3 ], 31, 20, "random", "X79730D657AB219DB" ], [ "\033[2XIndex\033[102X", "5.1-4", [ 5, 1, 4 ], 37, 21, "index", "X83A0356F839C696F" ], [ "\033[2X\\in\033[102X", "5.1-5", [ 5, 1, 5 ], 45, 21, "in", "X87BDB89B7AAFE8AD" ], [ "\033[2XElements\033[102X", "5.1-6", [ 5, 1, 6 ], 51, 21, "elements", "X79B130FC7906FB4C" ], [ "\033[2XClosureGroup\033[102X", "5.1-7", [ 5, 1, 7 ], 58, 21, "closuregroup", "X7D13FC1F8576FFD8" ], [ "\033[2XNormalClosure\033[102X", "5.1-8", [ 5, 1, 8 ], 64, 21, "normalclosure", "X7BDEA0A98720D1BB" ], [ "\033[2XHirschLength\033[102X", "5.1-9", [ 5, 1, 9 ], 70, 21, "hirschlength", "X839B42AE7A1DD544" ], [ "\033[2XCommutatorSubgroup\033[102X", "5.1-10", [ 5, 1, 10 ], 76, 21, "commutatorsubgroup", "X7A9A3D5578CE33A0" ], [ "\033[2XPRump\033[102X", "5.1-11", [ 5, 1, 11 ], 82, 21, "prump", "X796DA805853FAC90" ], [ "\033[2XSmallGeneratingSet\033[102X", "5.1-12", [ 5, 1, 12 ], 88, 22, "smallgeneratingset", "X814DBABC878D5232" ], [ "\033[2XIsSubgroup\033[102X", "5.2-1", [ 5, 2, 1 ], 97, 22, "issubgroup", "X7839D8927E778334" ], [ "\033[2XIsNormal\033[102X", "5.2-2", [ 5, 2, 2 ], 103, 22, "isnormal", "X838186F9836F678C" ], [ "\033[2XIsNilpotentGroup\033[102X", "5.2-3", [ 5, 2, 3 ], 109, 22, "isnilpotentgroup", "X87D062608719F2CD" ], [ "\033[2XIsAbelian\033[102X", "5.2-4", [ 5, 2, 4 ], 115, 22, "isabelian", "X7C12AA7479A6C103" ], [ "\033[2XIsElementaryAbelian\033[102X", "5.2-5", [ 5, 2, 5 ], 121, 22, "iselementaryabelian", "X813C952F80E775D4" ], [ "\033[2XIsFreeAbelian\033[102X", "5.2-6", [ 5, 2, 6 ], 127, 22, "isfreeabelian", "X84FFC668832F9ED6" ], [ "\033[2XIgs\033[102X", "5.3-1", [ 5, 3, 1 ], 149, 23, "igs", "X833011AD7DFD2C50" ], [ "\033[2XIgs\033[102X", "5.3-1", [ 5, 3, 1 ], 149, 23, "igs", "X833011AD7DFD2C50" ], [ "\033[2XIgsParallel\033[102X", "5.3-1", [ 5, 3, 1 ], 149, 23, "igsparallel", "X833011AD7DFD2C50" ], [ "\033[2XNgs\033[102X", "5.3-2", [ 5, 3, 2 ], 161, 23, "ngs", "X8364E0C5841B650A" ], [ "\033[2XNgs\033[102X", "5.3-2", [ 5, 3, 2 ], 161, 23, "ngs", "X8364E0C5841B650A" ], [ "\033[2XCgs\033[102X", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgs", "X83E969F083F072C1" ], [ "\033[2XCgs\033[102X", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgs", "X83E969F083F072C1" ], [ "\033[2XCgsParallel\033[102X", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgsparallel", "X83E969F083F072C1" ], [ "\033[2XSubgroupByIgs\033[102X", "5.3-4", [ 5, 3, 4 ], 187, 23, "subgroupbyigs", "X83B92A2679EAB1EB" ], [ "\033[2XSubgroupByIgs\033[102X", "5.3-4", [ 5, 3, 4 ], 187, 23, "subgroupbyigs", "X83B92A2679EAB1EB" ], [ "\033[2XAddToIgs\033[102X", "5.3-5", [ 5, 3, 5 ], 198, 24, "addtoigs", "X78107DE78728B26B" ], [ "\033[2XAddToIgsParallel\033[102X", "5.3-5", [ 5, 3, 5 ], 198, 24, "addtoigsparallel", "X78107DE78728B26B" ], [ "\033[2XAddIgsToIgs\033[102X", "5.3-5", [ 5, 3, 5 ], 198, 24, "addigstoigs", "X78107DE78728B26B" ], [ "\033[2XPcp\033[102X", "5.4-1", [ 5, 4, 1 ], 224, 24, "pcp", "X7DD931697DD93169" ], [ "\033[2XPcp\033[102X", "5.4-1", [ 5, 4, 1 ], 224, 24, "pcp", "X7DD931697DD93169" ], [ "\033[2XGeneratorsOfPcp\033[102X", "5.4-2", [ 5, 4, 2 ], 240, 24, "generatorsofpcp", "X821FF77086E38B3A" ], [ "\033[2X\\[\\]\033[102X", "5.4-3", [ 5, 4, 3 ], 246, 25, "[]", "X8297BBCD79642BE6" ], [ "\033[2XLength\033[102X", "5.4-4", [ 5, 4, 4 ], 252, 25, "length", "X780769238600AFD1" ], [ "\033[2XRelativeOrdersOfPcp\033[102X", "5.4-5", [ 5, 4, 5 ], 258, 25, "relativeordersofpcp", "X7ABCA7F2790E1673" ], [ "\033[2XDenominatorOfPcp\033[102X", "5.4-6", [ 5, 4, 6 ], 264, 25, "denominatorofpcp", "X7D16C299825887AA" ], [ "\033[2XNumeratorOfPcp\033[102X", "5.4-7", [ 5, 4, 7 ], 270, 25, "numeratorofpcp", "X803AED1A84FCBEE8" ], [ "\033[2XGroupOfPcp\033[102X", "5.4-8", [ 5, 4, 8 ], 276, 25, "groupofpcp", "X80BCCF0B81344933" ], [ "\033[2XOneOfPcp\033[102X", "5.4-9", [ 5, 4, 9 ], 282, 25, "oneofpcp", "X87F0BA5F7BA0F4B4" ], [ "\033[2XExponentsByPcp\033[102X", "5.4-10", [ 5, 4, 10 ], 293, 26, "exponentsbypcp", "X7A8C8BBC81581E09" ], [ "\033[2XPcpGroupByPcp\033[102X", "5.4-11", [ 5, 4, 11 ], 300, 26, "pcpgroupbypcp", "X87D75F7F86FEF203" ], [ "\033[2XNaturalHomomorphismByNormalSubgroup\033[102X", "5.5-1", [ 5, 5, 1 ], 367, 27, "naturalhomomorphismbynormalsubgroup", "X80FC390C7F38A13F" ], [ "\033[2X\\/\033[102X", "5.5-2", [ 5, 5, 2 ], 374, 27, "/", "X7F51DF007F51DF00" ], [ "\033[2XFactorGroup\033[102X", "5.5-2", [ 5, 5, 2 ], 374, 27, "factorgroup", "X7F51DF007F51DF00" ], [ "\033[2XGroupHomomorphismByImages\033[102X", "5.6-1", [ 5, 6, 1 ], 391, 27, "grouphomomorphismbyimages", "X7F348F497C813BE0" ], [ "\033[2XKernel\033[102X", "5.6-2", [ 5, 6, 2 ], 399, 27, "kernel", "X7DCD99628504B810" ], [ "\033[2XImage\033[102X", "5.6-3", [ 5, 6, 3 ], 405, 28, "image", "X87F4D35A826599C6" ], [ "\033[2XImage\033[102X", "5.6-3", [ 5, 6, 3 ], 405, 28, "image", "X87F4D35A826599C6" ], [ "\033[2XImage\033[102X", "5.6-3", [ 5, 6, 3 ], 405, 28, "image", "X87F4D35A826599C6" ], [ "\033[2XPreImage\033[102X", "5.6-4", [ 5, 6, 4 ], 414, 28, "preimage", "X836FAEAC78B55BF4" ], [ "\033[2XPreImagesRepresentative\033[102X", "5.6-5", [ 5, 6, 5 ], 422, 28, "preimagesrepresentative", "X7AE24A1586B7DE79" ], [ "\033[2XIsInjective\033[102X", "5.6-6", [ 5, 6, 6 ], 428, 28, "isinjective", "X7F065FD7822C0A12" ], [ "\033[2XRefinedPcpGroup\033[102X", "5.7-1", [ 5, 7, 1 ], 437, 28, "refinedpcpgroup", "X80E9B60E853B2E05" ], [ "\033[2XPcpGroupBySeries\033[102X", "5.7-2", [ 5, 7, 2 ], 446, 28, "pcpgroupbyseries", "X7F88F5548329E279" ], [ "\033[2XPrintPcpPresentation\033[102X", "5.8-1", [ 5, 8, 1 ], 481, 29, "printpcppresentation", "X863EE3547C3629C6" ], [ "\033[2XPrintPcpPresentation\033[102X", "5.8-1", [ 5, 8, 1 ], 481, 29, "printpcppresentation", "X863EE3547C3629C6" ], [ "\033[2XIsomorphismPcpGroup\033[102X", "5.9-1", [ 5, 9, 1 ], 499, 29, "isomorphismpcpgroup", "X8771540F7A235763" ], [ "\033[2XIsomorphismPcpGroupFromFpGroupWithPcPres\033[102X", "5.9-2", [ 5, 9, 2 ], 515, 30, "isomorphismpcpgroupfromfpgroupwithpcpres", "X7F5EBF1C831B4BA9" ], [ "\033[2XIsomorphismPcGroup\033[102X", "5.9-3", [ 5, 9, 3 ], 522, 30, "isomorphismpcgroup", "X873CEB137BA1CD6E" ], [ "\033[2XIsomorphismFpGroup\033[102X", "5.9-4", [ 5, 9, 4 ], 529, 30, "isomorphismfpgroup", "X7F28268F850F454E" ], [ "\033[2XAbelianPcpGroup\033[102X", "6.1-1", [ 6, 1, 1 ], 9, 31, "abelianpcpgroup", "X7AEDE1BA82014B86" ], [ "\033[2XDihedralPcpGroup\033[102X", "6.1-2", [ 6, 1, 2 ], 17, 31, "dihedralpcpgroup", "X7ACF57737D0F12DB" ], [ "\033[2XUnitriangularPcpGroup\033[102X", "6.1-3", [ 6, 1, 3 ], 25, 31, "unitriangularpcpgroup", "X864CEDAB7911CC79" ], [ "\033[2XSubgroupUnitriangularPcpGroup\033[102X", "6.1-4", [ 6, 1, 4 ], 34, 31, "subgroupunitriangularpcpgroup", "X812E35B17AADBCD5" ], [ "\033[2XInfiniteMetacyclicPcpGroup\033[102X", "6.1-5", [ 6, 1, 5 ], 42, 32, "infinitemetacyclicpcpgroup", "X7A80F7F27FDA6810" ], [ "\033[2XHeisenbergPcpGroup\033[102X", "6.1-6", [ 6, 1, 6 ], 62, 32, "heisenbergpcpgroup", "X81BEC875827D1CC2" ], [ "\033[2XMaximalOrderByUnitsPcpGroup\033[102X", "6.1-7", [ 6, 1, 7 ], 69, 32, "maximalorderbyunitspcpgroup", "X87F9B9C9786430D7" ], [ "\033[2XBurdeGrunewaldPcpGroup\033[102X", "6.1-8", [ 6, 1, 8 ], 78, 32, "burdegrunewaldpcpgroup", "X852283A77A2C93DD" ], [ "\033[2XExampleOfMetabelianPcpGroup\033[102X", "6.2-1", [ 6, 2, 1 ], 93, 32, "exampleofmetabelianpcpgroup", "X86293081865CDFC3" ], [ "\033[2XExamplesOfSomePcpGroups\033[102X", "6.2-2", [ 6, 2, 2 ], 100, 33, "examplesofsomepcpgroups", "X83A74A6E7E232FD6" ], [ "\033[2XPcpSeries\033[102X", "7.1-1", [ 7, 1, 1 ], 18, 34, "pcpseries", "X8037DAD77A19D9B2" ], [ "\033[2XEfaSeries\033[102X", "7.1-2", [ 7, 1, 2 ], 24, 34, "efaseries", "X86C633357ACD342C" ], [ "\033[2XSemiSimpleEfaSeries\033[102X", "7.1-3", [ 7, 1, 3 ], 30, 34, "semisimpleefaseries", "X80ED4F8380DC477E" ], [ "\033[2XDerivedSeriesOfGroup\033[102X", "7.1-4", [ 7, 1, 4 ], 37, 34, "derivedseriesofgroup", "X7A879948834BD889" ], [ "\033[2XRefinedDerivedSeries\033[102X", "7.1-5", [ 7, 1, 5 ], 43, 35, "refinedderivedseries", "X866D4C5C79F26611" ], [ "\033[2XRefinedDerivedSeriesDown\033[102X", "7.1-6", [ 7, 1, 6 ], 50, 35, "refinedderivedseriesdown", "X86F7DE927DE3B5CD" ], [ "\033[2XLowerCentralSeriesOfGroup\033[102X", "7.1-7", [ 7, 1, 7 ], 57, 35, "lowercentralseriesofgroup", "X879D55A67DB42676" ], [ "\033[2XUpperCentralSeriesOfGroup\033[102X", "7.1-8", [ 7, 1, 8 ], 64, 35, "uppercentralseriesofgroup", "X8428592E8773CD7B" ], [ "\033[2XTorsionByPolyEFSeries\033[102X", "7.1-9", [ 7, 1, 9 ], 71, 35, "torsionbypolyefseries", "X83CA5DE785AE3F2C" ], [ "\033[2XPcpsBySeries\033[102X", "7.1-10", [ 7, 1, 10 ], 100, 36, "pcpsbyseries", "X7E39431286969377" ], [ "\033[2XPcpsOfEfaSeries\033[102X", "7.1-11", [ 7, 1, 11 ], 108, 36, "pcpsofefaseries", "X79789A1C82139854" ], [ "\033[2XPcpOrbitStabilizer\033[102X", "7.2-1", [ 7, 2, 1 ], 160, 37, "pcporbitstabilizer", "X83E17DB483B33AB5" ], [ "\033[2XPcpOrbitsStabilizers\033[102X", "7.2-1", [ 7, 2, 1 ], 160, 37, "pcporbitsstabilizers", "X83E17DB483B33AB5" ], [ "\033[2XStabilizerIntegralAction\033[102X", "7.2-2", [ 7, 2, 2 ], 193, 37, "stabilizerintegralaction", "X80694BA480F69A0E" ], [ "\033[2XOrbitIntegralAction\033[102X", "7.2-2", [ 7, 2, 2 ], 193, 37, "orbitintegralaction", "X80694BA480F69A0E" ], [ "\033[2XNormalizerIntegralAction\033[102X", "7.2-3", [ 7, 2, 3 ], 204, 38, "normalizerintegralaction", "X875BE4077B32A411" ], [ "\033[2XConjugacyIntegralAction\033[102X", "7.2-3", [ 7, 2, 3 ], 204, 38, "conjugacyintegralaction", "X875BE4077B32A411" ], [ "\033[2XCentralizer\033[102X", "7.3-1", [ 7, 3, 1 ], 264, 39, "centralizer", "X7A2BF4527E08803C" ], [ "\033[2XIsConjugate\033[102X", "7.3-1", [ 7, 3, 1 ], 264, 39, "isconjugate", "X7A2BF4527E08803C" ], [ "\033[2XCentralizer\033[102X", "7.3-2", [ 7, 3, 2 ], 278, 39, "centralizer", "X7A2BF4527E08803C" ], [ "\033[2XNormalizer\033[102X", "7.3-2", [ 7, 3, 2 ], 278, 39, "normalizer", "X7A2BF4527E08803C" ], [ "\033[2XIsConjugate\033[102X", "7.3-2", [ 7, 3, 2 ], 278, 39, "isconjugate", "X7A2BF4527E08803C" ], [ "\033[2XIntersection\033[102X", "7.3-3", [ 7, 3, 3 ], 293, 39, "intersection", "X851069107CACF98E" ], [ "\033[2XTorsionSubgroup\033[102X", "7.4-1", [ 7, 4, 1 ], 309, 39, "torsionsubgroup", "X8036FA507A170DC4" ], [ "\033[2XNormalTorsionSubgroup\033[102X", "7.4-2", [ 7, 4, 2 ], 318, 40, "normaltorsionsubgroup", "X8082CD337972DC63" ], [ "\033[2XIsTorsionFree\033[102X", "7.4-3", [ 7, 4, 3 ], 325, 40, "istorsionfree", "X86D92DA17DCE22DD" ], [ "\033[2XFiniteSubgroupClasses\033[102X", "7.4-4", [ 7, 4, 4 ], 331, 40, "finitesubgroupclasses", "X819058217B4F3DC0" ], [ "\033[2XFiniteSubgroupClassesBySeries\033[102X", "7.4-5", [ 7, 4, 5 ], 341, 40, "finitesubgroupclassesbyseries", "X7E7C32EA81A297B6" ], [ "\033[2XMaximalSubgroupClassesByIndex\033[102X", "7.5-1", [ 7, 5, 1 ], 382, 41, "maximalsubgroupclassesbyindex", "X87D62D497A8715FB" ], [ "\033[2XLowIndexSubgroupClasses\033[102X", "7.5-2", [ 7, 5, 2 ], 390, 41, "lowindexsubgroupclasses", "X7800133F81BC7674" ], [ "\033[2XLowIndexNormalSubgroups\033[102X", "7.5-3", [ 7, 5, 3 ], 398, 41, "lowindexnormalsubgroups", "X7F7067C77F2DC32C" ], [ "\033[2XNilpotentByAbelianNormalSubgroup\033[102X", "7.5-4", [ 7, 5, 4 ], 404, 41, "nilpotentbyabeliannormalsubgroup", "X85A5BC447D83175F" ], [ "\033[2XFittingSubgroup\033[102X", "7.6-1", [ 7, 6, 1 ], 443, 42, "fittingsubgroup", "X780552B57C30DD8F" ], [ "\033[2XIsNilpotentByFinite\033[102X", "7.6-2", [ 7, 6, 2 ], 450, 42, "isnilpotentbyfinite", "X86BD63DC844731DF" ], [ "\033[2XCentre\033[102X", "7.6-3", [ 7, 6, 3 ], 456, 42, "centre", "X847ABE6F781C7FE8" ], [ "\033[2XFCCentre\033[102X", "7.6-4", [ 7, 6, 4 ], 462, 42, "fccentre", "X861C36368435EB09" ], [ "\033[2XPolyZNormalSubgroup\033[102X", "7.6-5", [ 7, 6, 5 ], 469, 43, "polyznormalsubgroup", "X7E75E2BC806746AC" ], [ "\033[2XNilpotentByAbelianByFiniteSeries\033[102X", "7.6-6", [ 7, 6, 6 ], 476, 43, "nilpotentbyabelianbyfiniteseries", "X86800BF783E30D4A" ], [ "\033[2XMinimalGeneratingSet\033[102X", "7.7-1", [ 7, 7, 1 ], 493, 43, "minimalgeneratingset", "X81D15723804771E2" ], [ "\033[2XRandomCentralizerPcpGroup\033[102X", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomcentralizerpcpgroup", "X7E5FE3E879D4E6BF" ], [ "\033[2XRandomCentralizerPcpGroup\033[102X", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomcentralizerpcpgroup", "X7E5FE3E879D4E6BF" ], [ "\033[2XRandomNormalizerPcpGroup\033[102X", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomnormalizerpcpgroup", "X7E5FE3E879D4E6BF" ], [ "\033[2XSchurExtension\033[102X", "7.9-1", [ 7, 9, 1 ], 572, 44, "schurextension", "X79EF28D9845878C9" ], [ "\033[2XSchurExtensionEpimorphism\033[102X", "7.9-2", [ 7, 9, 2 ], 596, 45, "schurextensionepimorphism", "X84B60EC978A9A05E" ], [ "\033[2XSchurCover\033[102X", "7.9-3", [ 7, 9, 3 ], 637, 46, "schurcover", "X7DD1E37987612042" ], [ "\033[2XAbelianInvariantsMultiplier\033[102X", "7.9-4", [ 7, 9, 4 ], 660, 46, "abelianinvariantsmultiplier", "X792BC39D7CEB1D27" ], [ "\033[2XNonAbelianExteriorSquareEpimorphism\033[102X", "7.9-5", [ 7, 9, 5 ], 678, 46, "nonabelianexteriorsquareepimorphism", "X822ED5978647C93B" ], [ "\033[2XNonAbelianExteriorSquare\033[102X", "7.9-6", [ 7, 9, 6 ], 707, 47, "nonabelianexteriorsquare", "X8739CD4686301A0E" ], [ "\033[2XNonAbelianTensorSquareEpimorphism\033[102X", "7.9-7", [ 7, 9, 7 ], 731, 47, "nonabeliantensorsquareepimorphism", "X86553D7B7DABF38F" ], [ "\033[2XNonAbelianTensorSquare\033[102X", "7.9-8", [ 7, 9, 8 ], 768, 48, "nonabeliantensorsquare", "X7C0DF7C97F78C666" ], [ "\033[2XNonAbelianExteriorSquarePlusEmbedding\033[102X", "7.9-9", [ 7, 9, 9 ], 798, 48, "nonabelianexteriorsquareplusembedding", "X7AE75EC1860FFE7A" ], [ "\033[2XNonAbelianTensorSquarePlusEpimorphism\033[102X", "7.9-10", [ 7, 9, 10 ], 806, 49, "nonabeliantensorsquareplusepimorphism", "X7D96C84E87925B0F" ], [ "\033[2XNonAbelianTensorSquarePlus\033[102X", "7.9-11", [ 7, 9, 11 ], 815, 49, "nonabeliantensorsquareplus", "X8746533787C4E8BC" ], [ "\033[2XWhiteheadQuadraticFunctor\033[102X", "7.9-12", [ 7, 9, 12 ], 821, 49, "whiteheadquadraticfunctor", "X78F9184078B2761A" ], [ "\033[2XSchurCovers\033[102X", "7.10-1", [ 7, 10, 1 ], 834, 49, "schurcovers", "X7D90B44E7B96AFF1" ], [ "\033[2XCRRecordByMats\033[102X", "8.1-1", [ 8, 1, 1 ], 19, 50, "crrecordbymats", "X7C97442C7B78806C" ], [ "\033[2XCRRecordBySubgroup\033[102X", "8.1-2", [ 8, 1, 2 ], 27, 50, "crrecordbysubgroup", "X8646DFA1804D2A11" ], [ "\033[2XCRRecordByPcp\033[102X", "8.1-2", [ 8, 1, 2 ], 27, 50, "crrecordbypcp", "X8646DFA1804D2A11" ], [ "\033[2XOneCoboundariesCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "onecoboundariescr", "X85EF170387D39D4A" ], [ "\033[2XOneCocyclesCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "onecocyclescr", "X85EF170387D39D4A" ], [ "\033[2XTwoCoboundariesCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "twocoboundariescr", "X85EF170387D39D4A" ], [ "\033[2XTwoCocyclesCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "twococyclescr", "X85EF170387D39D4A" ], [ "\033[2XOneCohomologyCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "onecohomologycr", "X85EF170387D39D4A" ], [ "\033[2XTwoCohomologyCR\033[102X", "8.2-1", [ 8, 2, 1 ], 104, 51, "twocohomologycr", "X85EF170387D39D4A" ], [ "\033[2XTwoCohomologyModCR\033[102X", "8.2-2", [ 8, 2, 2 ], 149, 52, "twocohomologymodcr", "X79B48D697A8A84C8" ], [ "\033[2XOneCoboundariesEX\033[102X", "8.3-1", [ 8, 3, 1 ], 165, 53, "onecoboundariesex", "X7E87E3EA81C84621" ], [ "\033[2XOneCocyclesEX\033[102X", "8.3-2", [ 8, 3, 2 ], 181, 53, "onecocyclesex", "X8111D2087C16CC0C" ], [ "\033[2XOneCohomologyEX\033[102X", "8.3-3", [ 8, 3, 3 ], 197, 53, "onecohomologyex", "X84718DDE792FB212" ], [ "\033[2X ComplementCR\033[102X", "8.4-1", [ 8, 4, 1 ], 209, 53, "complementcr", "X7DA9162085058006" ], [ "\033[2X ComplementsCR\033[102X", "8.4-2", [ 8, 4, 2 ], 219, 54, "complementscr", "X7F8984D386A813D6" ], [ "\033[2X ComplementClassesCR\033[102X", "8.4-3", [ 8, 4, 3 ], 226, 54, "complementclassescr", "X7FAB3EB0803197FA" ], [ "\033[2X ComplementClassesEfaPcps\033[102X", "8.4-4", [ 8, 4, 4 ], 233, 54, "complementclassesefapcps", "X8759DC59799DD508" ], [ "\033[2X ComplementClasses\033[102X", "8.4-5", [ 8, 4, 5 ], 244, 54, "complementclasses", "X7B0EC76D81A056AB" ], [ "\033[2XExtensionCR\033[102X", "8.4-6", [ 8, 4, 6 ], 254, 54, "extensioncr", "X85F3B55C78CF840B" ], [ "\033[2XExtensionsCR\033[102X", "8.4-7", [ 8, 4, 7 ], 260, 54, "extensionscr", "X81DC85907E0948FD" ], [ "\033[2XExtensionClassesCR\033[102X", "8.4-8", [ 8, 4, 8 ], 267, 55, "extensionclassescr", "X7AE16E3687E14B24" ], [ "\033[2XSplitExtensionPcpGroup\033[102X", "8.4-9", [ 8, 4, 9 ], 274, 55, "splitextensionpcpgroup", "X7986997B78AD3292" ], [ "\033[2XUnitriangularMatrixRepresentation\033[102X", "9.1-1", [ 9, 1, 1 ], 11, 57, "unitriangularmatrixrepresentation", "X7E6F320F865E309C" ], [ "\033[2XIsMatrixRepresentation\033[102X", "9.1-2", [ 9, 1, 2 ], 20, 57, "ismatrixrepresentation", "X7F5E7F5F7DDB2E2C" ], [ "\033[2XIsomorphismUpperUnitriMatGroupPcpGroup\033[102X", "9.2-1", [ 9, 2, 1 ], 39, 57, "isomorphismupperunitrimatgrouppcpgroup", "X8434972E7DDB68C1" ], [ "\033[2XSiftUpperUnitriMatGroup\033[102X", "9.2-2", [ 9, 2, 2 ], 49, 58, "siftupperunitrimatgroup", "X843C9D427FFA2487" ], [ "\033[2XRanksLevels\033[102X", "9.2-3", [ 9, 2, 3 ], 62, 58, "rankslevels", "X7CF8B8F981931846" ], [ "\033[2XMakeNewLevel\033[102X", "9.2-4", [ 9, 2, 4 ], 69, 58, "makenewlevel", "X81F3760186734EA7" ], [ "\033[2XSiftUpperUnitriMat\033[102X", "9.2-5", [ 9, 2, 5 ], 76, 58, "siftupperunitrimat", "X851A216C85B74574" ], [ "\033[2XDecomposeUpperUnitriMat\033[102X", "9.2-6", [ 9, 2, 6 ], 101, 59, "decomposeupperunitrimat", "X86D711217C639C2C" ], [ "\033[10XSchurCovering\033[110X", "a.", [ "A", 0, 0 ], 1, 60, "schurcovering", "X874ECE907CAF380D" ], [ "\033[10XSchurMultPcpGroup\033[110X", "a.", [ "A", 0, 0 ], 1, 60, "schurmultpcpgroup", "X874ECE907CAF380D" ] ] ); polycyclic-2.16/doc/chapBib_mj.html0000644000076600000240000003144713706672374016324 0ustar mhornstaff GAP (polycyclic) - References
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

References

[BCRS91] Baumslag, G., Cannonito, F. B., Robinson, D. J. S. and Segal, D., The algorithmic theory of polycyclic-by-finite groups, J. Algebra, 142 (1991), 118--149.

[BK00] Beuerle, J. R. and Kappe, L.-C., Infinite metacyclic groups and their non-abelian tensor squares, Proc. Edinburgh Math. Soc. (2), 43 (3) (2000), 651--662.

[dGN02] de Graaf, W. A. and Nickel, W., Constructing faithful representations of finitely-generated torsion-free nilpotent groups, J. Symbolic Comput., 33 (1) (2002), 31--41.

[Eic00] Eick, B., Computing with infinite polycyclic groups, in Groups and Computation III, (DIMACS, 1999), Amer. Math. Soc. DIMACS Series (2000).

[Eic01a] Eick, B., Computations with polycyclic groups (2001), Habilitationsschrift, Kassel.

[Eic01b] Eick, B., On the Fitting subgroup of a polycyclic-by-finite group and its applications, J. Algebra, 242 (2001), 176--187.

[Eic02] Eick, B., Orbit-stabilizer problems and computing normalizers for polycyclic groups, J. Symbolic Comput., 34 (2002), 1--19.

[EN08] Eick, B. and Nickel, W., Computing the Schur multiplicator and the non-abelian tensor square of a polycyclic group, J. Algebra, 320 (2) (2008), 927–-944.

[EO02] Eick, B. and Ostheimer, G., On the orbit stabilizer problem for integral matrix actions of polycyclic groups, Accepted by Math. Comp (2002).

[Hir38a] Hirsch, K. A., On Infinite Soluble Groups (I), Proc. London Math. Soc., 44 (2) (1938), 53-60.

[Hir38b] Hirsch, K. A., On Infinite Soluble Groups (II), Proc. London Math. Soc., 44 (2) (1938), 336-414.

[Hir46] Hirsch, K. A., On Infinite Soluble Groups (III), J. London Math. Soc., 49 (2) (1946), 184-94.

[Hir52] Hirsch, K. A., On Infinite Soluble Groups (IV), J. London Math. Soc., 27 (1952), 81-85.

[Hir54] Hirsch, K. A., On Infinite Soluble Groups (V), J. London Math. Soc., 29 (1954), 250-251.

[LGS90] Leedham-Green, C. R. and Soicher, L. H., Collection from the left and other strategies, J. Symbolic Comput., 9 (5-6) (1990), 665--675.

[LGS98] Leedham-Green, C. R. and Soicher, L. H., Symbolic collection using Deep Thought, LMS J. Comput. Math., 1 (1998), 9--24 (electronic).

[Lo98a] Lo, E. H., Enumerating finite index subgroups of polycyclic groups (1998), Unpublished report.

[Lo98b] Lo, E. H., Finding intersection and normalizer in finitely generated nilpotent groups, J. Symbolic Comput., 25 (1998), 45--59.

[Mer97] Merkwitz, W. W., Symbolische Multiplikation in nilpotenten Gruppen mit Deep Thought, Diplomarbeit, RWTH Aachen (1997).

[Rob82] Robinson, D. J., A Course in the Theory of Groups, Springer-Verlag, Graduate Texts in Math., 80, New York, Heidelberg, Berlin (1982).

[Seg83] Segal, D., Polycyclic Groups, Cambridge University Press, Cambridge (1983).

[Seg90] Segal, D., Decidable properties of polycyclic groups, Proc. London Math. Soc. (3), 61 (1990), 497-528.

[Sim94] Sims, C. C., Computation with finitely presented groups, Cambridge University Press, Encyclopedia of Mathematics and its Applications, 48, Cambridge (1994).

[VL90] Vaughan-Lee, M. R., Collection from the left, J. Symbolic Comput., 9 (5-6) (1990), 725--733.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/polycyclicbib.xml.bib0000644000076600000240000002217513706672367017524 0ustar mhornstaff @book{ Rob82, author = {Robinson, D. J.}, title = {A Course in the Theory of Groups}, publisher = {Springer-Verlag}, series = {Graduate Texts in Math.}, volume = {80}, address = {New York, Heidelberg, Berlin}, year = {1982}, printedkey = {Rob82} } @book{ Seg83, author = {Segal, D.}, title = {Polycyclic Groups}, publisher = {Cambridge University Press}, address = {Cambridge}, year = {1983}, printedkey = {Seg83} } @article{ Seg90, author = {Segal, D.}, title = {Decidable properties of polycyclic groups}, journal = {Proc. London Math. Soc. (3)}, volume = {61}, year = {1990}, pages = {497-528}, mrclass = {20F10 (03D40 20F16)}, mrnumber = {MR1069513}, printedkey = {Seg90} } @article{ Hir38a, author = {Hirsch, K. A.}, title = {On Infinite Soluble Groups {(I)}}, journal = {Proc. London Math. Soc.}, volume = {44}, number = {2}, year = {1938}, pages = {53-60}, printedkey = {Hir38} } @article{ Hir38b, author = {Hirsch, K. A.}, title = {On Infinite Soluble Groups {(II)}}, journal = {Proc. London Math. Soc.}, volume = {44}, number = {2}, year = {1938}, pages = {336-414}, printedkey = {Hir38} } @article{ Hir46, author = {Hirsch, K. A.}, title = {On Infinite Soluble Groups {(III)}}, journal = {J. London Math. Soc.}, volume = {49}, number = {2}, year = {1946}, pages = {184-94}, printedkey = {Hir46} } @article{ Hir52, author = {Hirsch, K. A.}, title = {On Infinite Soluble Groups {(IV)}}, journal = {J. London Math. Soc.}, volume = {27}, year = {1952}, pages = {81-85}, printedkey = {Hir52} } @article{ Hir54, author = {Hirsch, K. A.}, title = {On Infinite Soluble Groups {(V)}}, journal = {J. London Math. Soc.}, volume = {29}, year = {1954}, pages = {250-251}, printedkey = {Hir54} } @article{ BCRS91, author = {Baumslag, G. and Cannonito, F. B. and Robinson, D. J. S. and Segal, D.}, title = {The algorithmic theory of polycyclic-by-finite groups}, journal = {J. Algebra}, volume = {142}, year = {1991}, pages = {118--149}, printedkey = {BCRS91} } @book{ Sims94, author = {Sims, C. C.}, title = {Computation with finitely presented groups}, publisher = {Cambridge University Press}, series = {Encyclopedia of Mathematics and its Applications}, volume = {48}, address = {Cambridge}, year = {1994}, isbn = {0-521-43213-8}, mrclass = {20F05 (20-02 68Q40 68Q42)}, mrnumber = {95f:20053}, mrreviewer = {Friedrich Otto}, printedkey = {Sim94} } @article{ LGS90, author = {Leedham-Green, C. R. and Soicher, L. H.}, title = {Collection from the left and other strategies}, journal = {J. Symbolic Comput.}, volume = {9}, number = {5-6}, year = {1990}, pages = {665--675}, issn = {0747-7171}, mrclass = {20D10 (68Q25)}, mrnumber = {92b:20021}, mrreviewer = {M. Greendlinger}, printedkey = {LS90} } @article{ MVL90, author = {Vaughan-Lee, M. R.}, title = {Collection from the left}, journal = {J. Symbolic Comput.}, volume = {9}, number = {5-6}, year = {1990}, pages = {725--733}, issn = {0747-7171}, mrclass = {20F12 (20-04 20D15 20F18)}, mrnumber = {92c:20065}, mrreviewer = {M. Greendlinger}, printedkey = {Vau90} } @article{ B-K00, author = {Beuerle, J. R. and Kappe, L.-C.}, title = {Infinite metacyclic groups and their non-abelian tensor squares}, journal = {Proc. Edinburgh Math. Soc. (2)}, volume = {43}, number = {3}, year = {2000}, pages = {651--662}, issn = {0013-0915}, mrclass = {20F05}, mrnumber = {2003d:20037}, mrreviewer = {Graham J. Ellis}, printedkey = {BK00} } @mastersthesis{ WWM97, author = {Merkwitz, W. W.}, title = {{Symbolische Multiplikation in nilpotenten Gruppen mit Deep Thought}}, school = {RWTH Aachen}, year = {1997}, printedkey = {Mer97}, type = {Diplomarbeit} } @article{ LGS98, author = {Leedham-Green, C. R. and Soicher, L. H.}, title = {Symbolic collection using {D}eep {T}hought}, journal = {LMS J. Comput. Math.}, volume = {1}, year = {1998}, pages = {9--24 (electronic)}, issn = {1461-1570}, mrclass = {20-04 (20F18)}, mrnumber = {99f:20002}, mrreviewer = {Martyn R. Dixon}, printedkey = {LS98} } @inproceedings{ Eic00, author = {Eick, B.}, booktitle = {{Groups and Computation {III}}}, title = {Computing with infinite polycyclic groups}, organization = {(DIMACS, 1999)}, series = {Amer. Math. Soc. DIMACS Series}, year = {2000}, printedkey = {Eic00} } @article{ EOs01, author = {Eick, B. and Ostheimer, G.}, title = {On the orbit stabilizer problem for integral matrix actions of polycyclic groups}, journal = {Accepted by Math. Comp}, year = {2002}, printedkey = {EO02} } @article{ Eic01, author = {Eick, B.}, title = {On the {Fitting} subgroup of a polycyclic-by-finite group and its applications}, journal = {J. Algebra}, volume = {242}, year = {2001}, pages = {176--187}, printedkey = {Eic01} } @misc{ Eic01b, author = {Eick, B.}, title = {Computations with polycyclic groups}, year = {2001}, howpublished = {Habilitationsschrift, Kassel}, printedkey = {Eic01} } @article{ Eic02, author = {Eick, B.}, title = {Orbit-stabilizer problems and computing normalizers for polycyclic groups}, journal = {J. Symbolic Comput.}, volume = {34}, year = {2002}, pages = {1--19}, printedkey = {Eic02} } @misc{ Lo99, author = {Lo, E. H.}, title = {Enumerating finite index subgroups of polycyclic groups}, year = {1998}, howpublished = {Unpublished report}, printedkey = {Lo98} } @article{ LOs99, author = {Lo, E. H. and Ostheimer, G.}, title = {A practical algorithm for finding matrix representations for polycyclic groups}, journal = {J. Symbolic Comput.}, volume = {28}, year = {1999}, pages = {339--360}, printedkey = {LO99} } @article{ Lo98, author = {Lo, E. H.}, title = {Finding intersection and normalizer in finitely generated nilpotent groups}, journal = {J. Symbolic Comput.}, volume = {25}, year = {1998}, pages = {45--59}, printedkey = {Lo98} } @article{ Ost99, author = {Ostheimer, G.}, title = {Practical algorithms for polycyclic matrix groups}, journal = {J. Symbolic Comput.}, volume = {28}, year = {1999}, pages = {361--379}, printedkey = {Ost99} } @article{ dGN02, author = {de Graaf, W. A. and Nickel, W.}, title = {Constructing faithful representations of finitely-generated torsion-free nilpotent groups}, journal = {J. Symbolic Comput.}, volume = {33}, number = {1}, year = {2002}, pages = {31--41}, issn = {0747-7171}, mrclass = {20C15 (20F18)}, mrnumber = {MR1876310}, printedkey = {GN02} } @article{ EickNickel07, author = {Eick, B. and Nickel, W.}, title = {Computing the Schur multiplicator and the non-abelian tensor square of a polycyclic group}, journal = {J. Algebra}, volume = {320}, number = {2}, year = {2008}, pages = {927{\textendash}-944}, mrclass = {20J05 (20-04 20E22 20F05)}, mrnumber = {MR2422322}, printedkey = {EN08} } polycyclic-2.16/doc/chap8.txt0000644000076600000240000004035113706672367015160 0ustar mhornstaff 8 Cohomology for pcp-groups The GAP 4 package Polycyclic provides methods to compute the first and second cohomology group for a pcp-group U and a finite dimensional ℤ U or FU module A where F is a finite field. The algorithm for determining the first cohomology group is outlined in [Eic00]. As a preparation for the cohomology computation, we introduce the cohomology records. These records provide the technical setup for our cohomology computations. 8.1 Cohomology records Cohomology records provide the necessary technical setup for the cohomology computations for polycyclic groups. 8.1-1 CRRecordByMats CRRecordByMats( U, mats )  function creates an external module. Let U be a pcp group which acts via the list of matrices mats on a vector space of the form ℤ^n or F_p^n. Then this function creates a record which can be used as input for the cohomology computations. 8.1-2 CRRecordBySubgroup CRRecordBySubgroup( U, A )  function CRRecordByPcp( U, pcp )  function creates an internal module. Let U be a pcp group and let A be a normal elementary or free abelian normal subgroup of U or let pcp be a pcp of a normal elementary of free abelian subfactor of U. Then this function creates a record which can be used as input for the cohomology computations. The returned cohomology record C contains the following entries: factor a pcp of the acting group. If the module is external, then this is Pcp(U). If the module is internal, then this is Pcp(U, A) or Pcp(U, GroupOfPcp(pcp)). mats, invs and one the matrix action of factor with acting matrices, their inverses and the identity matrix. dim and char the dimension and characteristic of the matrices. relators and enumrels the right hand sides of the polycyclic relators of factor as generator exponents lists and a description for the corresponding left hand sides. central is true, if the matrices mats are all trivial. This is used locally for efficiency reasons. And additionally, if C defines an internal module, then it contains: group the original group U. normal this is either Pcp(A) or the input pcp. extension information on the extension of A by U/A. 8.2 Cohomology groups Let U be a pcp-group and A a free or elementary abelian pcp-group and a U-module. By Z^i(U, A) be denote the group of i-th cocycles and by B^i(U, A) the i-th coboundaries. The factor Z^i(U,A) / B^i(U,A) is the i-th cohomology group. Since A is elementary or free abelian, the groups Z^i(U, A) and B^i(U, A) are elementary or free abelian groups as well. The Polycyclic package provides methods to compute first and second cohomology group for a polycyclic group U. We write all involved groups additively and we use an explicit description by bases for them. Let C be the cohomology record corresponding to U and A. Let f_1, ..., f_n be the elements in the entry factor of the cohomology record C. Then we use the following embedding of the first cocycle group to describe 1-cocycles and 1-coboundaries: Z^1(U, A) -> A^n : δ ↦ (δ(f_1), ..., δ(f_n)) For the second cohomology group we recall that each element of Z^2(U, A) defines an extension H of A by U. Thus there is a pc-presentation of H extending the pc-presentation of U given by the record C. The extended presentation is defined by tails in A; that is, each relator in the record entry relators is extended by an element of A. The concatenation of these tails yields a vector in A^l where l is the length of the record entry relators of C. We use these tail vectors to describe Z^2(U, A) and B^2(U, A). Note that this description is dependent on the chosen presentation in C. However, the factor Z^2(U, A)/ B^2(U, A) is independent of the chosen presentation. The following functions are available to compute explicitly the first and second cohomology group as described above. 8.2-1 OneCoboundariesCR OneCoboundariesCR( C )  function OneCocyclesCR( C )  function TwoCoboundariesCR( C )  function TwoCocyclesCR( C )  function OneCohomologyCR( C )  function TwoCohomologyCR( C )  function The first four functions return bases of the corresponding group. The last two functions need to describe a factor of additive abelian groups. They return the following descriptions for these factors. gcc the basis of the cocycles of C. gcb the basis of the coboundaries of C. factor a description of the factor of cocycles by coboundaries. Usually, it would be most convenient to use additive mappings here. However, these are not available in case that A is free abelian and thus we use a description of this additive map as record. This record contains gens a base for the image. rels relative orders for the image. imgs the images for the elements in gcc. prei preimages for the elements in gens. denom the kernel of the map; that is, another basis for gcb. There is an additional function which can be used to compute the second cohomology group over an arbitrary finitely generated abelian group. The finitely generated abelian group should be realized as a factor of a free abelian group modulo a lattice. The function is called as 8.2-2 TwoCohomologyModCR TwoCohomologyModCR( C, lat )  function where C is a cohomology record and lat is a basis for a sublattice of a free abelian module. The output format is the same as for TwoCohomologyCR. 8.3 Extended 1-cohomology In some cases more information on the first cohomology group is of interest. In particular, if we have an internal module given and we want to compute the complements using the first cohomology group, then we need additional information. This extended version of first cohomology is obtained by the following functions. 8.3-1 OneCoboundariesEX OneCoboundariesEX( C )  function returns a record consisting of the entries basis a basis for B^1(U, A) ≤ A^n. transf There is a derivation mapping from A to B^1(U,A). This mapping is described here as transformation from A to basis. fixpts the fixpoints of A. This is also the kernel of the derivation mapping. 8.3-2 OneCocyclesEX OneCocyclesEX( C )  function returns a record consisting of the entries basis a basis for Z^1(U, A) ≤ A^n. transl a special solution. This is only of interest in case that C is an internal module and in this case it gives the translation vector in A^n used to obtain complements corresponding to the elements in basis. If C is not an internal module, then this vector is always the zero vector. 8.3-3 OneCohomologyEX OneCohomologyEX( C )  function returns the combined information on the first cohomology group. 8.4 Extensions and Complements The natural applications of first and second cohomology group is the determination of extensions and complements. Let C be a cohomology record. 8.4-1 ComplementCR  ComplementCR( C, c )  function returns the complement corresponding to the 1-cocycle c. In the case that C is an external module, we construct the split extension of U with A first and then determine the complement. In the case that C is an internal module, the vector c must be an element of the affine space corresponding to the complements as described by OneCocyclesEX. 8.4-2 ComplementsCR  ComplementsCR( C )  function returns all complements using the correspondence to Z^1(U,A). Further, this function returns fail, if Z^1(U,A) is infinite. 8.4-3 ComplementClassesCR  ComplementClassesCR( C )  function returns complement classes using the correspondence to H^1(U,A). Further, this function returns fail, if H^1(U,A) is infinite. 8.4-4 ComplementClassesEfaPcps  ComplementClassesEfaPcps( U, N, pcps )  function Let N be a normal subgroup of U. This function returns the complement classes to N in U. The classes are computed by iteration over the U-invariant efa series of N described by pcps. If at some stage in this iteration infinitely many complements are discovered, then the function returns fail. (Even though there might be only finitely many conjugacy classes of complements to N in U.) 8.4-5 ComplementClasses  ComplementClasses( [V, ]U, N )  function Let N and U be normal subgroups of V with N ≤ U ≤ V. This function attempts to compute the V-conjugacy classes of complements to N in U. The algorithm proceeds by iteration over a V-invariant efa series of N. If at some stage in this iteration infinitely many complements are discovered, then the algorithm returns fail. 8.4-6 ExtensionCR ExtensionCR( C, c )  function returns the extension corresponding to the 2-cocycle c. 8.4-7 ExtensionsCR ExtensionsCR( C )  function returns all extensions using the correspondence to Z^2(U,A). Further, this function returns fail, if Z^2(U,A) is infinite. 8.4-8 ExtensionClassesCR ExtensionClassesCR( C )  function returns extension classes using the correspondence to H^2(U,A). Further, this function returns fail, if H^2(U,A) is infinite. 8.4-9 SplitExtensionPcpGroup SplitExtensionPcpGroup( U, mats )  function returns the split extension of U by the U-module described by mats. 8.5 Constructing pcp groups as extensions This section contains an example application of the second cohomology group to the construction of pcp groups as extensions. The following constructs extensions of the group of upper unitriangular matrices with its natural lattice.  Example  # get the group and its matrix action gap> G := UnitriangularPcpGroup(3,0); Pcp-group with orders [ 0, 0, 0 ] gap> mats := G!.mats; [ [ [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],  [ [ 1, 0, 0 ], [ 0, 1, 1 ], [ 0, 0, 1 ] ],  [ [ 1, 0, 1 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]  # set up the cohomology record gap> C := CRRecordByMats(G,mats);;  # compute the second cohomology group gap> cc := TwoCohomologyCR(C);;  # the abelian invariants of H^2(G,M) gap> cc.factor.rels; [ 2, 0, 0 ]  # construct an extension which corresponds to a cocycle that has # infinite image in H^2(G,M) gap> c := cc.factor.prei[2]; [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ] gap> H := ExtensionCR( CR, c); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]  # check that the extension does not split - get the normal subgroup gap> N := H!.module; Pcp-group with orders [ 0, 0, 0 ]  # create the interal module gap> C := CRRecordBySubgroup(H,N);;  # use the complements routine gap> ComplementClassesCR(C); [ ]  polycyclic-2.16/doc/chap0_mj.html0000644000076600000240000011335313706672374015764 0ustar mhornstaff GAP (polycyclic) - Contents
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Polycyclic

Computation with polycyclic groups

2.16

25 July 2020

Bettina Eick
Email: beick@tu-bs.de
Homepage: http://www.iaa.tu-bs.de/beick
Address:
Institut Analysis und Algebra
TU Braunschweig
Universitätsplatz 2
D-38106 Braunschweig
Germany

Werner Nickel
Homepage: http://www.mathematik.tu-darmstadt.de/~nickel/

Max Horn
Email: horn@mathematik.uni-kl.de
Homepage: https://www.quendi.de/math
Address:
Fachbereich Mathematik
TU Kaiserslautern
Gottlieb-Daimler-Straße 48
67663 Kaiserslautern
Germany

Copyright

© 2003-2018 by Bettina Eick, Max Horn and Werner Nickel

The Polycyclic package is free software;you can redistribute it and/or modify it under the terms of theGNU General Public Licenseas published by the Free Software Foundation; either version 2 of the License,or (at your option) any later version.

Acknowledgements

We appreciate very much all past and future comments, suggestions andcontributions to this package and its documentation provided by GAPusers and developers.

Contents

5 Basic methods and functions for pcp-groups
7 Higher level methods for pcp-groups

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap9.txt0000644000076600000240000001254313706672367015163 0ustar mhornstaff 9 Matrix Representations This chapter describes functions which compute with matrix representations for pcp-groups. So far the routines in this package are only able to compute matrix representations for torsion-free nilpotent groups. 9.1 Unitriangular matrix groups 9.1-1 UnitriangularMatrixRepresentation UnitriangularMatrixRepresentation( G )  operation computes a faithful representation of a torsion-free nilpotent group G as unipotent lower triangular matrices over the integers. The pc-presentation for G must not contain any power relations. The algorithm is described in [dGN02]. 9.1-2 IsMatrixRepresentation IsMatrixRepresentation( G, matrices )  function checks if the map defined by mapping the i-th generator of the pcp-group G to the i-th matrix of matrices defines a homomorphism. 9.2 Upper unitriangular matrix groups We call a matrix upper unitriangular if it is an upper triangular matrix with ones on the main diagonal. The weight of an upper unitriangular matrix is the number of diagonals above the main diagonal that contain zeroes only. The subgroup of all upper unitriangular matrices of GL(n,ℤ) is torsion-free nilpotent. The k-th term of its lower central series is the set of all matrices of weight k-1. The ℤ-rank of the k-th term of the lower central series modulo the (k+1)-th term is n-k. 9.2-1 IsomorphismUpperUnitriMatGroupPcpGroup IsomorphismUpperUnitriMatGroupPcpGroup( G )  function takes a group G generated by upper unitriangular matrices over the integers and computes a polycylic presentation for the group. The function returns an isomorphism from the matrix group to the pcp group. Note that a group generated by upper unitriangular matrices is necessarily torsion-free nilpotent. 9.2-2 SiftUpperUnitriMatGroup SiftUpperUnitriMatGroup( G )  function takes a group G generated by upper unitriangular matrices over the integers and returns a recursive data structure L with the following properties: L contains a polycyclic generating sequence for G, using L one can decide if a given upper unitriangular matrix is contained in G, a given element of G can be written as a word in the polycyclic generating sequence. L is a representation of a chain of subgroups of G refining the lower centrals series of G.. It contains for each subgroup in the chain a minimal generating set. 9.2-3 RanksLevels RanksLevels( L )  function takes the data structure returned by SiftUpperUnitriMat and prints the ℤ-rank of each the subgroup in L. 9.2-4 MakeNewLevel MakeNewLevel( m )  function creates one level of the data structure returned by SiftUpperUnitriMat and initialises it with weight m. 9.2-5 SiftUpperUnitriMat SiftUpperUnitriMat( gens, level, M )  function takes the generators gens of an upper unitriangular group, the data structure returned level by SiftUpperUnitriMat and another upper unitriangular matrix M. It sift M through level and adds M at the appropriate place if M is not contained in the subgroup represented by level. The function SiftUpperUnitriMatGroup illustrates the use of SiftUpperUnitriMat.  Example  InstallGlobalFunction( "SiftUpperUnitriMatGroup", function( G )  local firstlevel, g;   firstlevel := MakeNewLevel( 0 );  for g in GeneratorsOfGroup(G) do  SiftUpperUnitriMat( GeneratorsOfGroup(G), firstlevel, g );  od;  return firstlevel; end );  9.2-6 DecomposeUpperUnitriMat DecomposeUpperUnitriMat( level, M )  function takes the data structure level returned by SiftUpperUnitriMatGroup and a upper unitriangular matrix M and decomposes M into a word in the polycyclic generating sequence of level. polycyclic-2.16/doc/chap2.html0000644000076600000240000002025213706672374015273 0ustar mhornstaff GAP (polycyclic) - Chapter 2: Introduction to polycyclic presentations
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

2 Introduction to polycyclic presentations

Let G be a polycyclic group and let G = C_1 ⊳ C_2 ... C_n⊳ C_n+1 = 1 be a polycyclic series, that is, a subnormal series of G with non-trivial cyclic factors. For 1 ≤ i ≤ n we choose g_i ∈ C_i such that C_i = ⟨ g_i, C_i+1 ⟩. Then the sequence (g_1, ..., g_n) is called a polycyclic generating sequence of G. Let I be the set of those i ∈ {1, ..., n} with r_i := [C_i : C_i+1] finite. Each element of G can be written uniquely as g_1^e_1⋯ g_n^e_n with e_i∈ ℤ for 1≤ i≤ n and 0≤ e_i < r_i for i∈ I.

Each polycyclic generating sequence of G gives rise to a power-conjugate (pc-) presentation for G with the conjugate relations

g_j^{g_i} = g_{i+1}^{e(i,j,i+1)} \cdots g_n^{e(i,j,n)} \hbox{ for } 1 \leq i < j \leq n,

g_j^{g_i^{-1}} = g_{i+1}^{f(i,j,i+1)} \cdots g_n^{f(i,j,n)} \hbox{ for } 1 \leq i < j \leq n,

and the power relations

g_i^{r_i} = g_{i+1}^{l(i,i+1)} \cdots g_n^{l(i,n)} \hbox{ for } i \in I.

Vice versa, we say that a group G is defined by a pc-presentation if G is given by a presentation of the form above on generators g_1,...,g_n. These generators are the defining generators of G. Here, I is the set of 1≤ i≤ n such that g_i has a power relation. The positive integer r_i for i∈ I is called the relative order of g_i. If G is given by a pc-presentation, then G is polycyclic. The subgroups C_i = ⟨ g_i, ..., g_n ⟩ form a subnormal series G = C_1 ≥ ... ≥ C_n+1 = 1 with cyclic factors and we have that g_i^r_i∈ C_i+1. However, some of the factors of this series may be smaller than r_i for i∈ I or finite if inot\in I.

If G is defined by a pc-presentation, then each element of G can be described by a word of the form g_1^e_1⋯ g_n^e_n in the defining generators with e_i∈ ℤ for 1≤ i≤ n and 0≤ e_i < r_i for i∈ I. Such a word is said to be in collected form. In general, an element of the group can be represented by more than one collected word. If the pc-presentation has the property that each element of G has precisely one word in collected form, then the presentation is called confluent or consistent. If that is the case, the generators with a power relation correspond precisely to the finite factors in the polycyclic series and r_i is the order of C_i/C_i+1.

The GAP package Polycyclic is designed for computations with polycyclic groups which are given by consistent pc-presentations. In particular, all the functions described below assume that we compute with a group defined by a consistent pc-presentation. See Chapter Collectors for a routine that checks the consistency of a pc-presentation.

A pc-presentation can be interpreted as a rewriting system in the following way. One needs to add a new generator G_i for each generator g_i together with the relations g_iG_i = 1 and G_ig_i = 1. Any occurrence in a relation of an inverse generator g_i^-1 is replaced by G_i. In this way one obtains a monoid presentation for the group G. With respect to a particular ordering on the set of monoid words in the generators g_1,... g_n,G_1,... G_n, the wreath product ordering, this monoid presentation is a rewriting system. If the pc-presentation is consistent, the rewriting system is confluent.

In this package we do not address this aspect of pc-presentations because it is of little relevance for the algorithms implemented here. For the definition of rewriting systems and confluence in this context as well as further details on the connections between pc-presentations and rewriting systems we recommend the book [Sim94].

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap3.html0000644000076600000240000007725413706672374015312 0ustar mhornstaff GAP (polycyclic) - Chapter 3: Collectors
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

3 Collectors

Let G be a group defined by a pc-presentation as described in the Chapter Introduction to polycyclic presentations.

The process for computing the collected form for an arbitrary word in the generators of G is called collection. The basic idea in collection is the following. Given a word in the defining generators, one scans the word for occurrences of adjacent generators (or their inverses) in the wrong order or occurrences of subwords g_i^e_i with i∈ I and e_i not in the range 0... r_i-1. In the first case, the appropriate conjugacy relation is used to move the generator with the smaller index to the left. In the second case, one uses the appropriate power relation to move the exponent of g_i into the required range. These steps are repeated until a collected word is obtained.

There exist a number of different strategies for collecting a given word to collected form. The strategies implemented in this package are collection from the left as described by [LGS90] and [Sim94] and combinatorial collection from the left by [VL90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LGS98].

The first step in defining a pc-presented group is setting up a data structure that knows the pc-presentation and has routines that perform the collection algorithm with words in the generators of the presentation. Such a data structure is called a collector.

To describe the right hand sides of the relations in a pc-presentation we use generator exponent lists; the word g_i_1^e_1g_i_2^e_2... g_i_k^e_k is represented by the generator exponent list [i_1,e_1,i_2,e_2,...,i_k,e_k].

3.1 Constructing a Collector

A collector for a group given by a pc-presentation starts by setting up an empty data structure for the collector. Then the relative orders, the power relations and the conjugate relations are added into the data structure. The construction is finalised by calling a routine that completes the data structure for the collector. The following functions provide the necessary tools for setting up a collector.

3.1-1 FromTheLeftCollector
‣ FromTheLeftCollector( n )( operation )

returns an empty data structure for a collector with n generators. No generator has a relative order, no right hand sides of power and conjugate relations are defined. Two generators for which no right hand side of a conjugate relation is defined commute. Therefore, the collector returned by this function can be used to define a free abelian group of rank n.

gap> ftl := FromTheLeftCollector( 4 );
<<from the left collector with 4 generators>>
gap> PcpGroupByCollector( ftl );
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> IsAbelian(last);
true

If the relative order of a generators has been defined (see SetRelativeOrder (3.1-2)), but the right hand side of the corresponding power relation has not, then the order and the relative order of the generator are the same.

3.1-2 SetRelativeOrder
‣ SetRelativeOrder( coll, i, ro )( operation )
‣ SetRelativeOrderNC( coll, i, ro )( operation )

set the relative order in collector coll for generator i to ro. The parameter coll is a collector as returned by the function FromTheLeftCollector (3.1-1), i is a generator number and ro is a non-negative integer. The generator number i is an integer in the range 1,...,n where n is the number of generators of the collector.

If ro is 0, then the generator with number i has infinite order and no power relation can be specified. As a side effect in this case, a previously defined power relation is deleted.

If ro is the relative order of a generator with number i and no power relation is set for that generator, then ro is the order of that generator.

The NC version of the function bypasses checks on the range of i.

gap> ftl := FromTheLeftCollector( 4 );
<<from the left collector with 4 generators>>
gap> for i in [1..4] do SetRelativeOrder( ftl, i, 3 ); od;
gap> G := PcpGroupByCollector( ftl );
Pcp-group with orders [ 3, 3, 3, 3 ]
gap> IsElementaryAbelian( G );
true

3.1-3 SetPower
‣ SetPower( coll, i, rhs )( operation )
‣ SetPowerNC( coll, i, rhs )( operation )

set the right hand side of the power relation for generator i in collector coll to (a copy of) rhs. An attempt to set the right hand side for a generator without a relative order results in an error.

Right hand sides are by default assumed to be trivial.

The parameter coll is a collector, i is a generator number and rhs is a generators exponent list or an element from a free group.

The no-check (NC) version of the function bypasses checks on the range of i and stores rhs (instead of a copy) in the collector.

3.1-4 SetConjugate
‣ SetConjugate( coll, j, i, rhs )( operation )
‣ SetConjugateNC( coll, j, i, rhs )( operation )

set the right hand side of the conjugate relation for the generators j and i with j larger than i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group. Conjugate relations are by default assumed to be trivial.

The generator number i can be negative in order to define conjugation by the inverse of a generator.

The no-check (NC) version of the function bypasses checks on the range of i and j and stores rhs (instead of a copy) in the collector.

3.1-5 SetCommutator
‣ SetCommutator( coll, j, i, rhs )( operation )

set the right hand side of the conjugate relation for the generators j and i with j larger than i by specifying the commutator of j and i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group.

The generator number i can be negative in order to define the right hand side of a commutator relation with the second generator being the inverse of a generator.

3.1-6 UpdatePolycyclicCollector
‣ UpdatePolycyclicCollector( coll )( operation )

completes the data structures of a collector. This is usually the last step in setting up a collector. Among the steps performed is the completion of the conjugate relations. For each non-trivial conjugate relation of a generator, the corresponding conjugate relation of the inverse generator is calculated.

Note that UpdatePolycyclicCollector is automatically called by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)).

3.1-7 IsConfluent
‣ IsConfluent( coll )( property )

tests if the collector coll is confluent. The function returns true or false accordingly.

Compare Chapter 2 for a definition of confluence.

Note that confluence is automatically checked by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)).

The following example defines a collector for a semidirect product of the cyclic group of order 3 with the free abelian group of rank 2. The action of the cyclic group on the free abelian group is given by the matrix

\pmatrix{ 0 & 1 \cr -1 & -1}.

This leads to the following polycyclic presentation:

\langle g_1,g_2,g_3 | g_1^3, g_2^{g_1}=g_3, g_3^{g_1}=g_2^{-1}g_3^{-1}, g_3^{g_2}=g_3\rangle.

gap> ftl := FromTheLeftCollector( 3 );
<<from the left collector with 3 generators>>
gap> SetRelativeOrder( ftl, 1, 3 );
gap> SetConjugate( ftl, 2, 1, [3,1] );
gap> SetConjugate( ftl, 3, 1, [2,-1,3,-1] );
gap> UpdatePolycyclicCollector( ftl );
gap> IsConfluent( ftl );
true

The action of the inverse of g_1 on ⟨ g_2,g_2⟩ is given by the matrix

\pmatrix{ -1 & -1 \cr 1 & 0}.

The corresponding conjugate relations are automatically computed by UpdatePolycyclicCollector. It is also possible to specify the conjugation by inverse generators. Note that you need to run UpdatePolycyclicCollector after one of the set functions has been used.

gap> SetConjugate( ftl, 2, -1, [2,-1,3,-1] );
gap> SetConjugate( ftl, 3, -1, [2,1] );
gap> IsConfluent( ftl );
Error, Collector is out of date called from
CollectWordOrFail( coll, ev1, [ j, 1, i, 1 ] ); called from
<function>( <arguments> ) called from read-eval-loop
Entering break read-eval-print loop ...
you can 'quit;' to quit to outer loop, or
you can 'return;' to continue
brk>
gap> UpdatePolycyclicCollector( ftl );
gap> IsConfluent( ftl );
true

3.2 Accessing Parts of a Collector

3.2-1 RelativeOrders
‣ RelativeOrders( coll )( attribute )

returns (a copy of) the list of relative order stored in the collector coll.

3.2-2 GetPower
‣ GetPower( coll, i )( operation )
‣ GetPowerNC( coll, i )( operation )

returns a copy of the generator exponent list stored for the right hand side of the power relation of the generator i in the collector coll.

The no-check (NC) version of the function bypasses checks on the range of i and does not create a copy before returning the right hand side of the power relation.

3.2-3 GetConjugate
‣ GetConjugate( coll, j, i )( operation )
‣ GetConjugateNC( coll, j, i )( operation )

returns a copy of the right hand side of the conjugate relation stored for the generators j and i in the collector coll as generator exponent list. The generator j must be larger than i.

The no-check (NC) version of the function bypasses checks on the range of i and j and does not create a copy before returning the right hand side of the power relation.

3.2-4 NumberOfGenerators
‣ NumberOfGenerators( coll )( operation )

returns the number of generators of the collector coll.

3.2-5 ObjByExponents
‣ ObjByExponents( coll, expvec )( operation )

returns a generator exponent list for the exponent vector expvec. This is the inverse operation to ExponentsByObj. See ExponentsByObj (3.2-6) for an example.

3.2-6 ExponentsByObj
‣ ExponentsByObj( coll, genexp )( operation )

returns an exponent vector for the generator exponent list genexp. This is the inverse operation to ObjByExponents. The function assumes that the generators in genexp are given in the right order and that the exponents are in the right range.

gap> G := UnitriangularPcpGroup( 4, 0 );
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]
gap> coll := Collector ( G );
<<from the left collector with 6 generators>>
gap> ObjByExponents( coll, [6,-5,4,3,-2,1] );
[ 1, 6, 2, -5, 3, 4, 4, 3, 5, -2, 6, 1 ]
gap> ExponentsByObj( coll, last );
[ 6, -5, 4, 3, -2, 1 ]

3.3 Special Features

In this section we descibe collectors for nilpotent groups which make use of the special structure of the given pc-presentation.

3.3-1 IsWeightedCollector
‣ IsWeightedCollector( coll )( property )

checks if there is a function w from the generators of the collector coll into the positive integers such that w(g) ≥ w(x)+w(y) for all generators x, y and all generators g in (the normal of) [x,y]. If such a function does not exist, false is returned. If such a function exists, it is computed and stored in the collector. In addition, the default collection strategy for this collector is set to combinatorial collection.

3.3-2 AddHallPolynomials
‣ AddHallPolynomials( coll )( function )

is applicable to a collector which passes IsWeightedCollector and computes the Hall multiplication polynomials for the presentation stored in coll. The default strategy for this collector is set to evaluating those polynomial when multiplying two elements.

3.3-3 String
‣ String( coll )( attribute )

converts a collector coll into a string.

3.3-4 FTLCollectorPrintTo
‣ FTLCollectorPrintTo( file, name, coll )( function )

stores a collector coll in the file file such that the file can be read back using the function 'Read' into GAP and would then be stored in the variable name.

3.3-5 FTLCollectorAppendTo
‣ FTLCollectorAppendTo( file, name, coll )( function )

appends a collector coll in the file file such that the file can be read back into GAP and would then be stored in the variable name.

3.3-6 UseLibraryCollector
‣ UseLibraryCollector( global variable )

this property can be set to true for a collector to force a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines.

3.3-7 USE_LIBRARY_COLLECTOR
‣ USE_LIBRARY_COLLECTOR( global variable )

this global variable can be set to true to force all collectors to use a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines.

3.3-8 DEBUG_COMBINATORIAL_COLLECTOR
‣ DEBUG_COMBINATORIAL_COLLECTOR( global variable )

this global variable can be set to true to force the comparison of results from the combinatorial collector with the result of an identical collection performed by a simple from-the-left collector. Its main purpose is to help debug the collection routines.

3.3-9 USE_COMBINATORIAL_COLLECTOR
‣ USE_COMBINATORIAL_COLLECTOR( global variable )

this global variable can be set to false in order to prevent the combinatorial collector to be used.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap1_mj.html0000644000076600000240000001342713706672374015766 0ustar mhornstaff GAP (polycyclic) - Chapter 1: Preface
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

1 Preface

A group \(G\) is called polycyclic if there exists a subnormal series in \(G\) with cyclic factors. Every polycyclic group is soluble and every supersoluble group is polycyclic. The class of polycyclic groups is closed with respect to forming subgroups, factor groups and extensions. Polycyclic groups can also be characterised as those soluble groups in which each subgroup is finitely generated.

K. A. Hirsch has initiated the investigation of polycyclic groups in 1938, see [Hir38a], [Hir38b], [Hir46], [Hir52], [Hir54], and their central position in infinite group theory has been recognised since.

A well-known result of Hirsch asserts that each polycyclic group is finitely presented. In fact, a polycyclic group has a presentation which exhibits its polycyclic structure: a pc-presentation as defined in the Chapter Introduction to polycyclic presentations. Pc-presentations allow efficient computations with the groups they define. In particular, the word problem is efficiently solvable in a group given by a pc-presentation. Further, subgroups and factor groups of groups given by a pc-presentation can be handled effectively.

The GAP 4 package Polycyclic is designed for computations with polycyclic groups which are given by a pc-presentation. The package contains methods to solve the word problem in such groups and to handle subgroups and factor groups of polycyclic groups. Based on these basic algorithms we present a collection of methods to construct polycyclic groups and to investigate their structure.

In [BCRS91] and [Seg90] the theory of problems which are decidable in polycyclic-by-finite groups has been started. As a result of these investigation we know that a large number of group theoretic problems are decidable by algorithms in polycyclic groups. However, practical algorithms which are suitable for computer implementations have not been obtained by this study. We have developed a new set of practical methods for groups given by pc-presentations, see for example [Eic00], and this package is a collection of implementations for these and other methods.

We refer to [Rob82], page 147ff, and [Seg83] for background on polycyclic groups. Further, in [Sim94] a variation of the basic methods for groups with pc-presentation is introduced. Finally, we note that the main GAP library contains many practical algorithms to compute with finite polycyclic groups. This is described in the Section on polycyclic groups in the reference manual.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/title.xml0000644000076600000240000000316513706672367015261 0ustar mhornstaff Polycyclic Computation with polycyclic groups 2.16 Bettina Eick
Institut Analysis und Algebra
TU Braunschweig
Universitätsplatz 2
D-38106 Braunschweig
Germany
beick@tu-bs.de http://www.iaa.tu-bs.de/beick
Werner Nickel
http://www.mathematik.tu-darmstadt.de/~nickel/
Max Horn
Fachbereich Mathematik
TU Kaiserslautern
Gottlieb-Daimler-Straße 48
67663 Kaiserslautern
Germany
horn@mathematik.uni-kl.de https://www.quendi.de/math
25 July 2020 License©right; 2003-2018 by Bettina Eick, Max Horn and Werner Nickel

The &Polycyclic; package is free software;you can redistribute it and/or modify it under the terms of thehttp://www.fsf.org/licenses/gpl.htmlas published by the Free Software Foundation; either version 2 of the License,or (at your option) any later version. We appreciate very much all past and future comments, suggestions andcontributions to this package and its documentation provided by &GAP;users and developers. polycyclic-2.16/doc/chapA_mj.html0000644000076600000240000001031513706672374015777 0ustar mhornstaff GAP (polycyclic) - Appendix A: Obsolete Functions and Name Changes

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

A Obsolete Functions and Name Changes

Over time, the interface of Polycyclic has changed. This was done to get the names of Polycyclic functions to agree with the general naming conventions used throughout GAP. Also, some Polycyclic operations duplicated functionality that was already available in the core of GAP under a different name. In these cases, whenever possible we now install the Polycyclic code as methods for the existing GAP operations instead of introducing new operations.

For backward compatibility, we still provide the old, obsolete names as aliases. However, please consider switching to the new names as soon as possible. The old names may be completely removed at some point in the future.

The following function names were changed.

OLD NOW USE
SchurCovering SchurCover (7.9-3)
SchurMultPcpGroup AbelianInvariantsMultiplier (7.9-4)

 


Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/manual.js0000644000076600000240000001003413706672374015220 0ustar mhornstaff/* manual.js Frank Lübeck */ /* This file contains a few javascript functions which allow to switch between display styles for GAPDoc HTML manuals. If javascript is switched off in a browser or this file in not available in a manual directory, this is no problem. Users just cannot switch between several styles and don't see the corresponding button. A style with name mystyle can be added by providing two files (or only one of them). mystyle.js: Additional javascript code for the style, it is read in the HTML pages after this current file. The additional code may adjust the preprocessing function jscontent() with is called onload of a file. This is done by appending functions to jscontentfuncs (jscontentfuncs.push(newfunc);). Make sure, that your style is still usable without javascript. mystyle.css: CSS configuration, read after manual.css (so it can just reconfigure a few details, or overwrite everything). Then adjust chooser.html such that users can switch on and off mystyle. A user can change the preferred style permanently by using the [Style] link and choosing one. Or one can append '?GAPDocStyle=mystyle' to the URL when loading any file of the manual (so the style can be configured in the GAP user preferences). */ /* generic helper function */ function deleteCookie(nam) { document.cookie = nam+"=;Path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT"; } /* read a value from a "nam1=val1;nam2=val2;..." string (e.g., the search part of an URL or a cookie */ function valueString(str,nam) { var cs = str.split(";"); for (var i=0; i < cs.length; i++) { var pos = cs[i].search(nam+"="); if (pos > -1) { pos = cs[i].indexOf("="); return cs[i].slice(pos+1); } } return 0; } /* when a non-default style is chosen via URL or a cookie, then the cookie is reset and the styles .js and .css files are read */ function overwriteStyle() { /* style in URL? */ var style = valueString(window.location.search, "GAPDocStyle"); /* otherwise check cookie */ if (style == 0) style = valueString(document.cookie, "GAPDocStyle"); if (style == 0) return; if (style == "default") deleteCookie("GAPDocStyle"); else { /* ok, we set the cookie for path "/" */ var path = "/"; /* or better like this ??? var here = window.location.pathname.split("/"); for (var i=0; i+3 < here.length; i++) path = path+"/"+here[i]; */ document.cookie = "GAPDocStyle="+style+";Path="+path; /* split into names of style files */ var stlist = style.split(","); /* read style's css and js files */ for (var i=0; i < stlist.length; i++) { document.writeln(''); document.writeln(''); } } } /* this adds a "[Style]" link next to the MathJax switcher */ function addStyleLink() { var line = document.getElementById("mathjaxlink"); var el = document.createElement("a"); var oncl = document.createAttribute("href"); var back = window.location.protocol+"//" if (window.location.protocol == "http:") { back = back+window.location.host; if (window.location.port != "") { back = back+":"+window.location.port; } } back = back+window.location.pathname; oncl.nodeValue = "chooser.html?BACK="+back; el.setAttributeNode(oncl); var cont = document.createTextNode(" [Style]"); el.appendChild(cont); line.appendChild(el); } var jscontentfuncs = new Array(); jscontentfuncs.push(addStyleLink); /* the default jscontent() only adds the [Style] link to the page */ function jscontent () { for (var i=0; i < jscontentfuncs.length; i++) jscontentfuncs[i](); } polycyclic-2.16/doc/collect.xml0000644000076600000240000003672213706672341015562 0ustar mhornstaff Collectors Let G be a group defined by a pc-presentation as described in the Chapter .

The process for computing the collected form for an arbitrary word in the generators of G is called collection. The basic idea in collection is the following. Given a word in the defining generators, one scans the word for occurrences of adjacent generators (or their inverses) in the wrong order or occurrences of subwords g_i^{e_i} with i\in I and e_i not in the range 0\ldots r_{i}-1. In the first case, the appropriate conjugacy relation is used to move the generator with the smaller index to the left. In the second case, one uses the appropriate power relation to move the exponent of g_i into the required range. These steps are repeated until a collected word is obtained.

There exist a number of different strategies for collecting a given word to collected form. The strategies implemented in this package are collection from the left as described by and and combinatorial collection from the left by . In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see and .

The first step in defining a pc-presented group is setting up a data structure that knows the pc-presentation and has routines that perform the collection algorithm with words in the generators of the presentation. Such a data structure is called a collector.

To describe the right hand sides of the relations in a pc-presentation we use generator exponent lists; the word g_{i_1}^{e_1}g_{i_2}^{e_2}\ldots g_{i_k}^{e_k} is represented by the generator exponent list [i_1,e_1,i_2,e_2,\ldots,i_k,e_k].

Constructing a Collector A collector for a group given by a pc-presentation starts by setting up an empty data structure for the collector. Then the relative orders, the power relations and the conjugate relations are added into the data structure. The construction is finalised by calling a routine that completes the data structure for the collector. The following functions provide the necessary tools for setting up a collector. returns an empty data structure for a collector with n generators. No generator has a relative order, no right hand sides of power and conjugate relations are defined. Two generators for which no right hand side of a conjugate relation is defined commute. Therefore, the collector returned by this function can be used to define a free abelian group of rank n. ftl := FromTheLeftCollector( 4 ); <> gap> PcpGroupByCollector( ftl ); Pcp-group with orders [ 0, 0, 0, 0 ] gap> IsAbelian(last); true ]]> If the relative order of a generators has been defined (see ), but the right hand side of the corresponding power relation has not, then the order and the relative order of the generator are the same. set the relative order in collector coll for generator i to ro. The parameter coll is a collector as returned by the function , i is a generator number and ro is a non-negative integer. The generator number i is an integer in the range 1,\ldots,n where n is the number of generators of the collector.

If ro is 0, then the generator with number i has infinite order and no power relation can be specified. As a side effect in this case, a previously defined power relation is deleted.

If ro is the relative order of a generator with number i and no power relation is set for that generator, then ro is the order of that generator.

The NC version of the function bypasses checks on the range of i. ftl := FromTheLeftCollector( 4 ); <> gap> for i in [1..4] do SetRelativeOrder( ftl, i, 3 ); od; gap> G := PcpGroupByCollector( ftl ); Pcp-group with orders [ 3, 3, 3, 3 ] gap> IsElementaryAbelian( G ); true ]]> set the right hand side of the power relation for generator i in collector coll to (a copy of) rhs. An attempt to set the right hand side for a generator without a relative order results in an error.

Right hand sides are by default assumed to be trivial.

The parameter coll is a collector, i is a generator number and rhs is a generators exponent list or an element from a free group.

The no-check (NC) version of the function bypasses checks on the range of i and stores rhs (instead of a copy) in the collector. set the right hand side of the conjugate relation for the generators j and i with j larger than i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group. Conjugate relations are by default assumed to be trivial.

The generator number i can be negative in order to define conjugation by the inverse of a generator.

The no-check (NC) version of the function bypasses checks on the range of i and j and stores rhs (instead of a copy) in the collector. set the right hand side of the conjugate relation for the generators j and i with j larger than i by specifying the commutator of j and i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group.

The generator number i can be negative in order to define the right hand side of a commutator relation with the second generator being the inverse of a generator. completes the data structures of a collector. This is usually the last step in setting up a collector. Among the steps performed is the completion of the conjugate relations. For each non-trivial conjugate relation of a generator, the corresponding conjugate relation of the inverse generator is calculated.

Note that UpdatePolycyclicCollector is automatically called by the function PcpGroupByCollector (see ). tests if the collector coll is confluent. The function returns true or false accordingly.

Compare Chapter for a definition of confluence.

Note that confluence is automatically checked by the function PcpGroupByCollector (see ).

The following example defines a collector for a semidirect product of the cyclic group of order 3 with the free abelian group of rank 2. The action of the cyclic group on the free abelian group is given by the matrix This leads to the following polycyclic presentation: \langle g_1,g_2,g_3 | g_1^3, g_2^{g_1}=g_3, g_3^{g_1}=g_2^{-1}g_3^{-1}, g_3^{g_2}=g_3\rangle. ftl := FromTheLeftCollector( 3 ); <> gap> SetRelativeOrder( ftl, 1, 3 ); gap> SetConjugate( ftl, 2, 1, [3,1] ); gap> SetConjugate( ftl, 3, 1, [2,-1,3,-1] ); gap> UpdatePolycyclicCollector( ftl ); gap> IsConfluent( ftl ); true ]]> The action of the inverse of g_1 on \langle g_2,g_2\rangle is given by the matrix The corresponding conjugate relations are automatically computed by UpdatePolycyclicCollector. It is also possible to specify the conjugation by inverse generators. Note that you need to run UpdatePolycyclicCollector after one of the set functions has been used. SetConjugate( ftl, 2, -1, [2,-1,3,-1] ); gap> SetConjugate( ftl, 3, -1, [2,1] ); gap> IsConfluent( ftl ); Error, Collector is out of date called from CollectWordOrFail( coll, ev1, [ j, 1, i, 1 ] ); called from ( ) called from read-eval-loop Entering break read-eval-print loop ... you can 'quit;' to quit to outer loop, or you can 'return;' to continue brk> gap> UpdatePolycyclicCollector( ftl ); gap> IsConfluent( ftl ); true ]]>

Accessing Parts of a Collector returns (a copy of) the list of relative order stored in the collector coll. returns a copy of the generator exponent list stored for the right hand side of the power relation of the generator i in the collector coll.

The no-check (NC) version of the function bypasses checks on the range of i and does not create a copy before returning the right hand side of the power relation. returns a copy of the right hand side of the conjugate relation stored for the generators j and i in the collector coll as generator exponent list. The generator j must be larger than i.

The no-check (NC) version of the function bypasses checks on the range of i and j and does not create a copy before returning the right hand side of the power relation. returns the number of generators of the collector coll. returns a generator exponent list for the exponent vector expvec. This is the inverse operation to ExponentsByObj. See for an example. returns an exponent vector for the generator exponent list genexp. This is the inverse operation to ObjByExponents. The function assumes that the generators in genexp are given in the right order and that the exponents are in the right range. G := UnitriangularPcpGroup( 4, 0 ); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] gap> coll := Collector ( G ); <> gap> ObjByExponents( coll, [6,-5,4,3,-2,1] ); [ 1, 6, 2, -5, 3, 4, 4, 3, 5, -2, 6, 1 ] gap> ExponentsByObj( coll, last ); [ 6, -5, 4, 3, -2, 1 ] ]]>

Special Features In this section we descibe collectors for nilpotent groups which make use of the special structure of the given pc-presentation. checks if there is a function w from the generators of the collector coll into the positive integers such that w(g) \geq w(x)+w(y) for all generators x, y and all generators g in (the normal of) [x,y]. If such a function does not exist, false is returned. If such a function exists, it is computed and stored in the collector. In addition, the default collection strategy for this collector is set to combinatorial collection. is applicable to a collector which passes IsWeightedCollector and computes the Hall multiplication polynomials for the presentation stored in coll. The default strategy for this collector is set to evaluating those polynomial when multiplying two elements. converts a collector coll into a string. stores a collector coll in the file file such that the file can be read back using the function 'Read' into &GAP; and would then be stored in the variable name. appends a collector coll in the file file such that the file can be read back into &GAP; and would then be stored in the variable name. this property can be set to true for a collector to force a simple from-the-left collection strategy implemented in the &GAP; language to be used. Its main purpose is to help debug the collection routines. this global variable can be set to true to force all collectors to use a simple from-the-left collection strategy implemented in the &GAP; language to be used. Its main purpose is to help debug the collection routines. this global variable can be set to true to force the comparison of results from the combinatorial collector with the result of an identical collection performed by a simple from-the-left collector. Its main purpose is to help debug the collection routines. this global variable can be set to false in order to prevent the combinatorial collector to be used.
polycyclic-2.16/doc/polycyclic.xml0000644000076600000240000000174713706672341016306 0ustar mhornstaff Polycyclic"> AClib"> Alnuth"> Cryst"> Polenta"> ]> <#Include SYSTEM "title.xml"> <#Include SYSTEM "preface.xml"> <#Include SYSTEM "intro.xml"> <#Include SYSTEM "collect.xml"> <#Include SYSTEM "defins.xml"> <#Include SYSTEM "basics.xml"> <#Include SYSTEM "libraries.xml"> <#Include SYSTEM "methods.xml"> <#Include SYSTEM "cohom.xml"> <#Include SYSTEM "matreps.xml"> Obsolete Functions and Name Changes <#Include Label="Obsolete"> polycyclic-2.16/doc/chap8_mj.html0000644000076600000240000007116013706672374015773 0ustar mhornstaff GAP (polycyclic) - Chapter 8: Cohomology for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

8 Cohomology for pcp-groups

The GAP 4 package Polycyclic provides methods to compute the first and second cohomology group for a pcp-group \(U\) and a finite dimensional \(ℤ U\) or \(FU\) module \(A\) where \(F\) is a finite field. The algorithm for determining the first cohomology group is outlined in [Eic00].

As a preparation for the cohomology computation, we introduce the cohomology records. These records provide the technical setup for our cohomology computations.

8.1 Cohomology records

Cohomology records provide the necessary technical setup for the cohomology computations for polycyclic groups.

8.1-1 CRRecordByMats
‣ CRRecordByMats( U, mats )( function )

creates an external module. Let U be a pcp group which acts via the list of matrices mats on a vector space of the form \(ℤ^n\) or \(\mathbb{F}_p^n\). Then this function creates a record which can be used as input for the cohomology computations.

8.1-2 CRRecordBySubgroup
‣ CRRecordBySubgroup( U, A )( function )
‣ CRRecordByPcp( U, pcp )( function )

creates an internal module. Let U be a pcp group and let A be a normal elementary or free abelian normal subgroup of U or let pcp be a pcp of a normal elementary of free abelian subfactor of U. Then this function creates a record which can be used as input for the cohomology computations.

The returned cohomology record C contains the following entries:

factor

a pcp of the acting group. If the module is external, then this is Pcp(U). If the module is internal, then this is Pcp(U, A) or Pcp(U, GroupOfPcp(pcp)).

mats, invs and one

the matrix action of factor with acting matrices, their inverses and the identity matrix.

dim and char

the dimension and characteristic of the matrices.

relators and enumrels

the right hand sides of the polycyclic relators of factor as generator exponents lists and a description for the corresponding left hand sides.

central

is true, if the matrices mats are all trivial. This is used locally for efficiency reasons.

And additionally, if \(C\) defines an internal module, then it contains:

group

the original group U.

normal

this is either Pcp(A) or the input pcp.

extension

information on the extension of A by U/A.

8.2 Cohomology groups

Let \(U\) be a pcp-group and \(A\) a free or elementary abelian pcp-group and a \(U\)-module. By \(Z^i(U, A)\) be denote the group of \(i\)-th cocycles and by \(B^i(U, A)\) the \(i\)-th coboundaries. The factor \(Z^i(U,A) / B^i(U,A)\) is the \(i\)-th cohomology group. Since \(A\) is elementary or free abelian, the groups \(Z^i(U, A)\) and \(B^i(U, A)\) are elementary or free abelian groups as well.

The Polycyclic package provides methods to compute first and second cohomology group for a polycyclic group U. We write all involved groups additively and we use an explicit description by bases for them. Let \(C\) be the cohomology record corresponding to \(U\) and \(A\).

Let \(f_1, \ldots, f_n\) be the elements in the entry \(factor\) of the cohomology record \(C\). Then we use the following embedding of the first cocycle group to describe 1-cocycles and 1-coboundaries: \(Z^1(U, A) \to A^n : \delta \mapsto (\delta(f_1), \ldots, \delta(f_n))\)

For the second cohomology group we recall that each element of \(Z^2(U, A)\) defines an extension \(H\) of \(A\) by \(U\). Thus there is a pc-presentation of \(H\) extending the pc-presentation of \(U\) given by the record \(C\). The extended presentation is defined by tails in \(A\); that is, each relator in the record entry \(relators\) is extended by an element of \(A\). The concatenation of these tails yields a vector in \(A^l\) where \(l\) is the length of the record entry \(relators\) of \(C\). We use these tail vectors to describe \(Z^2(U, A)\) and \(B^2(U, A)\). Note that this description is dependent on the chosen presentation in \(C\). However, the factor \(Z^2(U, A)/ B^2(U, A)\) is independent of the chosen presentation.

The following functions are available to compute explicitly the first and second cohomology group as described above.

8.2-1 OneCoboundariesCR
‣ OneCoboundariesCR( C )( function )
‣ OneCocyclesCR( C )( function )
‣ TwoCoboundariesCR( C )( function )
‣ TwoCocyclesCR( C )( function )
‣ OneCohomologyCR( C )( function )
‣ TwoCohomologyCR( C )( function )

The first four functions return bases of the corresponding group. The last two functions need to describe a factor of additive abelian groups. They return the following descriptions for these factors.

gcc

the basis of the cocycles of C.

gcb

the basis of the coboundaries of C.

factor

a description of the factor of cocycles by coboundaries. Usually, it would be most convenient to use additive mappings here. However, these are not available in case that A is free abelian and thus we use a description of this additive map as record. This record contains

gens

a base for the image.

rels

relative orders for the image.

imgs

the images for the elements in gcc.

prei

preimages for the elements in gens.

denom

the kernel of the map; that is, another basis for gcb.

There is an additional function which can be used to compute the second cohomology group over an arbitrary finitely generated abelian group. The finitely generated abelian group should be realized as a factor of a free abelian group modulo a lattice. The function is called as

8.2-2 TwoCohomologyModCR
‣ TwoCohomologyModCR( C, lat )( function )

where C is a cohomology record and lat is a basis for a sublattice of a free abelian module. The output format is the same as for TwoCohomologyCR.

8.3 Extended 1-cohomology

In some cases more information on the first cohomology group is of interest. In particular, if we have an internal module given and we want to compute the complements using the first cohomology group, then we need additional information. This extended version of first cohomology is obtained by the following functions.

8.3-1 OneCoboundariesEX
‣ OneCoboundariesEX( C )( function )

returns a record consisting of the entries

basis

a basis for \(B^1(U, A) \leq A^n\).

transf

There is a derivation mapping from \(A\) to \(B^1(U,A)\). This mapping is described here as transformation from \(A\) to basis.

fixpts

the fixpoints of \(A\). This is also the kernel of the derivation mapping.

8.3-2 OneCocyclesEX
‣ OneCocyclesEX( C )( function )

returns a record consisting of the entries

basis

a basis for \(Z^1(U, A) \leq A^n\).

transl

a special solution. This is only of interest in case that \(C\) is an internal module and in this case it gives the translation vector in \(A^n\) used to obtain complements corresponding to the elements in \(basis\). If \(C\) is not an internal module, then this vector is always the zero vector.

8.3-3 OneCohomologyEX
‣ OneCohomologyEX( C )( function )

returns the combined information on the first cohomology group.

8.4 Extensions and Complements

The natural applications of first and second cohomology group is the determination of extensions and complements. Let \(C\) be a cohomology record.

8.4-1 ComplementCR
‣ ComplementCR( C, c )( function )

returns the complement corresponding to the 1-cocycle c. In the case that C is an external module, we construct the split extension of \(U\) with \(A\) first and then determine the complement. In the case that C is an internal module, the vector c must be an element of the affine space corresponding to the complements as described by OneCocyclesEX.

8.4-2 ComplementsCR
‣ ComplementsCR( C )( function )

returns all complements using the correspondence to \(Z^1(U,A)\). Further, this function returns fail, if \(Z^1(U,A)\) is infinite.

8.4-3 ComplementClassesCR
‣ ComplementClassesCR( C )( function )

returns complement classes using the correspondence to \(H^1(U,A)\). Further, this function returns fail, if \(H^1(U,A)\) is infinite.

8.4-4 ComplementClassesEfaPcps
‣ ComplementClassesEfaPcps( U, N, pcps )( function )

Let \(N\) be a normal subgroup of \(U\). This function returns the complement classes to \(N\) in \(U\). The classes are computed by iteration over the \(U\)-invariant efa series of \(N\) described by pcps. If at some stage in this iteration infinitely many complements are discovered, then the function returns fail. (Even though there might be only finitely many conjugacy classes of complements to \(N\) in \(U\).)

8.4-5 ComplementClasses
‣ ComplementClasses( [V, ]U, N )( function )

Let \(N\) and \(U\) be normal subgroups of \(V\) with \(N \leq U \leq V\). This function attempts to compute the \(V\)-conjugacy classes of complements to \(N\) in \(U\). The algorithm proceeds by iteration over a \(V\)-invariant efa series of \(N\). If at some stage in this iteration infinitely many complements are discovered, then the algorithm returns fail.

8.4-6 ExtensionCR
‣ ExtensionCR( C, c )( function )

returns the extension corresponding to the 2-cocycle \(c\).

8.4-7 ExtensionsCR
‣ ExtensionsCR( C )( function )

returns all extensions using the correspondence to \(Z^2(U,A)\). Further, this function returns fail, if \(Z^2(U,A)\) is infinite.

8.4-8 ExtensionClassesCR
‣ ExtensionClassesCR( C )( function )

returns extension classes using the correspondence to \(H^2(U,A)\). Further, this function returns fail, if \(H^2(U,A)\) is infinite.

8.4-9 SplitExtensionPcpGroup
‣ SplitExtensionPcpGroup( U, mats )( function )

returns the split extension of U by the \(U\)-module described by mats.

8.5 Constructing pcp groups as extensions

This section contains an example application of the second cohomology group to the construction of pcp groups as extensions. The following constructs extensions of the group of upper unitriangular matrices with its natural lattice.

# get the group and its matrix action
gap> G := UnitriangularPcpGroup(3,0);
Pcp-group with orders [ 0, 0, 0 ]
gap> mats := G!.mats;
[ [ [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 1 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 1 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]

# set up the cohomology record
gap> C := CRRecordByMats(G,mats);;

# compute the second cohomology group
gap> cc := TwoCohomologyCR(C);;

# the abelian invariants of H^2(G,M)
gap> cc.factor.rels;
[ 2, 0, 0 ]

# construct an extension which corresponds to a cocycle that has
# infinite image in H^2(G,M)
gap> c := cc.factor.prei[2];
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ]
gap> H := ExtensionCR( CR, c);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]

# check that the extension does not split - get the normal subgroup
gap> N := H!.module;
Pcp-group with orders [ 0, 0, 0 ]

# create the interal module
gap> C := CRRecordBySubgroup(H,N);;

# use the complements routine
gap> ComplementClassesCR(C);
[  ]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chapInd.html0000644000076600000240000005471213706672374015654 0ustar mhornstaff GAP (polycyclic) - Index
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Index

ComplementClasses 8.4-5
ComplementClassesCR 8.4-3
ComplementClassesEfaPcps 8.4-4
ComplementCR 8.4-1
ComplementsCR 8.4-2
\/ 5.5-2
\= 5.1-1
\[\] 5.4-3
\in 5.1-5
AbelianInvariantsMultiplier 7.9-4
AbelianPcpGroup 6.1-1
AddHallPolynomials 3.3-2
AddIgsToIgs 5.3-5
AddToIgs 5.3-5
AddToIgsParallel 5.3-5
BurdeGrunewaldPcpGroup 6.1-8
Centralizer 7.3-1 7.3-2
Centre 7.6-3
Cgs 5.3-3 5.3-3
CgsParallel 5.3-3
ClosureGroup 5.1-7
Collector 4.2-1
CommutatorSubgroup 5.1-10
ConjugacyIntegralAction 7.2-3
CRRecordByMats 8.1-1
CRRecordByPcp 8.1-2
CRRecordBySubgroup 8.1-2
DEBUG_COMBINATORIAL_COLLECTOR 3.3-8
DecomposeUpperUnitriMat 9.2-6
DenominatorOfPcp 5.4-6
Depth 4.2-5
DerivedSeriesOfGroup 7.1-4
DihedralPcpGroup 6.1-2
EfaSeries 7.1-2
Elements 5.1-6
ExampleOfMetabelianPcpGroup 6.2-1
ExamplesOfSomePcpGroups 6.2-2
Exponents 4.2-2
ExponentsByObj 3.2-6
ExponentsByPcp 5.4-10
ExtensionClassesCR 8.4-8
ExtensionCR 8.4-6
ExtensionsCR 8.4-7
FactorGroup 5.5-2
FactorOrder 4.2-9
FCCentre 7.6-4
FiniteSubgroupClasses 7.4-4
FiniteSubgroupClassesBySeries 7.4-5
FittingSubgroup 7.6-1
FromTheLeftCollector 3.1-1
FTLCollectorAppendTo 3.3-5
FTLCollectorPrintTo 3.3-4
GeneratorsOfPcp 5.4-2
GenExpList 4.2-3
GetConjugate 3.2-3
GetConjugateNC 3.2-3
GetPower 3.2-2
GetPowerNC 3.2-2
Group 4.3-2
GroupHomomorphismByImages 5.6-1
GroupOfPcp 5.4-8
HeisenbergPcpGroup 6.1-6
HirschLength 5.1-9
Igs 5.3-1 5.3-1
IgsParallel 5.3-1
Image 5.6-3 5.6-3 5.6-3
Index 5.1-4
InfiniteMetacyclicPcpGroup 6.1-5
Intersection 7.3-3
IsAbelian 5.2-4
IsConfluent 3.1-7
IsConjugate 7.3-1 7.3-2
IsElementaryAbelian 5.2-5
IsFreeAbelian 5.2-6
IsInjective 5.6-6
IsMatrixRepresentation 9.1-2
IsNilpotentByFinite 7.6-2
IsNilpotentGroup 5.2-3
IsNormal 5.2-2
IsomorphismFpGroup 5.9-4
IsomorphismPcGroup 5.9-3
IsomorphismPcpGroup 5.9-1
IsomorphismPcpGroupFromFpGroupWithPcPres 5.9-2
IsomorphismUpperUnitriMatGroupPcpGroup 9.2-1
IsPcpElement 4.1-3
IsPcpElementCollection 4.1-4
IsPcpElementRep 4.1-5
IsPcpGroup 4.1-6
IsSubgroup 5.2-1
IsTorsionFree 7.4-3
IsWeightedCollector 3.3-1
Kernel 5.6-2
LeadingExponent 4.2-6
Length 5.4-4
License .-1
LowerCentralSeriesOfGroup 7.1-7
LowIndexNormalSubgroups 7.5-3
LowIndexSubgroupClasses 7.5-2
MakeNewLevel 9.2-4
MaximalOrderByUnitsPcpGroup 6.1-7
MaximalSubgroupClassesByIndex 7.5-1
MinimalGeneratingSet 7.7-1
NameTag 4.2-4
NaturalHomomorphismByNormalSubgroup 5.5-1
Ngs 5.3-2 5.3-2
NilpotentByAbelianByFiniteSeries 7.6-6
NilpotentByAbelianNormalSubgroup 7.5-4
NonAbelianExteriorSquare 7.9-6
NonAbelianExteriorSquareEpimorphism 7.9-5
NonAbelianExteriorSquarePlusEmbedding 7.9-9
NonAbelianTensorSquare 7.9-8
NonAbelianTensorSquareEpimorphism 7.9-7
NonAbelianTensorSquarePlus 7.9-11
NonAbelianTensorSquarePlusEpimorphism 7.9-10
NormalClosure 5.1-8
Normalizer 7.3-2
NormalizerIntegralAction 7.2-3
NormalTorsionSubgroup 7.4-2
NormedPcpElement 4.2-11
NormingExponent 4.2-10
NumberOfGenerators 3.2-4
NumeratorOfPcp 5.4-7
ObjByExponents 3.2-5
OneCoboundariesCR 8.2-1
OneCoboundariesEX 8.3-1
OneCocyclesCR 8.2-1
OneCocyclesEX 8.3-2
OneCohomologyCR 8.2-1
OneCohomologyEX 8.3-3
OneOfPcp 5.4-9
OrbitIntegralAction 7.2-2
Pcp 5.4-1 5.4-1
PcpElementByExponents 4.1-1
PcpElementByExponentsNC 4.1-1
PcpElementByGenExpList 4.1-2
PcpElementByGenExpListNC 4.1-2
PcpGroupByCollector 4.3-1
PcpGroupByCollectorNC 4.3-1
PcpGroupByPcp 5.4-11
PcpGroupBySeries 5.7-2
PcpOrbitsStabilizers 7.2-1
PcpOrbitStabilizer 7.2-1
PcpsBySeries 7.1-10
PcpSeries 7.1-1
PcpsOfEfaSeries 7.1-11
PolyZNormalSubgroup 7.6-5
PreImage 5.6-4
PreImagesRepresentative 5.6-5
PrintPcpPresentation 5.8-1 5.8-1
PRump 5.1-11
Random 5.1-3
RandomCentralizerPcpGroup 7.8-1 7.8-1
RandomNormalizerPcpGroup 7.8-1
RanksLevels 9.2-3
RefinedDerivedSeries 7.1-5
RefinedDerivedSeriesDown 7.1-6
RefinedPcpGroup 5.7-1
RelativeIndex 4.2-8
RelativeOrder 4.2-7
RelativeOrders 3.2-1
RelativeOrdersOfPcp 5.4-5
SchurCover 7.9-3
SchurCovering A.
SchurCovers 7.10-1
SchurExtension 7.9-1
SchurExtensionEpimorphism 7.9-2
SchurMultPcpGroup A.
SemiSimpleEfaSeries 7.1-3
SetCommutator 3.1-5
SetConjugate 3.1-4
SetConjugateNC 3.1-4
SetPower 3.1-3
SetPowerNC 3.1-3
SetRelativeOrder 3.1-2
SetRelativeOrderNC 3.1-2
SiftUpperUnitriMat 9.2-5
SiftUpperUnitriMatGroup 9.2-2
Size 5.1-2
SmallGeneratingSet 5.1-12
SplitExtensionPcpGroup 8.4-9
StabilizerIntegralAction 7.2-2
String 3.3-3
Subgroup 4.3-3
SubgroupByIgs 5.3-4 5.3-4
SubgroupUnitriangularPcpGroup 6.1-4
TorsionByPolyEFSeries 7.1-9
TorsionSubgroup 7.4-1
TwoCoboundariesCR 8.2-1
TwoCocyclesCR 8.2-1
TwoCohomologyCR 8.2-1
TwoCohomologyModCR 8.2-2
UnitriangularMatrixRepresentation 9.1-1
UnitriangularPcpGroup 6.1-3
UpdatePolycyclicCollector 3.1-6
UpperCentralSeriesOfGroup 7.1-8
USE_COMBINATORIAL_COLLECTOR 3.3-9
USE_LIBRARY_COLLECTOR 3.3-7
UseLibraryCollector 3.3-6
WhiteheadQuadraticFunctor 7.9-12

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap3_mj.html0000644000076600000240000010022713706672374015763 0ustar mhornstaff GAP (polycyclic) - Chapter 3: Collectors
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

3 Collectors

Let \(G\) be a group defined by a pc-presentation as described in the Chapter Introduction to polycyclic presentations.

The process for computing the collected form for an arbitrary word in the generators of \(G\) is called collection. The basic idea in collection is the following. Given a word in the defining generators, one scans the word for occurrences of adjacent generators (or their inverses) in the wrong order or occurrences of subwords \(g_i^{e_i}\) with \(i\in I\) and \(e_i\) not in the range \(0\ldots r_{i}-1\). In the first case, the appropriate conjugacy relation is used to move the generator with the smaller index to the left. In the second case, one uses the appropriate power relation to move the exponent of \(g_i\) into the required range. These steps are repeated until a collected word is obtained.

There exist a number of different strategies for collecting a given word to collected form. The strategies implemented in this package are collection from the left as described by [LGS90] and [Sim94] and combinatorial collection from the left by [VL90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LGS98].

The first step in defining a pc-presented group is setting up a data structure that knows the pc-presentation and has routines that perform the collection algorithm with words in the generators of the presentation. Such a data structure is called a collector.

To describe the right hand sides of the relations in a pc-presentation we use generator exponent lists; the word \(g_{i_1}^{e_1}g_{i_2}^{e_2}\ldots g_{i_k}^{e_k}\) is represented by the generator exponent list \([i_1,e_1,i_2,e_2,\ldots,i_k,e_k]\).

3.1 Constructing a Collector

A collector for a group given by a pc-presentation starts by setting up an empty data structure for the collector. Then the relative orders, the power relations and the conjugate relations are added into the data structure. The construction is finalised by calling a routine that completes the data structure for the collector. The following functions provide the necessary tools for setting up a collector.

3.1-1 FromTheLeftCollector
‣ FromTheLeftCollector( n )( operation )

returns an empty data structure for a collector with n generators. No generator has a relative order, no right hand sides of power and conjugate relations are defined. Two generators for which no right hand side of a conjugate relation is defined commute. Therefore, the collector returned by this function can be used to define a free abelian group of rank n.

gap> ftl := FromTheLeftCollector( 4 );
<<from the left collector with 4 generators>>
gap> PcpGroupByCollector( ftl );
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> IsAbelian(last);
true

If the relative order of a generators has been defined (see SetRelativeOrder (3.1-2)), but the right hand side of the corresponding power relation has not, then the order and the relative order of the generator are the same.

3.1-2 SetRelativeOrder
‣ SetRelativeOrder( coll, i, ro )( operation )
‣ SetRelativeOrderNC( coll, i, ro )( operation )

set the relative order in collector coll for generator i to ro. The parameter coll is a collector as returned by the function FromTheLeftCollector (3.1-1), i is a generator number and ro is a non-negative integer. The generator number i is an integer in the range \(1,\ldots,n\) where \(n\) is the number of generators of the collector.

If ro is \(0,\) then the generator with number i has infinite order and no power relation can be specified. As a side effect in this case, a previously defined power relation is deleted.

If ro is the relative order of a generator with number i and no power relation is set for that generator, then ro is the order of that generator.

The NC version of the function bypasses checks on the range of i.

gap> ftl := FromTheLeftCollector( 4 );
<<from the left collector with 4 generators>>
gap> for i in [1..4] do SetRelativeOrder( ftl, i, 3 ); od;
gap> G := PcpGroupByCollector( ftl );
Pcp-group with orders [ 3, 3, 3, 3 ]
gap> IsElementaryAbelian( G );
true

3.1-3 SetPower
‣ SetPower( coll, i, rhs )( operation )
‣ SetPowerNC( coll, i, rhs )( operation )

set the right hand side of the power relation for generator i in collector coll to (a copy of) rhs. An attempt to set the right hand side for a generator without a relative order results in an error.

Right hand sides are by default assumed to be trivial.

The parameter coll is a collector, i is a generator number and rhs is a generators exponent list or an element from a free group.

The no-check (NC) version of the function bypasses checks on the range of i and stores rhs (instead of a copy) in the collector.

3.1-4 SetConjugate
‣ SetConjugate( coll, j, i, rhs )( operation )
‣ SetConjugateNC( coll, j, i, rhs )( operation )

set the right hand side of the conjugate relation for the generators j and i with j larger than i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group. Conjugate relations are by default assumed to be trivial.

The generator number i can be negative in order to define conjugation by the inverse of a generator.

The no-check (NC) version of the function bypasses checks on the range of i and j and stores rhs (instead of a copy) in the collector.

3.1-5 SetCommutator
‣ SetCommutator( coll, j, i, rhs )( operation )

set the right hand side of the conjugate relation for the generators j and i with j larger than i by specifying the commutator of j and i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group.

The generator number i can be negative in order to define the right hand side of a commutator relation with the second generator being the inverse of a generator.

3.1-6 UpdatePolycyclicCollector
‣ UpdatePolycyclicCollector( coll )( operation )

completes the data structures of a collector. This is usually the last step in setting up a collector. Among the steps performed is the completion of the conjugate relations. For each non-trivial conjugate relation of a generator, the corresponding conjugate relation of the inverse generator is calculated.

Note that UpdatePolycyclicCollector is automatically called by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)).

3.1-7 IsConfluent
‣ IsConfluent( coll )( property )

tests if the collector coll is confluent. The function returns true or false accordingly.

Compare Chapter 2 for a definition of confluence.

Note that confluence is automatically checked by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)).

The following example defines a collector for a semidirect product of the cyclic group of order \(3\) with the free abelian group of rank \(2\). The action of the cyclic group on the free abelian group is given by the matrix

\[\pmatrix{ 0 & 1 \cr -1 & -1}.\]

This leads to the following polycyclic presentation:

\[\langle g_1,g_2,g_3 | g_1^3, g_2^{g_1}=g_3, g_3^{g_1}=g_2^{-1}g_3^{-1}, g_3^{g_2}=g_3\rangle.\]

gap> ftl := FromTheLeftCollector( 3 );
<<from the left collector with 3 generators>>
gap> SetRelativeOrder( ftl, 1, 3 );
gap> SetConjugate( ftl, 2, 1, [3,1] );
gap> SetConjugate( ftl, 3, 1, [2,-1,3,-1] );
gap> UpdatePolycyclicCollector( ftl );
gap> IsConfluent( ftl );
true

The action of the inverse of \(g_1\) on \(\langle g_2,g_2\rangle\) is given by the matrix

\[\pmatrix{ -1 & -1 \cr 1 & 0}.\]

The corresponding conjugate relations are automatically computed by UpdatePolycyclicCollector. It is also possible to specify the conjugation by inverse generators. Note that you need to run UpdatePolycyclicCollector after one of the set functions has been used.

gap> SetConjugate( ftl, 2, -1, [2,-1,3,-1] );
gap> SetConjugate( ftl, 3, -1, [2,1] );
gap> IsConfluent( ftl );
Error, Collector is out of date called from
CollectWordOrFail( coll, ev1, [ j, 1, i, 1 ] ); called from
<function>( <arguments> ) called from read-eval-loop
Entering break read-eval-print loop ...
you can 'quit;' to quit to outer loop, or
you can 'return;' to continue
brk>
gap> UpdatePolycyclicCollector( ftl );
gap> IsConfluent( ftl );
true

3.2 Accessing Parts of a Collector

3.2-1 RelativeOrders
‣ RelativeOrders( coll )( attribute )

returns (a copy of) the list of relative order stored in the collector coll.

3.2-2 GetPower
‣ GetPower( coll, i )( operation )
‣ GetPowerNC( coll, i )( operation )

returns a copy of the generator exponent list stored for the right hand side of the power relation of the generator i in the collector coll.

The no-check (NC) version of the function bypasses checks on the range of i and does not create a copy before returning the right hand side of the power relation.

3.2-3 GetConjugate
‣ GetConjugate( coll, j, i )( operation )
‣ GetConjugateNC( coll, j, i )( operation )

returns a copy of the right hand side of the conjugate relation stored for the generators j and i in the collector coll as generator exponent list. The generator j must be larger than i.

The no-check (NC) version of the function bypasses checks on the range of i and j and does not create a copy before returning the right hand side of the power relation.

3.2-4 NumberOfGenerators
‣ NumberOfGenerators( coll )( operation )

returns the number of generators of the collector coll.

3.2-5 ObjByExponents
‣ ObjByExponents( coll, expvec )( operation )

returns a generator exponent list for the exponent vector expvec. This is the inverse operation to ExponentsByObj. See ExponentsByObj (3.2-6) for an example.

3.2-6 ExponentsByObj
‣ ExponentsByObj( coll, genexp )( operation )

returns an exponent vector for the generator exponent list genexp. This is the inverse operation to ObjByExponents. The function assumes that the generators in genexp are given in the right order and that the exponents are in the right range.

gap> G := UnitriangularPcpGroup( 4, 0 );
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]
gap> coll := Collector ( G );
<<from the left collector with 6 generators>>
gap> ObjByExponents( coll, [6,-5,4,3,-2,1] );
[ 1, 6, 2, -5, 3, 4, 4, 3, 5, -2, 6, 1 ]
gap> ExponentsByObj( coll, last );
[ 6, -5, 4, 3, -2, 1 ]

3.3 Special Features

In this section we descibe collectors for nilpotent groups which make use of the special structure of the given pc-presentation.

3.3-1 IsWeightedCollector
‣ IsWeightedCollector( coll )( property )

checks if there is a function \(w\) from the generators of the collector coll into the positive integers such that \(w(g) \geq w(x)+w(y)\) for all generators \(x\), \(y\) and all generators \(g\) in (the normal of) \([x,y]\). If such a function does not exist, false is returned. If such a function exists, it is computed and stored in the collector. In addition, the default collection strategy for this collector is set to combinatorial collection.

3.3-2 AddHallPolynomials
‣ AddHallPolynomials( coll )( function )

is applicable to a collector which passes IsWeightedCollector and computes the Hall multiplication polynomials for the presentation stored in coll. The default strategy for this collector is set to evaluating those polynomial when multiplying two elements.

3.3-3 String
‣ String( coll )( attribute )

converts a collector coll into a string.

3.3-4 FTLCollectorPrintTo
‣ FTLCollectorPrintTo( file, name, coll )( function )

stores a collector coll in the file file such that the file can be read back using the function 'Read' into GAP and would then be stored in the variable name.

3.3-5 FTLCollectorAppendTo
‣ FTLCollectorAppendTo( file, name, coll )( function )

appends a collector coll in the file file such that the file can be read back into GAP and would then be stored in the variable name.

3.3-6 UseLibraryCollector
‣ UseLibraryCollector( global variable )

this property can be set to true for a collector to force a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines.

3.3-7 USE_LIBRARY_COLLECTOR
‣ USE_LIBRARY_COLLECTOR( global variable )

this global variable can be set to true to force all collectors to use a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines.

3.3-8 DEBUG_COMBINATORIAL_COLLECTOR
‣ DEBUG_COMBINATORIAL_COLLECTOR( global variable )

this global variable can be set to true to force the comparison of results from the combinatorial collector with the result of an identical collection performed by a simple from-the-left collector. Its main purpose is to help debug the collection routines.

3.3-9 USE_COMBINATORIAL_COLLECTOR
‣ USE_COMBINATORIAL_COLLECTOR( global variable )

this global variable can be set to false in order to prevent the combinatorial collector to be used.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chapInd.txt0000644000076600000240000001637013706672367015527 0ustar mhornstaff Index  ComplementClasses 8.4-5  ComplementClassesCR 8.4-3  ComplementClassesEfaPcps 8.4-4  ComplementCR 8.4-1  ComplementsCR 8.4-2 \/ 5.5-2 \= 5.1-1 \[\] 5.4-3 \in 5.1-5 AbelianInvariantsMultiplier 7.9-4 AbelianPcpGroup 6.1-1 AddHallPolynomials 3.3-2 AddIgsToIgs 5.3-5 AddToIgs 5.3-5 AddToIgsParallel 5.3-5 BurdeGrunewaldPcpGroup 6.1-8 Centralizer 7.3-1 7.3-2 Centre 7.6-3 Cgs 5.3-3 5.3-3 CgsParallel 5.3-3 ClosureGroup 5.1-7 Collector 4.2-1 CommutatorSubgroup 5.1-10 ConjugacyIntegralAction 7.2-3 CRRecordByMats 8.1-1 CRRecordByPcp 8.1-2 CRRecordBySubgroup 8.1-2 DEBUG_COMBINATORIAL_COLLECTOR 3.3-8 DecomposeUpperUnitriMat 9.2-6 DenominatorOfPcp 5.4-6 Depth 4.2-5 DerivedSeriesOfGroup 7.1-4 DihedralPcpGroup 6.1-2 EfaSeries 7.1-2 Elements 5.1-6 ExampleOfMetabelianPcpGroup 6.2-1 ExamplesOfSomePcpGroups 6.2-2 Exponents 4.2-2 ExponentsByObj 3.2-6 ExponentsByPcp 5.4-10 ExtensionClassesCR 8.4-8 ExtensionCR 8.4-6 ExtensionsCR 8.4-7 FactorGroup 5.5-2 FactorOrder 4.2-9 FCCentre 7.6-4 FiniteSubgroupClasses 7.4-4 FiniteSubgroupClassesBySeries 7.4-5 FittingSubgroup 7.6-1 FromTheLeftCollector 3.1-1 FTLCollectorAppendTo 3.3-5 FTLCollectorPrintTo 3.3-4 GeneratorsOfPcp 5.4-2 GenExpList 4.2-3 GetConjugate 3.2-3 GetConjugateNC 3.2-3 GetPower 3.2-2 GetPowerNC 3.2-2 Group 4.3-2 GroupHomomorphismByImages 5.6-1 GroupOfPcp 5.4-8 HeisenbergPcpGroup 6.1-6 HirschLength 5.1-9 Igs 5.3-1 5.3-1 IgsParallel 5.3-1 Image 5.6-3 5.6-3 5.6-3 Index 5.1-4 InfiniteMetacyclicPcpGroup 6.1-5 Intersection 7.3-3 IsAbelian 5.2-4 IsConfluent 3.1-7 IsConjugate 7.3-1 7.3-2 IsElementaryAbelian 5.2-5 IsFreeAbelian 5.2-6 IsInjective 5.6-6 IsMatrixRepresentation 9.1-2 IsNilpotentByFinite 7.6-2 IsNilpotentGroup 5.2-3 IsNormal 5.2-2 IsomorphismFpGroup 5.9-4 IsomorphismPcGroup 5.9-3 IsomorphismPcpGroup 5.9-1 IsomorphismPcpGroupFromFpGroupWithPcPres 5.9-2 IsomorphismUpperUnitriMatGroupPcpGroup 9.2-1 IsPcpElement 4.1-3 IsPcpElementCollection 4.1-4 IsPcpElementRep 4.1-5 IsPcpGroup 4.1-6 IsSubgroup 5.2-1 IsTorsionFree 7.4-3 IsWeightedCollector 3.3-1 Kernel 5.6-2 LeadingExponent 4.2-6 Length 5.4-4 License .-1 LowerCentralSeriesOfGroup 7.1-7 LowIndexNormalSubgroups 7.5-3 LowIndexSubgroupClasses 7.5-2 MakeNewLevel 9.2-4 MaximalOrderByUnitsPcpGroup 6.1-7 MaximalSubgroupClassesByIndex 7.5-1 MinimalGeneratingSet 7.7-1 NameTag 4.2-4 NaturalHomomorphismByNormalSubgroup 5.5-1 Ngs 5.3-2 5.3-2 NilpotentByAbelianByFiniteSeries 7.6-6 NilpotentByAbelianNormalSubgroup 7.5-4 NonAbelianExteriorSquare 7.9-6 NonAbelianExteriorSquareEpimorphism 7.9-5 NonAbelianExteriorSquarePlusEmbedding 7.9-9 NonAbelianTensorSquare 7.9-8 NonAbelianTensorSquareEpimorphism 7.9-7 NonAbelianTensorSquarePlus 7.9-11 NonAbelianTensorSquarePlusEpimorphism 7.9-10 NormalClosure 5.1-8 Normalizer 7.3-2 NormalizerIntegralAction 7.2-3 NormalTorsionSubgroup 7.4-2 NormedPcpElement 4.2-11 NormingExponent 4.2-10 NumberOfGenerators 3.2-4 NumeratorOfPcp 5.4-7 ObjByExponents 3.2-5 OneCoboundariesCR 8.2-1 OneCoboundariesEX 8.3-1 OneCocyclesCR 8.2-1 OneCocyclesEX 8.3-2 OneCohomologyCR 8.2-1 OneCohomologyEX 8.3-3 OneOfPcp 5.4-9 OrbitIntegralAction 7.2-2 Pcp 5.4-1 5.4-1 PcpElementByExponents 4.1-1 PcpElementByExponentsNC 4.1-1 PcpElementByGenExpList 4.1-2 PcpElementByGenExpListNC 4.1-2 PcpGroupByCollector 4.3-1 PcpGroupByCollectorNC 4.3-1 PcpGroupByPcp 5.4-11 PcpGroupBySeries 5.7-2 PcpOrbitsStabilizers 7.2-1 PcpOrbitStabilizer 7.2-1 PcpsBySeries 7.1-10 PcpSeries 7.1-1 PcpsOfEfaSeries 7.1-11 PolyZNormalSubgroup 7.6-5 PreImage 5.6-4 PreImagesRepresentative 5.6-5 PrintPcpPresentation 5.8-1 5.8-1 PRump 5.1-11 Random 5.1-3 RandomCentralizerPcpGroup 7.8-1 7.8-1 RandomNormalizerPcpGroup 7.8-1 RanksLevels 9.2-3 RefinedDerivedSeries 7.1-5 RefinedDerivedSeriesDown 7.1-6 RefinedPcpGroup 5.7-1 RelativeIndex 4.2-8 RelativeOrder 4.2-7 RelativeOrders 3.2-1 RelativeOrdersOfPcp 5.4-5 SchurCover 7.9-3 SchurCovering A. SchurCovers 7.10-1 SchurExtension 7.9-1 SchurExtensionEpimorphism 7.9-2 SchurMultPcpGroup A. SemiSimpleEfaSeries 7.1-3 SetCommutator 3.1-5 SetConjugate 3.1-4 SetConjugateNC 3.1-4 SetPower 3.1-3 SetPowerNC 3.1-3 SetRelativeOrder 3.1-2 SetRelativeOrderNC 3.1-2 SiftUpperUnitriMat 9.2-5 SiftUpperUnitriMatGroup 9.2-2 Size 5.1-2 SmallGeneratingSet 5.1-12 SplitExtensionPcpGroup 8.4-9 StabilizerIntegralAction 7.2-2 String 3.3-3 Subgroup 4.3-3 SubgroupByIgs 5.3-4 5.3-4 SubgroupUnitriangularPcpGroup 6.1-4 TorsionByPolyEFSeries 7.1-9 TorsionSubgroup 7.4-1 TwoCoboundariesCR 8.2-1 TwoCocyclesCR 8.2-1 TwoCohomologyCR 8.2-1 TwoCohomologyModCR 8.2-2 UnitriangularMatrixRepresentation 9.1-1 UnitriangularPcpGroup 6.1-3 UpdatePolycyclicCollector 3.1-6 UpperCentralSeriesOfGroup 7.1-8 USE_COMBINATORIAL_COLLECTOR 3.3-9 USE_LIBRARY_COLLECTOR 3.3-7 UseLibraryCollector 3.3-6 WhiteheadQuadraticFunctor 7.9-12 ------------------------------------------------------- polycyclic-2.16/doc/chap4.html0000644000076600000240000005234113706672374015301 0ustar mhornstaff GAP (polycyclic) - Chapter 4: Pcp-groups - polycyclically presented groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

4 Pcp-groups - polycyclically presented groups

4.1 Pcp-elements -- elements of a pc-presented group

A pcp-element is an element of a group defined by a consistent pc-presentation given by a collector. Suppose that g_1, ..., g_n are the defining generators of the collector. Recall that each element g in this group can be written uniquely as a collected word g_1^e_1 ⋯ g_n^e_n with e_i ∈ ℤ and 0 ≤ e_i < r_i for i ∈ I. The integer vector [e_1, ..., e_n] is called the exponent vector of g. The following functions can be used to define pcp-elements via their exponent vector or via an arbitrary generator exponent word as introduced in Chapter 3.

4.1-1 PcpElementByExponentsNC
‣ PcpElementByExponentsNC( coll, exp )( function )
‣ PcpElementByExponents( coll, exp )( function )

returns the pcp-element with exponent vector exp. The exponent vector is considered relative to the defining generators of the pc-presentation.

4.1-2 PcpElementByGenExpListNC
‣ PcpElementByGenExpListNC( coll, word )( function )
‣ PcpElementByGenExpList( coll, word )( function )

returns the pcp-element with generators exponent list word. This list word consists of a sequence of generator numbers and their corresponding exponents and is of the form [i_1, e_i_1, i_2, e_i_2, ..., i_r, e_i_r]. The generators exponent list is considered relative to the defining generators of the pc-presentation.

These functions return pcp-elements in the category IsPcpElement. Presently, the only representation implemented for this category is IsPcpElementRep. (This allows us to be a little sloppy right now. The basic set of operations for IsPcpElement has not been defined yet. This is going to happen in one of the next version, certainly as soon as the need for different representations arises.)

4.1-3 IsPcpElement
‣ IsPcpElement( obj )( category )

returns true if the object obj is a pcp-element.

4.1-4 IsPcpElementCollection
‣ IsPcpElementCollection( obj )( category )

returns true if the object obj is a collection of pcp-elements.

4.1-5 IsPcpElementRep
‣ IsPcpElementRep( obj )( representation )

returns true if the object obj is represented as a pcp-element.

4.1-6 IsPcpGroup
‣ IsPcpGroup( obj )( filter )

returns true if the object obj is a group and also a pcp-element collection.

4.2 Methods for pcp-elements

Now we can describe attributes and functions for pcp-elements. The four basic attributes of a pcp-element, Collector, Exponents, GenExpList and NameTag are computed at the creation of the pcp-element. All other attributes are determined at runtime.

Let g be a pcp-element and g_1, ..., g_n a polycyclic generating sequence of the underlying pc-presented group. Let C_1, ..., C_n be the polycyclic series defined by g_1, ..., g_n.

The depth of a non-trivial element g of a pcp-group (with respect to the defining generators) is the integer i such that g ∈ C_i ∖ C_i+1. The depth of the trivial element is defined to be n+1. If gnot=1 has depth i and g_i^e_i ⋯ g_n^e_n is the collected word for g, then e_i is the leading exponent of g.

If g has depth i, then we call r_i = [C_i:C_i+1] the factor order of g. If r < ∞, then the smallest positive integer l with g^l ∈ C_i+1 is the called relative order of g. If r=∞, then the relative order of g is defined to be 0. The index e of ⟨ g,C_i+1⟩ in C_i is called relative index of g. We have that r = el.

We call a pcp-element normed, if its leading exponent is equal to its relative index. For each pcp-element g there exists an integer e such that g^e is normed.

4.2-1 Collector
‣ Collector( g )( operation )

the collector to which the pcp-element g belongs.

4.2-2 Exponents
‣ Exponents( g )( operation )

returns the exponent vector of the pcp-element g with respect to the defining generating set of the underlying collector.

4.2-3 GenExpList
‣ GenExpList( g )( operation )

returns the generators exponent list of the pcp-element g with respect to the defining generating set of the underlying collector.

4.2-4 NameTag
‣ NameTag( g )( operation )

the name used for printing the pcp-element g. Printing is done by using the name tag and appending the generator number of g.

4.2-5 Depth
‣ Depth( g )( operation )

returns the depth of the pcp-element g relative to the defining generators.

4.2-6 LeadingExponent
‣ LeadingExponent( g )( operation )

returns the leading exponent of pcp-element g relative to the defining generators. If g is the identity element, the functions returns 'fail'

4.2-7 RelativeOrder
‣ RelativeOrder( g )( attribute )

returns the relative order of the pcp-element g with respect to the defining generators.

4.2-8 RelativeIndex
‣ RelativeIndex( g )( attribute )

returns the relative index of the pcp-element g with respect to the defining generators.

4.2-9 FactorOrder
‣ FactorOrder( g )( attribute )

returns the factor order of the pcp-element g with respect to the defining generators.

4.2-10 NormingExponent
‣ NormingExponent( g )( function )

returns a positive integer e such that the pcp-element g raised to the power of e is normed.

4.2-11 NormedPcpElement
‣ NormedPcpElement( g )( function )

returns the normed element corresponding to the pcp-element g.

4.3 Pcp-groups - groups of pcp-elements

A pcp-group is a group consisting of pcp-elements such that all pcp-elements in the group share the same collector. Thus the group G defined by a polycyclic presentation and all its subgroups are pcp-groups.

4.3-1 PcpGroupByCollector
‣ PcpGroupByCollector( coll )( function )
‣ PcpGroupByCollectorNC( coll )( function )

returns a pcp-group build from the collector coll.

The function calls UpdatePolycyclicCollector (3.1-6) and checks the confluence (see IsConfluent (3.1-7)) of the collector.

The non-check version bypasses these checks.

4.3-2 Group
‣ Group( gens, id )( function )

returns the group generated by the pcp-elements gens with identity id.

4.3-3 Subgroup
‣ Subgroup( G, gens )( function )

returns a subgroup of the pcp-group G generated by the list gens of pcp-elements from G.

gap>  ftl := FromTheLeftCollector( 2 );;
gap>  SetRelativeOrder( ftl, 1, 2 );
gap>  SetConjugate( ftl, 2, 1, [2,-1] );
gap>  UpdatePolycyclicCollector( ftl );
gap>  G:= PcpGroupByCollectorNC( ftl );
Pcp-group with orders [ 2, 0 ]
gap> Subgroup( G, GeneratorsOfGroup(G){[2]} );
Pcp-group with orders [ 0 ]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/cohom.xml0000644000076600000240000003264113706672341015236 0ustar mhornstaff Cohomology for pcp-groups The &GAP; 4 package &Polycyclic; provides methods to compute the first and second cohomology group for a pcp-group U and a finite dimensional &ZZ; U or FU module A where F is a finite field. The algorithm for determining the first cohomology group is outlined in .

As a preparation for the cohomology computation, we introduce the cohomology records. These records provide the technical setup for our cohomology computations.

Cohomology records Cohomology records provide the necessary technical setup for the cohomology computations for polycyclic groups. creates an external module. Let U be a pcp group which acts via the list of matrices mats on a vector space of the form &ZZ;^n or \mathbb{F}_p^n. Then this function creates a record which can be used as input for the cohomology computations. creates an internal module. Let U be a pcp group and let A be a normal elementary or free abelian normal subgroup of U or let pcp be a pcp of a normal elementary of free abelian subfactor of U. Then this function creates a record which can be used as input for the cohomology computations.

The returned cohomology record C contains the following entries: factor a pcp of the acting group. If the module is external, then this is Pcp(U). If the module is internal, then this is Pcp(U, A) or Pcp(U, GroupOfPcp(pcp)). mats, invs and one the matrix action of factor with acting matrices, their inverses and the identity matrix. dim and char the dimension and characteristic of the matrices. relators and enumrels the right hand sides of the polycyclic relators of factor as generator exponents lists and a description for the corresponding left hand sides. central is true, if the matrices mats are all trivial. This is used locally for efficiency reasons. And additionally, if C defines an internal module, then it contains: group the original group U. normal this is either Pcp(A) or the input pcp. extension information on the extension of A by U/A.

Cohomology groups Let U be a pcp-group and A a free or elementary abelian pcp-group and a U-module. By Z^i(U, A) be denote the group of i-th cocycles and by B^i(U, A) the i-th coboundaries. The factor Z^i(U,A) / B^i(U,A) is the i-th cohomology group. Since A is elementary or free abelian, the groups Z^i(U, A) and B^i(U, A) are elementary or free abelian groups as well.

The &Polycyclic; package provides methods to compute first and second cohomology group for a polycyclic group U. We write all involved groups additively and we use an explicit description by bases for them. Let C be the cohomology record corresponding to U and A.

Let f_1, \ldots, f_n be the elements in the entry factor of the cohomology record C. Then we use the following embedding of the first cocycle group to describe 1-cocycles and 1-coboundaries: Z^1(U, A) \to A^n : \delta \mapsto (\delta(f_1), \ldots, \delta(f_n))

For the second cohomology group we recall that each element of Z^2(U, A) defines an extension H of A by U. Thus there is a pc-presentation of H extending the pc-presentation of U given by the record C. The extended presentation is defined by tails in A; that is, each relator in the record entry relators is extended by an element of A. The concatenation of these tails yields a vector in A^l where l is the length of the record entry relators of C. We use these tail vectors to describe Z^2(U, A) and B^2(U, A). Note that this description is dependent on the chosen presentation in C. However, the factor Z^2(U, A)/ B^2(U, A) is independent of the chosen presentation.

The following functions are available to compute explicitly the first and second cohomology group as described above. The first four functions return bases of the corresponding group. The last two functions need to describe a factor of additive abelian groups. They return the following descriptions for these factors. gcc the basis of the cocycles of C. gcb the basis of the coboundaries of C. factor a description of the factor of cocycles by coboundaries. Usually, it would be most convenient to use additive mappings here. However, these are not available in case that A is free abelian and thus we use a description of this additive map as record. This record contains gens a base for the image. rels relative orders for the image. imgs the images for the elements in gcc. prei preimages for the elements in gens. denom the kernel of the map; that is, another basis for gcb. There is an additional function which can be used to compute the second cohomology group over an arbitrary finitely generated abelian group. The finitely generated abelian group should be realized as a factor of a free abelian group modulo a lattice. The function is called as where C is a cohomology record and lat is a basis for a sublattice of a free abelian module. The output format is the same as for TwoCohomologyCR.

Extended 1-cohomology In some cases more information on the first cohomology group is of interest. In particular, if we have an internal module given and we want to compute the complements using the first cohomology group, then we need additional information. This extended version of first cohomology is obtained by the following functions. returns a record consisting of the entries basis a basis for B^1(U, A) \leq A^n. transf There is a derivation mapping from A to B^1(U,A). This mapping is described here as transformation from A to basis. fixpts the fixpoints of A. This is also the kernel of the derivation mapping. returns a record consisting of the entries basis a basis for Z^1(U, A) \leq A^n. transl a special solution. This is only of interest in case that C is an internal module and in this case it gives the translation vector in A^n used to obtain complements corresponding to the elements in basis. If C is not an internal module, then this vector is always the zero vector. returns the combined information on the first cohomology group.
Extensions and Complements The natural applications of first and second cohomology group is the determination of extensions and complements. Let C be a cohomology record. returns the complement corresponding to the 1-cocycle c. In the case that C is an external module, we construct the split extension of U with A first and then determine the complement. In the case that C is an internal module, the vector c must be an element of the affine space corresponding to the complements as described by OneCocyclesEX. returns all complements using the correspondence to Z^1(U,A). Further, this function returns fail, if Z^1(U,A) is infinite. returns complement classes using the correspondence to H^1(U,A). Further, this function returns fail, if H^1(U,A) is infinite. Let N be a normal subgroup of U. This function returns the complement classes to N in U. The classes are computed by iteration over the U-invariant efa series of N described by pcps. If at some stage in this iteration infinitely many complements are discovered, then the function returns fail. (Even though there might be only finitely many conjugacy classes of complements to N in U.) Let N and U be normal subgroups of V with N \leq U \leq V. This function attempts to compute the V-conjugacy classes of complements to N in U. The algorithm proceeds by iteration over a V-invariant efa series of N. If at some stage in this iteration infinitely many complements are discovered, then the algorithm returns fail. returns the extension corresponding to the 2-cocycle c. returns all extensions using the correspondence to Z^2(U,A). Further, this function returns fail, if Z^2(U,A) is infinite. returns extension classes using the correspondence to H^2(U,A). Further, this function returns fail, if H^2(U,A) is infinite. returns the split extension of U by the U-module described by mats.
Constructing pcp groups as extensions This section contains an example application of the second cohomology group to the construction of pcp groups as extensions. The following constructs extensions of the group of upper unitriangular matrices with its natural lattice. G := UnitriangularPcpGroup(3,0); Pcp-group with orders [ 0, 0, 0 ] gap> mats := G!.mats; [ [ [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ], [ [ 1, 0, 0 ], [ 0, 1, 1 ], [ 0, 0, 1 ] ], [ [ 1, 0, 1 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ] # set up the cohomology record gap> C := CRRecordByMats(G,mats);; # compute the second cohomology group gap> cc := TwoCohomologyCR(C);; # the abelian invariants of H^2(G,M) gap> cc.factor.rels; [ 2, 0, 0 ] # construct an extension which corresponds to a cocycle that has # infinite image in H^2(G,M) gap> c := cc.factor.prei[2]; [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ] gap> H := ExtensionCR( CR, c); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] # check that the extension does not split - get the normal subgroup gap> N := H!.module; Pcp-group with orders [ 0, 0, 0 ] # create the interal module gap> C := CRRecordBySubgroup(H,N);; # use the complements routine gap> ComplementClassesCR(C); [ ] ]]>
polycyclic-2.16/doc/polycyclic.toc0000644000076600000240000006247613706672374016307 0ustar mhornstaff\contentsline {chapter}{\numberline {1}\leavevmode {\color {Chapter }Preface}}{5}{chapter.1} \contentsline {chapter}{\numberline {2}\leavevmode {\color {Chapter }Introduction to polycyclic presentations}}{6}{chapter.2} \contentsline {chapter}{\numberline {3}\leavevmode {\color {Chapter }Collectors}}{8}{chapter.3} \contentsline {section}{\numberline {3.1}\leavevmode {\color {Chapter }Constructing a Collector}}{8}{section.3.1} \contentsline {subsection}{\numberline {3.1.1}\leavevmode {\color {Chapter }FromTheLeftCollector}}{8}{subsection.3.1.1} \contentsline {subsection}{\numberline {3.1.2}\leavevmode {\color {Chapter }SetRelativeOrder}}{9}{subsection.3.1.2} \contentsline {subsection}{\numberline {3.1.3}\leavevmode {\color {Chapter }SetPower}}{9}{subsection.3.1.3} \contentsline {subsection}{\numberline {3.1.4}\leavevmode {\color {Chapter }SetConjugate}}{10}{subsection.3.1.4} \contentsline {subsection}{\numberline {3.1.5}\leavevmode {\color {Chapter }SetCommutator}}{10}{subsection.3.1.5} \contentsline {subsection}{\numberline {3.1.6}\leavevmode {\color {Chapter }UpdatePolycyclicCollector}}{10}{subsection.3.1.6} \contentsline {subsection}{\numberline {3.1.7}\leavevmode {\color {Chapter }IsConfluent}}{10}{subsection.3.1.7} \contentsline {section}{\numberline {3.2}\leavevmode {\color {Chapter }Accessing Parts of a Collector}}{11}{section.3.2} \contentsline {subsection}{\numberline {3.2.1}\leavevmode {\color {Chapter }RelativeOrders}}{11}{subsection.3.2.1} \contentsline {subsection}{\numberline {3.2.2}\leavevmode {\color {Chapter }GetPower}}{11}{subsection.3.2.2} \contentsline {subsection}{\numberline {3.2.3}\leavevmode {\color {Chapter }GetConjugate}}{12}{subsection.3.2.3} \contentsline {subsection}{\numberline {3.2.4}\leavevmode {\color {Chapter }NumberOfGenerators}}{12}{subsection.3.2.4} \contentsline {subsection}{\numberline {3.2.5}\leavevmode {\color {Chapter }ObjByExponents}}{12}{subsection.3.2.5} \contentsline {subsection}{\numberline {3.2.6}\leavevmode {\color {Chapter }ExponentsByObj}}{12}{subsection.3.2.6} \contentsline {section}{\numberline {3.3}\leavevmode {\color {Chapter }Special Features}}{13}{section.3.3} \contentsline {subsection}{\numberline {3.3.1}\leavevmode {\color {Chapter }IsWeightedCollector}}{13}{subsection.3.3.1} \contentsline {subsection}{\numberline {3.3.2}\leavevmode {\color {Chapter }AddHallPolynomials}}{13}{subsection.3.3.2} \contentsline {subsection}{\numberline {3.3.3}\leavevmode {\color {Chapter }String}}{13}{subsection.3.3.3} \contentsline {subsection}{\numberline {3.3.4}\leavevmode {\color {Chapter }FTLCollectorPrintTo}}{13}{subsection.3.3.4} \contentsline {subsection}{\numberline {3.3.5}\leavevmode {\color {Chapter }FTLCollectorAppendTo}}{13}{subsection.3.3.5} \contentsline {subsection}{\numberline {3.3.6}\leavevmode {\color {Chapter }UseLibraryCollector}}{13}{subsection.3.3.6} \contentsline {subsection}{\numberline {3.3.7}\leavevmode {\color {Chapter }USE{\textunderscore }LIBRARY{\textunderscore }COLLECTOR}}{14}{subsection.3.3.7} \contentsline {subsection}{\numberline {3.3.8}\leavevmode {\color {Chapter }DEBUG{\textunderscore }COMBINATORIAL{\textunderscore }COLLECTOR}}{14}{subsection.3.3.8} \contentsline {subsection}{\numberline {3.3.9}\leavevmode {\color {Chapter }USE{\textunderscore }COMBINATORIAL{\textunderscore }COLLECTOR}}{14}{subsection.3.3.9} \contentsline {chapter}{\numberline {4}\leavevmode {\color {Chapter }Pcp-groups - polycyclically presented groups}}{15}{chapter.4} \contentsline {section}{\numberline {4.1}\leavevmode {\color {Chapter }Pcp-elements -- elements of a pc-presented group}}{15}{section.4.1} \contentsline {subsection}{\numberline {4.1.1}\leavevmode {\color {Chapter }PcpElementByExponentsNC}}{15}{subsection.4.1.1} \contentsline {subsection}{\numberline {4.1.2}\leavevmode {\color {Chapter }PcpElementByGenExpListNC}}{15}{subsection.4.1.2} \contentsline {subsection}{\numberline {4.1.3}\leavevmode {\color {Chapter }IsPcpElement}}{16}{subsection.4.1.3} \contentsline {subsection}{\numberline {4.1.4}\leavevmode {\color {Chapter }IsPcpElementCollection}}{16}{subsection.4.1.4} \contentsline {subsection}{\numberline {4.1.5}\leavevmode {\color {Chapter }IsPcpElementRep}}{16}{subsection.4.1.5} \contentsline {subsection}{\numberline {4.1.6}\leavevmode {\color {Chapter }IsPcpGroup}}{16}{subsection.4.1.6} \contentsline {section}{\numberline {4.2}\leavevmode {\color {Chapter }Methods for pcp-elements}}{16}{section.4.2} \contentsline {subsection}{\numberline {4.2.1}\leavevmode {\color {Chapter }Collector}}{16}{subsection.4.2.1} \contentsline {subsection}{\numberline {4.2.2}\leavevmode {\color {Chapter }Exponents}}{17}{subsection.4.2.2} \contentsline {subsection}{\numberline {4.2.3}\leavevmode {\color {Chapter }GenExpList}}{17}{subsection.4.2.3} \contentsline {subsection}{\numberline {4.2.4}\leavevmode {\color {Chapter }NameTag}}{17}{subsection.4.2.4} \contentsline {subsection}{\numberline {4.2.5}\leavevmode {\color {Chapter }Depth}}{17}{subsection.4.2.5} \contentsline {subsection}{\numberline {4.2.6}\leavevmode {\color {Chapter }LeadingExponent}}{17}{subsection.4.2.6} \contentsline {subsection}{\numberline {4.2.7}\leavevmode {\color {Chapter }RelativeOrder}}{17}{subsection.4.2.7} \contentsline {subsection}{\numberline {4.2.8}\leavevmode {\color {Chapter }RelativeIndex}}{17}{subsection.4.2.8} \contentsline {subsection}{\numberline {4.2.9}\leavevmode {\color {Chapter }FactorOrder}}{18}{subsection.4.2.9} \contentsline {subsection}{\numberline {4.2.10}\leavevmode {\color {Chapter }NormingExponent}}{18}{subsection.4.2.10} \contentsline {subsection}{\numberline {4.2.11}\leavevmode {\color {Chapter }NormedPcpElement}}{18}{subsection.4.2.11} \contentsline {section}{\numberline {4.3}\leavevmode {\color {Chapter }Pcp-groups - groups of pcp-elements}}{18}{section.4.3} \contentsline {subsection}{\numberline {4.3.1}\leavevmode {\color {Chapter }PcpGroupByCollector}}{18}{subsection.4.3.1} \contentsline {subsection}{\numberline {4.3.2}\leavevmode {\color {Chapter }Group}}{18}{subsection.4.3.2} \contentsline {subsection}{\numberline {4.3.3}\leavevmode {\color {Chapter }Subgroup}}{18}{subsection.4.3.3} \contentsline {chapter}{\numberline {5}\leavevmode {\color {Chapter }Basic methods and functions for pcp-groups}}{20}{chapter.5} \contentsline {section}{\numberline {5.1}\leavevmode {\color {Chapter }Elementary methods for pcp-groups}}{20}{section.5.1} \contentsline {subsection}{\numberline {5.1.1}\leavevmode {\color {Chapter }\texttt {\char 92\relax }=}}{20}{subsection.5.1.1} \contentsline {subsection}{\numberline {5.1.2}\leavevmode {\color {Chapter }Size}}{20}{subsection.5.1.2} \contentsline {subsection}{\numberline {5.1.3}\leavevmode {\color {Chapter }Random}}{20}{subsection.5.1.3} \contentsline {subsection}{\numberline {5.1.4}\leavevmode {\color {Chapter }Index}}{21}{subsection.5.1.4} \contentsline {subsection}{\numberline {5.1.5}\leavevmode {\color {Chapter }\texttt {\char 92\relax }in}}{21}{subsection.5.1.5} \contentsline {subsection}{\numberline {5.1.6}\leavevmode {\color {Chapter }Elements}}{21}{subsection.5.1.6} \contentsline {subsection}{\numberline {5.1.7}\leavevmode {\color {Chapter }ClosureGroup}}{21}{subsection.5.1.7} \contentsline {subsection}{\numberline {5.1.8}\leavevmode {\color {Chapter }NormalClosure}}{21}{subsection.5.1.8} \contentsline {subsection}{\numberline {5.1.9}\leavevmode {\color {Chapter }HirschLength}}{21}{subsection.5.1.9} \contentsline {subsection}{\numberline {5.1.10}\leavevmode {\color {Chapter }CommutatorSubgroup}}{21}{subsection.5.1.10} \contentsline {subsection}{\numberline {5.1.11}\leavevmode {\color {Chapter }PRump}}{21}{subsection.5.1.11} \contentsline {subsection}{\numberline {5.1.12}\leavevmode {\color {Chapter }SmallGeneratingSet}}{22}{subsection.5.1.12} \contentsline {section}{\numberline {5.2}\leavevmode {\color {Chapter }Elementary properties of pcp-groups}}{22}{section.5.2} \contentsline {subsection}{\numberline {5.2.1}\leavevmode {\color {Chapter }IsSubgroup}}{22}{subsection.5.2.1} \contentsline {subsection}{\numberline {5.2.2}\leavevmode {\color {Chapter }IsNormal}}{22}{subsection.5.2.2} \contentsline {subsection}{\numberline {5.2.3}\leavevmode {\color {Chapter }IsNilpotentGroup}}{22}{subsection.5.2.3} \contentsline {subsection}{\numberline {5.2.4}\leavevmode {\color {Chapter }IsAbelian}}{22}{subsection.5.2.4} \contentsline {subsection}{\numberline {5.2.5}\leavevmode {\color {Chapter }IsElementaryAbelian}}{22}{subsection.5.2.5} \contentsline {subsection}{\numberline {5.2.6}\leavevmode {\color {Chapter }IsFreeAbelian}}{22}{subsection.5.2.6} \contentsline {section}{\numberline {5.3}\leavevmode {\color {Chapter }Subgroups of pcp-groups}}{23}{section.5.3} \contentsline {subsection}{\numberline {5.3.1}\leavevmode {\color {Chapter }Igs}}{23}{subsection.5.3.1} \contentsline {subsection}{\numberline {5.3.2}\leavevmode {\color {Chapter }Ngs}}{23}{subsection.5.3.2} \contentsline {subsection}{\numberline {5.3.3}\leavevmode {\color {Chapter }Cgs}}{23}{subsection.5.3.3} \contentsline {subsection}{\numberline {5.3.4}\leavevmode {\color {Chapter }SubgroupByIgs}}{23}{subsection.5.3.4} \contentsline {subsection}{\numberline {5.3.5}\leavevmode {\color {Chapter }AddToIgs}}{24}{subsection.5.3.5} \contentsline {section}{\numberline {5.4}\leavevmode {\color {Chapter }Polycyclic presentation sequences for subfactors}}{24}{section.5.4} \contentsline {subsection}{\numberline {5.4.1}\leavevmode {\color {Chapter }Pcp}}{24}{subsection.5.4.1} \contentsline {subsection}{\numberline {5.4.2}\leavevmode {\color {Chapter }GeneratorsOfPcp}}{24}{subsection.5.4.2} \contentsline {subsection}{\numberline {5.4.3}\leavevmode {\color {Chapter }\texttt {\char 92\relax }[\texttt {\char 92\relax }]}}{25}{subsection.5.4.3} \contentsline {subsection}{\numberline {5.4.4}\leavevmode {\color {Chapter }Length}}{25}{subsection.5.4.4} \contentsline {subsection}{\numberline {5.4.5}\leavevmode {\color {Chapter }RelativeOrdersOfPcp}}{25}{subsection.5.4.5} \contentsline {subsection}{\numberline {5.4.6}\leavevmode {\color {Chapter }DenominatorOfPcp}}{25}{subsection.5.4.6} \contentsline {subsection}{\numberline {5.4.7}\leavevmode {\color {Chapter }NumeratorOfPcp}}{25}{subsection.5.4.7} \contentsline {subsection}{\numberline {5.4.8}\leavevmode {\color {Chapter }GroupOfPcp}}{25}{subsection.5.4.8} \contentsline {subsection}{\numberline {5.4.9}\leavevmode {\color {Chapter }OneOfPcp}}{25}{subsection.5.4.9} \contentsline {subsection}{\numberline {5.4.10}\leavevmode {\color {Chapter }ExponentsByPcp}}{26}{subsection.5.4.10} \contentsline {subsection}{\numberline {5.4.11}\leavevmode {\color {Chapter }PcpGroupByPcp}}{26}{subsection.5.4.11} \contentsline {section}{\numberline {5.5}\leavevmode {\color {Chapter }Factor groups of pcp-groups}}{27}{section.5.5} \contentsline {subsection}{\numberline {5.5.1}\leavevmode {\color {Chapter }NaturalHomomorphismByNormalSubgroup}}{27}{subsection.5.5.1} \contentsline {subsection}{\numberline {5.5.2}\leavevmode {\color {Chapter }\texttt {\char 92\relax }/}}{27}{subsection.5.5.2} \contentsline {section}{\numberline {5.6}\leavevmode {\color {Chapter }Homomorphisms for pcp-groups}}{27}{section.5.6} \contentsline {subsection}{\numberline {5.6.1}\leavevmode {\color {Chapter }GroupHomomorphismByImages}}{27}{subsection.5.6.1} \contentsline {subsection}{\numberline {5.6.2}\leavevmode {\color {Chapter }Kernel}}{27}{subsection.5.6.2} \contentsline {subsection}{\numberline {5.6.3}\leavevmode {\color {Chapter }Image}}{28}{subsection.5.6.3} \contentsline {subsection}{\numberline {5.6.4}\leavevmode {\color {Chapter }PreImage}}{28}{subsection.5.6.4} \contentsline {subsection}{\numberline {5.6.5}\leavevmode {\color {Chapter }PreImagesRepresentative}}{28}{subsection.5.6.5} \contentsline {subsection}{\numberline {5.6.6}\leavevmode {\color {Chapter }IsInjective}}{28}{subsection.5.6.6} \contentsline {section}{\numberline {5.7}\leavevmode {\color {Chapter }Changing the defining pc-presentation}}{28}{section.5.7} \contentsline {subsection}{\numberline {5.7.1}\leavevmode {\color {Chapter }RefinedPcpGroup}}{28}{subsection.5.7.1} \contentsline {subsection}{\numberline {5.7.2}\leavevmode {\color {Chapter }PcpGroupBySeries}}{28}{subsection.5.7.2} \contentsline {section}{\numberline {5.8}\leavevmode {\color {Chapter }Printing a pc-presentation}}{29}{section.5.8} \contentsline {subsection}{\numberline {5.8.1}\leavevmode {\color {Chapter }PrintPcpPresentation}}{29}{subsection.5.8.1} \contentsline {section}{\numberline {5.9}\leavevmode {\color {Chapter }Converting to and from a presentation}}{29}{section.5.9} \contentsline {subsection}{\numberline {5.9.1}\leavevmode {\color {Chapter }IsomorphismPcpGroup}}{29}{subsection.5.9.1} \contentsline {subsection}{\numberline {5.9.2}\leavevmode {\color {Chapter }IsomorphismPcpGroupFromFpGroupWithPcPres}}{30}{subsection.5.9.2} \contentsline {subsection}{\numberline {5.9.3}\leavevmode {\color {Chapter }IsomorphismPcGroup}}{30}{subsection.5.9.3} \contentsline {subsection}{\numberline {5.9.4}\leavevmode {\color {Chapter }IsomorphismFpGroup}}{30}{subsection.5.9.4} \contentsline {chapter}{\numberline {6}\leavevmode {\color {Chapter }Libraries and examples of pcp-groups}}{31}{chapter.6} \contentsline {section}{\numberline {6.1}\leavevmode {\color {Chapter }Libraries of various types of polycyclic groups}}{31}{section.6.1} \contentsline {subsection}{\numberline {6.1.1}\leavevmode {\color {Chapter }AbelianPcpGroup}}{31}{subsection.6.1.1} \contentsline {subsection}{\numberline {6.1.2}\leavevmode {\color {Chapter }DihedralPcpGroup}}{31}{subsection.6.1.2} \contentsline {subsection}{\numberline {6.1.3}\leavevmode {\color {Chapter }UnitriangularPcpGroup}}{31}{subsection.6.1.3} \contentsline {subsection}{\numberline {6.1.4}\leavevmode {\color {Chapter }SubgroupUnitriangularPcpGroup}}{31}{subsection.6.1.4} \contentsline {subsection}{\numberline {6.1.5}\leavevmode {\color {Chapter }InfiniteMetacyclicPcpGroup}}{32}{subsection.6.1.5} \contentsline {subsection}{\numberline {6.1.6}\leavevmode {\color {Chapter }HeisenbergPcpGroup}}{32}{subsection.6.1.6} \contentsline {subsection}{\numberline {6.1.7}\leavevmode {\color {Chapter }MaximalOrderByUnitsPcpGroup}}{32}{subsection.6.1.7} \contentsline {subsection}{\numberline {6.1.8}\leavevmode {\color {Chapter }BurdeGrunewaldPcpGroup}}{32}{subsection.6.1.8} \contentsline {section}{\numberline {6.2}\leavevmode {\color {Chapter }Some assorted example groups}}{32}{section.6.2} \contentsline {subsection}{\numberline {6.2.1}\leavevmode {\color {Chapter }ExampleOfMetabelianPcpGroup}}{32}{subsection.6.2.1} \contentsline {subsection}{\numberline {6.2.2}\leavevmode {\color {Chapter }ExamplesOfSomePcpGroups}}{33}{subsection.6.2.2} \contentsline {chapter}{\numberline {7}\leavevmode {\color {Chapter }Higher level methods for pcp-groups}}{34}{chapter.7} \contentsline {section}{\numberline {7.1}\leavevmode {\color {Chapter }Subgroup series in pcp-groups}}{34}{section.7.1} \contentsline {subsection}{\numberline {7.1.1}\leavevmode {\color {Chapter }PcpSeries}}{34}{subsection.7.1.1} \contentsline {subsection}{\numberline {7.1.2}\leavevmode {\color {Chapter }EfaSeries}}{34}{subsection.7.1.2} \contentsline {subsection}{\numberline {7.1.3}\leavevmode {\color {Chapter }SemiSimpleEfaSeries}}{34}{subsection.7.1.3} \contentsline {subsection}{\numberline {7.1.4}\leavevmode {\color {Chapter }DerivedSeriesOfGroup}}{34}{subsection.7.1.4} \contentsline {subsection}{\numberline {7.1.5}\leavevmode {\color {Chapter }RefinedDerivedSeries}}{35}{subsection.7.1.5} \contentsline {subsection}{\numberline {7.1.6}\leavevmode {\color {Chapter }RefinedDerivedSeriesDown}}{35}{subsection.7.1.6} \contentsline {subsection}{\numberline {7.1.7}\leavevmode {\color {Chapter }LowerCentralSeriesOfGroup}}{35}{subsection.7.1.7} \contentsline {subsection}{\numberline {7.1.8}\leavevmode {\color {Chapter }UpperCentralSeriesOfGroup}}{35}{subsection.7.1.8} \contentsline {subsection}{\numberline {7.1.9}\leavevmode {\color {Chapter }TorsionByPolyEFSeries}}{35}{subsection.7.1.9} \contentsline {subsection}{\numberline {7.1.10}\leavevmode {\color {Chapter }PcpsBySeries}}{36}{subsection.7.1.10} \contentsline {subsection}{\numberline {7.1.11}\leavevmode {\color {Chapter }PcpsOfEfaSeries}}{36}{subsection.7.1.11} \contentsline {section}{\numberline {7.2}\leavevmode {\color {Chapter }Orbit stabilizer methods for pcp-groups}}{37}{section.7.2} \contentsline {subsection}{\numberline {7.2.1}\leavevmode {\color {Chapter }PcpOrbitStabilizer}}{37}{subsection.7.2.1} \contentsline {subsection}{\numberline {7.2.2}\leavevmode {\color {Chapter }StabilizerIntegralAction}}{37}{subsection.7.2.2} \contentsline {subsection}{\numberline {7.2.3}\leavevmode {\color {Chapter }NormalizerIntegralAction}}{38}{subsection.7.2.3} \contentsline {section}{\numberline {7.3}\leavevmode {\color {Chapter }Centralizers, Normalizers and Intersections}}{39}{section.7.3} \contentsline {subsection}{\numberline {7.3.1}\leavevmode {\color {Chapter }Centralizer}}{39}{subsection.7.3.1} \contentsline {subsection}{\numberline {7.3.2}\leavevmode {\color {Chapter }Centralizer}}{39}{subsection.7.3.2} \contentsline {subsection}{\numberline {7.3.3}\leavevmode {\color {Chapter }Intersection}}{39}{subsection.7.3.3} \contentsline {section}{\numberline {7.4}\leavevmode {\color {Chapter }Finite subgroups}}{39}{section.7.4} \contentsline {subsection}{\numberline {7.4.1}\leavevmode {\color {Chapter }TorsionSubgroup}}{39}{subsection.7.4.1} \contentsline {subsection}{\numberline {7.4.2}\leavevmode {\color {Chapter }NormalTorsionSubgroup}}{40}{subsection.7.4.2} \contentsline {subsection}{\numberline {7.4.3}\leavevmode {\color {Chapter }IsTorsionFree}}{40}{subsection.7.4.3} \contentsline {subsection}{\numberline {7.4.4}\leavevmode {\color {Chapter }FiniteSubgroupClasses}}{40}{subsection.7.4.4} \contentsline {subsection}{\numberline {7.4.5}\leavevmode {\color {Chapter }FiniteSubgroupClassesBySeries}}{40}{subsection.7.4.5} \contentsline {section}{\numberline {7.5}\leavevmode {\color {Chapter }Subgroups of finite index and maximal subgroups}}{41}{section.7.5} \contentsline {subsection}{\numberline {7.5.1}\leavevmode {\color {Chapter }MaximalSubgroupClassesByIndex}}{41}{subsection.7.5.1} \contentsline {subsection}{\numberline {7.5.2}\leavevmode {\color {Chapter }LowIndexSubgroupClasses}}{41}{subsection.7.5.2} \contentsline {subsection}{\numberline {7.5.3}\leavevmode {\color {Chapter }LowIndexNormalSubgroups}}{41}{subsection.7.5.3} \contentsline {subsection}{\numberline {7.5.4}\leavevmode {\color {Chapter }NilpotentByAbelianNormalSubgroup}}{41}{subsection.7.5.4} \contentsline {section}{\numberline {7.6}\leavevmode {\color {Chapter }Further attributes for pcp-groups based on the Fitting subgroup}}{42}{section.7.6} \contentsline {subsection}{\numberline {7.6.1}\leavevmode {\color {Chapter }FittingSubgroup}}{42}{subsection.7.6.1} \contentsline {subsection}{\numberline {7.6.2}\leavevmode {\color {Chapter }IsNilpotentByFinite}}{42}{subsection.7.6.2} \contentsline {subsection}{\numberline {7.6.3}\leavevmode {\color {Chapter }Centre}}{42}{subsection.7.6.3} \contentsline {subsection}{\numberline {7.6.4}\leavevmode {\color {Chapter }FCCentre}}{42}{subsection.7.6.4} \contentsline {subsection}{\numberline {7.6.5}\leavevmode {\color {Chapter }PolyZNormalSubgroup}}{43}{subsection.7.6.5} \contentsline {subsection}{\numberline {7.6.6}\leavevmode {\color {Chapter }NilpotentByAbelianByFiniteSeries}}{43}{subsection.7.6.6} \contentsline {section}{\numberline {7.7}\leavevmode {\color {Chapter }Functions for nilpotent groups}}{43}{section.7.7} \contentsline {subsection}{\numberline {7.7.1}\leavevmode {\color {Chapter }MinimalGeneratingSet}}{43}{subsection.7.7.1} \contentsline {section}{\numberline {7.8}\leavevmode {\color {Chapter }Random methods for pcp-groups}}{44}{section.7.8} \contentsline {subsection}{\numberline {7.8.1}\leavevmode {\color {Chapter }RandomCentralizerPcpGroup}}{44}{subsection.7.8.1} \contentsline {section}{\numberline {7.9}\leavevmode {\color {Chapter }Non-abelian tensor product and Schur extensions}}{44}{section.7.9} \contentsline {subsection}{\numberline {7.9.1}\leavevmode {\color {Chapter }SchurExtension}}{44}{subsection.7.9.1} \contentsline {subsection}{\numberline {7.9.2}\leavevmode {\color {Chapter }SchurExtensionEpimorphism}}{45}{subsection.7.9.2} \contentsline {subsection}{\numberline {7.9.3}\leavevmode {\color {Chapter }SchurCover}}{46}{subsection.7.9.3} \contentsline {subsection}{\numberline {7.9.4}\leavevmode {\color {Chapter }AbelianInvariantsMultiplier}}{46}{subsection.7.9.4} \contentsline {subsection}{\numberline {7.9.5}\leavevmode {\color {Chapter }NonAbelianExteriorSquareEpimorphism}}{46}{subsection.7.9.5} \contentsline {subsection}{\numberline {7.9.6}\leavevmode {\color {Chapter }NonAbelianExteriorSquare}}{47}{subsection.7.9.6} \contentsline {subsection}{\numberline {7.9.7}\leavevmode {\color {Chapter }NonAbelianTensorSquareEpimorphism}}{47}{subsection.7.9.7} \contentsline {subsection}{\numberline {7.9.8}\leavevmode {\color {Chapter }NonAbelianTensorSquare}}{48}{subsection.7.9.8} \contentsline {subsection}{\numberline {7.9.9}\leavevmode {\color {Chapter }NonAbelianExteriorSquarePlusEmbedding}}{48}{subsection.7.9.9} \contentsline {subsection}{\numberline {7.9.10}\leavevmode {\color {Chapter }NonAbelianTensorSquarePlusEpimorphism}}{49}{subsection.7.9.10} \contentsline {subsection}{\numberline {7.9.11}\leavevmode {\color {Chapter }NonAbelianTensorSquarePlus}}{49}{subsection.7.9.11} \contentsline {subsection}{\numberline {7.9.12}\leavevmode {\color {Chapter }WhiteheadQuadraticFunctor}}{49}{subsection.7.9.12} \contentsline {section}{\numberline {7.10}\leavevmode {\color {Chapter }Schur covers}}{49}{section.7.10} \contentsline {subsection}{\numberline {7.10.1}\leavevmode {\color {Chapter }SchurCovers}}{49}{subsection.7.10.1} \contentsline {chapter}{\numberline {8}\leavevmode {\color {Chapter }Cohomology for pcp-groups}}{50}{chapter.8} \contentsline {section}{\numberline {8.1}\leavevmode {\color {Chapter }Cohomology records}}{50}{section.8.1} \contentsline {subsection}{\numberline {8.1.1}\leavevmode {\color {Chapter }CRRecordByMats}}{50}{subsection.8.1.1} \contentsline {subsection}{\numberline {8.1.2}\leavevmode {\color {Chapter }CRRecordBySubgroup}}{50}{subsection.8.1.2} \contentsline {section}{\numberline {8.2}\leavevmode {\color {Chapter }Cohomology groups}}{51}{section.8.2} \contentsline {subsection}{\numberline {8.2.1}\leavevmode {\color {Chapter }OneCoboundariesCR}}{51}{subsection.8.2.1} \contentsline {subsection}{\numberline {8.2.2}\leavevmode {\color {Chapter }TwoCohomologyModCR}}{52}{subsection.8.2.2} \contentsline {section}{\numberline {8.3}\leavevmode {\color {Chapter }Extended 1-cohomology}}{52}{section.8.3} \contentsline {subsection}{\numberline {8.3.1}\leavevmode {\color {Chapter }OneCoboundariesEX}}{53}{subsection.8.3.1} \contentsline {subsection}{\numberline {8.3.2}\leavevmode {\color {Chapter }OneCocyclesEX}}{53}{subsection.8.3.2} \contentsline {subsection}{\numberline {8.3.3}\leavevmode {\color {Chapter }OneCohomologyEX}}{53}{subsection.8.3.3} \contentsline {section}{\numberline {8.4}\leavevmode {\color {Chapter }Extensions and Complements}}{53}{section.8.4} \contentsline {subsection}{\numberline {8.4.1}\leavevmode {\color {Chapter } ComplementCR}}{53}{subsection.8.4.1} \contentsline {subsection}{\numberline {8.4.2}\leavevmode {\color {Chapter } ComplementsCR}}{54}{subsection.8.4.2} \contentsline {subsection}{\numberline {8.4.3}\leavevmode {\color {Chapter } ComplementClassesCR}}{54}{subsection.8.4.3} \contentsline {subsection}{\numberline {8.4.4}\leavevmode {\color {Chapter } ComplementClassesEfaPcps}}{54}{subsection.8.4.4} \contentsline {subsection}{\numberline {8.4.5}\leavevmode {\color {Chapter } ComplementClasses}}{54}{subsection.8.4.5} \contentsline {subsection}{\numberline {8.4.6}\leavevmode {\color {Chapter }ExtensionCR}}{54}{subsection.8.4.6} \contentsline {subsection}{\numberline {8.4.7}\leavevmode {\color {Chapter }ExtensionsCR}}{54}{subsection.8.4.7} \contentsline {subsection}{\numberline {8.4.8}\leavevmode {\color {Chapter }ExtensionClassesCR}}{55}{subsection.8.4.8} \contentsline {subsection}{\numberline {8.4.9}\leavevmode {\color {Chapter }SplitExtensionPcpGroup}}{55}{subsection.8.4.9} \contentsline {section}{\numberline {8.5}\leavevmode {\color {Chapter }Constructing pcp groups as extensions}}{55}{section.8.5} \contentsline {chapter}{\numberline {9}\leavevmode {\color {Chapter }Matrix Representations}}{57}{chapter.9} \contentsline {section}{\numberline {9.1}\leavevmode {\color {Chapter }Unitriangular matrix groups}}{57}{section.9.1} \contentsline {subsection}{\numberline {9.1.1}\leavevmode {\color {Chapter }UnitriangularMatrixRepresentation}}{57}{subsection.9.1.1} \contentsline {subsection}{\numberline {9.1.2}\leavevmode {\color {Chapter }IsMatrixRepresentation}}{57}{subsection.9.1.2} \contentsline {section}{\numberline {9.2}\leavevmode {\color {Chapter }Upper unitriangular matrix groups}}{57}{section.9.2} \contentsline {subsection}{\numberline {9.2.1}\leavevmode {\color {Chapter }IsomorphismUpperUnitriMatGroupPcpGroup}}{57}{subsection.9.2.1} \contentsline {subsection}{\numberline {9.2.2}\leavevmode {\color {Chapter }SiftUpperUnitriMatGroup}}{58}{subsection.9.2.2} \contentsline {subsection}{\numberline {9.2.3}\leavevmode {\color {Chapter }RanksLevels}}{58}{subsection.9.2.3} \contentsline {subsection}{\numberline {9.2.4}\leavevmode {\color {Chapter }MakeNewLevel}}{58}{subsection.9.2.4} \contentsline {subsection}{\numberline {9.2.5}\leavevmode {\color {Chapter }SiftUpperUnitriMat}}{58}{subsection.9.2.5} \contentsline {subsection}{\numberline {9.2.6}\leavevmode {\color {Chapter }DecomposeUpperUnitriMat}}{59}{subsection.9.2.6} \contentsline {chapter}{\numberline {A}\leavevmode {\color {Chapter }Obsolete Functions and Name Changes}}{60}{appendix.A} \contentsline {chapter}{References}{62}{appendix*.4} \contentsline {chapter}{Index}{63}{section*.5} polycyclic-2.16/doc/chap8.html0000644000076600000240000006770313706672374015315 0ustar mhornstaff GAP (polycyclic) - Chapter 8: Cohomology for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

8 Cohomology for pcp-groups

The GAP 4 package Polycyclic provides methods to compute the first and second cohomology group for a pcp-group U and a finite dimensional ℤ U or FU module A where F is a finite field. The algorithm for determining the first cohomology group is outlined in [Eic00].

As a preparation for the cohomology computation, we introduce the cohomology records. These records provide the technical setup for our cohomology computations.

8.1 Cohomology records

Cohomology records provide the necessary technical setup for the cohomology computations for polycyclic groups.

8.1-1 CRRecordByMats
‣ CRRecordByMats( U, mats )( function )

creates an external module. Let U be a pcp group which acts via the list of matrices mats on a vector space of the form ℤ^n or F_p^n. Then this function creates a record which can be used as input for the cohomology computations.

8.1-2 CRRecordBySubgroup
‣ CRRecordBySubgroup( U, A )( function )
‣ CRRecordByPcp( U, pcp )( function )

creates an internal module. Let U be a pcp group and let A be a normal elementary or free abelian normal subgroup of U or let pcp be a pcp of a normal elementary of free abelian subfactor of U. Then this function creates a record which can be used as input for the cohomology computations.

The returned cohomology record C contains the following entries:

factor

a pcp of the acting group. If the module is external, then this is Pcp(U). If the module is internal, then this is Pcp(U, A) or Pcp(U, GroupOfPcp(pcp)).

mats, invs and one

the matrix action of factor with acting matrices, their inverses and the identity matrix.

dim and char

the dimension and characteristic of the matrices.

relators and enumrels

the right hand sides of the polycyclic relators of factor as generator exponents lists and a description for the corresponding left hand sides.

central

is true, if the matrices mats are all trivial. This is used locally for efficiency reasons.

And additionally, if C defines an internal module, then it contains:

group

the original group U.

normal

this is either Pcp(A) or the input pcp.

extension

information on the extension of A by U/A.

8.2 Cohomology groups

Let U be a pcp-group and A a free or elementary abelian pcp-group and a U-module. By Z^i(U, A) be denote the group of i-th cocycles and by B^i(U, A) the i-th coboundaries. The factor Z^i(U,A) / B^i(U,A) is the i-th cohomology group. Since A is elementary or free abelian, the groups Z^i(U, A) and B^i(U, A) are elementary or free abelian groups as well.

The Polycyclic package provides methods to compute first and second cohomology group for a polycyclic group U. We write all involved groups additively and we use an explicit description by bases for them. Let C be the cohomology record corresponding to U and A.

Let f_1, ..., f_n be the elements in the entry factor of the cohomology record C. Then we use the following embedding of the first cocycle group to describe 1-cocycles and 1-coboundaries: Z^1(U, A) -> A^n : δ ↦ (δ(f_1), ..., δ(f_n))

For the second cohomology group we recall that each element of Z^2(U, A) defines an extension H of A by U. Thus there is a pc-presentation of H extending the pc-presentation of U given by the record C. The extended presentation is defined by tails in A; that is, each relator in the record entry relators is extended by an element of A. The concatenation of these tails yields a vector in A^l where l is the length of the record entry relators of C. We use these tail vectors to describe Z^2(U, A) and B^2(U, A). Note that this description is dependent on the chosen presentation in C. However, the factor Z^2(U, A)/ B^2(U, A) is independent of the chosen presentation.

The following functions are available to compute explicitly the first and second cohomology group as described above.

8.2-1 OneCoboundariesCR
‣ OneCoboundariesCR( C )( function )
‣ OneCocyclesCR( C )( function )
‣ TwoCoboundariesCR( C )( function )
‣ TwoCocyclesCR( C )( function )
‣ OneCohomologyCR( C )( function )
‣ TwoCohomologyCR( C )( function )

The first four functions return bases of the corresponding group. The last two functions need to describe a factor of additive abelian groups. They return the following descriptions for these factors.

gcc

the basis of the cocycles of C.

gcb

the basis of the coboundaries of C.

factor

a description of the factor of cocycles by coboundaries. Usually, it would be most convenient to use additive mappings here. However, these are not available in case that A is free abelian and thus we use a description of this additive map as record. This record contains

gens

a base for the image.

rels

relative orders for the image.

imgs

the images for the elements in gcc.

prei

preimages for the elements in gens.

denom

the kernel of the map; that is, another basis for gcb.

There is an additional function which can be used to compute the second cohomology group over an arbitrary finitely generated abelian group. The finitely generated abelian group should be realized as a factor of a free abelian group modulo a lattice. The function is called as

8.2-2 TwoCohomologyModCR
‣ TwoCohomologyModCR( C, lat )( function )

where C is a cohomology record and lat is a basis for a sublattice of a free abelian module. The output format is the same as for TwoCohomologyCR.

8.3 Extended 1-cohomology

In some cases more information on the first cohomology group is of interest. In particular, if we have an internal module given and we want to compute the complements using the first cohomology group, then we need additional information. This extended version of first cohomology is obtained by the following functions.

8.3-1 OneCoboundariesEX
‣ OneCoboundariesEX( C )( function )

returns a record consisting of the entries

basis

a basis for B^1(U, A) ≤ A^n.

transf

There is a derivation mapping from A to B^1(U,A). This mapping is described here as transformation from A to basis.

fixpts

the fixpoints of A. This is also the kernel of the derivation mapping.

8.3-2 OneCocyclesEX
‣ OneCocyclesEX( C )( function )

returns a record consisting of the entries

basis

a basis for Z^1(U, A) ≤ A^n.

transl

a special solution. This is only of interest in case that C is an internal module and in this case it gives the translation vector in A^n used to obtain complements corresponding to the elements in basis. If C is not an internal module, then this vector is always the zero vector.

8.3-3 OneCohomologyEX
‣ OneCohomologyEX( C )( function )

returns the combined information on the first cohomology group.

8.4 Extensions and Complements

The natural applications of first and second cohomology group is the determination of extensions and complements. Let C be a cohomology record.

8.4-1 ComplementCR
‣ ComplementCR( C, c )( function )

returns the complement corresponding to the 1-cocycle c. In the case that C is an external module, we construct the split extension of U with A first and then determine the complement. In the case that C is an internal module, the vector c must be an element of the affine space corresponding to the complements as described by OneCocyclesEX.

8.4-2 ComplementsCR
‣ ComplementsCR( C )( function )

returns all complements using the correspondence to Z^1(U,A). Further, this function returns fail, if Z^1(U,A) is infinite.

8.4-3 ComplementClassesCR
‣ ComplementClassesCR( C )( function )

returns complement classes using the correspondence to H^1(U,A). Further, this function returns fail, if H^1(U,A) is infinite.

8.4-4 ComplementClassesEfaPcps
‣ ComplementClassesEfaPcps( U, N, pcps )( function )

Let N be a normal subgroup of U. This function returns the complement classes to N in U. The classes are computed by iteration over the U-invariant efa series of N described by pcps. If at some stage in this iteration infinitely many complements are discovered, then the function returns fail. (Even though there might be only finitely many conjugacy classes of complements to N in U.)

8.4-5 ComplementClasses
‣ ComplementClasses( [V, ]U, N )( function )

Let N and U be normal subgroups of V with N ≤ U ≤ V. This function attempts to compute the V-conjugacy classes of complements to N in U. The algorithm proceeds by iteration over a V-invariant efa series of N. If at some stage in this iteration infinitely many complements are discovered, then the algorithm returns fail.

8.4-6 ExtensionCR
‣ ExtensionCR( C, c )( function )

returns the extension corresponding to the 2-cocycle c.

8.4-7 ExtensionsCR
‣ ExtensionsCR( C )( function )

returns all extensions using the correspondence to Z^2(U,A). Further, this function returns fail, if Z^2(U,A) is infinite.

8.4-8 ExtensionClassesCR
‣ ExtensionClassesCR( C )( function )

returns extension classes using the correspondence to H^2(U,A). Further, this function returns fail, if H^2(U,A) is infinite.

8.4-9 SplitExtensionPcpGroup
‣ SplitExtensionPcpGroup( U, mats )( function )

returns the split extension of U by the U-module described by mats.

8.5 Constructing pcp groups as extensions

This section contains an example application of the second cohomology group to the construction of pcp groups as extensions. The following constructs extensions of the group of upper unitriangular matrices with its natural lattice.

# get the group and its matrix action
gap> G := UnitriangularPcpGroup(3,0);
Pcp-group with orders [ 0, 0, 0 ]
gap> mats := G!.mats;
[ [ [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 1 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 1 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]

# set up the cohomology record
gap> C := CRRecordByMats(G,mats);;

# compute the second cohomology group
gap> cc := TwoCohomologyCR(C);;

# the abelian invariants of H^2(G,M)
gap> cc.factor.rels;
[ 2, 0, 0 ]

# construct an extension which corresponds to a cocycle that has
# infinite image in H^2(G,M)
gap> c := cc.factor.prei[2];
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ]
gap> H := ExtensionCR( CR, c);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]

# check that the extension does not split - get the normal subgroup
gap> N := H!.module;
Pcp-group with orders [ 0, 0, 0 ]

# create the interal module
gap> C := CRRecordBySubgroup(H,N);;

# use the complements routine
gap> ComplementClassesCR(C);
[  ]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/nocolorprompt.css0000644000076600000240000000031313706672374017033 0ustar mhornstaff /* colors for ColorPrompt like examples */ span.GAPprompt { color: #000000; font-weight: normal; } span.GAPbrkprompt { color: #000000; font-weight: normal; } span.GAPinput { color: #000000; } polycyclic-2.16/doc/lefttoc.css0000644000076600000240000000047413706672374015566 0ustar mhornstaff/* leftmenu.css Frank Lübeck */ /* Change default CSS to show section menu on left side */ body { padding-left: 28%; } body.chap0 { padding-left: 2%; } div.ChapSects div.ContSect:hover div.ContSSBlock { left: 15%; } div.ChapSects { left: 1%; width: 25%; } polycyclic-2.16/doc/chap9.html0000644000076600000240000002626013706672374015307 0ustar mhornstaff GAP (polycyclic) - Chapter 9: Matrix Representations
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

9 Matrix Representations

This chapter describes functions which compute with matrix representations for pcp-groups. So far the routines in this package are only able to compute matrix representations for torsion-free nilpotent groups.

9.1 Unitriangular matrix groups

9.1-1 UnitriangularMatrixRepresentation
‣ UnitriangularMatrixRepresentation( G )( operation )

computes a faithful representation of a torsion-free nilpotent group G as unipotent lower triangular matrices over the integers. The pc-presentation for G must not contain any power relations. The algorithm is described in [dGN02].

9.1-2 IsMatrixRepresentation
‣ IsMatrixRepresentation( G, matrices )( function )

checks if the map defined by mapping the i-th generator of the pcp-group G to the i-th matrix of matrices defines a homomorphism.

9.2 Upper unitriangular matrix groups

We call a matrix upper unitriangular if it is an upper triangular matrix with ones on the main diagonal. The weight of an upper unitriangular matrix is the number of diagonals above the main diagonal that contain zeroes only.

The subgroup of all upper unitriangular matrices of GL(n,ℤ) is torsion-free nilpotent. The k-th term of its lower central series is the set of all matrices of weight k-1. The -rank of the k-th term of the lower central series modulo the (k+1)-th term is n-k.

9.2-1 IsomorphismUpperUnitriMatGroupPcpGroup
‣ IsomorphismUpperUnitriMatGroupPcpGroup( G )( function )

takes a group G generated by upper unitriangular matrices over the integers and computes a polycylic presentation for the group. The function returns an isomorphism from the matrix group to the pcp group. Note that a group generated by upper unitriangular matrices is necessarily torsion-free nilpotent.

9.2-2 SiftUpperUnitriMatGroup
‣ SiftUpperUnitriMatGroup( G )( function )

takes a group G generated by upper unitriangular matrices over the integers and returns a recursive data structure L with the following properties: L contains a polycyclic generating sequence for G, using L one can decide if a given upper unitriangular matrix is contained in G, a given element of G can be written as a word in the polycyclic generating sequence. L is a representation of a chain of subgroups of G refining the lower centrals series of G.. It contains for each subgroup in the chain a minimal generating set.

9.2-3 RanksLevels
‣ RanksLevels( L )( function )

takes the data structure returned by SiftUpperUnitriMat and prints the -rank of each the subgroup in L.

9.2-4 MakeNewLevel
‣ MakeNewLevel( m )( function )

creates one level of the data structure returned by SiftUpperUnitriMat and initialises it with weight m.

9.2-5 SiftUpperUnitriMat
‣ SiftUpperUnitriMat( gens, level, M )( function )

takes the generators gens of an upper unitriangular group, the data structure returned level by SiftUpperUnitriMat and another upper unitriangular matrix M. It sift M through level and adds M at the appropriate place if M is not contained in the subgroup represented by level.

The function SiftUpperUnitriMatGroup illustrates the use of SiftUpperUnitriMat.

InstallGlobalFunction( "SiftUpperUnitriMatGroup", function( G )
    local   firstlevel,  g;

    firstlevel := MakeNewLevel( 0 );
    for g in GeneratorsOfGroup(G) do
        SiftUpperUnitriMat( GeneratorsOfGroup(G), firstlevel, g );
    od;
    return firstlevel;
end );

9.2-6 DecomposeUpperUnitriMat
‣ DecomposeUpperUnitriMat( level, M )( function )

takes the data structure level returned by SiftUpperUnitriMatGroup and a upper unitriangular matrix M and decomposes M into a word in the polycyclic generating sequence of level.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/intro.xml0000644000076600000240000001066013706672341015261 0ustar mhornstaff Introduction to polycyclic presentations Let G be a polycyclic group and let G = C_1 \rhd C_2 \ldots C_n\rhd C_{n+1} = 1 be a polycyclic series, that is, a subnormal series of G with non-trivial cyclic factors. For 1 \leq i \leq n we choose g_i \in C_i such that C_i = \langle g_i, C_{i+1} \rangle. Then the sequence (g_1, \ldots, g_n) is called a polycyclic generating sequence of G. Let I be the set of those i \in \{1, \ldots, n\} with r_i := [C_i : C_{i+1}] finite. Each element of G can be written uniquely as g_1^{e_1}\cdots g_n^{e_n} with e_i\in &ZZ; for 1\leq i\leq n and 0\leq e_i < r_i for i\in I.

Each polycyclic generating sequence of G gives rise to a power-conjugate (pc-) presentation for G with the conjugate relations g_j^{g_i} = g_{i+1}^{e(i,j,i+1)} \cdots g_n^{e(i,j,n)} \hbox{ for } 1 \leq i < j \leq n, g_j^{g_i^{-1}} = g_{i+1}^{f(i,j,i+1)} \cdots g_n^{f(i,j,n)} \hbox{ for } 1 \leq i < j \leq n, and the power relations g_i^{r_i} = g_{i+1}^{l(i,i+1)} \cdots g_n^{l(i,n)} \hbox{ for } i \in I.

Vice versa, we say that a group G is defined by a pc-presentation if G is given by a presentation of the form above on generators g_1,\ldots,g_n. These generators are the defining generators of G. Here, I is the set of 1\leq i\leq n such that g_i has a power relation. The positive integer r_i for i\in I is called the relative order of g_i. If G is given by a pc-presentation, then G is polycyclic. The subgroups C_i = \langle g_i, \ldots, g_n \rangle form a subnormal series G = C_1 \geq \ldots \geq C_{n+1} = 1 with cyclic factors and we have that g_i^{r_i}\in C_{i+1}. However, some of the factors of this series may be smaller than r_i for i\in I or finite if i\not\in I.

If G is defined by a pc-presentation, then each element of G can be described by a word of the form g_1^{e_1}\cdots g_n^{e_n} in the defining generators with e_i\in &ZZ; for 1\leq i\leq n and 0\leq e_i < r_i for i\in I. Such a word is said to be in collected form. In general, an element of the group can be represented by more than one collected word. If the pc-presentation has the property that each element of G has precisely one word in collected form, then the presentation is called confluent or consistent. If that is the case, the generators with a power relation correspond precisely to the finite factors in the polycyclic series and r_i is the order of C_i/C_{i+1}.

The &GAP; package &Polycyclic; is designed for computations with polycyclic groups which are given by consistent pc-presentations. In particular, all the functions described below assume that we compute with a group defined by a consistent pc-presentation. See Chapter for a routine that checks the consistency of a pc-presentation.

A pc-presentation can be interpreted as a rewriting system in the following way. One needs to add a new generator G_i for each generator g_i together with the relations g_iG_i = 1 and G_ig_i = 1. Any occurrence in a relation of an inverse generator g_i^{-1} is replaced by G_i. In this way one obtains a monoid presentation for the group G. With respect to a particular ordering on the set of monoid words in the generators g_1,\ldots g_n,G_1,\ldots G_n, the wreath product ordering, this monoid presentation is a rewriting system. If the pc-presentation is consistent, the rewriting system is confluent.

In this package we do not address this aspect of pc-presentations because it is of little relevance for the algorithms implemented here. For the definition of rewriting systems and confluence in this context as well as further details on the connections between pc-presentations and rewriting systems we recommend the book . polycyclic-2.16/doc/chap2_mj.html0000644000076600000240000002134213706672374015762 0ustar mhornstaff GAP (polycyclic) - Chapter 2: Introduction to polycyclic presentations

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

2 Introduction to polycyclic presentations

Let \(G\) be a polycyclic group and let \(G = C_1 \rhd C_2 \ldots C_n\rhd C_{n+1} = 1\) be a polycyclic series, that is, a subnormal series of \(G\) with non-trivial cyclic factors. For \(1 \leq i \leq n\) we choose \(g_i \in C_i\) such that \(C_i = \langle g_i, C_{i+1} \rangle\). Then the sequence \((g_1, \ldots, g_n)\) is called a polycyclic generating sequence of \(G\). Let \(I\) be the set of those \(i \in \{1, \ldots, n\}\) with \(r_i := [C_i : C_{i+1}]\) finite. Each element of \(G\) can be written uniquely as \(g_1^{e_1}\cdots g_n^{e_n}\) with \(e_i\in ℤ\) for \(1\leq i\leq n\) and \(0\leq e_i < r_i\) for \(i\in I\).

Each polycyclic generating sequence of \(G\) gives rise to a power-conjugate (pc-) presentation for \(G\) with the conjugate relations

\[g_j^{g_i} = g_{i+1}^{e(i,j,i+1)} \cdots g_n^{e(i,j,n)} \hbox{ for } 1 \leq i < j \leq n,\]

\[g_j^{g_i^{-1}} = g_{i+1}^{f(i,j,i+1)} \cdots g_n^{f(i,j,n)} \hbox{ for } 1 \leq i < j \leq n,\]

and the power relations

\[g_i^{r_i} = g_{i+1}^{l(i,i+1)} \cdots g_n^{l(i,n)} \hbox{ for } i \in I.\]

Vice versa, we say that a group \(G\) is defined by a pc-presentation if \(G\) is given by a presentation of the form above on generators \(g_1,\ldots,g_n\). These generators are the defining generators of \(G\). Here, \(I\) is the set of \(1\leq i\leq n\) such that \(g_i\) has a power relation. The positive integer \(r_i\) for \(i\in I\) is called the relative order of \(g_i\). If \(G\) is given by a pc-presentation, then \(G\) is polycyclic. The subgroups \(C_i = \langle g_i, \ldots, g_n \rangle\) form a subnormal series \(G = C_1 \geq \ldots \geq C_{n+1} = 1\) with cyclic factors and we have that \(g_i^{r_i}\in C_{i+1}\). However, some of the factors of this series may be smaller than \(r_i\) for \(i\in I\) or finite if \(i\not\in I\).

If \(G\) is defined by a pc-presentation, then each element of \(G\) can be described by a word of the form \(g_1^{e_1}\cdots g_n^{e_n}\) in the defining generators with \(e_i\in ℤ\) for \(1\leq i\leq n\) and \(0\leq e_i < r_i\) for \(i\in I\). Such a word is said to be in collected form. In general, an element of the group can be represented by more than one collected word. If the pc-presentation has the property that each element of \(G\) has precisely one word in collected form, then the presentation is called confluent or consistent. If that is the case, the generators with a power relation correspond precisely to the finite factors in the polycyclic series and \(r_i\) is the order of \(C_i/C_{i+1}\).

The GAP package Polycyclic is designed for computations with polycyclic groups which are given by consistent pc-presentations. In particular, all the functions described below assume that we compute with a group defined by a consistent pc-presentation. See Chapter Collectors for a routine that checks the consistency of a pc-presentation.

A pc-presentation can be interpreted as a rewriting system in the following way. One needs to add a new generator \(G_i\) for each generator \(g_i\) together with the relations \(g_iG_i = 1\) and \(G_ig_i = 1\). Any occurrence in a relation of an inverse generator \(g_i^{-1}\) is replaced by \(G_i\). In this way one obtains a monoid presentation for the group \(G\). With respect to a particular ordering on the set of monoid words in the generators \(g_1,\ldots g_n,G_1,\ldots G_n\), the wreath product ordering, this monoid presentation is a rewriting system. If the pc-presentation is consistent, the rewriting system is confluent.

In this package we do not address this aspect of pc-presentations because it is of little relevance for the algorithms implemented here. For the definition of rewriting systems and confluence in this context as well as further details on the connections between pc-presentations and rewriting systems we recommend the book [Sim94].

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap5.html0000644000076600000240000014006113706672374015277 0ustar mhornstaff GAP (polycyclic) - Chapter 5: Basic methods and functions for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

5 Basic methods and functions for pcp-groups

5 Basic methods and functions for pcp-groups

Pcp-groups are groups in the GAP sense and hence all generic GAP methods for groups can be applied for pcp-groups. However, for a number of group theoretic questions GAP does not provide generic methods that can be applied to pcp-groups. For some of these questions there are functions provided in Polycyclic.

5.1 Elementary methods for pcp-groups

In this chapter we describe some important basic functions which are available for pcp-groups. A number of higher level functions are outlined in later sections and chapters.

Let U, V and N be subgroups of a pcp-group.

5.1-1 \=
‣ \=( U, V )( method )

decides if U and V are equal as sets.

5.1-2 Size
‣ Size( U )( method )

returns the size of U.

5.1-3 Random
‣ Random( U )( method )

returns a random element of U.

5.1-4 Index
‣ Index( U, V )( method )

returns the index of V in U if V is a subgroup of U. The function does not check if V is a subgroup of U and if it is not, the result is not meaningful.

5.1-5 \in
‣ \in( g, U )( method )

checks if g is an element of U.

5.1-6 Elements
‣ Elements( U )( method )

returns a list containing all elements of U if U is finite and it returns the list [fail] otherwise.

5.1-7 ClosureGroup
‣ ClosureGroup( U, V )( method )

returns the group generated by U and V.

5.1-8 NormalClosure
‣ NormalClosure( U, V )( method )

returns the normal closure of V under action of U.

5.1-9 HirschLength
‣ HirschLength( U )( method )

returns the Hirsch length of U.

5.1-10 CommutatorSubgroup
‣ CommutatorSubgroup( U, V )( method )

returns the group generated by all commutators [u,v] with u in U and v in V.

5.1-11 PRump
‣ PRump( U, p )( method )

returns the subgroup U'U^p of U where p is a prime number.

5.1-12 SmallGeneratingSet
‣ SmallGeneratingSet( U )( method )

returns a small generating set for U.

5.2 Elementary properties of pcp-groups

5.2-1 IsSubgroup
‣ IsSubgroup( U, V )( function )

tests if V is a subgroup of U.

5.2-2 IsNormal
‣ IsNormal( U, V )( function )

tests if V is normal in U.

5.2-3 IsNilpotentGroup
‣ IsNilpotentGroup( U )( method )

checks whether U is nilpotent.

5.2-4 IsAbelian
‣ IsAbelian( U )( method )

checks whether U is abelian.

5.2-5 IsElementaryAbelian
‣ IsElementaryAbelian( U )( method )

checks whether U is elementary abelian.

5.2-6 IsFreeAbelian
‣ IsFreeAbelian( U )( property )

checks whether U is free abelian.

5.3 Subgroups of pcp-groups

A subgroup of a pcp-group G can be defined by a set of generators as described in Section 4.3. However, many computations with a subgroup U need an induced generating sequence or igs of U. An igs is a sequence of generators of U whose list of exponent vectors form a matrix in upper triangular form. Note that there may exist many igs of U. The first one calculated for U is stored as an attribute.

An induced generating sequence of a subgroup of a pcp-group G is a list of elements of G. An igs is called normed, if each element in the list is normed. Moreover, it is canonical, if the exponent vector matrix is in Hermite Normal Form. The following functions can be used to compute induced generating sequence for a given subgroup U of G.

5.3-1 Igs
‣ Igs( U )( attribute )
‣ Igs( gens )( function )
‣ IgsParallel( gens, gens2 )( function )

returns an induced generating sequence of the subgroup U of a pcp-group. In the second form the subgroup is given via a generating set gens. The third form computes an igs for the subgroup generated by gens carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list.

5.3-2 Ngs
‣ Ngs( U )( attribute )
‣ Ngs( igs )( function )

returns a normed induced generating sequence of the subgroup U of a pcp-group. The second form takes an igs as input and norms it.

5.3-3 Cgs
‣ Cgs( U )( attribute )
‣ Cgs( igs )( function )
‣ CgsParallel( gens, gens2 )( function )

returns a canonical generating sequence of the subgroup U of a pcp-group. In the second form the function takes an igs as input and returns a canonical generating sequence. The third version takes a generating set and computes a canonical generating sequence carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list.

For a large number of methods for pcp-groups U we will first of all determine an igs for U. Hence it might speed up computations, if a known igs for a group U is set a priori. The following functions can be used for this purpose.

5.3-4 SubgroupByIgs
‣ SubgroupByIgs( G, igs )( function )
‣ SubgroupByIgs( G, igs, gens )( function )

returns the subgroup of the pcp-group G generated by the elements of the induced generating sequence igs. Note that igs must be an induced generating sequence of the subgroup generated by the elements of the igs. In the second form igs is a igs for a subgroup and gens are some generators. The function returns the subgroup generated by igs and gens.

5.3-5 AddToIgs
‣ AddToIgs( igs, gens )( function )
‣ AddToIgsParallel( igs, gens, igs2, gens2 )( function )
‣ AddIgsToIgs( igs, igs2 )( function )

sifts the elements in the list gens into igs. The second version has the same functionality and carries shadows. This means that each operation that is applied to the first list and the element gens is also applied to the second list and the element gens2. The third version is available for efficiency reasons and assumes that the second list igs2 is not only a generating set, but an igs.

5.4 Polycyclic presentation sequences for subfactors

A subfactor of a pcp-group G is again a polycyclic group for which a polycyclic presentation can be computed. However, to compute a polycyclic presentation for a given subfactor can be time-consuming. Hence we introduce polycyclic presentation sequences or Pcp to compute more efficiently with subfactors. (Note that a subgroup is also a subfactor and thus can be handled by a pcp)

A pcp for a pcp-group U or a subfactor U / N can be created with one of the following functions.

5.4-1 Pcp
‣ Pcp( U[, flag] )( function )
‣ Pcp( U, N[, flag] )( function )

returns a polycyclic presentation sequence for the subgroup U or the quotient group U modulo N. If the parameter flag is present and equals the string "snf", the function can only be applied to an abelian subgroup U or abelian subfactor U/N. The pcp returned will correspond to a decomposition of the abelian group into a direct product of cyclic groups.

A pcp is a component object which behaves similar to a list representing an igs of the subfactor in question. The basic functions to obtain the stored values of this component object are as follows. Let pcp be a pcp for a subfactor U/N of the defining pcp-group G.

5.4-2 GeneratorsOfPcp
‣ GeneratorsOfPcp( pcp )( function )

this returns a list of elements of U corresponding to an igs of U/N.

5.4-3 \[\]
‣ \[\]( pcp, i )( method )

returns the i-th element of pcp.

5.4-4 Length
‣ Length( pcp )( method )

returns the number of generators in pcp.

5.4-5 RelativeOrdersOfPcp
‣ RelativeOrdersOfPcp( pcp )( function )

the relative orders of the igs in U/N.

5.4-6 DenominatorOfPcp
‣ DenominatorOfPcp( pcp )( function )

returns an igs of N.

5.4-7 NumeratorOfPcp
‣ NumeratorOfPcp( pcp )( function )

returns an igs of U.

5.4-8 GroupOfPcp
‣ GroupOfPcp( pcp )( function )

returns U.

5.4-9 OneOfPcp
‣ OneOfPcp( pcp )( function )

returns the identity element of G.

The main feature of a pcp are the possibility to compute exponent vectors without having to determine an explicit pcp-group corresponding to the subfactor that is represented by the pcp. Nonetheless, it is possible to determine this subfactor.

5.4-10 ExponentsByPcp
‣ ExponentsByPcp( pcp, g )( function )

returns the exponent vector of g with respect to the generators of pcp. This is the exponent vector of gN with respect to the igs of U/N.

5.4-11 PcpGroupByPcp
‣ PcpGroupByPcp( pcp )( function )

let pcp be a Pcp of a subgroup or a factor group of a pcp-group. This function computes a new pcp-group whose defining generators correspond to the generators in pcp.

gap>  G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap>  pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]
gap>  pcp[1];
g1
gap>  Length(pcp);
2
gap>  RelativeOrdersOfPcp(pcp);
[ 2, 0 ]
gap>  DenominatorOfPcp(pcp);
[  ]
gap>  NumeratorOfPcp(pcp);
[ g1, g2 ]
gap>  GroupOfPcp(pcp);
Pcp-group with orders [ 2, 0 ]
gap> OneOfPcp(pcp);
identity
gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> D := DerivedSubgroup( G );
Pcp-group with orders [ 0, 0, 0 ]
gap>  GeneratorsOfGroup( G );
[ g1, g2, g3, g4 ]
gap>  GeneratorsOfGroup( D );
[ g2^-2, g3^-2, g4^2 ]

# an ordinary pcp for G / D
gap> pcp1 := Pcp( G, D );
Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ]

# a pcp for G/D in independent generators
gap>  pcp2 := Pcp( G, D, "snf" );
Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ]

gap>  g := Random( G );
g1*g2^-4*g3*g4^2

# compute the exponent vector of g in G/D with respect to pcp1
gap> ExponentsByPcp( pcp1, g );
[ 1, 0, 1, 0 ]

# compute the exponent vector of g in G/D with respect to pcp2
gap>  ExponentsByPcp( pcp2, g );
[ 0, 1, 1 ]

5.5 Factor groups of pcp-groups

Pcp's for subfactors of pcp-groups have already been described above. These are usually used within algorithms to compute with pcp-groups. However, it is also possible to explicitly construct factor groups and their corresponding natural homomorphisms.

5.5-1 NaturalHomomorphismByNormalSubgroup
‣ NaturalHomomorphismByNormalSubgroup( G, N )( method )

returns the natural homomorphism G -> G/N. Its image is the factor group G/N.

5.5-2 \/
‣ \/( G, N )( method )
‣ FactorGroup( G, N )( method )

returns the desired factor as pcp-group without giving the explicit homomorphism. This function is just a wrapper for PcpGroupByPcp( Pcp( G, N ) ).

5.6 Homomorphisms for pcp-groups

Polycyclic provides code for defining group homomorphisms by generators and images where either the source or the range or both are pcp groups. All methods provided by GAP for such group homomorphisms are supported, in particular the following:

5.6-1 GroupHomomorphismByImages
‣ GroupHomomorphismByImages( G, H, gens, imgs )( function )

returns the homomorphism from the (pcp-) group G to the pcp-group H mapping the generators of G in the list gens to the corresponding images in the list imgs of elements of H.

5.6-2 Kernel
‣ Kernel( hom )( function )

returns the kernel of the homomorphism hom from a pcp-group to a pcp-group.

5.6-3 Image
‣ Image( hom )( operation )
‣ Image( hom, U )( function )
‣ Image( hom, g )( function )

returns the image of the whole group, of U and of g, respectively, under the homomorphism hom.

5.6-4 PreImage
‣ PreImage( hom, U )( function )

returns the complete preimage of the subgroup U under the homomorphism hom. If the domain of hom is not a pcp-group, then this function only works properly if hom is injective.

5.6-5 PreImagesRepresentative
‣ PreImagesRepresentative( hom, g )( method )

returns a preimage of the element g under the homomorphism hom.

5.6-6 IsInjective
‣ IsInjective( hom )( method )

checks if the homomorphism hom is injective.

5.7 Changing the defining pc-presentation

5.7-1 RefinedPcpGroup
‣ RefinedPcpGroup( G )( function )

returns a new pcp-group isomorphic to G whose defining polycyclic presentation is refined; that is, the corresponding polycyclic series has prime or infinite factors only. If H is the new group, then H!.bijection is the isomorphism G -> H.

5.7-2 PcpGroupBySeries
‣ PcpGroupBySeries( ser[, flag] )( function )

returns a new pcp-group isomorphic to the first subgroup G of the given series ser such that its defining pcp refines the given series. The series must be subnormal and H!.bijection is the isomorphism G -> H. If the parameter flag is present and equals the string "snf", the series must have abelian factors. The pcp of the group returned corresponds to a decomposition of each abelian factor into a direct product of cyclic groups.

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap>  U := Subgroup( G, [Pcp(G)[2]^1440]);
Pcp-group with orders [ 0 ]
gap>  F := G/U;
Pcp-group with orders [ 2, 1440 ]
gap> RefinedPcpGroup(F);
Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 3, 3, 5 ]

gap> ser := [G, U, TrivialSubgroup(G)];
[ Pcp-group with orders [ 2, 0 ],
  Pcp-group with orders [ 0 ],
  Pcp-group with orders [  ] ]
gap>  PcpGroupBySeries(ser);
Pcp-group with orders [ 2, 1440, 0 ]

5.8 Printing a pc-presentation

By default, a pcp-group is printed using its relative orders only. The following methods can be used to view the pcp presentation of the group.

5.8-1 PrintPcpPresentation
‣ PrintPcpPresentation( G[, flag] )( function )
‣ PrintPcpPresentation( pcp[, flag] )( function )

prints the pcp presentation defined by the igs of G or the pcp pcp. By default, the trivial conjugator relations are omitted from this presentation to shorten notation. Also, the relations obtained from conjugating with inverse generators are included only if the conjugating generator has infinite order. If this generator has finite order, then the conjugation relation is a consequence of the remaining relations. If the parameter flag is present and equals the string "all", all conjugate relations are printed, including the trivial conjugate relations as well as those involving conjugation with inverses.

5.9 Converting to and from a presentation

5.9-1 IsomorphismPcpGroup
‣ IsomorphismPcpGroup( G )( attribute )

returns an isomorphism from G onto a pcp-group H. There are various methods installed for this operation and some of these methods are part of the Polycyclic package, while others may be part of other packages.

For example, Polycyclic contains methods for this function in the case that G is a finite pc-group or a finite solvable permutation group.

Other examples for methods for IsomorphismPcpGroup are the methods for the case that G is a crystallographic group (see Cryst) or the case that G is an almost crystallographic group (see AClib). A method for the case that G is a rational polycyclic matrix group is included in the Polenta package.

5.9-2 IsomorphismPcpGroupFromFpGroupWithPcPres
‣ IsomorphismPcpGroupFromFpGroupWithPcPres( G )( function )

This function can convert a finitely presented group with a polycyclic presentation into a pcp group.

5.9-3 IsomorphismPcGroup
‣ IsomorphismPcGroup( G )( method )

pc-groups are a representation for finite polycyclic groups. This function can convert finite pcp-groups to pc-groups.

5.9-4 IsomorphismFpGroup
‣ IsomorphismFpGroup( G )( method )

This function can convert pcp-groups to a finitely presented group.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/libraries.xml0000644000076600000240000001157213706672341016105 0ustar mhornstaff Libraries and examples of pcp-groups
Libraries of various types of polycyclic groups There are the following generic pcp-groups available. constructs the abelian group on n generators such that generator i has order rels[i]. If this order is infinite, then rels[i] should be either unbound or 0. constructs the dihedral group of order n. If n is an odd integer, then 'fail' is returned. If n is zero or not an integer, then the infinite dihedral group is returned. returns a pcp-group isomorphic to the group of upper triangular in GL(n, R) where R = &ZZ; if c = 0 and R = \mathbb{F}_p if c = p. The natural unitriangular matrix representation of the returned pcp-group G can be obtained as G!.isomorphism. mats should be a list of upper unitriangular n \times n matrices over &ZZ; or over \mathbb{F}_p. This function returns the subgroup of the corresponding 'UnitriangularPcpGroup' generated by the matrices in mats. Infinite metacyclic groups are classified in . Every infinite metacyclic group G is isomorphic to a finitely presented group G(m,n,r) with two generators a and b and relations of the form a^m = b^n = 1 and [a,b] = a^{1-r}, where (differing from the conventions used by GAP) we have [a,b] = a b a^-1 b^-1, and m,n,r are three non-negative integers with mn=0 and r relatively prime to m. If r \equiv -1 mod m then n is even, and if r \equiv 1 mod m then m=0. Also m and n must not be 1.

Moreover, G(m,n,r)\cong G(m',n',s) if and only if m=m', n=n', and either r \equiv s or r \equiv s^{-1} mod m.

This function returns the metacyclic group with parameters n, m and r as a pcp-group with the pc-presentation \langle x,y | x^n, y^m, y^x = y^r\rangle. This presentation is easily transformed into the one above via the mapping x \mapsto b^{-1}, y \mapsto a. returns the Heisenberg group on 2n+1 generators as pcp-group. This gives a group of Hirsch length 2n+1. takes as input a normed, irreducible polynomial over the integers. Thus f defines a field extension F over the rationals. This function returns the split extension of the maximal order O of F by the unit group U of O, where U acts by right multiplication on O. returns a nilpotent group of Hirsch length 11 which has been constructed by Burde und Grunewald. If s is not 0, then this group has no faithful 12-dimensional linear representation.

Some assorted example groups The functions in this section provide some more example groups to play with. They come with no further description and their investigation is left to the interested user. returns an example of a metabelian group. The input parameters must be two positive integers greater than 1. this function takes values n in 1 up to 16 and returns for each input an example of a pcp-group. The groups in this example list have been used as test groups for the functions in this package.
polycyclic-2.16/doc/chap6.html0000644000076600000240000003121313706672374015276 0ustar mhornstaff GAP (polycyclic) - Chapter 6: Libraries and examples of pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

6 Libraries and examples of pcp-groups

6.1 Libraries of various types of polycyclic groups

There are the following generic pcp-groups available.

6.1-1 AbelianPcpGroup
‣ AbelianPcpGroup( n, rels )( function )

constructs the abelian group on n generators such that generator i has order rels[i]. If this order is infinite, then rels[i] should be either unbound or 0.

6.1-2 DihedralPcpGroup
‣ DihedralPcpGroup( n )( function )

constructs the dihedral group of order n. If n is an odd integer, then 'fail' is returned. If n is zero or not an integer, then the infinite dihedral group is returned.

6.1-3 UnitriangularPcpGroup
‣ UnitriangularPcpGroup( n, c )( function )

returns a pcp-group isomorphic to the group of upper triangular in GL(n, R) where R = ℤ if c = 0 and R = F_p if c = p. The natural unitriangular matrix representation of the returned pcp-group G can be obtained as G!.isomorphism.

6.1-4 SubgroupUnitriangularPcpGroup
‣ SubgroupUnitriangularPcpGroup( mats )( function )

mats should be a list of upper unitriangular n × n matrices over or over F_p. This function returns the subgroup of the corresponding 'UnitriangularPcpGroup' generated by the matrices in mats.

6.1-5 InfiniteMetacyclicPcpGroup
‣ InfiniteMetacyclicPcpGroup( n, m, r )( function )

Infinite metacyclic groups are classified in [BK00]. Every infinite metacyclic group G is isomorphic to a finitely presented group G(m,n,r) with two generators a and b and relations of the form a^m = b^n = 1 and [a,b] = a^1-r, where (differing from the conventions used by GAP) we have [a,b] = a b a^-1 b^-1, and m,n,r are three non-negative integers with mn=0 and r relatively prime to m. If r ≡ -1 mod m then n is even, and if r ≡ 1 mod m then m=0. Also m and n must not be 1.

Moreover, G(m,n,r)≅ G(m',n',s) if and only if m=m', n=n', and either r ≡ s or r ≡ s^-1 mod m.

This function returns the metacyclic group with parameters n, m and r as a pcp-group with the pc-presentation ⟨ x,y | x^n, y^m, y^x = y^r⟩. This presentation is easily transformed into the one above via the mapping x ↦ b^-1, y ↦ a.

6.1-6 HeisenbergPcpGroup
‣ HeisenbergPcpGroup( n )( function )

returns the Heisenberg group on 2n+1 generators as pcp-group. This gives a group of Hirsch length 2n+1.

6.1-7 MaximalOrderByUnitsPcpGroup
‣ MaximalOrderByUnitsPcpGroup( f )( function )

takes as input a normed, irreducible polynomial over the integers. Thus f defines a field extension F over the rationals. This function returns the split extension of the maximal order O of F by the unit group U of O, where U acts by right multiplication on O.

6.1-8 BurdeGrunewaldPcpGroup
‣ BurdeGrunewaldPcpGroup( s, t )( function )

returns a nilpotent group of Hirsch length 11 which has been constructed by Burde und Grunewald. If s is not 0, then this group has no faithful 12-dimensional linear representation.

6.2 Some assorted example groups

The functions in this section provide some more example groups to play with. They come with no further description and their investigation is left to the interested user.

6.2-1 ExampleOfMetabelianPcpGroup
‣ ExampleOfMetabelianPcpGroup( a, k )( function )

returns an example of a metabelian group. The input parameters must be two positive integers greater than 1.

6.2-2 ExamplesOfSomePcpGroups
‣ ExamplesOfSomePcpGroups( n )( function )

this function takes values n in 1 up to 16 and returns for each input an example of a pcp-group. The groups in this example list have been used as test groups for the functions in this package.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chapBib.txt0000644000076600000240000001071513706672367015506 0ustar mhornstaff References [BCRS91] Baumslag, G., Cannonito, F. B., Robinson, D. J. S. and Segal, D., The algorithmic theory of polycyclic-by-finite groups, J. Algebra, 142 (1991), 118--149. [BK00] Beuerle, J. R. and Kappe, L.-C., Infinite metacyclic groups and their non-abelian tensor squares, Proc. Edinburgh Math. Soc. (2), 43, 3 (2000), 651--662. [dGN02] de Graaf, W. A. and Nickel, W., Constructing faithful representations of finitely-generated torsion-free nilpotent groups, J. Symbolic Comput., 33, 1 (2002), 31--41. [Eic00] Eick, B., Computing with infinite polycyclic groups, in Groups and Computation III, (DIMACS, 1999), Amer. Math. Soc. DIMACS Series (2000). [Eic01a] Eick, B., Computations with polycyclic groups (2001), Habilitationsschrift, Kassel. [Eic01b] Eick, B., On the Fitting subgroup of a polycyclic-by-finite group and its applications, J. Algebra, 242 (2001), 176--187. [Eic02] Eick, B., Orbit-stabilizer problems and computing normalizers for polycyclic groups, J. Symbolic Comput., 34 (2002), 1--19. [EN08] Eick, B. and Nickel, W., Computing the Schur multiplicator and the non-abelian tensor square of a polycyclic group, J. Algebra, 320, 2 (2008), 927–-944. [EO02] Eick, B. and Ostheimer, G., On the orbit stabilizer problem for integral matrix actions of polycyclic groups, Accepted by Math. Comp (2002). [Hir38a] Hirsch, K. A., On Infinite Soluble Groups (I), Proc. London Math. Soc., 44, 2 (1938), 53-60. [Hir38b] Hirsch, K. A., On Infinite Soluble Groups (II), Proc. London Math. Soc., 44, 2 (1938), 336-414. [Hir46] Hirsch, K. A., On Infinite Soluble Groups (III), J. London Math. Soc., 49, 2 (1946), 184-94. [Hir52] Hirsch, K. A., On Infinite Soluble Groups (IV), J. London Math. Soc., 27 (1952), 81-85. [Hir54] Hirsch, K. A., On Infinite Soluble Groups (V), J. London Math. Soc., 29 (1954), 250-251. [LGS90] Leedham-Green, C. R. and Soicher, L. H., Collection from the left and other strategies, J. Symbolic Comput., 9, 5-6 (1990), 665--675. [LGS98] Leedham-Green, C. R. and Soicher, L. H., Symbolic collection using Deep Thought, LMS J. Comput. Math., 1 (1998), 9--24 (electronic). [Lo98a] Lo, E. H., Enumerating finite index subgroups of polycyclic groups (1998), Unpublished report. [Lo98b] Lo, E. H., Finding intersection and normalizer in finitely generated nilpotent groups, J. Symbolic Comput., 25 (1998), 45--59. [Mer97] Merkwitz, W. W., Symbolische Multiplikation in nilpotenten Gruppen mit Deep Thought, Diplomarbeit, RWTH Aachen (1997). [Rob82] Robinson, D. J., A Course in the Theory of Groups, Springer-Verlag, Graduate Texts in Math., 80, New York, Heidelberg, Berlin (1982). [Seg83] Segal, D., Polycyclic Groups, Cambridge University Press, Cambridge (1983). [Seg90] Segal, D., Decidable properties of polycyclic groups, Proc. London Math. Soc. (3), 61 (1990), 497-528. [Sim94] Sims, C. C., Computation with finitely presented groups, Cambridge University Press, Encyclopedia of Mathematics and its Applications, 48, Cambridge (1994). [VL90] Vaughan-Lee, M. R., Collection from the left, J. Symbolic Comput., 9, 5-6 (1990), 725--733.  polycyclic-2.16/doc/chap7_mj.html0000644000076600000240000020571313706672374015775 0ustar mhornstaff GAP (polycyclic) - Chapter 7: Higher level methods for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

7 Higher level methods for pcp-groups

7 Higher level methods for pcp-groups

This is a description of some higher level functions of the Polycyclic package of GAP 4. Throughout this chapter we let G be a pc-presented group and we consider algorithms for subgroups U and V of G. For background and a description of the underlying algorithms we refer to [Eic01a].

7.1 Subgroup series in pcp-groups

Many algorithm for pcp-groups work by induction using some series through the group. In this section we provide a number of useful series for pcp-groups. An efa series is a normal series with elementary or free abelian factors. See [Eic00] for outlines on the algorithms of a number of the available series.

7.1-1 PcpSeries
‣ PcpSeries( U )( function )

returns the polycyclic series of U defined by an igs of U.

7.1-2 EfaSeries
‣ EfaSeries( U )( attribute )

returns a normal series of U with elementary or free abelian factors.

7.1-3 SemiSimpleEfaSeries
‣ SemiSimpleEfaSeries( U )( attribute )

returns an efa series of U such that every factor in the series is semisimple as a module for U over a finite field or over the rationals.

7.1-4 DerivedSeriesOfGroup
‣ DerivedSeriesOfGroup( U )( method )

the derived series of U.

7.1-5 RefinedDerivedSeries
‣ RefinedDerivedSeries( U )( function )

the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the top.

7.1-6 RefinedDerivedSeriesDown
‣ RefinedDerivedSeriesDown( U )( function )

the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the bottom.

7.1-7 LowerCentralSeriesOfGroup
‣ LowerCentralSeriesOfGroup( U )( method )

the lower central series of U. If U does not have a largest nilpotent quotient group, then this function may not terminate.

7.1-8 UpperCentralSeriesOfGroup
‣ UpperCentralSeriesOfGroup( U )( method )

the upper central series of U. This function always terminates, but it may terminate at a proper subgroup of U.

7.1-9 TorsionByPolyEFSeries
‣ TorsionByPolyEFSeries( U )( function )

returns an efa series of U such that all torsion-free factors are at the top and all finite factors are at the bottom. Such a series might not exist for U and in this case the function returns fail.

gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> Igs(G);
[ g1, g2, g3, g4 ]

gap> PcpSeries(G);
[ Pcp-group with orders [ 2, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0 ],
  Pcp-group with orders [ 0, 0 ],
  Pcp-group with orders [ 0 ],
  Pcp-group with orders [  ] ]

gap> List( PcpSeries(G), Igs );
[ [ g1, g2, g3, g4 ], [ g2, g3, g4 ], [ g3, g4 ], [ g4 ], [  ] ]

Algorithms for pcp-groups often use an efa series of \(G\) and work down over the factors of this series. Usually, pcp's of the factors are more useful than the actual factors. Hence we provide the following.

7.1-10 PcpsBySeries
‣ PcpsBySeries( ser[, flag] )( function )

returns a list of pcp's corresponding to the factors of the series. If the parameter flag is present and equals the string "snf", then each pcp corresponds to a decomposition of the abelian groups into direct factors.

7.1-11 PcpsOfEfaSeries
‣ PcpsOfEfaSeries( U )( attribute )

returns a list of pcps corresponding to an efa series of U.

gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]

gap> PcpsBySeries( DerivedSeriesOfGroup(G));
[ Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ],
  Pcp [ g2^-2, g3^-2, g4^2 ] with orders [ 0, 0, 4 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]
gap> PcpsBySeries( RefinedDerivedSeries(G));
[ Pcp [ g1, g2, g3 ] with orders [ 2, 2, 2 ],
  Pcp [ g4 ] with orders [ 2 ],
  Pcp [ g2^2, g3^2 ] with orders [ 0, 0 ],
  Pcp [ g4^2 ] with orders [ 2 ],
  Pcp [ g4^4 ] with orders [ 2 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]

gap> PcpsBySeries( DerivedSeriesOfGroup(G), "snf" );
[ Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ],
  Pcp [ g4^2, g3^-2, g2^2*g4^2 ] with orders [ 4, 0, 0 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]
gap> G.1^4 in DerivedSubgroup( G );
true
gap> G.1^2 = G.4;
true

gap>  PcpsOfEfaSeries( G );
[ Pcp [ g1 ] with orders [ 2 ],
  Pcp [ g2 ] with orders [ 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ] ]

7.2 Orbit stabilizer methods for pcp-groups

Let U be a pcp-group which acts on a set \(\Omega\). One of the fundamental problems in algorithmic group theory is the determination of orbits and stabilizers of points in \(\Omega\) under the action of U. We distinguish two cases: the case that all considered orbits are finite and the case that there are infinite orbits. In the latter case, an orbit cannot be listed and a description of the orbit and its corresponding stabilizer is much harder to obtain.

If the considered orbits are finite, then the following two functions can be applied to compute the considered orbits and their corresponding stabilizers.

7.2-1 PcpOrbitStabilizer
‣ PcpOrbitStabilizer( point, gens, acts, oper )( function )
‣ PcpOrbitsStabilizers( points, gens, acts, oper )( function )

The input gens can be an igs or a pcp of a pcp-group U. The elements in the list gens act as the elements in the list acts via the function oper on the given points; that is, oper( point, acts[i] ) applies the \(i\)th generator to a given point. Thus the group defined by acts must be a homomorphic image of the group defined by gens. The first function returns a record containing the orbit as component 'orbit' and and igs for the stabilizer as component 'stab'. The second function returns a list of records, each record contains 'repr' and 'stab'. Both of these functions run forever on infinite orbits.

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> mats := [ [[-1,0],[0,1]], [[1,1],[0,1]] ];;
gap> pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]
gap> PcpOrbitStabilizer( [0,1], pcp, mats, OnRight );
rec( orbit := [ [ 0, 1 ] ],
     stab := [ g1, g2 ],
     word := [ [ [ 1, 1 ] ], [ [ 2, 1 ] ] ] )

If the considered orbits are infinite, then it may not always be possible to determine a description of the orbits and their stabilizers. However, as shown in [EO02] and [Eic02], it is possible to determine stabilizers and check if two elements are contained in the same orbit if the given action of the polycyclic group is a unimodular linear action on a vector space. The following functions are available for this case.

7.2-2 StabilizerIntegralAction
‣ StabilizerIntegralAction( U, mats, v )( function )
‣ OrbitIntegralAction( U, mats, v, w )( function )

The first function computes the stabilizer in U of the vector v where the pcp group U acts via mats on an integral space and v and w are elements in this integral space. The second function checks whether v and w are in the same orbit and the function returns either false or a record containing an element in U mapping v to w and the stabilizer of v.

7.2-3 NormalizerIntegralAction
‣ NormalizerIntegralAction( U, mats, B )( function )
‣ ConjugacyIntegralAction( U, mats, B, C )( function )

The first function computes the normalizer in U of the lattice with the basis B, where the pcp group U acts via mats on an integral space and B is a subspace of this integral space. The second functions checks whether the two lattices with the bases B and C are contained in the same orbit under U. The function returns either false or a record with an element in U mapping B to C and the stabilizer of B.

# get a pcp group and a free abelian normal subgroup
gap> G := ExamplesOfSomePcpGroups(8);
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> efa := EfaSeries(G);
[ Pcp-group with orders [ 0, 0, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0 ],
  Pcp-group with orders [  ] ]
gap> N := efa[3];
Pcp-group with orders [ 0, 0, 0 ]
gap> IsFreeAbelian(N);
true

# create conjugation action on N
gap> mats := LinearActionOnPcp(Igs(G), Pcp(N));
[ [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 0, 0, 1 ], [ 1, -1, 1 ], [ 0, 1, 0 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]

# take an arbitrary vector and compute its stabilizer
gap> StabilizerIntegralAction(G,mats, [2,3,4]);
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> Igs(last);
[ g1, g3, g4, g5 ]

# check orbits with some other vectors
gap> OrbitIntegralAction(G,mats, [2,3,4],[3,1,5]);
rec( stab := Pcp-group with orders [ 0, 0, 0, 0 ], prei := g2 )

gap> OrbitIntegralAction(G,mats, [2,3,4], [4,6,8]);
false

# compute the orbit of a subgroup of Z^3 under the action of G
gap> NormalizerIntegralAction(G, mats, [[1,0,0],[0,1,0]]);
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> Igs(last);
[ g1, g2^2, g3, g4, g5 ]

7.3 Centralizers, Normalizers and Intersections

In this section we list a number of operations for which there are methods installed to compute the corresponding features in polycyclic groups.

7.3-1 Centralizer
‣ Centralizer( U, g )( method )
‣ IsConjugate( U, g, h )( method )

These functions solve the conjugacy problem for elements in pcp-groups and they can be used to compute centralizers. The first method returns a subgroup of the given group U, the second method either returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in [EO02]. For nilpotent groups, an algorithm to solve the conjugacy problem for elements is described in [Sim94].

7.3-2 Centralizer
‣ Centralizer( U, V )( method )
‣ Normalizer( U, V )( method )
‣ IsConjugate( U, V, W )( method )

These three functions solve the conjugacy problem for subgroups and compute centralizers and normalizers of subgroups. The first two functions return subgroups of the input group U, the third function returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in [Eic02]. For nilpotent groups, an algorithm to solve the conjugacy problems for subgroups is described in [Lo98b].

7.3-3 Intersection
‣ Intersection( U, N )( function )

A general method to compute intersections of subgroups of a pcp-group is described in [Eic01a], but it is not yet implemented here. However, intersections of subgroups \(U, N \leq G\) can be computed if \(N\) is normalising \(U\). See [Sim94] for an outline of the algorithm.

7.4 Finite subgroups

There are various finite subgroups of interest in polycyclic groups. See [Eic00] for a description of the algorithms underlying the functions in this section.

7.4-1 TorsionSubgroup
‣ TorsionSubgroup( U )( attribute )

If the set of elements of finite order forms a subgroup, then we call it the torsion subgroup. This function determines the torsion subgroup of U, if it exists, and returns fail otherwise. Note that a torsion subgroup does always exist if U is nilpotent.

7.4-2 NormalTorsionSubgroup
‣ NormalTorsionSubgroup( U )( attribute )

Each polycyclic groups has a unique largest finite normal subgroup. This function computes it for U.

7.4-3 IsTorsionFree
‣ IsTorsionFree( U )( property )

This function checks if U is torsion free. It returns true or false.

7.4-4 FiniteSubgroupClasses
‣ FiniteSubgroupClasses( U )( attribute )

There exist only finitely many conjugacy classes of finite subgroups in a polycyclic group U and this function can be used to compute them. The algorithm underlying this function proceeds by working down a normal series of U with elementary or free abelian factors. The following function can be used to give the algorithm a specific series.

7.4-5 FiniteSubgroupClassesBySeries
‣ FiniteSubgroupClassesBySeries( U, pcps )( function )
gap> G := ExamplesOfSomePcpGroups(15);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0 ]
gap> TorsionSubgroup(G);
Pcp-group with orders [ 5, 2 ]
gap> NormalTorsionSubgroup(G);
Pcp-group with orders [ 5, 2 ]
gap> IsTorsionFree(G);
false
gap> FiniteSubgroupClasses(G);
[ Pcp-group with orders [ 5, 2 ]^G,
  Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [ 5 ]^G,
  Pcp-group with orders [  ]^G ]

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> TorsionSubgroup(G);
fail
gap> NormalTorsionSubgroup(G);
Pcp-group with orders [  ]
gap> IsTorsionFree(G);
false
gap> FiniteSubgroupClasses(G);
[ Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [  ]^G ]

7.5 Subgroups of finite index and maximal subgroups

Here we outline functions to determine various types of subgroups of finite index in polycyclic groups. Again, see [Eic00] for a description of the algorithms underlying the functions in this section. Also, we refer to [Lo98a] for an alternative approach.

7.5-1 MaximalSubgroupClassesByIndex
‣ MaximalSubgroupClassesByIndex( U, p )( operation )

Each maximal subgroup of a polycyclic group U has p-power index for some prime p. This function can be used to determine the conjugacy classes of all maximal subgroups of p-power index for a given prime p.

7.5-2 LowIndexSubgroupClasses
‣ LowIndexSubgroupClasses( U, n )( operation )

There are only finitely many subgroups of a given index in a polycyclic group U. This function computes conjugacy classes of all subgroups of index n in U.

7.5-3 LowIndexNormalSubgroups
‣ LowIndexNormalSubgroups( U, n )( operation )

This function computes the normal subgroups of index n in U.

7.5-4 NilpotentByAbelianNormalSubgroup
‣ NilpotentByAbelianNormalSubgroup( U )( function )

This function returns a normal subgroup N of finite index in U such that N is nilpotent-by-abelian. Such a subgroup exists in every polycyclic group and this function computes such a subgroup using LowIndexNormal. However, we note that this function is not very efficient and the function NilpotentByAbelianByFiniteSeries may well be more efficient on this task.

gap> G := ExamplesOfSomePcpGroups(2);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]

gap> MaximalSubgroupClassesByIndex( G, 61 );;
gap> max := List( last, Representative );;
gap> List( max, x -> Index( G, x ) );
[ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 226981 ]

gap> LowIndexSubgroupClasses( G, 61 );;
gap> low := List( last, Representative );;
gap> List( low, x -> Index( G, x ) );
[ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61 ]

7.6 Further attributes for pcp-groups based on the Fitting subgroup

In this section we provide a variety of other attributes for pcp-groups. Most of the methods below are based or related to the Fitting subgroup of the given group. We refer to [Eic01b] for a description of the underlying methods.

7.6-1 FittingSubgroup
‣ FittingSubgroup( U )( attribute )

returns the Fitting subgroup of U; that is, the largest nilpotent normal subgroup of U.

7.6-2 IsNilpotentByFinite
‣ IsNilpotentByFinite( U )( property )

checks whether the Fitting subgroup of U has finite index.

7.6-3 Centre
‣ Centre( U )( method )

returns the centre of U.

7.6-4 FCCentre
‣ FCCentre( U )( method )

returns the FC-centre of U; that is, the subgroup containing all elements having a finite conjugacy class in U.

7.6-5 PolyZNormalSubgroup
‣ PolyZNormalSubgroup( U )( function )

returns a normal subgroup N of finite index in U, such that N has a polycyclic series with infinite factors only.

7.6-6 NilpotentByAbelianByFiniteSeries
‣ NilpotentByAbelianByFiniteSeries( U )( function )

returns a normal series \(1 \leq F \leq A \leq U\) such that \(F\) is nilpotent, \(A/F\) is abelian and \(U/A\) is finite. This series is computed using the Fitting subgroup and the centre of the Fitting factor.

7.7 Functions for nilpotent groups

There are (very few) functions which are available for nilpotent groups only. First, there are the different central series. These are available for all groups, but for nilpotent groups they terminate and provide series through the full group. Secondly, the determination of a minimal generating set is available for nilpotent groups only.

7.7-1 MinimalGeneratingSet
‣ MinimalGeneratingSet( U )( method )
gap> G := ExamplesOfSomePcpGroups(14);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0, 5, 5, 4, 0, 6,
  5, 5, 4, 0, 10, 6 ]
gap> IsNilpotent(G);
true

gap> PcpsBySeries( LowerCentralSeriesOfGroup(G));
[ Pcp [ g1, g2 ] with orders [ 0, 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ],
  Pcp [ g5 ] with orders [ 0 ],
  Pcp [ g6, g7 ] with orders [ 0, 0 ],
  Pcp [ g8 ] with orders [ 0 ],
  Pcp [ g9, g10 ] with orders [ 0, 0 ],
  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],
  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],
  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]

gap> PcpsBySeries( UpperCentralSeriesOfGroup(G));
[ Pcp [ g1, g2 ] with orders [ 0, 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ],
  Pcp [ g5 ] with orders [ 0 ],
  Pcp [ g6, g7 ] with orders [ 0, 0 ],
  Pcp [ g8 ] with orders [ 0 ],
  Pcp [ g9, g10 ] with orders [ 0, 0 ],
  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],
  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],
  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]

gap> MinimalGeneratingSet(G);
[ g1, g2 ]

7.8 Random methods for pcp-groups

Below we introduce a function which computes orbit and stabilizer using a random method. This function tries to approximate the orbit and the stabilizer, but the returned orbit or stabilizer may be incomplete. This function is used in the random methods to compute normalizers and centralizers. Note that deterministic methods for these purposes are also available.

7.8-1 RandomCentralizerPcpGroup
‣ RandomCentralizerPcpGroup( U, g )( function )
‣ RandomCentralizerPcpGroup( U, V )( function )
‣ RandomNormalizerPcpGroup( U, V )( function )
gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> mats := [[[-1, 0],[0,1]], [[1,1],[0,1]]];
[ [ [ -1, 0 ], [ 0, 1 ] ], [ [ 1, 1 ], [ 0, 1 ] ] ]
gap> pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]

gap> RandomPcpOrbitStabilizer( [1,0], pcp, mats, OnRight ).stab;
#I  Orbit longer than limit: exiting.
[  ]

gap> g := Igs(G)[1];
g1
gap> RandomCentralizerPcpGroup( G, g );
#I  Stabilizer not increasing: exiting.
Pcp-group with orders [ 2 ]
gap> Igs(last);
[ g1 ]

7.9 Non-abelian tensor product and Schur extensions

7.9-1 SchurExtension
‣ SchurExtension( G )( attribute )

Let G be a polycyclic group with a polycyclic generating sequence consisting of \(n\) elements. This function computes the largest central extension H of G such that H is generated by \(n\) elements. If \(F/R\) is the underlying polycyclic presentation for G, then H is isomorphic to \(F/[R,F]\).

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> Centre( G );
Pcp-group with orders [  ]
gap> H := SchurExtension( G );
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> Centre( H );
Pcp-group with orders [ 0, 0 ]
gap> H/Centre(H);
Pcp-group with orders [ 2, 0 ]
gap> Subgroup( H, [H.1,H.2] ) = H;
true

7.9-2 SchurExtensionEpimorphism
‣ SchurExtensionEpimorphism( G )( attribute )

returns the projection from the Schur extension \(G^{*}\) of G onto G. See the function SchurExtension. The kernel of this epimorphism is the direct product of the Schur multiplicator of G and a direct product of \(n\) copies of \(ℤ\) where \(n\) is the number of generators in the polycyclic presentation for G. The Schur multiplicator is the intersection of the kernel and the derived group of the source. See also the function SchurCover.

gap> gl23 := Range( IsomorphismPcpGroup( GL(2,3) ) );
Pcp-group with orders [ 2, 3, 2, 2, 2 ]
gap> SchurExtensionEpimorphism( gl23 );
[ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 ] -> [ g1, g2, g3, g4, g5,
id, id, id, id, id ]
gap> Kernel( last );
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> AbelianInvariantsMultiplier( gl23 );
[  ]
gap> Intersection( Kernel(epi), DerivedSubgroup( Source(epi) ) );
[  ]

There is a crossed pairing from G into \((G^{*})'\) which can be defined via this epimorphism:

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> epi := SchurExtensionEpimorphism( G );
[ g1, g2, g3, g4 ] -> [ g1, g2, id, id ]
gap> PreImagesRepresentative( epi, G.1 );
g1
gap> PreImagesRepresentative( epi, G.2 );
g2
gap> Comm( last, last2 );
g2^-2*g4

7.9-3 SchurCover
‣ SchurCover( G )( function )

computes a Schur covering group of the polycyclic group G. A Schur covering is a largest central extension H of G such that the kernel M of the projection of H onto G is contained in the commutator subgroup of H.

If G is given by a presentation \(F/R\), then M is isomorphic to the subgroup \(R \cap [F,F] / [R,F]\). Let \(C\) be a complement to \(R \cap [F,F] / [R,F]\) in \(R/[R,F]\). Then \(F/C\) is isomorphic to H and \(R/C\) is isomorphic to M.

gap> G := AbelianPcpGroup( 3,[] );
Pcp-group with orders [ 0, 0, 0 ]
gap> ext := SchurCover( G );
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]
gap> Centre( ext );
Pcp-group with orders [ 0, 0, 0 ]
gap> IsSubgroup( DerivedSubgroup( ext ), last );
true

7.9-4 AbelianInvariantsMultiplier
‣ AbelianInvariantsMultiplier( G )( attribute )

returns a list of the abelian invariants of the Schur multiplier of G.

Note that the Schur multiplicator of a polycyclic group is a finitely generated abelian group.

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> DirectProduct( G, AbelianPcpGroup( 2, [] ) );
Pcp-group with orders [ 0, 0, 2, 0 ]
gap> AbelianInvariantsMultiplier( last );
[ 0, 2, 2, 2, 2 ]

7.9-5 NonAbelianExteriorSquareEpimorphism
‣ NonAbelianExteriorSquareEpimorphism( G )( function )

returns the epimorphism of the non-abelian exterior square of a polycyclic group G onto the derived group of G. The non-abelian exterior square can be defined as the derived subgroup of a Schur cover of G. The isomorphism type of the non-abelian exterior square is unique despite the fact that the isomorphism type of a Schur cover of a polycyclic groups need not be unique. The derived group of a Schur cover has a natural projection onto the derived group of G which is what the function returns.

The kernel of the epimorphism is isomorphic to the Schur multiplicator of G.

gap> G := ExamplesOfSomePcpGroups( 3 );
Pcp-group with orders [ 0, 0 ]
gap> G := DirectProduct( G,G );
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> AbelianInvariantsMultiplier( G );
[ [ 0, 1 ], [ 2, 3 ] ]
gap> epi := NonAbelianExteriorSquareEpimorphism( G );
[ g2^-2*g5, g4^-2*g10, g6, g7, g8, g9 ] -> [ g2^-2, g4^-2, id, id, id, id ]
gap> Kernel( epi );
Pcp-group with orders [ 0, 2, 2, 2 ]
gap> Collected( AbelianInvariants( last ) );
[ [ 0, 1 ], [ 2, 3 ] ]

7.9-6 NonAbelianExteriorSquare
‣ NonAbelianExteriorSquare( G )( attribute )

computes the non-abelian exterior square of a polycylic group G. See the explanation for NonAbelianExteriorSquareEpimorphism. The natural projection of the non-abelian exterior square onto the derived group of G is stored in the component !.epimorphism.

There is a crossed pairing from G into \(G\wedge G\). See the function SchurExtensionEpimorphism for details. The crossed pairing is stored in the component !.crossedPairing. This is the crossed pairing \(\lambda\) in [EN08].

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> GwG := NonAbelianExteriorSquare( G );
Pcp-group with orders [ 0 ]
gap> lambda := GwG!.crossedPairing;
function( g, h ) ... end
gap> lambda( G.1, G.2 );
g2^2*g4^-1

7.9-7 NonAbelianTensorSquareEpimorphism
‣ NonAbelianTensorSquareEpimorphism( G )( function )

returns for a polycyclic group G the projection of the non-abelian tensor square \(G\otimes G\) onto the non-abelian exterior square \(G\wedge G\). The range of that epimorphism has the component !.epimorphism set to the projection of the non-abelian exterior square onto the derived group of G. See also the function NonAbelianExteriorSquare.

With the result of this function one can compute the groups in the commutative diagram at the beginning of the paper [EN08]. The kernel of the returned epimorphism is the group \(\nabla(G)\). The kernel of the composition of this epimorphism and the above mention projection onto \(G'\) is the group \(J(G)\).

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> G := DirectProduct(G,G);
Pcp-group with orders [ 2, 0, 2, 0 ]
gap> alpha := NonAbelianTensorSquareEpimorphism( G );
[ g9*g25^-1, g10*g26^-1, g11*g27, g12*g28, g13*g29, g14*g30, g15, g16,
g17,
  g18, g19, g20, g21, g22, g23, g24 ] -> [ g2^-2*g6, g4^-2*g12, g8,
  g9, g10,
  g11, id, id, id, id, id, id, id, id, id, id ]
gap> gamma := Range( alpha )!.epimorphism;
[ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11 ] -> [ g2^-2, g4^-2, id, id,
id, id ]
gap> JG := Kernel( alpha * gamma );
Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]
gap> Image( alpha, JG );
Pcp-group with orders [ 2, 2, 2, 2 ]
gap> AbelianInvariantsMultiplier( G );
[ [ 2, 4 ] ]

7.9-8 NonAbelianTensorSquare
‣ NonAbelianTensorSquare( G )( attribute )

computes for a polycyclic group G the non-abelian tensor square \(G\otimes G\).

gap> G := AlternatingGroup( IsPcGroup, 4 );
<pc group of size 12 with 3 generators>
gap> PcGroupToPcpGroup( G );
Pcp-group with orders [ 3, 2, 2 ]
gap> NonAbelianTensorSquare( last );
Pcp-group with orders [ 2, 2, 2, 3 ]
gap> PcpGroupToPcGroup( last );
<pc group of size 24 with 4 generators>
gap> DirectFactorsOfGroup( last );
[ Group([ f1, f2, f3 ]), Group([ f4 ]) ]
gap> List( last, Size );
[ 8, 3 ]
gap> IdGroup( last2[1] );
[ 8, 4 ]       # the quaternion group of Order 8

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> ten := NonAbelianTensorSquare( G );
Pcp-group with orders [ 0, 2, 2, 2 ]
gap> IsAbelian( ten );
true

7.9-9 NonAbelianExteriorSquarePlusEmbedding
‣ NonAbelianExteriorSquarePlusEmbedding( G )( function )

returns an embedding from the non-abelian exterior square \(G\wedge G\) into an extensions of \(G\wedge G\) by \(G\times G\). For the significance of the group see the paper [EN08]. The range of the epimorphism is the group \(\tau(G)\) in that paper.

7.9-10 NonAbelianTensorSquarePlusEpimorphism
‣ NonAbelianTensorSquarePlusEpimorphism( G )( function )

returns an epimorphisms of \(\nu(G)\) onto \(\tau(G)\). The group \(\nu(G)\) is an extension of the non-abelian tensor square \(G\otimes G\) of \(G\) by \(G\times G\). The group \(\tau(G)\) is an extension of the non-abelian exterior square \(G\wedge G\) by \(G\times G\). For details see [EN08].

7.9-11 NonAbelianTensorSquarePlus
‣ NonAbelianTensorSquarePlus( G )( function )

returns the group \(\nu(G)\) in [EN08].

7.9-12 WhiteheadQuadraticFunctor
‣ WhiteheadQuadraticFunctor( G )( function )

returns Whitehead's universal quadratic functor of \(G\), see [EN08] for a description.

7.10 Schur covers

This section contains a function to determine the Schur covers of a finite \(p\)-group up to isomorphism.

7.10-1 SchurCovers
‣ SchurCovers( G )( function )

Let G be a finite \(p\)-group defined as a pcp group. This function returns a complete and irredundant set of isomorphism types of Schur covers of G. The algorithm implements a method of Nickel's Phd Thesis.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chapA.html0000644000076600000240000000772113706672374015320 0ustar mhornstaff GAP (polycyclic) - Appendix A: Obsolete Functions and Name Changes
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

A Obsolete Functions and Name Changes

Over time, the interface of Polycyclic has changed. This was done to get the names of Polycyclic functions to agree with the general naming conventions used throughout GAP. Also, some Polycyclic operations duplicated functionality that was already available in the core of GAP under a different name. In these cases, whenever possible we now install the Polycyclic code as methods for the existing GAP operations instead of introducing new operations.

For backward compatibility, we still provide the old, obsolete names as aliases. However, please consider switching to the new names as soon as possible. The old names may be completely removed at some point in the future.

The following function names were changed.

OLD NOW USE
SchurCovering SchurCover (7.9-3)
SchurMultPcpGroup AbelianInvariantsMultiplier (7.9-4)

 


Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/rainbow.js0000644000076600000240000000533613706672374015415 0ustar mhornstaff function randchar(str) { var i = Math.floor(Math.random() * str.length); while (i == str.length) i = Math.floor(Math.random() * str.length); return str[i]; } hexdigits = "0123456789abcdef"; function randlight() { return randchar("cdef")+randchar(hexdigits)+ randchar("cdef")+randchar(hexdigits)+ randchar("cdef")+randchar(hexdigits) } function randdark() { return randchar("012345789")+randchar(hexdigits)+ randchar("012345789")+randchar(hexdigits)+ randchar("102345789")+randchar(hexdigits) } document.write('\n'); polycyclic-2.16/doc/chap6_mj.html0000644000076600000240000003216213706672374015770 0ustar mhornstaff GAP (polycyclic) - Chapter 6: Libraries and examples of pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

6 Libraries and examples of pcp-groups

6.1 Libraries of various types of polycyclic groups

There are the following generic pcp-groups available.

6.1-1 AbelianPcpGroup
‣ AbelianPcpGroup( n, rels )( function )

constructs the abelian group on n generators such that generator \(i\) has order \(rels[i]\). If this order is infinite, then \(rels[i]\) should be either unbound or 0.

6.1-2 DihedralPcpGroup
‣ DihedralPcpGroup( n )( function )

constructs the dihedral group of order n. If n is an odd integer, then 'fail' is returned. If n is zero or not an integer, then the infinite dihedral group is returned.

6.1-3 UnitriangularPcpGroup
‣ UnitriangularPcpGroup( n, c )( function )

returns a pcp-group isomorphic to the group of upper triangular in \(GL(n, R)\) where \(R = ℤ\) if \(c = 0\) and \(R = \mathbb{F}_p\) if \(c = p\). The natural unitriangular matrix representation of the returned pcp-group \(G\) can be obtained as \(G!.isomorphism\).

6.1-4 SubgroupUnitriangularPcpGroup
‣ SubgroupUnitriangularPcpGroup( mats )( function )

mats should be a list of upper unitriangular \(n \times n\) matrices over \(ℤ\) or over \(\mathbb{F}_p\). This function returns the subgroup of the corresponding 'UnitriangularPcpGroup' generated by the matrices in mats.

6.1-5 InfiniteMetacyclicPcpGroup
‣ InfiniteMetacyclicPcpGroup( n, m, r )( function )

Infinite metacyclic groups are classified in [BK00]. Every infinite metacyclic group \(G\) is isomorphic to a finitely presented group \(G(m,n,r)\) with two generators \(a\) and \(b\) and relations of the form \(a^m = b^n = 1\) and \([a,b] = a^{1-r}\), where (differing from the conventions used by GAP) we have \([a,b] = a b a^-1 b^-1\), and \(m,n,r\) are three non-negative integers with \(mn=0\) and \(r\) relatively prime to \(m\). If \(r \equiv -1\) mod \(m\) then \(n\) is even, and if \(r \equiv 1\) mod \(m\) then \(m=0\). Also \(m\) and \(n\) must not be \(1\).

Moreover, \(G(m,n,r)\cong G(m',n',s)\) if and only if \(m=m'\), \(n=n'\), and either \(r \equiv s\) or \(r \equiv s^{-1}\) mod \(m\).

This function returns the metacyclic group with parameters n, m and r as a pcp-group with the pc-presentation \(\langle x,y | x^n, y^m, y^x = y^r\rangle\). This presentation is easily transformed into the one above via the mapping \(x \mapsto b^{-1}, y \mapsto a\).

6.1-6 HeisenbergPcpGroup
‣ HeisenbergPcpGroup( n )( function )

returns the Heisenberg group on \(2\textit{n}+1\) generators as pcp-group. This gives a group of Hirsch length \(2\textit{n}+1\).

6.1-7 MaximalOrderByUnitsPcpGroup
‣ MaximalOrderByUnitsPcpGroup( f )( function )

takes as input a normed, irreducible polynomial over the integers. Thus f defines a field extension F over the rationals. This function returns the split extension of the maximal order O of F by the unit group U of O, where U acts by right multiplication on O.

6.1-8 BurdeGrunewaldPcpGroup
‣ BurdeGrunewaldPcpGroup( s, t )( function )

returns a nilpotent group of Hirsch length 11 which has been constructed by Burde und Grunewald. If s is not 0, then this group has no faithful 12-dimensional linear representation.

6.2 Some assorted example groups

The functions in this section provide some more example groups to play with. They come with no further description and their investigation is left to the interested user.

6.2-1 ExampleOfMetabelianPcpGroup
‣ ExampleOfMetabelianPcpGroup( a, k )( function )

returns an example of a metabelian group. The input parameters must be two positive integers greater than 1.

6.2-2 ExamplesOfSomePcpGroups
‣ ExamplesOfSomePcpGroups( n )( function )

this function takes values n in 1 up to 16 and returns for each input an example of a pcp-group. The groups in this example list have been used as test groups for the functions in this package.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap7.html0000644000076600000240000020432613706672374015306 0ustar mhornstaff GAP (polycyclic) - Chapter 7: Higher level methods for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

7 Higher level methods for pcp-groups

7 Higher level methods for pcp-groups

This is a description of some higher level functions of the Polycyclic package of GAP 4. Throughout this chapter we let G be a pc-presented group and we consider algorithms for subgroups U and V of G. For background and a description of the underlying algorithms we refer to [Eic01a].

7.1 Subgroup series in pcp-groups

Many algorithm for pcp-groups work by induction using some series through the group. In this section we provide a number of useful series for pcp-groups. An efa series is a normal series with elementary or free abelian factors. See [Eic00] for outlines on the algorithms of a number of the available series.

7.1-1 PcpSeries
‣ PcpSeries( U )( function )

returns the polycyclic series of U defined by an igs of U.

7.1-2 EfaSeries
‣ EfaSeries( U )( attribute )

returns a normal series of U with elementary or free abelian factors.

7.1-3 SemiSimpleEfaSeries
‣ SemiSimpleEfaSeries( U )( attribute )

returns an efa series of U such that every factor in the series is semisimple as a module for U over a finite field or over the rationals.

7.1-4 DerivedSeriesOfGroup
‣ DerivedSeriesOfGroup( U )( method )

the derived series of U.

7.1-5 RefinedDerivedSeries
‣ RefinedDerivedSeries( U )( function )

the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the top.

7.1-6 RefinedDerivedSeriesDown
‣ RefinedDerivedSeriesDown( U )( function )

the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the bottom.

7.1-7 LowerCentralSeriesOfGroup
‣ LowerCentralSeriesOfGroup( U )( method )

the lower central series of U. If U does not have a largest nilpotent quotient group, then this function may not terminate.

7.1-8 UpperCentralSeriesOfGroup
‣ UpperCentralSeriesOfGroup( U )( method )

the upper central series of U. This function always terminates, but it may terminate at a proper subgroup of U.

7.1-9 TorsionByPolyEFSeries
‣ TorsionByPolyEFSeries( U )( function )

returns an efa series of U such that all torsion-free factors are at the top and all finite factors are at the bottom. Such a series might not exist for U and in this case the function returns fail.

gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> Igs(G);
[ g1, g2, g3, g4 ]

gap> PcpSeries(G);
[ Pcp-group with orders [ 2, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0 ],
  Pcp-group with orders [ 0, 0 ],
  Pcp-group with orders [ 0 ],
  Pcp-group with orders [  ] ]

gap> List( PcpSeries(G), Igs );
[ [ g1, g2, g3, g4 ], [ g2, g3, g4 ], [ g3, g4 ], [ g4 ], [  ] ]

Algorithms for pcp-groups often use an efa series of G and work down over the factors of this series. Usually, pcp's of the factors are more useful than the actual factors. Hence we provide the following.

7.1-10 PcpsBySeries
‣ PcpsBySeries( ser[, flag] )( function )

returns a list of pcp's corresponding to the factors of the series. If the parameter flag is present and equals the string "snf", then each pcp corresponds to a decomposition of the abelian groups into direct factors.

7.1-11 PcpsOfEfaSeries
‣ PcpsOfEfaSeries( U )( attribute )

returns a list of pcps corresponding to an efa series of U.

gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]

gap> PcpsBySeries( DerivedSeriesOfGroup(G));
[ Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ],
  Pcp [ g2^-2, g3^-2, g4^2 ] with orders [ 0, 0, 4 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]
gap> PcpsBySeries( RefinedDerivedSeries(G));
[ Pcp [ g1, g2, g3 ] with orders [ 2, 2, 2 ],
  Pcp [ g4 ] with orders [ 2 ],
  Pcp [ g2^2, g3^2 ] with orders [ 0, 0 ],
  Pcp [ g4^2 ] with orders [ 2 ],
  Pcp [ g4^4 ] with orders [ 2 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]

gap> PcpsBySeries( DerivedSeriesOfGroup(G), "snf" );
[ Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ],
  Pcp [ g4^2, g3^-2, g2^2*g4^2 ] with orders [ 4, 0, 0 ],
  Pcp [ g4^8 ] with orders [ 0 ] ]
gap> G.1^4 in DerivedSubgroup( G );
true
gap> G.1^2 = G.4;
true

gap>  PcpsOfEfaSeries( G );
[ Pcp [ g1 ] with orders [ 2 ],
  Pcp [ g2 ] with orders [ 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ] ]

7.2 Orbit stabilizer methods for pcp-groups

Let U be a pcp-group which acts on a set . One of the fundamental problems in algorithmic group theory is the determination of orbits and stabilizers of points in under the action of U. We distinguish two cases: the case that all considered orbits are finite and the case that there are infinite orbits. In the latter case, an orbit cannot be listed and a description of the orbit and its corresponding stabilizer is much harder to obtain.

If the considered orbits are finite, then the following two functions can be applied to compute the considered orbits and their corresponding stabilizers.

7.2-1 PcpOrbitStabilizer
‣ PcpOrbitStabilizer( point, gens, acts, oper )( function )
‣ PcpOrbitsStabilizers( points, gens, acts, oper )( function )

The input gens can be an igs or a pcp of a pcp-group U. The elements in the list gens act as the elements in the list acts via the function oper on the given points; that is, oper( point, acts[i] ) applies the ith generator to a given point. Thus the group defined by acts must be a homomorphic image of the group defined by gens. The first function returns a record containing the orbit as component 'orbit' and and igs for the stabilizer as component 'stab'. The second function returns a list of records, each record contains 'repr' and 'stab'. Both of these functions run forever on infinite orbits.

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> mats := [ [[-1,0],[0,1]], [[1,1],[0,1]] ];;
gap> pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]
gap> PcpOrbitStabilizer( [0,1], pcp, mats, OnRight );
rec( orbit := [ [ 0, 1 ] ],
     stab := [ g1, g2 ],
     word := [ [ [ 1, 1 ] ], [ [ 2, 1 ] ] ] )

If the considered orbits are infinite, then it may not always be possible to determine a description of the orbits and their stabilizers. However, as shown in [EO02] and [Eic02], it is possible to determine stabilizers and check if two elements are contained in the same orbit if the given action of the polycyclic group is a unimodular linear action on a vector space. The following functions are available for this case.

7.2-2 StabilizerIntegralAction
‣ StabilizerIntegralAction( U, mats, v )( function )
‣ OrbitIntegralAction( U, mats, v, w )( function )

The first function computes the stabilizer in U of the vector v where the pcp group U acts via mats on an integral space and v and w are elements in this integral space. The second function checks whether v and w are in the same orbit and the function returns either false or a record containing an element in U mapping v to w and the stabilizer of v.

7.2-3 NormalizerIntegralAction
‣ NormalizerIntegralAction( U, mats, B )( function )
‣ ConjugacyIntegralAction( U, mats, B, C )( function )

The first function computes the normalizer in U of the lattice with the basis B, where the pcp group U acts via mats on an integral space and B is a subspace of this integral space. The second functions checks whether the two lattices with the bases B and C are contained in the same orbit under U. The function returns either false or a record with an element in U mapping B to C and the stabilizer of B.

# get a pcp group and a free abelian normal subgroup
gap> G := ExamplesOfSomePcpGroups(8);
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> efa := EfaSeries(G);
[ Pcp-group with orders [ 0, 0, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0, 0 ],
  Pcp-group with orders [ 0, 0, 0 ],
  Pcp-group with orders [  ] ]
gap> N := efa[3];
Pcp-group with orders [ 0, 0, 0 ]
gap> IsFreeAbelian(N);
true

# create conjugation action on N
gap> mats := LinearActionOnPcp(Igs(G), Pcp(N));
[ [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 0, 0, 1 ], [ 1, -1, 1 ], [ 0, 1, 0 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],
  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]

# take an arbitrary vector and compute its stabilizer
gap> StabilizerIntegralAction(G,mats, [2,3,4]);
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> Igs(last);
[ g1, g3, g4, g5 ]

# check orbits with some other vectors
gap> OrbitIntegralAction(G,mats, [2,3,4],[3,1,5]);
rec( stab := Pcp-group with orders [ 0, 0, 0, 0 ], prei := g2 )

gap> OrbitIntegralAction(G,mats, [2,3,4], [4,6,8]);
false

# compute the orbit of a subgroup of Z^3 under the action of G
gap> NormalizerIntegralAction(G, mats, [[1,0,0],[0,1,0]]);
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> Igs(last);
[ g1, g2^2, g3, g4, g5 ]

7.3 Centralizers, Normalizers and Intersections

In this section we list a number of operations for which there are methods installed to compute the corresponding features in polycyclic groups.

7.3-1 Centralizer
‣ Centralizer( U, g )( method )
‣ IsConjugate( U, g, h )( method )

These functions solve the conjugacy problem for elements in pcp-groups and they can be used to compute centralizers. The first method returns a subgroup of the given group U, the second method either returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in [EO02]. For nilpotent groups, an algorithm to solve the conjugacy problem for elements is described in [Sim94].

7.3-2 Centralizer
‣ Centralizer( U, V )( method )
‣ Normalizer( U, V )( method )
‣ IsConjugate( U, V, W )( method )

These three functions solve the conjugacy problem for subgroups and compute centralizers and normalizers of subgroups. The first two functions return subgroups of the input group U, the third function returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in [Eic02]. For nilpotent groups, an algorithm to solve the conjugacy problems for subgroups is described in [Lo98b].

7.3-3 Intersection
‣ Intersection( U, N )( function )

A general method to compute intersections of subgroups of a pcp-group is described in [Eic01a], but it is not yet implemented here. However, intersections of subgroups U, N ≤ G can be computed if N is normalising U. See [Sim94] for an outline of the algorithm.

7.4 Finite subgroups

There are various finite subgroups of interest in polycyclic groups. See [Eic00] for a description of the algorithms underlying the functions in this section.

7.4-1 TorsionSubgroup
‣ TorsionSubgroup( U )( attribute )

If the set of elements of finite order forms a subgroup, then we call it the torsion subgroup. This function determines the torsion subgroup of U, if it exists, and returns fail otherwise. Note that a torsion subgroup does always exist if U is nilpotent.

7.4-2 NormalTorsionSubgroup
‣ NormalTorsionSubgroup( U )( attribute )

Each polycyclic groups has a unique largest finite normal subgroup. This function computes it for U.

7.4-3 IsTorsionFree
‣ IsTorsionFree( U )( property )

This function checks if U is torsion free. It returns true or false.

7.4-4 FiniteSubgroupClasses
‣ FiniteSubgroupClasses( U )( attribute )

There exist only finitely many conjugacy classes of finite subgroups in a polycyclic group U and this function can be used to compute them. The algorithm underlying this function proceeds by working down a normal series of U with elementary or free abelian factors. The following function can be used to give the algorithm a specific series.

7.4-5 FiniteSubgroupClassesBySeries
‣ FiniteSubgroupClassesBySeries( U, pcps )( function )
gap> G := ExamplesOfSomePcpGroups(15);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0 ]
gap> TorsionSubgroup(G);
Pcp-group with orders [ 5, 2 ]
gap> NormalTorsionSubgroup(G);
Pcp-group with orders [ 5, 2 ]
gap> IsTorsionFree(G);
false
gap> FiniteSubgroupClasses(G);
[ Pcp-group with orders [ 5, 2 ]^G,
  Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [ 5 ]^G,
  Pcp-group with orders [  ]^G ]

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> TorsionSubgroup(G);
fail
gap> NormalTorsionSubgroup(G);
Pcp-group with orders [  ]
gap> IsTorsionFree(G);
false
gap> FiniteSubgroupClasses(G);
[ Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [ 2 ]^G,
  Pcp-group with orders [  ]^G ]

7.5 Subgroups of finite index and maximal subgroups

Here we outline functions to determine various types of subgroups of finite index in polycyclic groups. Again, see [Eic00] for a description of the algorithms underlying the functions in this section. Also, we refer to [Lo98a] for an alternative approach.

7.5-1 MaximalSubgroupClassesByIndex
‣ MaximalSubgroupClassesByIndex( U, p )( operation )

Each maximal subgroup of a polycyclic group U has p-power index for some prime p. This function can be used to determine the conjugacy classes of all maximal subgroups of p-power index for a given prime p.

7.5-2 LowIndexSubgroupClasses
‣ LowIndexSubgroupClasses( U, n )( operation )

There are only finitely many subgroups of a given index in a polycyclic group U. This function computes conjugacy classes of all subgroups of index n in U.

7.5-3 LowIndexNormalSubgroups
‣ LowIndexNormalSubgroups( U, n )( operation )

This function computes the normal subgroups of index n in U.

7.5-4 NilpotentByAbelianNormalSubgroup
‣ NilpotentByAbelianNormalSubgroup( U )( function )

This function returns a normal subgroup N of finite index in U such that N is nilpotent-by-abelian. Such a subgroup exists in every polycyclic group and this function computes such a subgroup using LowIndexNormal. However, we note that this function is not very efficient and the function NilpotentByAbelianByFiniteSeries may well be more efficient on this task.

gap> G := ExamplesOfSomePcpGroups(2);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]

gap> MaximalSubgroupClassesByIndex( G, 61 );;
gap> max := List( last, Representative );;
gap> List( max, x -> Index( G, x ) );
[ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 226981 ]

gap> LowIndexSubgroupClasses( G, 61 );;
gap> low := List( last, Representative );;
gap> List( low, x -> Index( G, x ) );
[ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
  61, 61, 61, 61, 61, 61 ]

7.6 Further attributes for pcp-groups based on the Fitting subgroup

In this section we provide a variety of other attributes for pcp-groups. Most of the methods below are based or related to the Fitting subgroup of the given group. We refer to [Eic01b] for a description of the underlying methods.

7.6-1 FittingSubgroup
‣ FittingSubgroup( U )( attribute )

returns the Fitting subgroup of U; that is, the largest nilpotent normal subgroup of U.

7.6-2 IsNilpotentByFinite
‣ IsNilpotentByFinite( U )( property )

checks whether the Fitting subgroup of U has finite index.

7.6-3 Centre
‣ Centre( U )( method )

returns the centre of U.

7.6-4 FCCentre
‣ FCCentre( U )( method )

returns the FC-centre of U; that is, the subgroup containing all elements having a finite conjugacy class in U.

7.6-5 PolyZNormalSubgroup
‣ PolyZNormalSubgroup( U )( function )

returns a normal subgroup N of finite index in U, such that N has a polycyclic series with infinite factors only.

7.6-6 NilpotentByAbelianByFiniteSeries
‣ NilpotentByAbelianByFiniteSeries( U )( function )

returns a normal series 1 ≤ F ≤ A ≤ U such that F is nilpotent, A/F is abelian and U/A is finite. This series is computed using the Fitting subgroup and the centre of the Fitting factor.

7.7 Functions for nilpotent groups

There are (very few) functions which are available for nilpotent groups only. First, there are the different central series. These are available for all groups, but for nilpotent groups they terminate and provide series through the full group. Secondly, the determination of a minimal generating set is available for nilpotent groups only.

7.7-1 MinimalGeneratingSet
‣ MinimalGeneratingSet( U )( method )
gap> G := ExamplesOfSomePcpGroups(14);
Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0, 5, 5, 4, 0, 6,
  5, 5, 4, 0, 10, 6 ]
gap> IsNilpotent(G);
true

gap> PcpsBySeries( LowerCentralSeriesOfGroup(G));
[ Pcp [ g1, g2 ] with orders [ 0, 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ],
  Pcp [ g5 ] with orders [ 0 ],
  Pcp [ g6, g7 ] with orders [ 0, 0 ],
  Pcp [ g8 ] with orders [ 0 ],
  Pcp [ g9, g10 ] with orders [ 0, 0 ],
  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],
  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],
  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]

gap> PcpsBySeries( UpperCentralSeriesOfGroup(G));
[ Pcp [ g1, g2 ] with orders [ 0, 0 ],
  Pcp [ g3 ] with orders [ 0 ],
  Pcp [ g4 ] with orders [ 0 ],
  Pcp [ g5 ] with orders [ 0 ],
  Pcp [ g6, g7 ] with orders [ 0, 0 ],
  Pcp [ g8 ] with orders [ 0 ],
  Pcp [ g9, g10 ] with orders [ 0, 0 ],
  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],
  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],
  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]

gap> MinimalGeneratingSet(G);
[ g1, g2 ]

7.8 Random methods for pcp-groups

Below we introduce a function which computes orbit and stabilizer using a random method. This function tries to approximate the orbit and the stabilizer, but the returned orbit or stabilizer may be incomplete. This function is used in the random methods to compute normalizers and centralizers. Note that deterministic methods for these purposes are also available.

7.8-1 RandomCentralizerPcpGroup
‣ RandomCentralizerPcpGroup( U, g )( function )
‣ RandomCentralizerPcpGroup( U, V )( function )
‣ RandomNormalizerPcpGroup( U, V )( function )
gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> mats := [[[-1, 0],[0,1]], [[1,1],[0,1]]];
[ [ [ -1, 0 ], [ 0, 1 ] ], [ [ 1, 1 ], [ 0, 1 ] ] ]
gap> pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]

gap> RandomPcpOrbitStabilizer( [1,0], pcp, mats, OnRight ).stab;
#I  Orbit longer than limit: exiting.
[  ]

gap> g := Igs(G)[1];
g1
gap> RandomCentralizerPcpGroup( G, g );
#I  Stabilizer not increasing: exiting.
Pcp-group with orders [ 2 ]
gap> Igs(last);
[ g1 ]

7.9 Non-abelian tensor product and Schur extensions

7.9-1 SchurExtension
‣ SchurExtension( G )( attribute )

Let G be a polycyclic group with a polycyclic generating sequence consisting of n elements. This function computes the largest central extension H of G such that H is generated by n elements. If F/R is the underlying polycyclic presentation for G, then H is isomorphic to F/[R,F].

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> Centre( G );
Pcp-group with orders [  ]
gap> H := SchurExtension( G );
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> Centre( H );
Pcp-group with orders [ 0, 0 ]
gap> H/Centre(H);
Pcp-group with orders [ 2, 0 ]
gap> Subgroup( H, [H.1,H.2] ) = H;
true

7.9-2 SchurExtensionEpimorphism
‣ SchurExtensionEpimorphism( G )( attribute )

returns the projection from the Schur extension G^* of G onto G. See the function SchurExtension. The kernel of this epimorphism is the direct product of the Schur multiplicator of G and a direct product of n copies of where n is the number of generators in the polycyclic presentation for G. The Schur multiplicator is the intersection of the kernel and the derived group of the source. See also the function SchurCover.

gap> gl23 := Range( IsomorphismPcpGroup( GL(2,3) ) );
Pcp-group with orders [ 2, 3, 2, 2, 2 ]
gap> SchurExtensionEpimorphism( gl23 );
[ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 ] -> [ g1, g2, g3, g4, g5,
id, id, id, id, id ]
gap> Kernel( last );
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> AbelianInvariantsMultiplier( gl23 );
[  ]
gap> Intersection( Kernel(epi), DerivedSubgroup( Source(epi) ) );
[  ]

There is a crossed pairing from G into (G^*)' which can be defined via this epimorphism:

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> epi := SchurExtensionEpimorphism( G );
[ g1, g2, g3, g4 ] -> [ g1, g2, id, id ]
gap> PreImagesRepresentative( epi, G.1 );
g1
gap> PreImagesRepresentative( epi, G.2 );
g2
gap> Comm( last, last2 );
g2^-2*g4

7.9-3 SchurCover
‣ SchurCover( G )( function )

computes a Schur covering group of the polycyclic group G. A Schur covering is a largest central extension H of G such that the kernel M of the projection of H onto G is contained in the commutator subgroup of H.

If G is given by a presentation F/R, then M is isomorphic to the subgroup R ∩ [F,F] / [R,F]. Let C be a complement to R ∩ [F,F] / [R,F] in R/[R,F]. Then F/C is isomorphic to H and R/C is isomorphic to M.

gap> G := AbelianPcpGroup( 3,[] );
Pcp-group with orders [ 0, 0, 0 ]
gap> ext := SchurCover( G );
Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]
gap> Centre( ext );
Pcp-group with orders [ 0, 0, 0 ]
gap> IsSubgroup( DerivedSubgroup( ext ), last );
true

7.9-4 AbelianInvariantsMultiplier
‣ AbelianInvariantsMultiplier( G )( attribute )

returns a list of the abelian invariants of the Schur multiplier of G.

Note that the Schur multiplicator of a polycyclic group is a finitely generated abelian group.

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> DirectProduct( G, AbelianPcpGroup( 2, [] ) );
Pcp-group with orders [ 0, 0, 2, 0 ]
gap> AbelianInvariantsMultiplier( last );
[ 0, 2, 2, 2, 2 ]

7.9-5 NonAbelianExteriorSquareEpimorphism
‣ NonAbelianExteriorSquareEpimorphism( G )( function )

returns the epimorphism of the non-abelian exterior square of a polycyclic group G onto the derived group of G. The non-abelian exterior square can be defined as the derived subgroup of a Schur cover of G. The isomorphism type of the non-abelian exterior square is unique despite the fact that the isomorphism type of a Schur cover of a polycyclic groups need not be unique. The derived group of a Schur cover has a natural projection onto the derived group of G which is what the function returns.

The kernel of the epimorphism is isomorphic to the Schur multiplicator of G.

gap> G := ExamplesOfSomePcpGroups( 3 );
Pcp-group with orders [ 0, 0 ]
gap> G := DirectProduct( G,G );
Pcp-group with orders [ 0, 0, 0, 0 ]
gap> AbelianInvariantsMultiplier( G );
[ [ 0, 1 ], [ 2, 3 ] ]
gap> epi := NonAbelianExteriorSquareEpimorphism( G );
[ g2^-2*g5, g4^-2*g10, g6, g7, g8, g9 ] -> [ g2^-2, g4^-2, id, id, id, id ]
gap> Kernel( epi );
Pcp-group with orders [ 0, 2, 2, 2 ]
gap> Collected( AbelianInvariants( last ) );
[ [ 0, 1 ], [ 2, 3 ] ]

7.9-6 NonAbelianExteriorSquare
‣ NonAbelianExteriorSquare( G )( attribute )

computes the non-abelian exterior square of a polycylic group G. See the explanation for NonAbelianExteriorSquareEpimorphism. The natural projection of the non-abelian exterior square onto the derived group of G is stored in the component !.epimorphism.

There is a crossed pairing from G into G∧ G. See the function SchurExtensionEpimorphism for details. The crossed pairing is stored in the component !.crossedPairing. This is the crossed pairing λ in [EN08].

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> GwG := NonAbelianExteriorSquare( G );
Pcp-group with orders [ 0 ]
gap> lambda := GwG!.crossedPairing;
function( g, h ) ... end
gap> lambda( G.1, G.2 );
g2^2*g4^-1

7.9-7 NonAbelianTensorSquareEpimorphism
‣ NonAbelianTensorSquareEpimorphism( G )( function )

returns for a polycyclic group G the projection of the non-abelian tensor square G⊗ G onto the non-abelian exterior square G∧ G. The range of that epimorphism has the component !.epimorphism set to the projection of the non-abelian exterior square onto the derived group of G. See also the function NonAbelianExteriorSquare.

With the result of this function one can compute the groups in the commutative diagram at the beginning of the paper [EN08]. The kernel of the returned epimorphism is the group ∇(G). The kernel of the composition of this epimorphism and the above mention projection onto G' is the group J(G).

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap> G := DirectProduct(G,G);
Pcp-group with orders [ 2, 0, 2, 0 ]
gap> alpha := NonAbelianTensorSquareEpimorphism( G );
[ g9*g25^-1, g10*g26^-1, g11*g27, g12*g28, g13*g29, g14*g30, g15, g16,
g17,
  g18, g19, g20, g21, g22, g23, g24 ] -> [ g2^-2*g6, g4^-2*g12, g8,
  g9, g10,
  g11, id, id, id, id, id, id, id, id, id, id ]
gap> gamma := Range( alpha )!.epimorphism;
[ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11 ] -> [ g2^-2, g4^-2, id, id,
id, id ]
gap> JG := Kernel( alpha * gamma );
Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]
gap> Image( alpha, JG );
Pcp-group with orders [ 2, 2, 2, 2 ]
gap> AbelianInvariantsMultiplier( G );
[ [ 2, 4 ] ]

7.9-8 NonAbelianTensorSquare
‣ NonAbelianTensorSquare( G )( attribute )

computes for a polycyclic group G the non-abelian tensor square G⊗ G.

gap> G := AlternatingGroup( IsPcGroup, 4 );
<pc group of size 12 with 3 generators>
gap> PcGroupToPcpGroup( G );
Pcp-group with orders [ 3, 2, 2 ]
gap> NonAbelianTensorSquare( last );
Pcp-group with orders [ 2, 2, 2, 3 ]
gap> PcpGroupToPcGroup( last );
<pc group of size 24 with 4 generators>
gap> DirectFactorsOfGroup( last );
[ Group([ f1, f2, f3 ]), Group([ f4 ]) ]
gap> List( last, Size );
[ 8, 3 ]
gap> IdGroup( last2[1] );
[ 8, 4 ]       # the quaternion group of Order 8

gap> G := DihedralPcpGroup( 0 );
Pcp-group with orders [ 2, 0 ]
gap> ten := NonAbelianTensorSquare( G );
Pcp-group with orders [ 0, 2, 2, 2 ]
gap> IsAbelian( ten );
true

7.9-9 NonAbelianExteriorSquarePlusEmbedding
‣ NonAbelianExteriorSquarePlusEmbedding( G )( function )

returns an embedding from the non-abelian exterior square G∧ G into an extensions of G∧ G by G× G. For the significance of the group see the paper [EN08]. The range of the epimorphism is the group τ(G) in that paper.

7.9-10 NonAbelianTensorSquarePlusEpimorphism
‣ NonAbelianTensorSquarePlusEpimorphism( G )( function )

returns an epimorphisms of ν(G) onto τ(G). The group ν(G) is an extension of the non-abelian tensor square G⊗ G of G by G× G. The group τ(G) is an extension of the non-abelian exterior square G∧ G by G× G. For details see [EN08].

7.9-11 NonAbelianTensorSquarePlus
‣ NonAbelianTensorSquarePlus( G )( function )

returns the group ν(G) in [EN08].

7.9-12 WhiteheadQuadraticFunctor
‣ WhiteheadQuadraticFunctor( G )( function )

returns Whitehead's universal quadratic functor of G, see [EN08] for a description.

7.10 Schur covers

This section contains a function to determine the Schur covers of a finite p-group up to isomorphism.

7.10-1 SchurCovers
‣ SchurCovers( G )( function )

Let G be a finite p-group defined as a pcp group. This function returns a complete and irredundant set of isomorphism types of Schur covers of G. The algorithm implements a method of Nickel's Phd Thesis.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/manual.pdf0000644000076600000240000132047613706672374015374 0ustar mhornstaff%PDF-1.5 % 194 0 obj << /Length 267 /Filter /FlateDecode >> stream xuOO0|ˁqfY5hdLiOZC=,،[ ՛TŘүqx  sI4(Hy:yWspTޥZ͢Jn<@(>`Ġv]FY]*n endstream endobj 202 0 obj << /Length 570 /Filter /FlateDecode >> stream xڵSMs0W>@Li h D]^ 7~ oW"\M.IH` /$ Q a#n[V>ê+s:\`S:e6' )# "[C ysi8vye9sLq54GH Yr&Xj_?VN_o+kZ"-1Y{(9({VW|R]j+^Qy\Ų-S݇]ԼQzU\wh\wOb[:(2zWrs:9e@q\[~cF6' >kմe?X(OoI J_x7/ii҆禝e%2|:X;Spÿ;^oݝac_mΎʓ/K[i"lc|U,Em[8o&<*>7\j7Fo)X\V]bp])$KϦGhg endstream endobj 2 0 obj << /Type /ObjStm /N 100 /First 811 /Length 1606 /Filter /FlateDecode >> stream xڝWaS8_[-7L(m̴=ڙ8xX9٦p*v`Ѿ}vWER$>"&$%*<ǗH%d$$?+I %JRSJJy$?T )RQRJc0M(qQpSL*d(IEWr?Aq\"MdR$XW\BHQ8xq#2*H a2I( ,| %@D@ c~IJJ& >9 ?OS& ?@1r@p"" `PǂJ8 &!)(@.)9$F2 R#cȘ9@&$`%+!$RIPɒ*R$#aRe<rr989jL m2@d\y 2 R FFd!_W(X? A?dH^H.yt`S -oEЫθxI_/Хy(KӶlw]גx*@teJ45%j~}t]ߌ9gF}~(k:1; G(CU\ƪ٥X1e`h4zr0L=|#,'5onf ȫ1'8?ը/?L;N(D }c}i۠3Ĵw裙W^|p1ɋH^>xϿ 8eY2㦯G8yS&s*03'@L -OMGg(;`<(!2~Fi,A)9_h}g <| Ib;yzH=;Г ~RyX5!P5_Ã}!<_NbTߺ~oN%h$3LRݕ'*3,>Db]o߾M*'#5с1 endstream endobj 210 0 obj << /Length 677 /Filter /FlateDecode >> stream xuTKo0 Wh* 6[T[I:!'rEin~H3q E=bc4CX5>]?QKRBXZ`rwpIkU+@W_>jӂ$DҴҀՅԙ_.] uQBecәm-;ó礜ᝨAXν}U #oF=-F֪F=ŌDpVkepcҜBzMϋ /3dhiY~6_L%k-mc[4m'O$Vl{"=;폲j17OE{aK/ >?ڵ*Md5xiz÷!DLOPat:$oZ+Pa(YKS\ nV6_YIn x$J 4Tǀk:` `+}p{Jpo t{ԳpU? +9'S_Tg^ݼZowֿps}@F1zBEj<b~ q endstream endobj 249 0 obj << /Length 1216 /Filter /FlateDecode >> stream x[oH)xRa>궫TۼF8/0 1$M>i?~ojڹ|ϙlv2ɳ(peE}&qwLjGͫ+SCo=YX AgZve^V8c)<% ;>%蠢Y%ˆMV "u3G03> n @5EUľ u֤4_ $e Қа(UP"RL7̂\Gd.Ć,yكugdIv oRݮv7rQd:TK6 R"rA:UZ 1J ɗWw^>cڐ.f 6q7C ^B驁ҿ*ݯlw[u e^Nq"pK ?i&Q(=%c- Sy!JGtd* A,eŏ$}2&[AЈInn SjDVj0iBt례jĚ5`1oGinI̸53fSbTS|ϧ$n[-p&Qnz@Y}{$#wM`kS&كa5>-2#kUSv[+weµ")?w _gɢʾSQѶHogF-\m]3]l&g #ڥefͷ]&*/h|^cR SވM[jwM(Ǧe "DpbrBUFg-$;PK[n_"'-4Oǹ ϵ2-7^E`,w[&3Y/x]R+ʽzwIHO&75^%!dKU_6l;@|F5 y|Wʾ6, w'=_D.;Hjf[Cq9v97rh,Iٮ tpp7W7SX endstream endobj 268 0 obj << /Length 677 /Filter /FlateDecode >> stream xMo0>ׯ6iݔjJ 02%~,dK$=9$6g 0<M(J0-b&1<;?t\̝]#݄HH0CL5UmFAs%h0}i G9 |n0ۣv8z ~0ș\'GL=QJ йdQ-sɊnOW33G)LJS2Sި0쑚Q^uZ$J=\7Qi L[6uUmB nx{E#]-KVW|*ޡ0ٓWq(!;+y^Dz%ig0cqhv{hr|>Dv ;5Pf h|U&:Mc/7€-r6-֗Mm5ID倣G1Rt )ހT1-ug@Z~e<1B=շ[cW3 sr"$..d/O͔t'&D'N}6Xe/eFyjB3a.K_ RY,MȈaR S5DG0ͪxSPVҨQ |u l 8#eẼ~dB endstream endobj 284 0 obj << /Length 2080 /Filter /FlateDecode >> stream xڭXK۶WxWUI^%Ews.z idHr=,:I t!5갊VDҾ|$_%QeIzܯ,LU8TIzܭ~~>`&I ^ۛ(V'aücd6 X1nݛ23Q8 ˨RjnQ]{>?jJ0+3Cԟ_Qx3Ե}婭_vo]:YpŮ :N~i'Slo;g5K:Yf9Gep|_Q`p8ٮ6*I,*VX.'vpJWȬ8շy[[fN`BvW#7-L(h$x}Ëeŭ,2Y ;D.HEoGu;Kb;Wù?7`^**H]jr9uk.j, b1~%` xVv>ʔ [:X8mWpvc;!ÖKĪRNtڶ̠0qOJd}:J s3$j#o1+%lUtߢ#&vmڮ"'Rf"*_`l!/J: AWA hxu­F5D>;r1KݘP´…tht븣ro}g[L$E4x=!`RWӻKP'p8"Sхa :n\.<8vaÌ884?RJ>ϟIGZd/5zB,֡ACuqdd@Z &Ӟ%-8Q?ʣH,I/e?a??.W7aɘ2_agQp= `.S~HxA,-\Kn^u &?lEj=ghڗS=ϟ B>)z'/eS 2` 'Ѿ؊3L! Z8#_}}ٌ UD3Fqy S)w=&\΁8!R~@g˭~; 1SnO[|c;NMƵ%(GdLLC*->ƉRa]"SE gzE1릫)Z(X]gPf`q8v8[vH8i_dM;pG>-&UI朞  5;)"nf!qJmmO$Fq 8- <D6@8/7Jʍ3&˵vʏAsyk .n~|aϒy[mznZ޳H]X¤S#X NA}$j! A@ fˠTLB,ؗSTW6 +cy 87˩%e'{\֨8.\* $ߩ ĿXƿ6!jm;Co0;}D-ޱ A`v3W𧐇IhEcBo0r2v. P]unO /WA3tÅb6 (gS׎E ס*V?"o endstream endobj 302 0 obj << /Length 3669 /Filter /FlateDecode >> stream x\ݓ۶_JS G?4qu3SwNRxwIEN&HB,.ntׯ.^|-7 N\-.F%B+Fw׷ñnVk蒯~ J,^A%10.Dج]7~>a~~s{߷>Իc-%v*% >bϾ5",ќCWuqʴ].ZSbjg7!]`~SՑ8 #v$~ݭgPX3X襲44 Sl68h>_RZ*'drrnԓDi6`bKgU;̟ ; sJb`VFU‚2[XĊNvRßf[J3ArT-1p{xmhaQ2jdnWw6L";1TLr]0n[튫vLuzWm@Vke[o+ v\9-AٕfMzWǥ47!W!W"6Q^V)gJmK"𸉼bXm8AhȕRu b2H#\vɷ%$BdANP&S(<0Г8ɨo$Ҋ<9Ŕ./nkE:?ֻM]fW.E0ԄlEm +dY_ ,[G,`ҵ{DJ GR2"M  \4\|oO8nP|/z21wn- hoJݽ-vQ x OWBɲ 2ǨV.K[N3Lܩ Tѝ>;w#lSLm›+P |΍$%>;8]7Ϗ71҉NfQ?C&I}$ Su,oPTDWISSKMA_EqN):߁Fe XREH\N!aV%ɦ-"F )/ x+C0%X1|QA#H΍SP;Qx B6P&!d zOzױ[ų A4SwElI>R^DY3O%Ķs1R"Я S0ó$y:}r,"ECϰ?f}hRlUʂ81A{{C^>Mtx{>a~hl-'O !d4yFǺq bKo rr-.8E'+W1oC"LwE(5x&~J&@[L[#E F OqYgH.|eN}ayMr+܌c_b?<B#Bµh&L3g u(qF2?7e0~Y2 i/dknR9VᒸvxۺvX[5k=&@nwoo겵pX$,c>e|d}(0_Fc%FSDh!%)`G&ZvzBEZ걁Е.Nq7#X*tMhN >B*\vӗ6zܕ!4%jHI~Wt/PjX.C{Z!>[Ȧ)T:6>Y:6[[}"g/o,Y+mG,bPis3 ˦ MLRG&O\P.=!x_WmЗhX:I$`@_1q4@-ϩ DU7WyH]L6-ъzl>F}W}sx5Ϋﳳ,㤾o-ѧY^_i +d’^U(QzYU _>A(3J-g3/Ҵ2i/W3dgJkU,ӦeZ8gR/ ML'A23 $4$fzBJ_O=B*[ރ9qV AL]Q v I,EKwSGRp7$0B7#aʸty74qU2纒n?}@+XU0b5aq=*x@mw'nJ?܅(b^ ‡tɪCp xM))HsD79}xImed/[:{V 8z(bF ޷ro`K "5̦S<L=u'E+v|0ozZUǨee^s[<~oAi? ^hEn~$jKuS B߯gݧ%3xMp`{b N46/>'1b ?nbת J 2E~*.9v^WqyK0X4uM\&vRxq ZToە*:XgaM+ܾcS#Hw?F$ TGA_,9u7YcUa&ڏ]qqW`r8 ViŒ"'n15ukA endstream endobj 315 0 obj << /Length 1197 /Filter /FlateDecode >> stream xڽWK6Qb/TR92"D򚶺Ev{{/dH(Vی+E2U2R16C힚+kŧoy1J*Zc$L(P(l)YE4h[BK!E~hwkm:N&mrlB=1Q`QQNpPANC~O39sTIJVLۈ=F0[uvAYih^?-"'eK˼7f:eK)ΖL7`h x_v7C0gn -Y9hUu P]HmBE$ZVV ȢH@sj]ヂK| 4W9`&NK}!#E9O0FrŜSDhZ"R\BP{+$Ի޳N("4a0}I@/U0L9~m’_ 3QËͩ :$r?(g.:w$zP)˰"dK~3X^8nȫ"]'TQv"b D¬$ vWEd+RPe0O%Ѣ2B ]c{4.^jX6f'H|4.Y |?NcDw8g,#ʊP.R9Iы.<""u(bw80௹ PWrb3p!n#>Kůtjq7`QM)qC\Hy~N!1RrsTbΑD'vfK>kXX%6=@[޽:^b[x\A} U NP6f\Q}Lj]\e]v5Ff*Xd*ʼvF?tf|kХlizacocJ6\B8]x4&Îw C8 'C~2]lij /Sw|^bl:+c>6dL x3p:`o„)s6v\%a>_,:t74{?Jk'LO@ endstream endobj 325 0 obj << /Length 2569 /Filter /FlateDecode >> stream xڽYOۺ SV{&QNmڤ=4;eZYkŖ\Ino_ %RMvgmߜ |v3 ?1XQ>3禞a$^Ne63Íh·fB*SRKHb6,$ m|{ەz/ˮ9Pl:4cMT:C{臨uO'7`l9v|\񾛆pz[z>5e6{ƅ N93Ye QK@uަ;' 55e5pNC5|;݊隡b+0R'i),+8=q[kdq9$aEgQtjd1Q4N wh1cŎ;4/:0k9<#̯Cn!r*Y#hkJz~ӡm<`@u?+ekYkwc3 tfyHCO(U\|Jxgy$yڎ{dR Ǚ`B ֚HQ[G6 M;28eh s)rk;RKH4 20}Jb>UM:dؾH'rjl+_Y _ob D0М}x/ _mH `qb7rrz^*Pk7v;^`&>7%wp%@ Wň$Fh*Ks%pFp|1raor5q(`#?\wNrRu^N?o{hƒ&Յ6:/Q\3!Ulqj#PchMEI|ݺ=;  v.T=a N|2j;Zڄ{t!cߡ}{#)pll85F1صhtY{AÖ ֬l-S& U\Ffp`:Z_*8H)Yİ0!A.z!AYJV.`e䂉6vzvzt3q!`9_ ^LXD<49rLBsQ:oxҜq3 /v+hu;Z5S-Rc?1aG%$%ڕA3 ЛZXtH'_ MsݩO}P~w Y%bT Qҥ߯ţpa&91+̢RJk)tTx^i{;ż*\żZ m%vQ]d\tQ | togZҿ"7*=u]1'> HNac99Ͽrurt.ű\'iJkYϠlW(=>XԢa{lr nl(Hb˽Ԥ AtH[ w"]dpv(v;ۄl娨S Y-)w7M-wB1P=ccz0ݽKu7$|%*Hu Vz}=WWT<`rͧZ`(2跐nPփOzu MWo/vU<-Jۋ.@1x8B2ﯘS)Ob^d(eb[H'zݑv^%_B%&_ѳ )2^5_{Z0L\ң)Az+DzOY+‹{\ؤߓDE~6P׮LeTRC4Q=l@멵u.4;GKG}\n  ^{FQ endstream endobj 206 0 obj << /Type /ObjStm /N 100 /First 910 /Length 2783 /Filter /FlateDecode >> stream x[]o}_$@8$//? #I0,ikA6²֕Vsf%{wdI8;p\{%3m0 NL 3*m5Im69b8xαf\hBERSdėOoPώwq'`p7F-2 w3( O2DFrbD5!T#_p%cYEY jhI&ƌH61y)&>RFrČs Yb4?ŨXT F @TI@AzH(I4m+bf&%Dդ̑KLwl&WTFr&G|SH! \b(W,ߕ)ϐ.P0DgpfLɘ؀]3ko9> ^SQjr5tΰ36sxU-><īZg؛]|>/O/C7+pT3!$5L`+x V* y1!$+h ~ $=3+{fr5,O/F?0lx6?^>D f]b,-)[h6ׄ~/Wsx8{3ܿwo|pxX _{Z0޾=Z?7vuۋʞ̇-Oinةz\VLZ=Z@zy~ _-~|s w،kq\ *o4E.g' P?y@Yg'U8J[9<==t}<[{ s#,uy.\bxz<>Gfx>2Wm|6<ěg xe sOqR_6Cs)ҝr@fCr Pw#p"]D&G9 -1\TPj {TcJ'(jsџlX&"ư{iw[Hd^s6ki 5T8f2%+x0SC iGw.2/dwpwVXT8@S_,L܈ˡ-8R5\W(j+ QGj&.vQeHa2Eݏ,8afԺ[M5,{X²i бJ q<.8PDV5آH7Rizۺy334pM\""?:1؄:+L|ĨpׁYCHU, <) $l! UtDuG=+~ƕbR,7*h~ѵըշ՝8?՝n[]JRJw.VVVVVMVVVIZu'R/RNBt ~T[q ,NȎ!ik\KdG#1)&(-2H\ `EIE`vךOKB2EGxjR}eq5E( nj"!.H2\MHFbɣRēd kGTrLW7Xj ڿ0uCƍk֨˖Y,}",Ee/("/&B&_=GXnӓwy1Nwl`slqZI%?\]+R)c/tU wUPnz(7=M_妯rWUi7-O7OEd6!22{"#uq..ܺ E-̀I,&s D9N1}>pP&84tġ G Vs}/"_hm!m^3c,)!$䰃>;#n:p4$VUu=-2Fg6˖f _Ǯ#1/Cepyrcvϖ/KG~+qm1p`^tbAxe=20o/jܸDrɖ 97SG;#.\<| @.W#"F`).p\Ÿ0Bnxgk}U2$q)i Nmq}Gyu>u/#4H"F4AQ1u̼]T?%߷O4$;Os7mm]k}kC_'±g-ϕrse<L*ؚWڌO+kk<;z9詉Z屔"c.ۘʷ,6dQD4m2 TFI"PhvRsG!PU'a endstream endobj 342 0 obj << /Length 2670 /Filter /FlateDecode >> stream xZKs6W(UAdS5Le+6-A62%8xDIuT !|&n3}껟 zQ:S MxvȾN>W|O]FkߍIDmp%q[ ͘@]Q.3Y-,|1mobRAmEJj|0cLK﷭Af9%6oI{gb۪m݉ye{Yn˹K3/_n'ʗqŲ6ƬWQm]m7сe]xL' XG1Ir>^_@h &24 g γ>#Z!lu?=4 N&ԏ~6#3J+IJE:{A9"$r$PK #v?b,id s;]w~߹~Q8יblSl'?=l_̲wX`ߊ*0ЩGlV)rPmV~X~V؇5ƿ }}Xw HZ.I>;]>q}-IqOְ=u|9>E8&c,! n|W_7c%Ծיs_<(.gRvܼG%ݮEӾbK ~&18xޚ# 8wБis#w^."&V8 $pNb7Iܠ( 3SٺW:NVǜNmTG[޲z_uWcJi1?fB坿i 3!yxX C{pg8DcðLl[V>Wwg 7q}^> -4Blżklr*o}զ xۦotڇj;Ac8TN7cwJ/P'c[VO j+bphƸÔ`;w<1%:C[t)>'-%"v'|ŞqQU(jl@i6*N9_mZ)˅8맹׺~6uӏy +ٵ&3y'PC! Vhy4{c V=1D!am<#w՘Xx}FgHbc9N߳g2~@lLBkFg8">iB]T"OHhyIqklI0۩ s-?Jށp"c'ԛ18sFN)]Q9. -7&<ۣd A H$)vЩʪ,KwðZ7OK:#X5a-cK)z5z2{BIu209_zk-ꢼ 3 +pp!:N-/p,ކ, hұHN648*G#"D u ۍmxĈ^G&uNDn<#\qsrJիfGa5VP#pB㤄ұ;!t0%cWbqv[ࢻ{eF٘ʎhv#K7I7D,hKP_Qe^4.^Y_UfƔCy2v&Y9OS|blN+{PU"! Î `" VX`K&nM87!E1p4SQ֯|V7 V:Th }gDKWt':X͎IY BY),CEcDc!"NBPh._'*xs9?r>}4ai󗰏y-1a3k=]< ĿEa  HBɆ l|Udox(쉺TER0LFZ|N "Vp>SX#I$ΟTS$됍jcuyfܛ-lqb:}ps|fr0v9R8:-| Ǵ80V%( >]K|RpméaaX ~<0ǒ{"C$3AQ@6_ U<{ #?>-d(?<SdR hV> stream xڽYK6HEτ}pL{)u7c *ZfJ! 9T A*SJe~IE~zQā",xx IyF2sS]ᵨ_?|Y(hf AbR3Q HB)9JG`gI IDA0qï}ݿ ~49g>*>: "=&,]sV7>D,nT2i<*I/YgҭYMc"h )1Iۼ/z򍞉bAq~EE鞋8,#IedG"H*D"4KN"8_m{Gʣ,i!_dk5Lv1Y8<3ȟi_ֲ"8E,%(S_V'I㯘O2DžbIIWg1L"d$V e8YAI`x\glK);8 عCLVT,e&qzZ1lu;,cgQ1St@mxfaUcԺ 7TIx}Jp<}^hm[ms90hxg"|2s[jGtr򮻜vIQ}[ S F)$<<]k 5#2"T>:K]! 7Qu pbZ/6|K iv%ɒ:EpljMTC7%03_LC?E>KX6Xwg 1l5Aʮ^ !3pc&ցj D d[iMvu%)soJ6FFgyQ~-k<.ɱNeEMs&EcN\:@Ñ"lZR-Ken."a$E31{Gb)_IlU@>I)"MR+$fNκ(eC1ek,@vke}tݥٌE1@0],ܢsp(&2q$,:x̚( vs=",BHHSGg d a1m̛w#sf@oi>< 9M[U+lm+\:aG ?Mʼ>)d2 pq|h/ȝ8>⣶81x4`RUmuO|tu-}߳ڻ3L*?_-<6~[sj0JDJsRJgn-h^2t*K7].T xLlot߻-TX9?xjPn]:1<浚D{LNaW tY^l}EJQUqm}1.!ײ"oʿU@$L˘1_K-l@wІL$`:mx\ia 'd?]൹RNNU69Qm욈&8,e#'0XS8 W/=bwX~]'$zxY:3_;z2|<]$V Ztiu`sx_F9 1/Hd|&-gL‘<^ݿgwnxSA|v># +o!s@}SiZ7~CsWF\P:V5 A'"{ H !1{{UAYve{0k^*99+3vsʌA3}<-H>$b;Əlͱȗns)i; 'uUt’lcBS!GB09/evWHp?N[ec j G> !xiDaFB +Bi' ]S`;Um"S2]b IK^24*ReE`c $ endstream endobj 367 0 obj << /Length 2290 /Filter /FlateDecode >> stream xZKs6WDjwjf*[[[{́ YZTHʎ6ŇdӖ݃MO2dz_}H!0.UqJ#% ҄GsϦDLgj6z?P4~cc BB3bZF1(^Ә >;5.˳?@D  f7g_h$ѝy CE^*#^h#TzgzLeF "R?gʩa;$Eݓ H'/Q+(,9,@NE`Ob;~8 b0^ y'pF_bȢʦÏ',wYa(AބӼNZ{ހ=>tM}$.lJ&0Vu kS0;~h pEOr"BrAr{kT͎?9% Umb'BcD~2!M;a䫿 hS$9v.<&< c:h(P$=D"ʟʄRW}ǃy4F}1F i`Ɛg{O%8"ۚWv4*=`E %1);bk(./i_ "V)\SZ$<2r,Y˷E9[On-Qf4* H "Pr{Y8:N!WpO+, I8b5\YVѫΎޤU3E2@?0$i:0A8 JFºa+QJjИqW1QJM $*1q&-Ivb16$P0P)7zgg@t]jR<_aHZmîfi7n6deoAmdvs CH5#5fa&?A!5w^giV澵ruI83[-燶~lսٻuKz (;`u|[ߵ68r@O]Hkw>?ku3?]Tkp'4J]l׳:-kWHu}[9K3q(!cLV~2{!s/ /Q+ui<*}`1A(83\"/KۥL}6P6 ܥ#dM-8逳bk=Xƌ"J4F|'X*>vx䀏m|kDX?ZbW?xK`))QwC"`N9.N۴X#lo(fw.Zyln,|sіa|ϑ]k\nm*Lޢm߇t-{G+۷W@R)B!X(=]wlW՛¯1Bw{ĄΊω0ڈ>8pi~ cȶXwZP0"|ܯP0'8F؇ DD-jo> stream xZKsFWVy?v>ؕ*$hOZ` .HֿOϓDYQ =|_l\ \\^G ¤ZTq)-$^\a1#|Xl֋ǫ`d186`Խ@t2E GLbi)nVQ.7n;#|nmcTjڦ]ۮmڽ٬wi/C׫0nô&3ZXnFE vfS] 0-C路 pu9~ z b$UiF&ni'KHzΜ>B Ps$KW~QR]mcNlo}aB# vu:" 1 ^ά'! ީ@ Aw( SG(b>(.~U T+1./| X~u8clw=2VϱVTS!sNg RB" o9Fbd)PJDfnkCcx:fh .p[ŴAZ@r a H3R08n9MZ`}o%Q)SM&V>H4ZfH'`#«>9IYX\w0bu*'y,$@$ c khXyn'cD"~$icFZFZAFxR| | }~Cȴo˟Җ?vGk3Q4_4kzGA1#34Awm21e41[`*Ib#4?\Iu{lF@8 OLpYT c|9Ήtv Ր4%ߛf2wMt ϳE< >`pbMKo>@ W"~*)?y!3$u,Weu]^pu +.HK@tLwqq9ῸwBa)~K%FB>- RL]r;^Q/Xz}ρf1hsdA+'b"nBPD{܂OD& `v(0,B3ƫz.}op*/g?l}.Oi|[gۤ$p 'Ft!r&NC :e׈ŶZY_[+ݝ̧Z~<DZ9ۛc@L߈8! Mg@LDa:FHĤzz:~ww_fס~pP T"]߽{ogp戓μoYa ik9ĜټssxAN EPŽ@HA4FC@l>>eWс%d<ߵct4~#BBm ~)!NrSgiM E u|IJ]`[V L*Z CIE*5? endstream endobj 390 0 obj << /Length 1755 /Filter /FlateDecode >> stream xڽXKFfT󆩜6[R9}@Ie>=/zlJ0{q pݻƃ` h4$A`ǪxI'D/io?=PVccD;( 1.= d0eRG W_ /)n4Oɔ >LYs} +Laj+wE֤mҪ(RU$\VPŶjU]:OvIO]Kٴ.ˠօ0$ʘ \[GtKzȹz(0{WSD|n{o7SzC_P+:;vCdp'Sev1(¿֞A1> z#nm]m}/N) nAa;8̗|OB.w\IA6νlcI KB͞]2z#V_H.?2ȱD˶:b[5yۋogv&dHYN1Kqjh(2Hu]M&kFI+q-ajbV ]NdۉGz$A>04\$&]Ԡ@90+~ݞ|6:9*vb(z=HwFX8ym@)|&reCY ɇlO%.WvriUhv3KYPMVddsI5ӟ9X)%I3{~5 BF\Bȃc׿mc؟CD q !:!:1h,@@9!0<W;(jdР娮yA$ d㗧bIX5Ġ|`ı<԰E+y1[2yI; Jd>Yp'Iݮv_ta$i<7_&Yl ɂqT7l(mxW,<{Оg>!}Y]؊=|$ד7%?`:Z6+7CKU.$rAaR@ѩW?0u!E$W 0 o!2@<¨!@ic!$TUmmȅGziRӸgOj%1;IW}uR\az@PImUTs0~sup endstream endobj 407 0 obj << /Length 988 /Filter /FlateDecode >> stream x͖M6+tbiGoY&9i6m -CS}9n( ;<hvy材&FS-גH#=c0-7cU>uY/_߼&bjkBI /0n(R$DP$&d).anx8qNa,S/)6BHϙyzP>_qu!>ΔQ3\313uH-^T1 UHg,+B ngDg'݊|3{&+hvީ'Fb+~XPDO%\/5 Ʌ#Irձ*g* !;r]= T۰ֶ9-hUq́ D`3fOqhv1B`0VlbbTy7ehSXp/C0PTZY*Z+ikLxkjZ{~-cH s& = p䁅5 5`9ۼl%VPQ[`غj4}s="[=}80&_>w˛- endstream endobj 418 0 obj << /Length 2050 /Filter /FlateDecode >> stream xڽYKs6W̑SA$^J*rtZ;jX5&'$'6 PF=x t7?|Æn޾w?sᔔ%Wۍ.jS*F7⧻4~rկ(_woâJ3(!oYUtӀ9u>yQ;v7nw ׋L!KPt ZHžaQJh?AK[l~gg P3 XIT'Z"*K"EKNS%)u5 7NaÊulNz+ ZCt37_}0uzl6l*2Uذ9.ÖeGHU w%D) >Ӣ1%ex"}]wD):HH1Z5 :54ጰr D6wȒŪ 'մlkz'6;R1r/xa%* \~d*"#i

a3ǰ Nv ĔA T9QI&+킘[<e˜ ' }jƻyI{ӡfDM·8K")RL6L"u6ΐr;x~LmgsrjNJB ]$9_r-_pn) JAʁ$htpM=aoT%jV} luI? r1C=y' x>;G_>{s{nعoJWy ba;+717u6=v}qE%v=-{A~mܑ0.H9'th"" n7}$z|A Bxk[ߠ*0!^ "`|;jL ]sb㋜8RRB-_C/`YcT!e &?%#:a~ɢopY %XX8ŀ*r2DmHry*~GRzQk=^w~qVU[Ezc?8-_ Y>!xJ(w6ˠ+ X]KjV <>X`|kl8- truwмowc JQHt9{W!%Rw)'!R*6Rh \bmv)_,EpCSէmWxE*U_%BkBcF8u~ۏuN?J!{kәh )0$4tnD&}[WY`\Y9=3z?NYo endstream endobj 428 0 obj << /Length 2400 /Filter /FlateDecode >> stream xZ[o~ϯ\)MEOQnIT8+ߟEʔl잇@Dq3|C/xqs5 d[Pt$Af?mWD,b}ӛk#1ıIDAW8jA`B! bʤqD!o7Wk1^o~ͣ)H\+:V/~@~yJnu՚,#8|%_)hƻ?SKMA0;xafQm~ %H=QĪH2ek$n-ݳ{SRߍhm0GErgS?&!bXeޥ퀯 44{mP0{7?F*7*2Lj/Ycꢋ1#) d±lU|Im|;FBYbgCoHL{*5>[*hݐӤ‘lʀثy6yu~ZVU[K `}⻪GǦG>xDdEzVS Y$!7Bsh b8E҉lFXO i2 ե CEǒg4LIHhYҀ<0To 󡢺PYs&<;89Gw1ᭃ <!u_&ÎBཫ~Q^$ &YᡑFgX@bGL?27>8dLUD|3Qh[s%_NН0PF %ZDDS0|(qsnPC|K-r)CZ+ъʹ0;>SB pofRCoٕO+XmU_wU*Ww'2Lblt"q./7->&_-+ɍ :RD9t Hմ!ӳP$id0#2)gaf[}͡KHۤN'zL~)4sۋ( &RK**_V:!L5}wp+Sf^d-lQ7Q5*= $>R.ݑvWN7`"]m ?6h=ffU>Vq<,(t248}ṵ8p2[ r,8a9PJ)ži`#>8p]{T,nLfG;,U?> yYՏ '4n[+whemo ~\2).Eν2 xNJ?CbCz$MHFG|; M۝Rb$3)iyF*Ǥl=o3z֟"gRN{MjW `l'S|up1ǎLLsW?=hAy$(2ZXIܾ* > wu endstream endobj 337 0 obj << /Type /ObjStm /N 100 /First 874 /Length 1574 /Filter /FlateDecode >> stream xڽYKo7W^΃$`n @q6h ~7k'Zk%7B K VtZ ΁9*'! Bs *lj^p%T+xjh M} A>P#h`i %Z0&RdVEB@4 +7dfPR)l=A*mN qYU1j BXo` (AI Y\ 4-1h`**`Du pT1}Jnt̪nh_겫$O_w-!o C;n+p~ܲOİԒaRa,Hbؓcra:ɄF$2>-IkDl>$]H„"udh'.h"B!÷au2 Yӂb PVG@M)<*95;SS }MچFu053+fc:$ۨ~.!2407ag$6Ljp[-WᔡC obQ1^EWן>[<{4Nl9 ܱ N .Gta͢j=^Qav Yy@ś{X:N눓tAt'@?`*0,\oy%[.ufy8_^k|dz/_hӘ`jgR j1lJ6yG^~\^/>,/ƕһ)\{1I4? ߆:~~׫ȑbtm8MOKuJ6sKh[z7E"(E mtBEfx!Pwa%9" 2լ#c }I]Aytuٛ:ScC ԇ/8O}$氄L}XЧF/z(b>t".(J˛'Ǒ=iu og]@bK 0*^ N,pʔT,.,ڨ_̕iY_|MRӔ8$M4Jr779d.M1P4/-GXj(GvK?6/vʖ9ԺʸD>\kŠB_"Z{_aso}Ax×vuܢ]b,un}$a> stream x՗K0JתVU[U=IX@l7c $4U7ÀѻqO"-1&3J8 ҄{tLtsj`vb!5Z i7pvKBP; C/`P8!(Na+^n}\ɪ@˲7^@aF39ߤqֵN,p=o!bN(d.`Qb]8/LUI"#`J#%$x'ʝQjYfq@yWH؋mr슙Vyv bZ&5VbG(H wRr*J(@SSzcc%=KaŲlq!SbS+'.?Թ-D\ؚXvw&|>%ң]lHPw6T!E2_äl ;REa |^Α/ C"^תz:D%pQGpn_ 0x=t /?ѣ5U n7C.C.ZmM1g)0[յ2ɚd+AJ@ao( QBd ]©!mê5s۠ Xn)g0Y4\4ywvv< X;R"=oe8!ޛR9PkܗQ ƅϹlѿv#EH;_Kc*Qok,x.`$"mc'Šm( TO8 տ/ o;!|/JesM6:Y!M_n"'*߰nOZ )w?,{^\̉1,1T+G}1[fS{KMlL%#;yB|s!ORBs4q> stream xXr6+xB+A3.ە&S4Ɂ`TT&4d+q*^nGGWWwG)I~Sɔ ExD_OXX`rC+ t-# A3J "!fM_E_1VTy/F73ot= 3œ"Y(t rW ߱P:=Jrچ(e h R]8ﺦ|XQ::|)`DXeFwjW 8<vnu: {>;.d>$B,쟫PJݓ^W/ndLںVWAn7JRtc<ۿu-~7 SSȭ : =+mxc&-ToGPݖ]i2/C0>ei3ݺ50•DBʷ⩇P]b‘F|3lr$)P>#o7>$zNr)@.LToGkx& ' c&AJP[$YusvRԍº dw .Y(@1zlB.@.6+a]x_,fb[{Nec1LʽS #5!RLqK[])-΅~LsYx2vnx{Y[mF?Q" j:=&i[Յ8xthݯN f}cf82/pË~S&4` I sy/pAyÃOKK"0:ŝ $^#C2@r3=YVp( HG30 ɠCFy!0aY3>ޜtoƫCP{7P1= ʝ%MGXc=:ɖHEX՛nf".Lvmȹ m;^A7s$/׉,c,,!Q:L_k".lbכXbvUWV됕 ut?7u; |Q9Bd4g^}(6,cTipcr;Í+Ix^)+[9E4 R(l`ȸ܄n¶O[3 w߻"7m)%ө"16..ػuA@, 4ӗ8MN4H=8&q(;כ׽;ԴW9h"?,w8WLe|b̏w#{J_ endstream endobj 482 0 obj << /Length 666 /Filter /FlateDecode >> stream xڽMo@{4Lvۨ=$J,EU&D9P0$)qjJcֻ>z=l8 wt.,G3a(!5XϿJŰ_ a߽8:!]1MJPܑh>]6eC I Bl8z}Fq.`O)CgAl^nE0Pg@JCb"ُtGy+@@ ӗ& 4P7+u)K|V;5)' “]J%\ APq߼Zϓi6z$a?(SZn 8x(\PL5({t9EKpi/p7 ƴ PkirO>NizO;4|cnuV y 6@loO'W*B9Pg2S6Dp(@`7xXʿy8;Y`p_A7T'$U,;G0*J,: F䧻d'C%~>yYpl TCYKQbVFKΰUGrVCX=T#gձ 3`iWqkJ rw{"7)(2 U_mQ endstream endobj 486 0 obj << /Length 1112 /Filter /FlateDecode >> stream xڽWKo6W*"v[$9h\1_Hʒm6o|3qr /J(FRR\o%`" FMrI>CoUFNg$7t2r"'7H]YxwͦX}a}"mZAQCCvӮNA&!lb!] 31&[:._w!_ 1!x|wnѨugKw[uת*akIKnl縕h9ZPv3sK2A:$aU5Kk\n}ED 8)Tq/Mk{h;n}6B M}EX3IG1FyŽލg-9VBd5~q$pKΪk#.,ݳw`:&V& E!JFʮVPMuWUbc<ڣ`:Lk"=X%q{[y{Mb&/кthC lQ![7+rf>enTMu׾IZvW;?27 VC5ו ~`IXn) ﮼ŵjRbRrǏ}U6ti||:;;yt Ai{fHQeO0ShDG7K Imŧ%PąF|:ױNSO ۓ!kD xyV_82ABr|Ɨ|Jt_ogyb2N&b5Bg@p/X`*aEA"IR:+bAƜ)dڐ&V7hMdH;4UMWC 8&HzmI5~:2^l,a혇*ϻX}>c2ޫG]k_"Þ|FDd&dIhDjG,|4ǦO#.h?> stream xXnF+xasK0v`FAhE \OdJMFZ^*6V.~_0)d&#N$Apon&_i1%bn5`c186:@I)tVBR j3@q{aKW^3'ɟ2ٳs]SE5l\Jtexv+ 7)E>M}AzH+HF+($͝`\ EG o|,k3#Bh{> "OnBLO3yL+ĵʿAB5?q- xvɊUC* ݔ ާIn7u]  „sa PWRIZvx .k̮'MdE\n6<Cj )b-m#h{XƇlJވ a"УIt,NJVu^:Э_M:fԳ#5r5d](ފ&TW)G[k=6@# $Uݪ̵]0sy^$q>KpQCCv8FdTYcyPx'^ g@catB1̂}| 9l =J"= .ŵu'SvCT%~Vd8z )Ae ( |%Ts|J_K6"I`%qwݶ$j (DRud[ҏ %ܸYcC҅᭎C:8#Ï!O8Ҽ1L?hv Ae;hD4}XE;ҢP__snrBuVl>+_nn>$Ő嶏:}JPFKU#֪2Mյ'%3 X jjf{]ibsh-fev endstream endobj 520 0 obj << /Length 787 /Filter /FlateDecode >> stream xŗMo0| AZ%& iet;hiBķ@ (~^??v(YJ&OXDєp9ъa>>4ބ#&0כ17Q083!n 8/ hWkСX7xBB3@Id@=F)>xb(Y|/q'7c͛PqDŽK\Z6׸U# ~QIK[oѠ2_ZX@!Dc}SXov}O#OTb }fuFt8s_0sQw|=Yk><-,\𕣃)T[:>$e2`.mG6¥pQp H09՗kRVaIwm9 T V$̣49V[\sU^*""޵g@zEt~ݤq1ޘLhP =nЌxxfYߘ$!IA u>D)> stream xZK6Qƌc2y,`sJV˶[Jr:﷊E=d Uz|vxI2̚+aSBL™ju9>dW·@!ѧC}|jE;3$4GgYm辠8+UU=_~kP!AAG еdo\2IgE?,;PZѹ+)[ LCeO}QOGD7CpbP]"Ls{=s&4Xu2[mUT#Xl;.;,o ʘGǡG'LFt:P@ k(u y B"F"+*;gP=a6D#6PfQ9[:; >!$U 2/ @B_d48d&fj1 *ǀcIh5)X@l` C"eRޓaζ>(;ǩs43^@_rn BOW).=+V(OJ2u2Y*r_f$2AͲD$_d0) F;z 0jMT@5er~܅*Da)4z$`_b/i(bېf`ƛ4۬̊/"o4KY`Ꮍ} Lzã%%$ * ">wuh6@^ЇH,7"}]"9 bDSt%.rLJi^M'\Jh7LBph?]I!ި[?E^WuĈ$D$FMkh9,bo%!9 *i\b~8tBiiwyݝ\ӧqt0+.?⹨D{\o i3kMiQS &$O\du8XmAv{A[(}Hɹe\SP`ǎEV(@C$|rgYW)t:>Mo8C-Kp*#ea>~EOFSY̠ObLWIŁ7Jɟ:ūSȋ%#fQnNYf>$xK\א57!n;d8| WJaʘ8MWrnF$UX4P.=@n5]×eѸUq_$;[}Wsoy 4ARA 0PRR7—2L%ɍ0|M(~a ag>mjx+(dݤ0"P$T2_Fso?1]$Qpǎ pߙZϢFJexf_mRC1h[~S@e8y7 84b!TH%E;ݾ~lsۯ]t-t!OpF@7FG^,rTзH'WG\kEO_tjG'=F̋Xs2UB\.w1<} q:7-^A[1"/2J3)b`=> stream xXn\7 WhntER0Z Hkx8h0S;v1cF3#/Q7K )䌟BՐcu 9)mk*EV 39J9CUǯk i i iWRD}T,95!f7ėU bzO=a-:*wI8U⌷0H2+7 ,(@* w s%5s ,p5kc7#w\؛n1]& C J֠)1i"@B($+<, ɚ0@R YV2p $k ܃ 05i0ArlkM0S*ƢQ܀7q`x>Tȃi”-VfYlZDEKI S>ڐՔlOX.`[ -D[eaZ+GNDk$č*Byoo1 GRxjFzӧŋ_"#RQa[2i_,f>[Q<dan`}F<.k#9 4=_,/Q޾e🿗8s^ruyȘe-wˋ^oˏg'֟Q Z1CD tB(c-UjLT"[r(_>,X{qaU%T$ QR}X󉓃4 ]}nCYus`DC>1) %ϑͷo-׾\֐x4oFpa0Շ׳_|֔_G4?S,J]Klyd4zևpqs֫G3w!Ch{$I,3vV]lm6@_7`.lKA FUpn²lA(/Z!ʸ"ru.[||Z3݉=(X xxHka#,5r{VK|AZ>ϞG{N~t7˓>-9b9ߏ;}sa St>g1/}r3R!p*>cfq}F`&[@egWOf`Z㷂\%r(蓚}Tn-6n&e'ι\\lC(/#,NyU,*[r endstream endobj 553 0 obj << /Length 2278 /Filter /FlateDecode >> stream xYK6ϯQ$=$@2E0XtN39ȶlk!K$~HlOfw=YMW]}xӻ? RD+VOWHW*cD3zڭ>'m]4yv?Qۄ j8lA2IDvp~ r آ(ahr#Ljv@n⠷ H=dRH$?]o3}qr-֢;B)ALŞ-*{Ů]4y$y:TMQ@ EYl! |h8#P\'P;TLh5KZ A-e.RE+ޱsyWYFd&B~Fm8:G4rI֚SU]ljǛ' puՕEzp`EF_N Dk uEWO*}MHMxŷ;ZP3b@6PD*W4`#bb޹[<){b`;錉,[v7' Q% v/9Rm̨ ?m13Rv@)cN?>Dž#4bXa܅Zđ-#9!ԉUFm{:_RqR%Mg&lS05c FTӮC5QrKVBz\dN僑: A*sPtE 8xhc 6nf/L8> endstream endobj 565 0 obj << /Length 1004 /Filter /FlateDecode >> stream xW]o0}uZiiʞ>P⤖l5-i5:eK$-8J f6Z1[,}: i26 p3ܺ :tAT f%REq:W.7g`646đS0 9LZ؊K Oa ."0[w 刌DuM9bAThq eq,HNC2ż #fZ", KN2 f* V}vJtq୙Ybʲg6*ށДb"TL5;Tc~1{]SӈK}r!")BFJ^L _ʈ$aݼ?WevBt~BΧ 0%oD$k}79`R;(pb1y5NKP/v_~<+>}]"89AVń!ۈfMnaRnZF?r67i;]DLYle\{S>E Mwa6ŀe!(46_M 5Lm~2Sw&]+@Nei*p=M YV#ـ!f(5WΛl8^jkS6F`|^W~># endstream endobj 583 0 obj << /Length 1945 /Filter /FlateDecode >> stream xڽZKo8W=i fOi e4CQr$ӏP}0hIp7áF8J#;o;2 vQ4R Cxt;/Ǘae8vo?]~&"l0:z@! jdci) 8" D8*4^;]`gY_  hU{(\AB%}ש=aq:Ns$z|8Qqm@QSVvd ve4AJ/<~ʆ,+GTSE*v"i-=DzH'v ;ݣN2\lb(lmE/WP'7挗seSdb Ѐ4MQNcXķjpA*7N Dl`XDڬ1c!\6LҥK3^K[s]^NBY$$x9 rKt<{󙭣sKOG &'a`!-c&HuK(g^rȘx`mp@ӏԢV<\ǃ"jϱ2pR/ Ʌ,tJiL%]*g療"aXu̵t e!4dEfhmdgVa.οت6 [apڹ}?`@4"F!koS Hh @ n0?& Ai2uM}'G«$HprR ; EBk 썛 HԠA8,bWhe\N!<ۊj2`ٰm}#R )^C -?Dn܂ Х[&h-F?{5µ5>kL!fLdaڜL6Tt (Һ6(;1V$%DqK! 6G\6c;[wvxl J#n(R{ՆV3-:p d)x yLډ#(@JlgF@Y}!UXiD4_qLPy=.6V *H 8G8z@ ̹( rd%HfOIaG0>%h;(g͎}|WmW1>?M |8e 3;1`Qd:>z[Q<`-lND{ 85‘_j($6<܆k,\=w#X$RD-R'I.tuFԙl5YA[eǼ;BDjݛ\sĞfN h;/OH3Ɨ rΏ["J% gJ8SX1{Ec4)_.m`||!- 7fj-˶g?G3QBFdc])v,&?ע2Tlf0CF$i+4& 8\kp ;< ӣ∤8/23Di TBJHTwoH3%Vs6{ei{luOo~{[ʾ0;=̟`| !ѼHk5Q6mK] W [&`@ 7_TbeYM`7\ΛX"0vq'G[o[VԤ>X]=٭<> R,Q!I^*oclLl_de;JI6?+7y,5-vLnc#4/l^ˇle 6aO>p8 ˇ 8PQA-|'l1>ffӝ$/AET4Kٻ> stream xYYoF~`чRރKh$@6~s@SĖYq;{%Y°ggfgsP!RG 9 hF$}}{qE`pB TEgn3M$㎤y:3v0WjPs'u>G%QX B '@z$ٜSF:tՌͳMLKUYaكy.s^_n ѭ".sxѬ23G98xL t`Xr?p3O1EƧ0ĕ~@sz܂?D'q󞧹 s+“8t:/gƅwr|ntby"ῌ@T?遃;]q O}2_p1KHrvԧU]/3I)^$.=>>C?Y 8ǂ<7](Iash=+L pm# cHy[A(x0tؽC"uŸfu^0jL3?8w<nY?6{fLofV _3/fEqؚ8ޘE\FEr/Dxw̋h6gޮ2ӅVuVZ]י8=UD-a ]LpzLxΪAj,qo2X53,`nRgeܧpXIN*eMYudEu25tYFQV(6d4C05U"_%L4\4ML"`("'D{ۍG٢ܾϊu~aWzb5Dl^9q[ R3A|#P .+@*4龟nRU'Owi" YUxWlѬߛ|̩"7eGߪ> &5C2zI1 1FR'(/e)!ykod" 4 T:\Z$crB ]C=Z"ZOd"-|Cv>!;/f}qP|j0ojSH3uĤ%}.e#)KTۉrO@+ p/8ߧ% JI@h@O ](%qCŦtVWLtA6E/_aHHPo* ubj㯺l5""ȃ)cRǙ_nՉvw "R?@ H(=XDᤠCj[ 'qa/DU b3cm!'12/ kL *MKWctCEmHQhX[*EhkU\dnbj8!N &NnEl8)",f>Z )pi +g*MͨtzZX[#3lU̵uqLCGYQZyU8wc",$ӰW,M ˆ3'emĞIXeoi>'㺆@(1'DkizYN)>#UE7}#ġ1zt w I >M2s"rDӆv 4j̈́xݽQk( 2#zb/;Q_ mE۳ɲ6RwQ3"/|b?*CD.vi|x>ҷ?:&@~P6=[<{7(NAM~ﻸ-a 6" 7' rG#̼~+6 ?} &ø>\< ;l>?UgӳhxgK endstream endobj 605 0 obj << /Length 1673 /Filter /FlateDecode >> stream xXKs6W7j&D$N'O꜒h JJJwA<0eYL! X]GGWon^e_2fB@DL>ɄB< .2؝ ) ˭2fk^CT_b"Zfdf 5M q;ǻ!"2`!@mU0/fAX4Q$KU3 sF{Z\(!94C@3q8& g񻕵LxYy&,pX0O5!&U}>> }M[~?f۫+q mqIe9M9"8=՗2h-{VW濗`<lg#v;_Oj*2TJ"ӇM|SypȞmK[g X[>rJO!<ϐVC;NJ`4SR>K-N$DoSltᥫ\]84{Kt#SZ @!/@Zz *X@3, vޚ˛bu P^`̟Bˆ빥L.|ȏNrG NH[υĭa8B.9U [3ۘ}6A ,D.E)+z璩WqLؘG]--3`fXR[7)wnϲtu( Ym͋Cݸ]渭l<8R$'\. t;hPYϫ9 "xR$Κ@_Z#j1v( ~a:|14L7=ܼ@ "> stream xڽZKs8WHU vj*Ɏf'T%s"5$6 EJ*M_EUD7o fq#<2dtnOU81?΋|>R˄ Z`p&]аͯ7^0҈g ׄ$/nhoEDuĬ!ʸuE?(-ב%VC㥵D 7E%,8#Ll=x)4&b QC'uẕJS^Ec[Lm9"J7:. KU|=Z3uZ|ojJ%=&eȞ ZNc(:8|sr #EV7Hņbswhϝ' {nbdJZ* dhK*_u6-βn33),t7 9,M$vjjAx"_´&y]-X¨za.JBA`rtrNPgI޼Hȹ=7-2[ KW 8'cq'7D`B} [^pSvw@1(*xN *+?5Z\oz\vKho[äydV9CYE+T0I͏tɋfDH%D=yJՓ γƅ)gKqО'>HCU`{A s:Y &K^=^"<9J>yPqKG&b;UpIMlg̵6/WKƛb_nK6mKy\,rƀhxEֱ &( {}oÄ:+@7!,/ǙV1]JA㛇0gYE5q]gCij Vзq*U=!_M)p&WlNLcUj]n(mǁc_U*B83TA u^c 桪, 6Z]T.hbN[E`8$6 H $x&"@y(q^$ an2ʬv48+>.0U H_bçM{8!m:"6yeuNpKp1`>xcTp%NyݔU؝>LM6+ҫ^pH-=WݹC)$̬Q.ʺ45?^i~Fb]+"- hCFi_X8K1ʼn;#T!we, mG`X3c wun{Vc !MXӹC,烾eN3)v"t {BI궠Ů\!n5 BjH)yP(^ΓM97k"m7Z^!Nj/U ߴXWMvПOo afr> "I N;>3&:(OK% %Y90h0 rFoP2_@3N¡ ۋd\0X endstream endobj 632 0 obj << /Length 578 /Filter /FlateDecode >> stream xՔ͎0<`3$V*=*R= xz<$]v[Vz<̟g> -|(&IX&]D&Z,UҩkRNr_o\;@:pfLM S>Eb.eXtRvN[6T,&A(͡9MvW˲]x-S;'0ﯾYwe WyT)gL Lh)CKjIf6$^;BbS8 "P,ĨJ*vq/Y@6~*2MFy:]f-3t&O3@oߑeW@_Ho}cWIbHGHRtml}WQfVH&T*PF[B818x;azd{`o1r 2 7r:2fܡvOK%\R[0'r:UQS5m|p却_; -"6"F-ɞ}'Iي9,p$_%Q$&o'q(Z!qpd> stream xXKFϯ wӉrkG!L.ci%qҰ'ӑBZQJT3)=y_6{'L3PBY;DOjn0K7uVI*Hz1!9Yo:-2WR(,eVUm! 0)ư`#}#动nz7Hk썙MyPTlᙹgY쟀.p Pə%0az NNj!I 7&6>+-bn) ҳaFV>a {X<*i Ը K!f )DNV/[A3#DZ& I)]K+ v>Op1rr!Ĥ0۪f5:%~៛+ʉ8ۊ@jeJFg8p^Ix&,1!r39sΣKCަ9o: ݶ7KixYot,0*(a䴠2Wc`"yز~x{i endstream endobj 550 0 obj << /Type /ObjStm /N 100 /First 869 /Length 1289 /Filter /FlateDecode >> stream xXMoU7ݿ__XX(JhX x*!RA$^r!Q)9c*m4Sm17s6g4D.d61fTޙYys^V 6)Ѧ-`-3jKkkg?hbXYpq`o$F*XkN, n e_ , !Sd<+ m<:Z10p("IKv0 [ X6 S>)a 30O\p\X -lbp>!> H@ aMdc'%r!W#ɗq:`~vI0Oe$t9Yеs 1%+,Oër,Q4MxU_xߞ:ÕP걞ao`~B]2`z VЭ`%VN^kY*XZۢoݑiC!-:EZ1plkh r5DQܧ;*\54(ST ;¯ }xaP#QuԤa8S}3wK~Z^ٷEe\oUo(S:򰓶t-]'n-=Iy԰sVF:#VXtx cma#]5E}e,5Emk=2CB%#6(:L'>*y;)|[c藋&3r mzKYk]gh .jܔY7NkX.XnEiX9$KwRljTN#\W?Uot˛Pt!5(Ϯ%,j"C@S+nCΌp]7 u(T? ; 68?6}./a 5pTmAQQDEʕNԃ/RЯaka> endstream endobj 659 0 obj << /Length 2776 /Filter /FlateDecode >> stream xɎ8dLp%4 H0S}Jrmִ,UhLy)TP}0DS7 ϶3<{ͻ$%&͌J83>_Ͼ_i5'"xZj>xF0ql18aSQcZl(U q8[0PE!"Hu_ ۂ`Owi5IV]5'QP |Q!9x02$K11ÖO&3$pTcݤx;_"}VsYx6&<RI X,C8\!C%q2{` \k*jk3N* KUu֧k*+̛o#q ] -0vhXThՓ5;u(1Z2-|N(¬}frA faH؀<h1UZEAH0;GG_$P_o#(-B6W~ڣC=*9٘{PC,۴H)ڇ@$;J|HX/I6p]2-7F1]jT BiU:MYHpK !"8W% ℎ(>{XZ\9=ldaxx/ wG!Xo^G1N4X\K"/hMR"Ƨ:8Jk 0HG$zc62; Ɉ! r?UPL2l ^B`q:.=sIAB/UΘJ)@X\ตX )Ic^n QH;e{#5b9tJ`}zU֢v?TپM;'d$qGȦS*km82Pf>~ǔ(ɤR!,0r^62X理;!^y@!DH0UxLT)tB_!E~Ed}lK2U\'d\);`!^eHfpF ?(`'6TtlsYR[# oOqZFu_FOBþݰ {.9c.Y>ZsB٥FI`R]*$$}qAW028Uxg}Fm8)EA̗N˞)$=i%"+74#QzQ."CU|*&sw)ETLX wE /RXT_s܈_F L*̈́n |uPG(r)^XDڧ3X$xv gr 2Ϙrױ4z2J;f̬/4\PMԙ)㠩V=K@s3h 5@fݫ5"4p[r8~OBjTo5 Mӑ)%SE=peFw}y!yp[F-!wlj|po\ˌ:!n2~ۼ><3ֶeQ5/CI7%ʾϩHEh&.r"jsbМSY;$gceUYAQ'bb3qrո(~KGsKNOYS1 qQ49$Ӵͳ sV$(T2Tply:8JUSPOEϒܬw:8FFrޤ&>mJuo;j 2?_9}cuIhq3aKpHJI9?ԿEv5 C5f袮CF m jj5?*`2E]_snj κ]>^E]99؂!F~pt 3h?8쿈D?",B4(ȴ;5>B痗qJVmkʶ;*&ɯ|JŵT4Ntt~uEWsC4@u(:'R/Yjহ׼ȧL(<,? @eHiz@M]JcB̒]־%vef*+I~dicai F=~‰kpMгM0|v[W/v-zyn4Ys{?AlolbHi:,㡭 Y1GRAޗ$u]VF 1%̚X}VJm.r7FYPm  r.5j/+;21b&ƥ'OfFT* iJ̋U.B_p)b+ݟX&@CwU'u<߽qaFNmΘ0/M]ΰuRZIHA17)܏2~2&=J5fË^_5 h endstream endobj 675 0 obj << /Length 462 /Filter /FlateDecode >> stream x}SKo0+|4kJ4j=ֻB_{NJ{@71@)T%T`N bJRHu&Oqޚktm>̀2)HRzPC;BGgJr&LN2Yk,ʙ`MWuqױ˷>6iJyIuwv8bH&%Pm4?s9@naTd?jY.pD7%SFnPpYfi>Vl[Y&d/A L˭L{@%@mHLuCy\3_ˈh{l]1^g LvY>:LusPۏfX~qS._}$ʽƼtdZbޑ3~xykv^кթw}rug@XmxG?'楾Z1P%Np endstream endobj 683 0 obj << /Length 1334 /Filter /FlateDecode >> stream xXr6+B$en;өVqHH"5|ŋeْiPsu?-n>QQhdQ"b4EQ^98ϋo0Qka{#Jbh3Z7!\gD_gǺr][osՌwnng$a9 ~+d2%C-ؔ6ݓ5 m˦vʽv=,3`+> stream xXK8+|4UcEoY{&C%ΐ$p`bLfo˲y,RS-coao{;콹eSH+Lģ#N$Apo8y'͓Q[=.c qAh 3m`gQ- Ja)DPaW zC@0]c,0qonB0+Qc޻xXENXrl&!b!u˿A B7 i ff9ʓtVqd 1sT!C+"r TPj>4FD;57Hfd<EKۏ'LhV.E}E{=>g4l(V}/ D"))4\A؟dq2;mp}['rRn<]$H -S՘.5C/f̻iy5ڌd:AQ(5XRi%쫨3*|f˙dJs0׾IM͑NjiGY4"\XBa6 "YFyReO14y!tMCF C0R^e6Lb(bbvr5,4,m[QKF5v /,5Z? An&uf ]N"lX ݁þ蕭÷ϟ .pz/R[-5-rHIuz+Mt@Ut砇LSD%iEx**WD lWː|;;eY3]zI#2-g)bK9I{`[$YCbNewf{PF9= mdpq(Z;X5],:ɼ`Cg #τGC 0!h{1cr P_!̺w~%e!{FI&s_I-V3H} U>Cc2GuK !DSBAlyTm, `p)b!)qQbv>}Ɠv=mxz%I~}4u68`TƔN1WLrm6!<*5. M_M@J)p[ .vF $ƈ2u 5xǷ#ي$  E@P xvx7ۙLwtx2*CV]2iw׷mp iK(9S;6UPɕNWHݍ{N%zMqI.JNpjP$'(\\*6\Cvjd+JbBeWK_qjCM{E(FRH={5..i8\Ldna~ "[Hzɝw#9E^}+' endstream endobj 712 0 obj << /Length 1874 /Filter /FlateDecode >> stream xڽZMs6Wpr)ձ|hi^&ɱg5HwA)H`1:-p4pt{s{BZa݌#8FJ nFm= DOtؿTG#XѪvDN8bBG&P"}٤(?`,Ei`REuCxzك]}Ԟ,r9k:XQ08 !HHZ?#k TڳQѧ"~t^o_VZWQpU[Ǫv0] Fр0$XP ?-lԇ+-臾qeT8V+twE}`׳Eazè9ҟY>t>vQZlkDۡ7'M2k!@5L"`)D囧M*lM&7PsU[Ú-ہgX`K6{)h^0׷0xI$ )єd|SWiBҥ?8]QhS]I¥VwnBjNY5+;}}齽}@Pq${w8='$zGDLY-[lш1 m|1J[}9fGD{I@ @m \pbKH5“tSRnU_5?bZ^Ohc1@U22F?CiMs p8>>J!`0| #ld\ #efa4WomCl[-=Y ?w?x)/3q 0ݗlKiި?$kLuw-٥ݸ{mメ bp DKylP ς $0qT0'~P6aAu$O)8B$`-a{[K`rֈ y.y6j˂6%]BA=Ȃ3^A (>!=}MD !Q.M&#]%u2/RfFҦx&T? @~<`vW`=Cfޙ@EzvH.S"0?\jRګeƯYguC8~P&vR7`+CΪNCQJ$"Du2h‡S?m{˔skx>P~vRH:w %5*RU%"NACh/:o6;_Ph ?EV:;BQ}Y '(I΢K=50 H*a' G?zG7?IYE96j70 D@r7!Um~ -]phϼ9}}+p} EC/g߸{.%Rtˆz xy ۭn "Z 7 6Cgp;0/Xܔ`b+ S endstream endobj 723 0 obj << /Length 2910 /Filter /FlateDecode >> stream xڽZ[o~ϯ@Uwl:Efчl[ʒ+)7e{IAÏFzg7W?^ 3dv8#NL)g]kS,D$/˪\o/TF +;1ıfzvh9#1R=zAT(K$v= ofw 1N>e?_Pʪo7ͪ9I~-wu;'Y<}قr9^v2 RBX$eX,yޔˍ;[khJ70"UDҌu9I4_p&jQh~:U->mPZzuӖf ra:~`ق(2L. 4 ,);[E*ݖuޗfeӥ>o7>W2|5eGQIPJIĠ*@ 9NHFfRDR) UpQ)Oe2=SX=ँL6@嚶-]SlWhnjG'`6yolr{0@}7{Z7 ǦmAvoAGy;n~Ul=Ÿۊva𻀺=Ѧ(\{ΡCNsz;@ OxȡW9Ml9"̛kOĞMR/HY <鮰D>I8{.Uqfgs@`X Ox%N  X,,xuG#43ֽ1tC $yU{S 1TQrM& .9GJj2KCc>8]vf#.- ұ㠣1|g91K ICS=&LI8O aZLQ-29Xl)v( :q(J9> _uzOùXZDBR/P߬ csP# 4dѵ~i{ۈ+Rd((w|pp(3+KDhiFlWHSao }84e ?*,=+(ȠۧkkDh>J|B*\`.Nӌ;,``P@kBӵ1?f/7AJ>jaڀ3},Cs;jFlg.VĝSmrQa~C4i†WҮLH0Q!WV >6=Q U߮aT@! L0 TI%*Wf ,/ ; L@A3R^?0^ϋb˰ÆDfn%ORnUW2hpv2n x1[tt3Ku(R- jUR+jg{lA#x{n?G} L$mwl9y5Zwna;@ }Iysyz|f{9fC2As#\k zTZ4@/!#*`1)!4|uiu؁ >՟cGr9Rc*P6Ɖ#:.t6z ɩ+ÌDz}B/ppc<a 2x)L!i/fp Pf9kl ƧJ:=6(p?śK ~NN@]C4OƇS3$DWdOP% Jܮfqv?/b2/fv&st#k|\$ @ם ;cX`dC,MwDH}ŕmR4v}o3aH{.[jȕ zke|d!!.YqUg XSD{Q /z# 1 *]-lmOбƨ%CzYLm1aAުjOumVOUv6sqqi^u;5tgܘ; )06/ܞ'7o :Q0= cc.O2Ǻ/py1)  I4: MlP2HLJbƧ[L|5@&p5hcCrq W>O-ٹ%:s%"핅i/J_R& tGO.'#3u(  Q2. ZR }9F~gE͹AhMaL~vU̧B26SYj/ c>~e !ګ{5~jZVJ: T#P:Wig,n3Nm^ .ea:3RIw<G2qILy=#TO-O|c*'BjoX(cS61b2my׬?lk0v3m饧(E1RC I18O5~~3}l1S endstream endobj 734 0 obj << /Length 2303 /Filter /FlateDecode >> stream x[[o~ϯ_zp tnmKPѩoGgϯPd˒V>)j8L\_zD!0In8&JdOnɐ, o{󎚄`d186 i nJcF&c&EpRdOWݘ`4K"E?et;{& GmWiS>=u|4_nV]H1&ZTmn:).L"uӛI$uݪ;%)sz;u%Y#}7; e ÒLWi'KP|Y"801'vBi70ʄӐrUxvFq첝X # |C8zzNɃKf {yh?nHi*Q&#a*ǂG &_8جE 8Vt]0 c%|HH?0PElj-] rBfr& ^,]R Ɏ>"Xv!5 я֠ 91wHBηWr@$A.6_}aATqA(,xOtdP-AA9}/f3i麡rc)<&'L%" A!a`/ vmRWpe6b[[veB9>0fѐk!b( 9c'`JhL=vtUHwTt§k˗?<}\յkR9r-&[yGӆ% y{\{J?<%cW4&mX98v P%f8 d4xtd#,5 S2<x hZ ֿ @@ |ˣG62fy?Ê'zMŐQJgs3zq(@0Dz, tn$D XVK;Hv'+wh)IX|c Ӷނ!Y"j5.Q ,X/qo,&M  `ۑ)l}G`Vm/Ue+=Ơ<{ }i?,ѹx3ƣ:y,ɠhPngC8JhH9ux-N&VL@kKűg]gΤhcrY jdJom 0wVd0H'I$"mQ@B0^ƶR8)/Ő7NMn&typIsJ .a]EC$}{*OϾ;)qOcRC T)SVccmQu#R}!Z?Gե~:b#~[SjJaY@J7ˎp-Z6#+q0pgs-dسt]ǡh[R5aҍ3bɔbLyU#NR$^}Z'KdTj(^bT)jaW=Ryz9!a鯂T!Z/QֽmzJD}NYu<؈Dq!6ʬ pu^I#P͍ac{kf>X6e}ͯh 2uq2*5E+M~As :ᰒXRн+\9H^HE%\izhuvS/oǐ݆p]ܹSilxo?AAvg9|s}xn#4:`M n/ws5!5}>66(!QP*w􉷢 |ZgDh?+VU!BpK%9j]ŪVmSAZK.^ "\u>9vuӵGP -- endstream endobj 747 0 obj << /Length 1617 /Filter /FlateDecode >> stream xXI6ϯQ 7-DOmISi8!Ai [4$#%Q;,#^^zv#dQ-6$GYʈb2Zwfw$_g7\EE.H@[ tE60;$BΞKWi4 eD*BQUn(s]u+us=ƯM=QTkZԫ4U3Ah4௪\peck$Ix4*! {)Gg7ZZO͒pV;/Y\ pQTfݢl );e 29fQq7 KƉ3yh5jhj}"2ePW4TB&`PŭnBy|<(OE$K|h|௟5*_^F4Ey`c9)~:Q?"1ڵyo$xiJb{غs4vpgЫ1BK?+9' C|-gD\veee|ǽvm2ז6=wW]=,+'J0keAYbLJ =7')gpJ$\[֡R $Jrs>iFܙ`pob6XY ߛ 'K2֧{8zSVeZ<܅Ϯ2ᳫLB0@Dui ؋ne\F l4kjdžF3/ zHV8IA^mOP39~h5j}'q1-L񸧲(;)Ŀ E͋a*)\0G4 -)A?|5NRx endstream endobj 655 0 obj << /Type /ObjStm /N 100 /First 876 /Length 1640 /Filter /FlateDecode >> stream xYMo7 б=T+~ @>hqmgQMx-{]yCR#EiM-dIqOVK"J +'6U&֔tYXmcՒ&Z{|qjLRWh5M&* f84'" AL*6k^H`'0I7Ui@&+k!yƉ$'.O aۘ;^OZ *C\3 ,0 C\ȠCA${8 M-\܀ic| t[x)IHzw&ㅓ0p0&T j 1*v 4-`H'#&R1-'Hvb b pNw +W%AT0'IN g&59W ^gK:)L疴a#m ء7JોaO؅13!Y-=U'|RVF`j%HU]}!5,zQ^`ZJ VgCc#XiCXc2{gؑ>iz -vlzQog7*o<2o,Ya'H ,Ȏ hذ|q>[|YZZ}> stream xXMϯZWrp;9$W>U0H<ֿO RRYjsPF뇆h hpwBĆ` DrHdZ. iO$1~&4I; I"T,"V8f& "6Y#.+f0 ~yz]siۺ:QmW*:MXo\F #\Ma[ U9Xi!UCpU,"c Ed d62m BpYˬ}K"@nQF`:g~<&n0(k;=oOH5P͍) x]?Mh@UEx BiDx9+:֕E>%]-|c4_]&&D[/b-񟘀A)'KO#)3?5iBhm0q(TK"X1O f>Z!yLEpq]b:xOgaܡ3T+(E*a=m΢IÂfg=o4Dl=&#d~snfwY1ip0P2|.1LT ^3|hkE@Z9Ady)ϓN@S o,}`ɲ0T|1Cehe1Dg`1s_Wn_BJGizc\0*eWhHZ7'H{/ɵ#>HK;#.pz1,~Ϝaͭ,ۼUR=Y[6;}t*znFj~SoAP/z. AܪYŔDRcD ߮~izKPnX'Yf` > \c 2D܁uw~= zF+ @q#s /٠QMSr6  ySBV% Wuu/̴ļ/c13Pt[;Bx_|ok(81o3,Teap*f{; X#N+Yt+I!{f#1SYN<{6 4s.y-W`t xܐ sB {'W^Qԋ"O?߿~OBgPmD_86I$S"ډP9 *HbF$^ϋ/ u`̹IpOpp͊A(_<g;: XȫL^| 8/c,3FٿٺN&^nA,a9.o!L Y̿9i<"T#Z?s>S8mbb//.`S$L !F? D}u: endstream endobj 776 0 obj << /Length 2000 /Filter /FlateDecode >> stream xYYoF~#D-"MO 2-Hw(V,Y0{-3|34˳' IryP4Q Cxry\?WdDD̊$#1ıN3^3]qI' Fՠ,tW #XA"st,ӺZ.FcAEPӪ^ ^;5D#M L?MRO_/8pMذi{.Jq 0ry\VuSTy="i K ڙ{Li@8 ^0Ee&Hhv, ΋hO˛Dwip8#}OI~8Hڻ5] `}&KV2`4@_JbQӍ=XӾCKNc}L~"ػ؃5RAڕxJ8bU\ > ;)#NhK>vSKO~Tg[ߙgy6fKR٭^p2ipS 4}mlgEwrBl|{y=H`EVk$.Z ɑHƋbFհCBvnky쪋y>j&@Vӗ6mM&=]8PVI:74xl[?b.=ЙTKG~WQsp&K$,pSD ظ9i8-~۰o |vj7ZcIR.zw}FECuӥG9pxbM윂.]uϙ`Vlbs,v]Gl6;ti`k Ǩ 9ERF\O"N0|)0މI5_,H,y,5nϫ:zJ9/c2)͚P:3\!&b- 1@{*N>glS PIs̫{7DPq Q Te>5~ _}/&5?}ϓźX's.)"O0>S~5?!nM!ZyWϐ0|!c ٹ(`fIII|!~}h8e, td endstream endobj 791 0 obj << /Length 1504 /Filter /FlateDecode >> stream xYKs6WHX0 iędNӃ-C2[JTIʲ}Q3>xBv0>u>t. fP4P Mxp܆gKKDO~5Hc18֠Z`ǛοCЌqIs{KQ0$GV"Ho?:t'"`uw@#-viT"̵3nO;uJ( n{p?wO?'I;ͦqQ^W3Mae\&O yυ !+0kAX v<0~8x޻y^~p{)4h c#R*&q}\be3;gp{tG `"@V$bCC&P%$VEm@&"R"b-p}xk O*s3Q"oolE$5+JbP1{Mg~XwAI ex&K7e&0X` /JgYvX,0f짬DnCSo:0~e|W|WKǤJTaԎ`~L| MY4*O_D(#y5w %a:? 9oۨ[嵢D7O7Xe7w{êj,B^ iY/snyXUZߠ/fH%!rU[+ b"M9tkS`IȳjUD{WnkFq١)8I'Y _M5Gqzs7Ǡaܥ-u2NJsV.ܝ۰P84veC-|9P`hx͢>Ad#d'K4tc 1B f<"ZH&X ]#r^Tne ӈKr|pebrSl*Z%,ׯHTR6ȇz!ަ]4:w;u 7HaqtUmq9ߖBCc^19ccbraORKpԌ@oclKB= O]ݿ߫ 36o/~ D>U&.. endstream endobj 806 0 obj << /Length 1954 /Filter /FlateDecode >> stream xZ[o6~ϯУ , ak&}Yǖm͖Ph˒+ѼBE:4՛>BFaM"8FJd}f5u<ɨ7}j"mc ql@h3 c`=FF=&mRH"!6*qKq4jowMj.~ZڛHpԣFS 9JqMС# 7fB"$S D7 œ“r&e c!f/*v"6q,]4C5,3n#FH 2u JIp]Z%(Ļ*AEΐÙ<`J|V[ "9E#G"TJ<ܼ 2G %ԷBmЙ3r\%[R$>6 yVA~8 frqcL1j-8]n,nb7'ifC,#čgV>vE!rĸ#(T#jD5}qTt%؛{ Eda#sZc iwo͓4Ypr{,^e78[E}|0]xË'dqjk6h1$ckC$qjc"!9OD# U)I5"r%4b,݋k$6Iy/G쾂yN8?ೃn}&E|3Z2wFD5\NJ 7&̱2"Ty=_UX^\WfYmƱ{'~O~Nuuu٩Սy*S-aUTCHF*F&mUTA@vfc09TВ]kF\zow];h9PA|Qm8'D]|ZVx2AWaF.dZFw/4EO.`Yp1ju̧ 33L1_%)TXVLblX:H3%>Mi F^7~ˉN %}5RX[HSP`rsHƪ+Eh)h z:ƪKںzɑ2uW/ZT2vn-ݼH󺛗ޙgc@Mb@c e.B&8> stream xZKFPU.Pp*98؉ɅpЂ hjW#1uOwq4ptw#$Q4R Cxt?ƃ_鐈2'^&"lk! ^N~7∀dKMW7 fۻ#ftA !e7,AFV%# Ȓs㕝#A`.sm|0q_rFy1KMc.vɐ n+  v~'aÏXonsR|ox%-0Q'*`;FBj4mxNLhRDu H~" FDc"QC$둪Gz6~ڭ E<1&y?4J#J1 k9%ea4|l*Zu웜38m|%ER_j5qOh"1 <ݾWTwh\}_,]˻$KE\SR?{/)n5ic4RJ^:1N>S{DqvV5HFlrJOIƻ{f5U!G$M` 3<g|5Q\䳍{:p4.!VeaK7 禄yqAݍ{-&3?܏7۲5N2AQ>ojކHghg5봼ow;#ԩUWbw"Qn>ڀ&v "dC#u DAs`9(GS~V;',޸ cOpK𲳁yl93'.e`J"'7nbm U T>\`V)5"D"L$؜$ 4sy\^sHfѻŽ%݁ic9Šhs%*oviPN:sڶ f"BL@֎/&$z&c\1% ݎOzXwڐk 1͐Q5/#RRZekY4I{m1J碱"Hz)ס]X ߎCw4ڀ 臤 Ա^]ݪ;v~nF 5⪶52"Ɵ6ǣR<۾S]%Wi Y`C!lݏtŲ5EJArwo!\b̳/Xh'_29: Un[5 Ezl>]WnND\]jh:E]TkD`Ѝ]|SRcO66 V$ 9NkՖI쟇#bu \WtF 6PAd/X :Εf"m7OEMfIcz1cD}2;ePʡzRxSo#[PHĔ;[|5hƸ-wִKL9N$ p>FCLmX$٦>EtT@viIWJgc1@4Wв1LT.6ҶqsCL=2g n<."gI> stream xڽ[[s۸~ԉANvfl3i⧺,#2[IR/"uC>|>8G8 KDGHI4S0.Dl׿Q4g!5->h GLh(KaҖM[D!Ƥ7uWFRs_ь'TѴ|y,ݮmR&fei5h6ɦ5>2*PvĈ 40K7h"LM*:)W;o0DPCx>Xtda1:?9Ș`M.a Hsמ>͞ٻoyD 2lh0 6cсS@K OB3$3O^½6XAȯ=MtgY[ABG)Gj`}Q 5W"P(gq aqP=!TTޔaUnN Vsʳ9Z3h[x a 6rNðŖDDw8 L$:Y٘9,[ja{J&k PTI/DԞ@<<)<)FJ!aFD1&з:7Kig67 `yBBtcsfmYQ*QlR:KkJkWD`Ic8A}qS pc0|0S"4*GsuPۃPVRV]YO yD#Ū 1ҽ{0a00w-|KvX61Z'١bYV̭H_L2{OId(6.dHfӼ_=G!JGtw]ӕƕ*F=c!LYrUŃYN3I9c>G}k W)Cb]>rx4sGe@LʞIʮtr^StWd.bixbٚu 6ÊfrTfsJE#ľ;.5J@ؘ!yx?1Y#*Dߤlf\V.6ǡyPXqZަ/&QHX0NPљYaTtmNSϨ.1mX~It D̑*='~ jnj?,wu>æ+VG 8.QI%{|U>= oDע&L}4>nC Rg?;o"/8*͉ܓhY5>!|Bc}TGRSO`D2xTɵa79LJg{B(vvοQR,vΰ0.y;lEK_g һ'q%¼>T _"ޯ^0d7p~ Sij/A(G5%O8w RgjƸ>3 z?q}(y ǂ"|7ip]joHi6gjԩx󹝽Mym$.j jإMJl_#ƴ)ﻮG)WmW[l-dbW@K}}N|t?tQ~I^R8ګP=zxyBFu31ŗ Mx'=)'sAmB<  L[ u+ĵvLtUT?o3̡+a:mYr0Յ> stream x[Ks8W(UY g*S7Sl-6w%R(' DJ-F=Ĥ@C@a7o>2)(L*8{7 v%]̧DL^x>Gxc ԕ.j+@!= Ho0@ L;_5~ngcgk$P0A6哿Th~.lJcm(ɳpո( /f0QLI](B("TAKlO`P;P_3 hՕ ॠϽ]#\t~Y m`h/.u7IvR"x/2ŐVsI0JɎ6430 xxB A;w\tǡ]}*_(q1rFh?1BsEa^S9=uQ~%E .*fGʪ%&΋hětf'㒀)~˯Ǻ Fa>w(h0n*VIemWW aJ".8wɀQg.=UuK3{m!;"pl+FyWw!ZKD-;s^ӈbj» f)|񑢞ڝҋ觕PH+k =H+ۃkzu# Bѥ4'fڑ:ë]Z7ɪOʜfl<ELʈ0YtrVS,,5QI0]CFqŇ4`“[{OH$!?+0K&#Rvi! <,3~3kG}@ĠD`V]^prRX)0<J0Q:.zq}-FZ6LY`rմxV?!Uq|txU$Evy{gtNƿO2uFy0Lmp{0*['̦qdIEmLy˝yfyePfKV$!Nɫ ][05Ʀ` Ijj #PT8Hƴ`Ar28:ʽDQ.^Sp<~ F (ƐҎB0|.k60?@o?x5>mFCͦQ?G¥p3: q%{XY6:v(Iݫ%X }Q,vc0 #Q pbcOɔÔ0ve< =lYtwBN>nLoqKY|?xͣ\(\lE6K6fS94el$=P$0 = “vz" cy|5$¼ؿE?߄3o2]<7_u%@@hOI'(#yTKH)ZdᲝr qVQX8c6zt?\ٶ>>y5Y4Ͽdb;%y.-d{77֚[co0 1F!e0Aʷg22XB!3*`8Lm)C4K4/ۂETd=1ni$֗͹ɢ@Dޚ7#>sAK"%70קp YOZߢ` \ uIm$ehhGC10+~Sv hN9SnHKB(WʭT1ߓ]s(0è%-ƍUHjEO'!piS?'Ucw* [S\܀ll$hWR ;7e ǩ6poI M.I rQvo"!nǞƿVE$AXS¬DJ"ϓ8A*#ޓwG mp2s4GcՆc+⧜õm ӝsHtݷj1D~8Fm:ߏ!Ԉ'4||͙N e1*Y-8 endstream endobj 845 0 obj << /Length 2562 /Filter /FlateDecode >> stream xZn8}WhF;Y{I zC&Զhז<2=[BKv` RU,U:o7W>2)&CDGHI4*?ߗ3"ߗ|9#Hc>c qA+ ao~"PH1)J4Znnpw"No%6ї9e4 UC$R0n\P+)x|>xK#s]SIJ VXM4۟HtTߋ!aWqwbkXZKdܙ仿8XmΪV.ߖ1og$ [ȟe3'o`A 0E[9}>kTTSI:@Vn #wD8chD"JBYUd0:C"*A&ޏ/wuU>ܔBYj?9iP>$k?4H&o()UF&[٪?t_ws/fZ^`(E'8b$ycyysgEPa%Z^(!)X@o}gGaD!!L~F8 B>|T&hN͆\(D6dlN0갻IuFQL5=Vl^,,DzᴮΚZcFsf [aYnw5[~\(yL4d&v-{;mW/\b׆&BMӏ؇jpx>KK4m"ҫڎ|`H9169\k1ya%Va(JI|1T]USyf6pd0<sh2fUqYԥY'k>\?'։Z#ҴpwuY5Ł.zev?b&[k%FM!C+#10QՔ+U.ͫX?Tv85TKZuqX0 V DŽh>Ieae ,꘹?<#0u;/ǧ8boGY3 rx&fΩ:7{Ќ3լ[ӧ]Ƣ [Aݢ 9AIQU(`̈́Pc_Mvtzu:)8` HFc Nֽl8fnFs0{?$8D cscq23NJ}|,@JONn f "kFDYUA }NuF %{I.8_҄K.!1[œ &$Kfh[ YK2̵Iytm;[ 2*_oyiDe9yYNr!AYtu:s9pYug;b/fZD!6@z5[֞4{ )#\%+V'P4Y\ EXB{:m D/LN7L%P4Gx=}g7 ǁ1}I1Y"|4k$8^w1 ̒Rnf5@^g\r5#8n3 0JӬy=ʆVϲaslɰ{ ms<`~lKE!0yޅ9۶.;>B`r6=H!lH!E! LCmW(FIbN;{:-1rUMZ>M:Q&z׌Ė$0MN3 8ɟ@D' R0WiΜfLS~B=^ľzLM|H1+~jg}cDwt!o(:  4SZlptIL.eO[ͥngy5Gý?vx/4{4X;O9۟zQ1wa鹣<&g 7\Z}sQ֗o}C;hHx)~iP ˼ۂkGs5J2t]` " % |vj*_uvW`?-;+O]yrAe\q^HNy>P ^ bӇ7&bh;0CBIE RNv8ai|#0I$r`aӲ6HZ_\K24$>ȥ"" `> 5Y8o-κ+6ӱnvD${wK@!W 3‹mknM4y 4@BK(> stream xZ[o~`*X6 mMܬ\ʔL@n= %,u(F}H g|΍8X8BFa,VUqJ% 21|m.gD_d9{Z|zGM@02CZu9en78H1(J4Xnop >1_r6(!D >*Ke`pU H3$صl.h-/F}{#'N' dCF%|&A{ H԰&G{}J`s&vYP4#(]ۿf& v_%-ynQZw-01ҡ*H4>:?7kص_BkI\BjU{lJnv9{ISQ-r>C"u OJbc ӽos[f澙MWV8y#]`8I#8bqaפxeq 61~,F(H)y%s(%ê&t\ƥ-JNU!盒}H@o!}$o(;4#"H$Ύrrycԇ2CCtof"&qKV3a@Cz&J*sWB"@ PQ,༉r%0iRpB5Evt134|}5fDq <-͒s ȖJ4Ӻ<2pqTg{lOaBHÌojQӘȒ/3": -0%lYA|rnl䞱[ AjE0M,^wѲؕ4W?Cb|18v_"sUgjgUnFxcY濩>.JD=4T|,Eg1P`ӱ>!ΦaTӢ]ݮ;EՕ:SLh||n9%^9%H7&}X"y%D0Ey D-,KrвNv!2+sBO{;}DBB2j5J@0b_^%~΢M &AY w  QƗ#ֶajW"5Q=XE(b+ϓp> !Fp nkeȕ8TotÎ@? }L!GlR v5>AU K:pݜRz(R |ؼ/sЖf+foG)qq{6tY@F[.=5KΨaՙT*HWWnk'f& p%.;&3Mn!!iN3TVOIbg몜_nA=:QrVa}VaCRD˧H9h06|)}+]9"+yNre.c{ZZ\.8\GI}F;*۟ؓt:zYAηv ɏ3@pe2t@o~#@Zֈã.5v#f> stream xXKoFWV ~6PFM-QKjٙyq5BFa"8FJdV.PnsկD#؀n7vǀ(KFFK&n#v#Qz?n.%Ƿe}͓b04Ί>~9$Ղ8=W|W+F?ZQA-)fmE6J2ѺŒīueO,S)(ΛcL a% áH,>ZKfb-ZT!lUQe͡*Œj>R-})$eH֛SX%uj/alPBZB^,rdJ C#tf5gs-x, UyO9!2|L@C&u륌X&.@?x5!CKUCBJDV߲f䁈g2f|rdsdsL]glNSvtzYoyT-X'Ec/wПk#]-ף]hY /2Wy1\.6Ɵv(Pl*o6ήy;5?ٔk^]m_?lΌUwf8kZ]  endstream endobj 759 0 obj << /Type /ObjStm /N 100 /First 879 /Length 1580 /Filter /FlateDecode >> stream xYn7 W^"ER`H-AC[ÇYFo8@}ة;q"mf(|(-jÒ;-QD5M(UFjxJ3<5)a4ڪh֒v7O&gO5`*~ 2YqңQ LDPAJ` % Nh8;cŧi)yE(B.!Cjҙ t윘+Ti& 9В+ٺ& }`V:$6X!-L 8û {(ظׅcJHhZN-jT/LABjؼ744c m)Z?lP:)'L!jא ڔ9lI@pZ8)c,ya5xd=>AN֗,0]@.N L*Ac N$DMHpS{=aBׂQ sL N f:8dR|3 cm&1)b!r 4_,QZ%=K_)Jg޼9^%ĒIVp}v aju,/;-B貃cGABꀈ e!W0 :v|}|uô|z>jVbڭ.b ,)L~^:}}:5s nP~^CDj4)pFFl7229Kag0`\m%|K5+RVgwIf (^sg nihD c}(lMv1T174pw\L'gVLx#t`"$H{EeF3Pdi:]ߜ^'|ړ=Ӕ{.z y6Odu5NMuzd˩3XT9aCX.9*, a C`ǐ, Uϰ, #.{Үai+%Q {};aE,G)J=f2gЫx,ư$ynJOǰ:C؊M΅:>C-<_`ky˖}.-a8|%ek"ڿf2ilB?XՖK1,l75 5" Z\D14)Fxzϩm|$#mVp41,E 9#Xɀy j8ȸ Djr os9lU`m'6M=t))B1{y-U3$!^Ybte_lj+OnWnqHq9*>o`iI w|@dVoJ}9j\ͣ~>kDozu@1hiٔqQư@qEL a Uaߎ]G zm2JӅ.jKqFT?LSΜ2gv!,vd~Frot˧גM4? Xn\ endstream endobj 883 0 obj << /Length 1830 /Filter /FlateDecode >> stream xڵXK6bomE$EmdH&;|鵲 ڃ! ͐8&8U HJ*M$L$RĨNɧ]vlM\Qo7$m2~L9R")NV\Uj_m<͒Ej[/IN [4֑`{;c_߱ Hh '3I%"E8]l;˔K%vʟ}k)q%bm/`]vh+W5aqggiݴ~5+~1y mk0*YN q~žw O"Si<ǹ5[ MQ޳r DHggB bBi0nb PO{3ǐqIvUR#ŷgQ clF ,Psۙٯ4s i۪. #T (~GȄI>IqPnTsjEiaOx`]c<X)Dn;XcVg-] iy-j'i!c>8y#kdžVfJ]GlmzYXaSa#ZS)LC}0 x {hTqTxs*s른lfP BXgZ[rٯXzHZ`.6$~Ñfnq1eN TY`Phcs{uE慅"џN_n'ͩ*8"}vN.MNۏb򰑧 P)ҔNMa!mJ)f2Km4gelB#Y󌐳8REHtNdw} ; LS%%m<Kb71^90  @|,7QVR\XvA6)_)F ]]`#C`(*a$1+Ek o+_.|O( 5!G1zXC`k%)'kz\xNyg.9 Rw^Oh>J}뮹0/_Hm'v)G%yuRNo`lzvC uJ)FZhY2d|ӨC)8! S1a)ҫZFD~yjֱR^@'Oųiߘ~͟X0Wqs /% W#A%Z; ܪ|cG|samF/*:!g8BYHIwhik$ ,2{pE%̏>܊{`雉N5G> O2vjek2iPmͿԱ endstream endobj 893 0 obj << /Length 2789 /Filter /FlateDecode >> stream xYo8f5S:؇`-f3`GaɐfxHBIbR4HY/^x^ȕ&luuXq-|F2&WWoMu7LOyU?=VfvD 65 I/;Ʒdj+#`.8t~ʾ@^#}y&(/2Q]b"`>}ߵ%Ɲ[A3&Ht&4,Uxn3ߢ6H. bc[_%C 3kL)<-$<ݫ>pt=aĹ##X] J&?}-),iM4iHCaN0ӵEUټ#Nʫu<:U?E]qwwSX= F]FٛОPn m[]OM~(Z]5'M"GU))]P^H%nU٤:|Ж38ĔCp7?n8R#β S71S"b1tݾ; %\ êp €@$w/aL\wt@'\-AJ„HO4KإjYD*$Űv#F'G7[z MԠqɦaEqH2u;Ψu 4asݷK>erV(N't|(]*գru+)l ZvobB\FI(#.d ¹Ru!s %)c|^b,2> Xa)  Zb-k(dM4C(x( @!l੷5H&^ ZP,Z|H4n!haL}sNcQ^,Vg6Մt"R*M >q&/g3RЧHQ6-.nLuWP lZb~YIv+QܟWuLF9Rf5q*aLDJe//E<7i10ϒ XMv 1tef:D%,B~|1zˀx~<Dvլ-E#Qm_wZ(S-RS-p5$%"$1GU*{s\>乩z58uU3?%lЇP2w+"drqn˿FY'>axbx7))|0}v!(x,JY'CL<|Ezfݕ)$t_,ůD!DuS'[/S@$" LW5l?W7+%×* W/C endstream endobj 900 0 obj << /Length 1632 /Filter /FlateDecode >> stream xXɎ6WhcQ rJ3Y 9Mr,"8OEj=Ir0,XJ484" bƔ}㐄qH`[WifrO?4`4cB=P'_s b> #<$$$(6SR|Rգ>ꓮ҇OSIhz0wq$=aݣDHE}J]_xB"n'l/9A/Ϝp7 "]ԙ=FP (o_oS[hT75>Yܲ#utӨ뢬tht&MG*"˚ѕJ]CdhU+U8Bm̄ˊH ]Z]SKoogN7nEQ.WrUY/V(m'̳5ۊpXtv>Df8֪k[ru@p}HR۽xp<>^=vao=d!$d*fm L"g]޺\u5 aIVŒϭcLa,MTpW`M4fq/@pu?9Ϫi;C1_p<ԓn;ϭ^%W]C=\ӹYM8^JK@Sv>CNHsT2 t4FQ6 YԺsܨ5n2Rw̺ *8.I =(7F@l";;.b/.vtEχeփx1^9[TwY vZ|V7W:&N|(OA}!,ۘHᚬFUdn_}K{VUۇ_B<+'*˵,!,: >wQl4:Fsx\KID+TOw'OUL4/e}R` `\+)yۦ}tވl $j|GuVo)r,wGGWf >mc`\ O l [䮯^ŅR5y5Yc8$vu٩ʾ ;:l/CKclrGW]a9wU?xR/gl붱-6.tћfr@+wj#BmD}* ])P! /o c߅<.?ao~hUxnd4&(+Yl<$ 9 C! SCa,.6P#%2;ynF|' >Sr@OW3R= ֎qsӸl3GIMsvmf,E A;O#l_a{Լ{|!%I7%%Ƌi0،pI0߂W䇿;U>fr/EI$h$\]"m'I7nѡx`rΧOfu{> +XMPsv憹9g DJ_eÀ!aX> stream xY[o6~ϯУ 6h6)0݃"ˉ04$ymw(v^P C"9<|Tpr۫뗌')R)&&)G$)“un>K"Ŷ*t`rb!l f]aV v3Ɋ I2qr'᧾OƯo^]%VcE7:yD=_tl?NVs wT׸a8}" o{,SQxJMl{$K ؈~EGQĈ›. 1v$D+b4EXenln+*"w,LvjJ߻wqtR3㯈D  0|g-;i@!&:n1w'  ɂG)2aHy$*P 0x!{WLr,CdaŏQb+̱6$le1]v8S N{G;<`)ʔ&B[\ ems0.R%˺ZRmq]߻DMmvQ }bV`V&-J͓AX+,!p)\!r9NbeB QMbmD|*LXLI̎2W!γOn^㾽({~17n#^J3G2vXC`a̷?u=D >Y|;)Jo)0$^J" 5߇@t-8C )J;0;?w;oB-?;O<7aiIx/cW{EY-l)PCedarܕn7?zRWp=ܗ:}!7oBsu.}1NCdpzo =&rcIӎQ¸?̻]kf2dU 9rҴFAfj!ʯXƱiwBDVp ;w#%tǎ1zkWݥE^0h1PK`Z]aì9 bYF ^j!O}F$ WyeA > stream xYKoFWHfvڢɡiD,$ .%dql{''o~:{}xQ$W7 UqJ% 2'Wc{_ΈH|9tKjmc qlvvj8PjW9#̙@Z I#ۇpU${s|qz^n66+辇T4's =DH$ oIO,&mJ#CI-v@9 TͮX6yYt;BY43g!feMF#UE=A.6 rX6sc˲,VYdYcc?"I$IP.񼓑56UG1$u/~HC @p9@2lL_czlΔH/wȰqRS Ҽv$(f|Jq0HnobPQY+nwiůi,p`ZM]|J#)1l$$L:,:{dO@E&O!|""QG*ϟ pl](h:2qK~6O(v*FgpvE)ELN0aa/(3`T J kOj Jp9b^D1@)TPKfFSRPšݛ{*AT< {%z#[ z귬Q`5ocfÒk ]Z QiQV ջuU\:tb 5҆(@Yz| |0abg*@`%?(" <Z fY,]?tHZgi={Tkc2?Ϩ4I3:A\AzY@+lpi2R_nCaLwOfZ|uHvA{t(?t`֭zj2[MPf'#zRrNl bf[s[ַ. 6_6yʢZ#CaMk,޼Q7P2.H8-q^UHQ{Q|I@`~cߧ>F#oű$4yHPHZ8B(!k(HJJZ!ЄL W}8pؿ6@yb»(??bP&9nK#,Ɛ`ޢi]Aگ~0TE6qz<@8gp "Qi 5MKZuYm]*Y_{FGO{J4lx åNgĆt!vܳ@ B)1l\9 В}} '|/AAw 4 B{Wgq\C!k;#?MV _+oچ{K AXm;4@oP2wIkd :|+>”˨(WJC< A9"QA$|b:/P͌NmmpS1Hɀ> stream xZm4_ėVj}~mN O"l7MJrgiӤMUCx㙱],܎~xx qJ |O4<=Hcm18֠j a HˀB!qFa0cPF ql;+(x$ۤyv6dssw$w3 OY[uR}{}u?7,k6JDo 2%@P=)-ao ҌiJ*f2L31Jcݤޖ/ȋ"٬ldq>Ad GpquPJZ xSK菡PՀ$ݑzPwa8́ w ہdp8-bx |I~Pq.KMg!!}E賷/bCC'Sa,8)iQJP Z.F1zu5^v=@oU{f`M3+: "ǫtu4BÈa9D҇K`fc ^Hjqզ }+iD>N!7Nbۥs`l"#tazb0/#E.4Ҝ ǪdL|WHMmb cYe0z S䮉Ɂ7\Y`TMã4(+ :s_#ZWКk GС`cC&z_Uyafm .!VYLdvEN ]t@Zω/ eS}g<4q^ۮא-"vT+8q9-_\4-ݫ,Y-2*K7aȰ*L$ƈrī ϕ* u!MeqZa*B\63Z!0 @QHa4+GOEZA`qr oi 6` Ck` mSȼd17 Z·Ծ|ȪXv &FT3/oON]ٹ߹ i5 ( 4o?32bc qo| 0z~ rg{Tptz<=Zɧmuqk(8EsU z~aX6PQ5 Ec&B1~vr@&]G>/|nFbT]TiHq0KхiȿoKXiz; RAap.&9p9R͸D7Cj-2@`AY(d6F4*}\*GmWC(-Nna[nן!vw ?8m|UfpX;i9D/]esHd]v\׌+ D>qd ȴ%Xf` W:A ,j0ѧd-4"j0׻? E?ۉ#T $ֈ|zgX*LtX@DHЊ$X54z=N}a ͟1H(gG_+d qh#)(†!H8ˊ Tx#5 #!n\$nȝY~mIj jy~tQd/m G 0ZQol< endstream endobj 946 0 obj << /Length 368 /Filter /FlateDecode >> stream xڭn0 } K)Ѥ81ML[ C+q>m tkvBį#FkB clMM`@:rյ0DCN$2) 1 "X I c9aZQfl <{OXmHU'-!jd\U&hf]Uz+'|y:[r^Mgi@!QWH<~x] 㖃73>(MF ~ J?{ځQQEBLmۚM[zGZ'(!*g4>>mxщ? _*FE֜#QK\e Vd endstream endobj 951 0 obj << /Length 1568 /Filter /FlateDecode >> stream xWݏ4"Yv,@p*VH}vM$ $qmEBg<o&49&4N8%Jq2QM;$oOE;f%M;Jh~1B3sP(M!9NOU9q/˶۰<-j]UQ&aj()HNU &}P_e慎=^l__ hJAEBP ދZyX-ˤfHQ#ܩPI83 [ykWJD8@5&FS/''2g3.ڴeg=ܬۭ5&w2|bBrvgjC)-5ppKC8ItO͆XD|mXMT*mfXj(l82 m $by+$PNTrY7~o_TԨ;sAUY[/xHy|l:UBx _L ĆYrD Z1ᅣ+W*Nt T-BCC~hAQ 8'L ]QpT vQJymضU}:D<'AZ1/-xѱmo idX P[D^_iQ3{fٰ+ "Lxr̀43!y] Seuz)ASjUۆi3r(i7Som=LkF@z=H6{H8=pt$c-r5@=ϝpRUqlL擟 sxBq 4> stream xYKϯ`DU`A8KSvy_KA)ԎߧK9Fw)"=>뷌G iIS)I&ؙ3 exj'%ܸjGD! nX^_a5:>֦1eyUڙ$V43Wh扌2^M{?JdT>ީvb)xj1UX?V>m(- =/{UpX.TyU݀KLH,:- R A-`-Հ1kDF8]4 P~M0S]>+}\@PJa^5~àjMAW,RWYM7H2խAQV ⚍Vcz4CLKW!y>¨;LACvvP c$-o jj⹚$69@ϫҖYĠssP`քb I3Dzc33wPCǟhf7%YE6g!OD\u: WiSpj &P L"lޘ:w5\:}"r(kLi`yeؤN&~wyղ&T: C^懴OatA'~JߛL.P%D$0\΀?sT#GAȎ̫P9P9ݭȾ ,yHH3Һ(0`ʾe $8B3U1u0fh$]AU~]y6X>Fn6*B¸-]b[CXt_ubRK0wɻp?IJx> R(hңp)XUW6kcK<7 ϶' (On9 C2rj YfE'2 ÷O<،#)vK7mPmPgp~~Jpq # LD-3= 3*("RӞ;Wd"AyN az4$f2$5*!@d~a,7,ݽ }|Y Rk8Ƣ@wp/jSJ endstream endobj 879 0 obj << /Type /ObjStm /N 100 /First 883 /Length 1493 /Filter /FlateDecode >> stream xY]k\7}_EW%iQ6$mRg)&[l3kz&״Pr{F33ܫzD*xz^So% ;}𔤕ddOEo5U- 5S\ߨTFC7H^* EB$-)A|hcLh30~o:$bxXc ɎvhBa1 `oD F3{/oXC69 /5q*M ¦N  VudY$I]H Ú8AR ~1R"$&O YŌL Ctՠ8 c{. C] c._ w%L)I0;upj7MF̥csipl=sc]RF[ "˜j:q2i 9 xj[1-a^3,α``* $d- >0 Pp pu0h (T?Xr1duny .Bg1\\7L%sL(YZ:etpWizzJӳ2wz~}=ZouD{.RD"ι:қ2M?kuИ[f(ٕrws@M+:.0y$PRJvXQP%J8`jCwqêeAF1}s:&ݑr;f#:6Շhou(wFoG+:n)3.%%9\QG؉e4E ; 7RfZ|wӽn%,㽔m}{Ò.`Y9[qƺ NqKw\ rј6/39A1N-crelm"u @0.\T=Μy+?Ź4w4 ;*s؊N/c:ฝf*XUDdR3a 6 [,!\ۘ tAv3~dLʄܡL`wvW`B_!J[y5.E-ި! km.R/K.An9nGn=Q֍@ܼF+Q;u#0-81; }x,va6>{֜*A\ZswaK8[g3jj^Ȯg:=z5u\ .H endstream endobj 978 0 obj << /Length 497 /Filter /FlateDecode >> stream xڍSM0WRq<|]T)RfO=ġĴͿC${4̼1#%ady8n;!IFuƀτgJI DOM}-bPѵ"zqMQtHJa# FgP)>D'R:%P4OUHҔ4QFӕdߐc{[4/msmkGW:`7vy$_ BA4tyLN'-xf{ZNewq kRQ)Q.L6Д >bjܔ1Wc]"bPg77?b`8/8o⻾}gWd@Ek 1u=o׵y(9Ә:QAq5:@>-a6n2~@ qe_n9+: 6atXFP O;uYQ+k=t#W ]~vW [WyL.sM'p]6). endstream endobj 986 0 obj << /Length 1134 /Filter /FlateDecode >> stream xڭVKs6WHDG7tĭ!́"!S`],hi8ŷ>ܬ]I 9c>Hbb-Y-ZL[֟9H>m\qƃo9bĨMT(2a7ƌ ^Om1ֶh% Gۃ`YƳX_ ~Qc D f`l*hZ!M3^_i!%"No`6CmXb0_Y/%[ot۪aJm=ђ`F/<{/#R0-xa6B1"Co OXʋ5}ƅm)4i0(#FZ] !BFӫfHsZgW7> stream xXKs6WH͔( |q&(9Hu}M)vvzX`W.*+ƳW?*Tf)1F7ї,M=#ilʅifݼ=FkgD0ĽݗkM~۳$x|mM~S8,2ouX0^Ul2l[\j&"3~OV:o+;S^ "AdYr_vmml<{RK]^{&ȳVk Bt6lL4bzE\-aonr>83՝Wwau+?2E+a 1!Frf?r"JI"?_F" `xE &ſ4uQ+i|F?lU>A7g| sjyU6m]yۗv=\n oT d6ksUg))MڪnCM2/6HeTLz^jٶhi q3G4qD33'iѬl|1NJJ90<#ʮ $u-WHu.l9ղOv]u`Ă)a@1CZN;3:į/:qb֍>Ç C֖Z1ƿ]p:t3| ;lDaHS1κCJ_Qmqg.E]/@iL1y)xu%/AYf6Qvmކf;wvpm.w,}Ml6`䑞,ϜHsHx %-*9Զ[Jv+UO0?t*MTi҇$\6iZaoj^u{AXtUV:sVa㲪#cTiUbĝDԿpmWo>;J:):)+uR Rst`Io؏;T~rkQG]P0PI:# R`v;ص< ;Ȋ\ΘYݼBIdRu|.˟8 Y{Pa{.Ao`#r<nl7޹CasahxҔ}GA`n0ˀ9(0y_އK/G H ef!OA :I]ߙ/ 3)R>f+dK",vV (h?^\OaDRMqјۻl\Ɣ^?v5*_ 0 X۸nʪ lG~zqbڏy܆wrG$jH ԯ^4sоxcDxM^zsfW endstream endobj 1033 0 obj << /Length 1365 /Filter /FlateDecode >> stream xڵWn:+S$k4E\B, Eʖ9@z{_34y GGWg'g/2I%&!#Ni$34d}ob;KΒ/R4ލ1ıM;:3o3΢ 2?__k%%): 1eJcJplޅi!baմrWgUQYkҏ7k[.odYm[K65 [_;+K!_}TZ՛6 nv@I_xkrPQ_ݢJ;VCpBYDw .NPVQJ)$Vi?4t7t8eY&o=R1MH\ [΍cOLM~X=>$d7}(ʹ륙}SWMѣ\\pqup1;\%Tn֖i=_LG|)fp=֡-~0Cn֔mX q˞ʞ!9̳=(TsH.Sl Dtc-iJ4}˷&y5HNX -%wqk;MXj6umʶ~!q CxTfjl@siΏ^prKp@ͻj= Ђ-U9;*A{~.զIƏ;Pmab4UN2j.NJFxHDeC"rO -4nϑf.G@&oS9v}x% 2v+Bb"D p[X|gSp&*=^ݓCgC 2z2|ZoQ7Um֚\!=*n@Iv̿1r=ϻ' s/Ą013rεt%"!u4PDA^A]> stream x͚o8WqW= ]PKzSJ d( g'!UU|4g][i6k2P/5eB[˧q䓷_/X:puJ׮1P ]@߃ z^~&еcSKWZ+a2\st?H<~: ya/_E4Ȋ48 7_H cR$BoRܢolHe$8HdxIBk]7p;rқ-rzk ,R%2q @YLHe9fG~"T̀W^4{ *aA$.&F骮3-brqJ^2&"2Ǥ7S-e~p㇅0;ÊM!0. Y֫>*E8ۦ0 q;|xmY0lCUCƍUf{特hjw%K5v-CɊ"bxG.*ȪQiJHqr sEM~O5cTBOUoU&Cr7=Ozcv>}N&A!0UEu6I(f$}< ]t#LF0r&v&k`IU(p54N'\DY^AlSu30d!ƭV`M|ZdٺQFV (s*=sx6jϬ^6]L[qx[}[ :AhX TreT&0l#56m0G#)oKcQcj@z 9{07Z3hT}' Ma0Ȕ9O Hh%꥘Q? > stream xڽZKoW1v= %uH" D =%Jr$@L曚z~սgieVTq2.\HW) W-LLmg12G+fz1qW?t+1 &P ?&܂/YųC $s6 Thxǂ '4@P=d @١uo9glkTUQ ]к5+ҹʋ [rKn% YdJ⢮+)z_1@=79{ZS)X V Q< 6Q:73o) PÌ3(V6@Ko//3ޔx["+NoT<G,!ŸÇ6;F81bfZ[_Bc5OaA& ӋcaT:]B53؃aM7}r|p&Gȃ/B$@܈X$]Q r 1ŋ($LCDMC+$.0G.ߕC9_kV8n_~yvW'eYY#7R0QQ Iz%WoʣGv1śp}CqAAp {/ߔwooʭFO%?o՛(K߾zqz?.~xM6C O{ ezm{6e##zc.Fu$K^~ЗxGYF뫫k >]]*T.4Xrf}˫^~>!t{("JDD? ] }c9uOOq0.(أUA#UmuFxP[9.>ȚyudI}w56_z%꓿/M5 ss=UW;^x8^ol1r\h.,DDDD}SiJ#hhu4'̠6jz MuQ-] ZYu)!St _ހfg $(4*>J;V j( +1wTu9=Ҏ:2vTba_F+1bQC|0ӿa9VbLjC13dG(OĎuA3hT+Ř=b=ajdR A/YYOṶ S{NE(bzXghՂ .-bh~bHA2E!d nwq1P-A1Č:텢oՁdNv31 GhA1p4E}wfhqr Ϛg|C(D1CҡυcBޒa]ebDVVaHAAϡyޑM*|lne#^q06&m\d#YíVSFd5_pA5IHRI#Iu$)&$Q$JDi"k"k"k"k"["["["["["["["["["["{"{"{"{"{"{"{"{"{"{"D'rO={"D<y$H#G"D<y$L3g"D<y&L<;<;<;<;<;crDLN)|^18)ޱ FAGQE18ߜRXCu1V=u;ˀ؈C֡yjGwb,8c[EsA6Ke=Ew<ҡ))9H]n`?sAcZ`9P|D3)A ;mv!HDb@МN(!{*1ש{vXPG"=B'"k֎سu#H wh ,~M.H:;HPgeZ0ۘ?rq endstream endobj 1198 0 obj << /Length 1832 /Filter /FlateDecode >> stream x͛[sF+M1C&f +Db7lWM9guc[k˶.FSL, ţ\B@Cb-VwwI_~-~0Em۾ƀ>*? [?ԥ$;%su6NhlKPJtmzy t=';{Ӊce$0qT~-K?󸔆<ʯϿ6l.6k;qt Z/.uxo\~ifc*s@J 'Q$2ɬ$Q3H0O ť[oQ`p滇 8uˌHҌ;Tָ^FM맜ƂJyz >3= c;Kcf>xCvMOPnX yE%m\C+^Okwn%3Ttw H~tuBќ!f5 ֭+qR-x3/#/q'3 8m>#._(FB%%8 tyav^:SrkҕJG{QU&9H@6m~un>')_m;K9yIL,RvS^X2y03c U"K4ZIt 6lqāEt-DUAKWx^*WJ\w@ 8ݮa>Dm9ꬨ*0Cz0QHh(@=6pq6I&9zL߻ 50o]~6d$^mv'8-NL]&<up~nOd|nKwN&D9\#2B` 4a9HVrd'lS2:܁^:w kdz_{|5dW}Qe GS8G0 3yy3pѽP^GŽw:m58yHvJoD=7z>n3hM&O[-ˈ*̬NI6HJeVU7(m̟a~- y;s{PKmAO4^|HދR1$w+m mPq\ PKT )8Mri{I%:4]E4MW;PrTϠ+^#:"NLy|d2YLfl./=y&0sY7jy[qs SIg4&ͫOݩ \z5T 1C{]u~gɹ*+mkʮ7_5zͫLv/M}g_p 4siTkНX}v8MƜZI}īdSzo>fOV(jEDL\wcBH;&Q y?(9L\CL+ endstream endobj 1110 0 obj << /Type /ObjStm /N 100 /First 1017 /Length 2745 /Filter /FlateDecode >> stream xڽ[ˊdWʌWf Ѓ 6 ,d1bZF ɪs+DWVM։ʨVZZQ*H^E+…q-}:Vt..nA>]W3T qpeE>/>rЈn1AO0xjdnڈ׵v+}󢽒&hA7 V iډ7Z1R,\]YY>/^ނ1zqk(nNY.)'[f%%lh-Pt_fZWF*  ֹ,x4!{챢0A4W@ Θd5-55/Si$EN#i= 4c24K,)iIB Ri),r5X:Z{١& Rw$FEAV#҂R 7]bhhObpAMk?+]i2$ Ԕxyrk/͵g2d>zp?=˧=}x|?>ݿ.=7Ρߴw7U؅)^(ۄq1:}Z^*O_?w?}MOLHl< TmN26ճmkfFl}s0e&O)I3r$n6>qxԭ0d-bAtGI6*Y-bG/66eJehbblEӵG >y;@. dc'! qZC޷`7˘|#f؆PJ7GÐ9Dx=*!3H&!8!`"֐ uDZ'K̩;̼wpl#6i`{ʘe~|A@ܘ~(bJڎA~ SXXH M2`u/ *ŗ@V@B]\q0 Z4XsbQq90hL)NUP͘Wۄ6 -;LU(m9 F9Tb5#R9 /w]qTv&/Af$|/=0:F,Mޑ߉k]6i64~}'#}D*1_ JIL$aͰ$ 9ͽJ8s}s͕Ve5N ޗUN|Y.KwdD^:V|R7_?xykWް6zo]޼G/{\<~÷=Z}eTқo4K?Y Wkbᶐ\h.,͑y[HE"K"K"K"K"K"K"K"K"k"k"k"k"k"k"k"k"k"k"["["["["["["["["["["{"{"{"{"{"{"{"{"{"{"G"G"G"G"G"G"G"G"G"G"D'rO={"D<y$H#G"D<y$L3ΝZ]:w5d1{7J^y/'M! UL:T䐵xퟏ fP|v7mDȒ:bT~N1pL  {`jw$5m;!kEu:7mS[ #9|Y{(x"xc>>8!~W5gs.}$NYTRsxIN!^;Q֙R {[C}u`+?rƹ!;gX)MWISk:@kjfyĄe&;@,ZP? o7Xݪqn*ˤɁ[*Y97:LlU8>ϹDROSCRNeũ`ّC1NFOrF(r]t;~TqDt- \ŵo{ 2}ojC 8ˈ?FZc)OO vFظv"{*6OH  :˨Hq N*:J1bmlR*t;UBW RCᏼS ޑWN~EyY2429> stream x͙moH)x&딙axxYj[ׇl67  ƽ#A}h sΜʸ7XFXS `*TR  jrxE}קKʯa 4bt?S1O#>0ҕ>&I9!]؇t`o^t9KyPMI<|S~QpQ1 U-oC"W!x#&QLfI!\96pRNuE?EPȔ4꡸JפJT7L`K*J 3aܫ)^MqATpYbGb`Khz='N)BGO*aH i$>M 3>7wsM=/xw`{,m`ꗌ/Yܜ1_Kwa\JFwd~JXbҥvetQ[IN¿3›i]3:b◖:n߿C5TK6MӪX;i,@uwŵςKl0B VS2(7*i <& {^hW8̢28 Gu;&`uc*+V|1;[`#( E f>rxQ7ES MTPPm]a\Hx%Zڵ=_,7cќ5=9qB4dC۵d@EVlL\b .@,NJ˥=+vMSo-F,Wb#"pmײu ,nVXfѲө8.u,FG-QJe &ߟs|> stream xڽ[[\~_Efaw@f$f,3=Ϳz{Zݭ.J-- =)9-΁ 8'[(eP{ cN r/`HrqɝĊJj$ٰFS\VFApȭV!;s-$'J`Hz0w,WV95X5$5j.k\TW89q^<^˘Z Z QƃJٍT K tCH[}( 44K(Q`EtRݰ4Ve`&@zJhvԔC6PKj-y V`ZJ 0Ќ2PB+X#R*9VƼZc^ ]K+^zBo^h\jP R٩b׉Őj:ym~\KU:Vէ0IQzM('P(,BbJ6q/pp ևĹa64b|vF;܆~I8PXΫq+fo^9/w<_7>x]BlH?t7>| <@цGAmp$؛"lG-20!ΩD:8\,"0Q2z^J̰Ӫ)g}bui:,{4ļWbhoLBRD`:* y(rxDYbpDC( cG@=&VzfRkκs;*#@D$(Tי %%XAa:`llw3slaEJHf3 XRhit[꠫e/kYn I2Rn֐D+! w/9nc7_'^NlHGUPr]}l\Q{<Qf [i(D5V!O@{y|=PDdO`BB^_)`ѿ)q3 3n"EYw#I"OIg U({'%Nx"E `w<Blx2!aJZA~oW:nD^yм5mR P{I,Ҹz@#Ƞe~6{%oWb OLI - vL"9^DW! 4PϷsApJ?m/QuzÛ;N_ w>J59}wؒ(>7w_~w!)jFG Ę> ˏ=1`Kk\&9soIs0)ˤ,L2)ˤ,L2)뤬N:)뤬N:)뤬M6)ۤlM6)ۤlM6)쓲O~|Ãz_khd Ge;+@J9k(Nf9  jDfe”U[(]vg`NpNYLA.\" s4O!J觬;_(gO Muˇwi#әntn%=mp{Hc6=o"&U?Ǥs*ag endstream endobj 1268 0 obj << /Length1 1411 /Length2 6115 /Length3 0 /Length 7074 /Filter /FlateDecode >> stream xڍt4ֶ.j>{ c Fa:zDDt{AQDDO=5k=v6f=C^y{TGEme3?_/@fCB@0\?H(ucSn8@D$@~~@ h8ԓM9:n p@<nP$ ('MF`(rB%|||`7O (PO(j2@ ` 0D8|H( @7.^p{(p`u"kEW?޿࿝ 0W(@WE Ep_D' nKT AQ@O~fe" GyO Bnݏp]8 n {/w>c8 sc" /ĉW#?wo|C pis=P  D'!AP;# N7f_#aK~zQ=oY詚(qi_ @BBQQ@?a gu W7wDgC8.o[ Cnd+H7n0W?zn@q B]m=QufᎮH jCAR_v_ C__/|A\3i<& M,A[, $M/T xvߧc>x?A;.-Y,`NrpV<5 Ex=c%-U  a&[EG>y;wO*5aG=27BzW E[T -{K"&H<)VX#Ѐ\y%Nˇb%.3/ooI^Fp|0_a|OŭTi*_һ4Qϸǰ?+Yf!+^ĿN#/POZ[r!#6sNS>.+젊/>O+>OD_Qwc`i!pIQ+&,Y"]Wr} [25'#(q1 DS?"]zl/g'E߫^E[/"r1|G. ,/Cj7u<]-eH\↻@Z_a1l2O+22w6o{;w]yadTЪqs';i ^)U$Fv]'w!$^j.V~_ b2YaƇS~LÒ %HYD8z_kޕUdtt$V7%dZ 8dÌI̾87M%e=cbuUԩig]~cXÐ;K_>]aqN:c-{oA!vPU}J|g'R =nv;rcM`Hߡ:Am:8{yQ'˕9Χj[^1r(;俰Ο'B)N Tu|V--`:sX=W;)8>thХ*6 $\pR:؜y {z29wRЌ!ph;]lMs-gW3K8~`pd4߶ɾaMKw) {9zk&.zP`#@[s!%f*xSs0nߢTV\@b?ގ'qQ&s5x+vDB4a'k:9 m仚JnKo\;oyI+#Me >L/[kk2w pxn9H *jUYؑqlxvX FĤ/μ.)F$7D"CŃmdp~W. TyU ҾQݦjG/`A/(/+|uCLb"aQZtӣ'T0/X$f@W=_sn[<#+ּJg 9J3Y}eF*Fտ?yl2_Dˏ N\2 b[[U̼yN{gUFk*O#;b;%hȵwI 8(^|nCJl ּXqb3o8&a3Il4%T,A p202Nbz cwX^cN;L0T? eqHZ7-)a0 "Vir0m: !;<n0 S@Hʔ9jBbO{)Rs[/C ܿt7O=suӯ1H%}v ܓ O O>#iXplZiꄄuk<9nlo%j fR\IT2 a=E/Jez|rZ鿇60Ym"0c)%0n)aڬ6W=XcD"ӊ|-$ޏck۶9H8yMKsN#-ԲI"');#nߎyVӣ*G7L$TL&1R՚ΏڵQ8Ћ ћ$Ri[Itke9[#f# "?sls&xF*sXwP 8mh?`h%*ʱy7W/NL`X d<`!eJUvu q搤WKC3\0;||(?_ֲg5uV(d^lg|iz'6Um8_E?>5Nj> vʇͅrP|%q{S\qQ;sz$D7MەOE)%M!̦#'e]wZ -- Fo)܆ʁ tqD܊<5!!tA?~TMݢh H7ND^9͵ ~U!GVڵO -g;VX ԌNa9ȦE){}a_ %OXd{ mmji^Z1F}q g'ma(|uiN_W`/bl`fdc.ck/0=2SmÂ,󐦜Jl2VXgF? DGq~ϻ1n /QZI]M'HTAWp^ِd.*#je2݆8KK(ylj>_J}%u3=$%h'_Ѳk ^G@֮p|˧c%q*bж H>YIL&>J8}z'#eC^'<ǚ|/*U_0YFjH褁<'eG||ҔꚄs;.'3Pc%'+ q`!2@'d>v0n^fU If?`shh" :{G;ōߛzVyWtDs=>nrKcH\sNo07u;XgKiuR (ڗWX.]TTUEm8AFk;%Vʃ6'6ov 1UIC~o*%&xLw2NwT?f=/V6mURe) #*j%/r#*MӂsOqC:YY+z{j ꊓt'`*qb3jb>;v!,j4o= Gw+|fE+Try.:-4~m U&f: '`ILew\Xٝ5o,[mX3/ؾ]=m0HoK=CXByTFLO3bVҖ$g\}J%4> ;Ut2+W q{]%r0xR΍ yS3Wݭd*nts aWCfzM{@nv?WK5aEn*6j}&PH>1bjGR%4q4iJ&/`g9Nzd: &Uy!YSȿdϨI"*%Wk3qH$֧`ž%ϊ/mzSF}g+9^;x}KINڕݹ Ad)ͽf GH??H6W;X|wal uLýDy%<[=oj9~Adm/*?tS:1O?=~&Kf R> stream xڍwT6 M"H zB{.R $$;HM"һ4Jt"MPzzZ߷Vg=白ް{# AXAN>^@^SSxA ~bVV}7NjEa'`ȣ` ScDM@O',P @(1hNP41<鎂a`>u``'&cuh v!aP?BKa0H1 ՕEl80@\U-#OiĬ};Aaq,:.O P;@OU:&k&p_x_9r[[#`'w-h+ib0v@#`0l%J Pm!1h^4FU1+:AP' *? j=wwNl`N2 O@'S"7f @"DPgx;wckD"lPo C@S"@`DXj{? 0a]}dUo p)_F99G/E : W9 3+](J7 _|gr*H驃/;oc;0}&; NM5]M(񿭪0vdl ah%-߸9hˆ2k8AcFcGWk(v5r5}B0 v'6xaun 5{l(F v_ap߀67D;vB@W[ WjKc:^5P75Z<о2\ޕgeP'1?`9!Sqb1N/3cJXiOܾUK8kӞrs!Қ-Uᵱx#d8/ Q8#ۈ!zd^Uwe.RzԤV^A jE`գ"OWKaZi%ZN\{{f(:E|g P(0((3yygvf2&K)5}#C^K۔sYSeBՌWZދ>jԊHyWC2E3nGD&Go (#bWQaL/u0@pr^ t!p]"O7=7qZjG GZm_vJMr_jLUt N4pRxpi\X =]LX֖!桚fOM'uFLHK!uYx<NTgx4W+|O6)$ƽtBZ9̄8#(=nwtF];]V+|Q lAЏG6 FX-YSEx:rV}Μ=Gu)',z u]FňCҡ ;]5Fw4goy`V hw`sF:Q,r]OwqJ t5}yb@#9ksƠO(`NI+yG hr䫧T8e sW2P}ȭ 0 bbX2Cߩ{$K 7SHmf}'񥵡*]90Jq&0[of / ]J0RߺOٙsSQ2lJUsWs3W#Eț8_Ny}apS5-zw +~oжa9B60.\..a[5nn.T޾J<,ӭxtt0Uݷ;2$ZtoF ORf.e9-mGBt+* 4`GZ ȈBٔL#_ :9)BKij/ A1bMh4w( h(6 +e+: 0RtR3ȯg^IՖ,~~{)1xoǝm1-/c ӗ!* =N#P˟ 3 A/3f@Y*G߬V)[q>`^(x굍!%_@u\ӧJ )h$u\_j_<^,K Gұ5ݽwKW4a;7oQt d=SaCd-ewk7^R|ox)xqg$5UƤvϴ4kTGj&q>6xIܱ;q|ŠǍ/\:doBvs݁IZm3ގA QkGN}jјi|#m[wd*U[`yɁ#L|a{|#BF^#{K߽j17 ASmj>M&-5.9H'TQq!W*cZjzJ~8!zY˷Vi8ٍ84ÌOR,䊷07 e"Bn ); ?4"et+';TNMdLf_EoO6E|9E1)+9:PќUO0’/WL *V/GsL52Td#OJkg̞/d_.WyCozېՕ_?{T1+3g'꧶'m,P"17QmQHH˞8zL|͸iCK0WԂo:L]O'\p7^cvytǼ8@ƶ0yXrΓ羗jVb%z7VE uc`Es<ⱬcfξ5L Ħ1*7vzOR +D0hi*^ċ2s!ǠPBg)\ O/,Bj8%(G;sy%yX ~\>UWt [oưo%o)Kv/KoXˠ)_mj=ض/[R֟ixpWwmP·Z}Fҿ:FN|^;[cMf.B.q(XiY͸mW F=8ˀfrI3eOdDԇ31~oǵ+f>K2{)Fy\Y‘ϛe$A͡j~eE15A\Rz%ш{)Sx~.ḍ쯻 ăy#[}mZ.k̟"zKY=MzGaO~UX~TUX6;T tmQl՘xO/2y~h.z-V1Yzjxt79kI)5L;Nd9;SeKk`&:s2걃=y/]Z#/(pSOgdGVxb-f*A͕ՕԔ8A s7x0|џwݱV33'}br}1|\VjY铵O7ģ,P>ؿ*ea@O5aWmRN>ЋIѽI˒Y'{pc";@ -_;b[9R),-G Ic^'!@~Ӻn6?a1wvܾ^{z(yp Oܖy˝1Bd흪dw@ahY }q[[:,]郛!S/6:Mʓ㬟7bo$5aZ?1ld_ptinpfH&QHGb!gn2+ґ~{ OV;4} 90r ?NSf/pOzB2!x}yGo ?elmr^wuO(J٥r9kԒaˤ] 7fZPLPt/yT-|.%g/OV٪rQ,D,&"nH//Jk8q-sPWG$o D;1H4)MCx^nlSV\PpMd75^]JD*ff (ZSgKu[~Cmg.n=ÍHhwʮa F$׺]Z>Rp_u;w %q A?}ڽv_QPQe;VMݹg%4k#y-AuUi?tFo,fzp\^'Nq1VK1K?N|=ܺnw8ui,k8i"4@MFْwqc17ELkV' ׈iC%o_W~C \?XY")HkOCuoU)"2\J hfwB!J͕toďbS2+lD[t%Xֻ)ɢ9EoͿ& Z6 '$z]!aQŃ8e+ٛvB7e# ʰ42$ mKBc$pp>Q8֑اsAk6_ %COT+ p#gwnE7} ?ˍ=Sˆx8`? 'K4Ғl#^]@ J+Z~4 /ce`Ӭw5K :Y/"uK| `xA(` 8쵸kWK㙴;BǬńgHRxʭ5ȯW_顠͸8p,2דlȊ zBN~?E|[uejmP= ľGQ>X7Qd֟]B;藴 6v5Č;RcKy5Ȱ\7eMD^e{}`@zcyL׊ul Y盲&.|sۇ#H;ǮB}^"v( vNu2IB)HVP]N*w:r&9,J]gyH0^6ʼ0`.ПZs H(i)=Ytsbv)zf35w"G)kޱIiA^LT-V.]h=S۵] ;gXB.'C㯩$!a vYfl/gޓk筳< ' VAʁ{L}B#T=T,doЍ}QIRHcJ LC'y\-Wg Q<|Bzҋ._^aS:],.$)y w50s(W-'NaGNq䱉[A'[HOg҈cُ!w:7 3IY5<XyVK b|\jEܖ ~64RrCsu$r)'+!07_&<:yxjZJkG8>M [lU@׍Ua*Ml~ڤnfea.-R#V7YaG4W}OAjVrƙ9;yх'\e\4x_o'Ou6'YL]s*ԍ12>3hƳlX{S *=W+ @L7l=igpvrߒ9=Oi /^)WQwI}}I{>yRFY8Bj.nGXmaąt\dc!~;BMGO5A+xQ1j`)BP3eҦ |81ALi.A 7(h\ք>uXc[zShD"FFꫵ]k  6Axk_R[>|kyS"2`rz^q)IwkKŷ?M gb7TƫniN)x?q ͞ ~%F9qA5e&VO7 endstream endobj 1272 0 obj << /Length1 1478 /Length2 6403 /Length3 0 /Length 7390 /Filter /FlateDecode >> stream xڍtT>H( R3tww30 /!ҭ04ҡtҠt|ck}ߚ~sγgVc~E'D GRe]# B&P &0 =8(#!`S0~8@  @bR q)  J@JTP'@ xs(#PW昿nGHRRW8@ :]09 #?Rp˸PR>>>`7OEE 7 0@L` #Q>`$`PGw ƚ:}wo>@J'g"(W0A.g( W@`OG0{0W`!!#~Rs˪p'e$Y  q\>#|6PON^pDS "7AD@0: Loe1 g H#{C($(? @'# q‰C1GB}V@@߿V6y9!0ꯠ!o))!|"@()2q"i ?eG&]- B5PRS, 5/엙1ݠ0?z0]!Vro& E _TB (Gj21@xB>+~Lkˆ-LJ#_&fry*sĄD`$Gi2f' af KA8 `H`LJi bQ#e c? uyy F_/ q$G8J? o WnW)Iv! 4 l?"͸5#cm]SocXˬ`֯E:jҢq8DN։吴Y+ySŪiƊ.VO]&a +c^z<9KBlu<YKlhoDkbϳ}s %wbWϲX'uh+n_. asxLq;kYf2!e߈@X55_6ūAśZxSZXZ(4g{8S ⻡f-ccwc?TpaS}oX~0XxAB2dL&3XHz-mt2cuo>'|kau۷)4$v}9xVϛ%| dD@cL'XdbuAHm/W4Se, }Z֦%W4SJ0Wb Z7y;k3 kDASKSԠߍn2h =}Egg5`a}aN9ﰜSbG$i0dkYm8{^X1x30Ƃ{ȟ޴mv?U=Jwx=+J_I'[*+i^qw_z %ub9Qղٍdj lٺ/{_CIa5C; Y /C/ޝ)C9=`ު!bDCB/N= 5;/.Wnվf~?oeD⵻A+Y&SdbdgRI/vjxr2vR{\$5P/j4V-vΈt~˷dX7>da+l/NWoηJo],kz|2JRZ=LY>kbSoaZ! civX0;iېp㱁xO(l.Rf-޳­ϑjXH3l"8D|(a$kB滔>s][l)?S|e3 }fm=,Ԅf?~VpdkViaN^[<(u| Y~crX1HZ{SĄ'jD ~6#oJ_$dzO7jbԞ=&[8)V][E?v>1 шȡF`~Q%=T4U&ܘa}RL4~T`ǘȯ09v.A>Ae{@ o2z.MXebjE[U_7lIB']7g/JxͲ#R{;9z=bqf:ATf4>|Dz\4(.UpuTQkJR꫐󇒚ߓ3p?_RNPzrG֖v{Z37[- 7v̐1qO)nhEk .]i`by3te:/E^˧c}::n? Vu2S]~~Uh+X[QՎ{I>pb1"-={CwRAncD^/m3AC1=]&==$t7^'=3Ƌai>;hSsSzv4D:%@ź1רصaKLCi߻OXYnopԢ >_d0!C}m7,fVۄ2OqDͷsդoqqQBc+ [54FțHm+LҮ][+d4.68שԸ&L3ck7 #WJ=Ē$R6z'8lM~}ueE>V]ok|iV`,ERuwT-1Mk# ^3rc$ihF& wM{V2q "~,}Q}-A´0̇lυ2ǨԢӃuѮ$2:$bmue@kUA:cUƔ1R!1m» {$BVj&/2g028nMӅ\B5? u+#bZʮY">?74Eax켌i#yG# mY%cQȓw,w.&_Vl; g+|ߜlP%/GR"K- e #9i/4 F?`[]dh0槟3/4^5/SDsut̞ѾS0o-g޶\1T+mRjYt;&Ui9]W '+wb;#{|UqPv_h6.~/V IRwu:4P4lתjs^&\?u?F,RnѕPKJ Hħ>ÑGj؀ϣ}_O*F!|=]/b:t9M9hN64c'˅i^qKGcJnhT [Q5fB<b]iIM3A9]쁱Â5c{,m_^s[D.7[*\xcܒPrDC3_U/q_j;v4?Ī7|<l佷X=p7m0e_}2)wb\;ǦZ+-iChg@fi/snNN0cl'2*_ wtGrc M.FRF83T7.Ney,Ay RpJR9l0Grԥ +Vv4I@opw:-e.ҝgρʥX{օX6&ǝCZUFe> stream xڍT7R"n1 l4tHw - !!]%]"H<\q_t9`8 - U5psqrs22AP_8.D! "QRT*.PGPGH-"Y tX9*p( wp"Q b6H;! DڂP7P.#=-( t@pm$Xn-@;RhƉг ­n@g0@! 0 2qYjMG0e??wZr6@pG XC`'¬~)8 @(ߡ *?C!H'#/72ìd`+>93ן͵`^!0+_iX8r N.`e?uPߘ  `w-ׯ <aT>^pG5* B] 럂pyxV` pQwLQpz)aVpo-RU14`3 ed/^ 7G $$- 8a DUBv ߾4(7L&,JF3ȁǟ( QSG쿪?FWlqqT DM4h~Nn?pB҂ A\׼A!0aPV d"50װ @\TQ'j* (*G5WcyPqra6@=  SGCdp(!a߾PDA@܂`(u!? _^AgEBQUKɯU sr`<._ @ jyqc5J'nOW-\F=mU__o:Q7`GO i]6/o(+%zOP(ػ^X{]bٸ=W| m9[\ I)s> miK-3T4+~%O{f_~/Z^#ʠ_+vDd7Wc̖OU lY)J%wp5vVP]z_r5ޑu:PL|AᳮH/'`vėY-l . hd +ܚ` T|#N3z+ďHvFWjL޴Ϗ޹L5Zy߻wR Z*S+~{MqRi}@\"}I .zlS*+]y:/?>L70|xyrlJ$ MhxfƒF wZunzԯE>[,z' M7 WZ8UPFh!]]5L4~QLlʅ9:/5: q/L~*(G;eD|/=5̬joloLOw$[")慘:C"1Ztڎ81T ΂bϓvz7\giExE*y8QϬ(x2DtM˖-=e%Knt;UIÂ:Z; [Do[>D?ɷb*lh{-'d*bv`P+5|ӹHI(zYxRFӓL3,LGbpS~V\bsk j,TLUcHݢ<+ɥ*# b〠 { Zg;2>I ]X+:SfW_"Fvɏ_)7I={Gϻ>~x2&/;A8s}l+%pZlͮmzEA_beedKŴG$yoW2o4L}[ W,* ZS>\v2?HT?l(`n#J >ZPz&w*c]8s'}!eءW)f7:Z֪l4=?wĈzMVk[08~>RE(B9uM3{WSe i{0d[ǻ-_C/-үo+y©pFv"$p1펈罦MPe_n؞{pփd^)p sbwFNvK6Kt)CO[`9p]Gd E wwk.'䓰'y4rO?9:V(U-:kNZ`&ZVP}1=kݖkNe^VF${ c"iaTe3RD_0c{~2$M3ςz>zzJ9+5fAmd_exLRtmRp$xfۣehIO0 [9i\\MxF~t hzkua DFllƳҰ8Jbd+ J?zwTQ4N$t獗۽ s|6modRw6ҿH>\mqz?z4Hx}y+6$/f+;-kO8e'Ӻ=Y:.uZ|~\[3B6*0o'aP‡"VPeUZ8yy5ٝǬ-%\na:Rss<@-R]O3r%>:$ H@I o' dv.'2C_Y{Ԩ'V6 a!S$ٺ5NRGbbFO=-b5eI_=le'/v\ [UݳL_RwD[ 6 4kWH ۙ<Id{J׸ b ãr"4ffy?l_w _ D[: SZċs[k4{*dꭗ|@~k79T"Nm[Jdƹ==)~ Ⱥ>Ga,aO7Ɏo㵷R0,WHOԞ֋wjU0&kkºqPz)l-x"44JZ1T1d1KiCAZΥ y*Bfm .SmR)ׇ+&yMUH\ Q~ɐܳP&MQO1(QwQgZ宦wvlydw@DI B .d_$5:`cd?N#abn_{_]:ϩi]&ZsDW,)qR+5ǛlU gZM#lƭ`!bIRslUZ=HCfceŻbS$[cJ5ޮ+X׮5)yջQeP($7wl@)x1HkQAɿW&|<[QuV˶FI=^+Y#=R kmgI W~w,QOeyi% LyE4xs'p!7{5Ǯ{m ^|3ёǦE )> "Lg&v/PqNXq4֪d^^(闻Ǎo_ Դr~f] C%\ e)H:b6Z}? [+I~)j?2I+~ZǢnbRkՙj6&8zA}JnmLSEU@n q}owL ݐەWA_j2eW4BwŽۛXzYW6k݄C !O+k±_&.)l g&eW`n0ۑ! WxFs`MGL{ч Ƀ4gc)C9#rʏptLZRla0ow7ⷋ<&8 z4,: < ^'~OjQܳYM@,Wb3z[h|~"sutaӶ% WMA@ASȩQGYa[T09ŷu0\)$go>?}Ш)W gg4Vv |>O(%#HD䅪460S"zL#pPF4EoEJ=8ܑt7.U[덐$|N@d>&^&MHϜרS13hxm2\`Mw>(fkbo,>>Nah_!TʔߛCKvixQn^^6 /HΒJьP bMdH]mc]IpA7}aטiܬ~Ip,($,p!{'XJq/GO|g D!l 7<#Bs`_0|0 9ӇʋE77@hhåF[X$גmC3tZIx],a*&EGu:uA`ɞﻴf|e8}kuw\zԫ_1=ytOѸ) 0Oق@ ){mG?b+=znW`KKItCRJ=TqȧDllh[)L._I$~rDu5MK gJP9P>PS7صkthѹXCU {{aE>HaJuKLv !u/iXvu.Kyk]pSqlsL؏ R>o[j<=ԅ>̲9PPzbR.Z{IBQvx:؀wH\ NOg9OtRw^'tc<=AaX~VMϟ;PBӫQ/{BSk/?K W2$SWL ٩&t|$RR;Y2]?Mϥ*Jbkh4gcY4ITu4GM`~\)2!r)<8+tF99fyOhu%6K;C%Db1f^!xF+64-+ 7w1 vn۩Pa!=OeYClPVLMzv(/w9>fqqJ-߁ˋ6Q&dR /Z-Z2s-:q7fSv?}g8|,Q%:=Zz̓c\#Ej|Yh{Šu(.+ އcϮQj\X8=ɿèdIp% LWWUK.kVA֮[jpqu:t;tZT}=ngԡKYwnc=5ٷj} ~L#z2AFTĢ8y4VA#}d*Kk4 @ }~aOo7T*d]= v6VYhc놜fEȈF͵W>xi^HuG\ƱB4og[勉  y9o{dLeJ2  $JT<@~OhYОabγQCQ'sj SSzn:uc SHrWb["ï:I(\Q ό&3GR0nKdW/:/!3=0JItkxz-̿6 ?@0uufCzD]s2)Ē"&a\ɱXgW@1|iLKIbF3Ze_ONN<67m!E V{$,6I"ޯH7gYꍾof^oT^Vad̊xu4a#YjO?iGǻt2AK\\)pFlpFv*1c< _0Z1ދ/Z`|/ '{!|xnC!Qd͉VAkRAQw;b}JSDXdYŒ|<:E О|f ں1xFxe7ƎU{ {ޔw)քט4l7X0X>+YioFvm1D23H>UnQ kaqW$oQjB}o~1탓G22Y.{Xl A;RICwtsL$ l8m u L7*f0MC}Hdj`T V0h~|ez_^{yx/O'gDS ؄pm w**<=̏Kn4fuaz&˂B*@~:CD~Co.k7`)a׹ƌĎ^r/.K4z"zlf4m+Vdn=^sֳMVYupb:Zt_yAk.ŠGSGC'Y(Ioi?sQ[j^=7b񄠾Ai/\wtXjm v)J<>MnX`;x[`-K(<%}3ܤo~R˶~k\tE܎ֱ.Ы|[' @OTGwy!kMZNbo}y~NsFx89]fO`5αDGǡb&ElޫQ<mGo3 O9^~׌ ZML\Jo;XIx;?b̹W"KTH%;Cd`u8Ae[l]4[U<؉.pZxD'm/8;X*޶!>̄3jjrV(LP :o@_Yh9[p5UϊeMHg3>bU)~%ƇI5P8ͼʨ~"ׯtNVtkL%1`- Pw&cQ<%@DQk;-iSժh>jByUQ R3;p2X`b)eHYNd22R Z_Z4?E_x{ռG^Uaa;}t̒.3=ozY_3hoL"lhCS +K;kB+r7 aipUfrr>3cČ3>wHs;a(igU[X-CmjP{a:CPSr D|# <`cf/"Nj߆O+2(*2Lw3Gl3mM_M%N[>i3wL~̲-}6f%Y^Y)xNĪ#ă~%o>{ѯ/(~dP~`/ok6K8<,ǒY' Чć=8,r#qq DPҙ%"|\8zyxDќ,\A+ibe+FhZ ȆQ}90'؂2zV喎=wb?mWA&0*K $Pj;mN%Y#jk/΂~WֶQtU} endstream endobj 1276 0 obj << /Length1 1317 /Length2 6543 /Length3 0 /Length 7438 /Filter /FlateDecode >> stream xڍtTT/!ݠ C]"%03034(1ttIHw+Hwwww{Y+"yM=YM YY!Hp/< B@@J IA P_@\rX4j0(*sCll 1?YG0b 4AH[]DK@f #=Ctwss9"`pNi xF`+Z Gߥm!z0k  `(jT5N`_`<;@,-aN j8J|Hw$ 9 `w Wdq:$ Uw}K8 C@~]V0GG0õܠ^YCVֿ˰rq7B] cDـ [# +} W0 wx7| @~'[u?0Odv0+??#P72+`/q]wӍwe Mo p҂ M"@˻7H;#%?zq o!}[A\V [YÿA(AV:_,#ߑPK-p> ""/ua! Cޙ\>kHDEw\Wa-ʟ)V`K)ʀrYZ7!qMw^Kbβ73لϤ Ooqtcn6όPv ߗBGǫt=fzk8NŹ[{՗Mݭ2Qu1pS lIGH^z\.$g÷j1>By^+%vcjzSQ6/D5i¼z]3l w6$WPpw1Qv[t"2:\uIeZ{%$.ZnGI٠SL]`YK,QI-(<xH@p#,W^WR dprvIJst"S/IaK#]zp^ק۷d@=F Vgk\R큓ykvhG&}vVOcܧY~q 4_le$,c/MK )5WD&?k)LGEʳa?сHؙg4Ds{bl1J5zFD)JʉE`NJb6U͖}D$oI &Mqꩢ¢l>n]_r-/94-:1F?e8h^bM2N8I,MQ yl6'LUxҵڑ vGb5² da[%' \Ib0a˥LQx7Tсf={1ku‰밾 &lF2m+„F dmfV8@IҺ8v}=xOK jڂ[T6ǛunvߧS)oE"Cx&1e+_G\e =Za*ԓ[.oPrtfv#¿xΚGlۢC2>^U3-8W աZ֣WYޢ xD-_Q»DqmCq UXwQG5燝F9Ifؤ"tĴ3ݚ 8nڬCe<ۗspTA#HRTb Dur5K )@Ɂ~$\< K=JM'㏞q ^d :Z-lFzKTI( 5<8ۖ`P41WKcr6Bq%ܝ!+ٓJ}/pbuNDP0%-W D ~@ HT[Q K@njHSr2cOӀg_1v0\dF .UR^P">\x$rݚ%RWgz<_X6wF{ h=w;e*^9a>yED[Z< + ćMzȶSFK5Ap~|fZ3$ܾbzxQeW9xϕ 9cFm_QME#N"'~f2.OQc!cZJ $y}UYl/E{N^E7)ĝC`s[M$MK}.VB啤d/Pnސ.Rbѐ>n)yکa< I5وo@B)양[h[(?RnO( pyXU<,stM.Z/ب|Z cښ0/7=G9t$c}MwȩJRi)"Ӿ\l[{6g%D?B :@1l9>.Z{yZ-*k3z d) $ϲnQHr)/٣:W b:& ѷJ:Z0HlÈ׫C?; ^ ~U 0`D:T3H Lc=ʍYzu>ed%I#ϓC/WRBkIRCP[_0*[UzB3*zRS`ַ,oN?3RFVmGkI{S bLض`8M֌D $sŔ~&['M %\B@MYh Z>I!&nKET?BLIXURB:^e7LYK'8f> 07*faxyo>P oS']NARA u"v7Vjr6y⛉YRRy|37e14}joBe\Gɟ{5:O-{lX|:#`shxza M'Ї.J6#$&놫bv b,I%DHA͜CNIt5!gtj};Òhp׭H?)7\)f+3Pp\R0x:qNvEJBʋ)H:UtRzP#t[H8uJ =*J)*gk[bߧcKKZ3y!M0d;֘W;롗W@=ER9j,f>]&nrąl$K*1 JQhI3ؤT~#~7yCu $ ̛a!_Irפfl2mw睉zqMӤW3 ֭J$KĒ`}}5`&t*A`:,io%K]ֆaNm| 37 "7E~j{-I|ʚ 86&GRA4ݽpB/]\5?`wBQD:㘘X'~df%01#s32LR~Ǖ@__l#0"ˣb#`,I~Ro ֘6HĔgs%ڀbLoKchwNY9Y]NjiӞ:)@< Mw3L4D!A^|y.4BC\'nGJUK{IվX,4JЅO;Wm&Qa h?eG<_67?GMy vV E‹iRW.):p)kv#kșy9GiX5Z@. e ?6^ޟ`RjxY\.yR>Wm:l^5R(RDr-H Pi)Z6-6laUfe ڰ5Ut Y(sJ:Ayoҍ9z1>6Uߴu k_-U_U<)e ǘ-!R ٳ*ߖAzDm9}rm"nj-OTbԄ3]pO vJD5^(tX/(YϿ@ ۋ-%,i-~z8lev5/v鋾+ǩTyr&!~3xC$J[w&@9(9d83\LqqJүF+߯]'ɲ?}GRR{0Ǐ ~P /цwMe=#A==|{j&BII|kixyO}hw}W\@aMN&м67$1#a"k͇,T{B9:sz 2 8dyK/˙#mSHP^v1qWzAZb-/r\}@ LFCߓ|y&t?EƘʲBҖ}Mŭc1( D+z݇|HbyQU씋Uz3Fs lZAx@f FgN;R{Ys8r{\@H&OcBS-g5fd"c&IЀxi d8 /qxs,RG52.&Rx{ܜ-qwd|b=rR0=[M3*^>!qCgT!To_o{IhaU}K3xUb D1~ΨeM7re[fY%f vQ\$<瘮 xK4Nhu2w<2bdcՑ0fKL;fq.l\E}Yh/kCR[ݼAz_spݗ) ~R (W+Љ=#)٥ J] FUg' Ĕ 8Wo!K'T?:|%zrjnXJfFJ=j\n Xo ~*4 ;{O:|AJ2,Yex.iVgW`C|*l #y럝yXεxSΘ ̬\Uz蒙K3h7ЀWDX׀Z<7B ="]?r endstream endobj 1278 0 obj << /Length1 1320 /Length2 1508 /Length3 0 /Length 2355 /Filter /FlateDecode >> stream xڍS 4TON7M=ǿ.E1OH! NHecv3cL"G(EA衒" %t]kݻf=~5wb0 Cqsj |]hT@2T*dlBd ,"j UC8[ J 2!1ҖfeK:jIlR(/2Xd슉$ۂ/O`14+3;p ȁ 0.=Ŷ\.'C")99’ -oHF&?>"3b<\I`@(F @d`Fc30>@#ӾVBP3`"1*4 ! VK<mpeb?`BE q2h6t8ç()İHSĘ6h()\"5|/h4E885:yc2  P UZK0BWjŔ@נ%-1` ʜt !:@y+#9`2~ X\|%z0IMwc2$dBn2;$B1LK~ "2яV"n ~"e!0910}PU i R¢h=qH b82<0 I+**dCHA*$DB܊jDeap$!5=8v J*?t'7ﬧ[h]OX67xW9_"=2AUuSFNIvMwf8 H=e+.^ a07ӟ:dr_X^gEg{ɚS&ϱfhNLrΪ*v)qnHT:McƵZ:GԐyZYݎ-uW nsSM*LBuuI`!.=)wdN|`r"ooGFx`ţO6oH;xoV` f]gb dA+,4=Xi1D#)!t˛6$ݩ߻VWK:nl`uR`YSiwUmo4]I3(YĪMM}2fCql ŪCl,{sobrƳӏ @%nfgcUqd8spS--o e.Qhܖ<`wX?p]g Ha}zeYّ+v1X>3xSFO9i 'eI`_+~B6/=ʎþq:X[\⩗sZ=GۤgN}sOtw{OZg[.g)}r5>1^+\.:Ldk!xFӫ*8}Ehq0vApkԉSz;JQ1/w\,mz9L6]wcי4۵Lq%7x4G[k?Y˗w?0a873ژ׼/DVo\W$_4ܚ6wkMaeҝ 4j=L>wi=yz7Ӷ8z^.jN. 6:b}䉿ɿ4&4VVF͌ =|j\63SM- v8_siܽW?]ATေCB'ZMB E]> stream xlctf.[;wlc۸c;۶mNǶm{c|c)YuU9ȉlU< L<eqef&nv+9#VPf 39@@eLo!@fgmjalsuuturaptgI8@JBN :Z\-2@[' 5``lgkb񯘜+Llb*Br*1F @?NɢN)BaU8WPg J&CLei-qv$m+,Q~k_f@]IDZm'M\GzK LdABʟ W;|䆌{ cGr&{F}&7j֜TIE^_U̖q8\J_y gǞȧ%QB¿,lƍQ`XHगEcֽa-m=L="WS&?uu'鮫0'Er3\I~xwNV5s{D; ELRNS5$\(fn }UφȨ""ys|mnA3o\-`H8nZaeYfkה/UM2{&嚂'>6`%yr_JI.IH $mnP,$ Zr+7di NZeRղMO,pkp+`lGNȜ7'tr )6qV+ozVS ِy JJiy^L]YQa}Dh፧ͭ )8tξPtU;~!>4IN'ZmOC-ZRB??[BܾrbEmx"gHۢǵIhs<㓟$tbi:f%h2-vf$놞O/"džFh[r#~謩:JKq&Esx_+sRq*Qj\t[T xrb={=~&>5|,"A&˺nօ'r|ߡq`<[ӎ?] uAx!#MCm = +.ШSYJ"Noė 4TA3 Djȕ|= ?39VuQZfұå mn҈U[z٩~)C#2Y_Ò^Gmް=wKXyGǐsQml/ot럨DQ;ryB&a')_YL,v*/4YXɚ)d:NIer uh~'1$4‹_Cʽ >|6/fgۀ) a7߲I.z^ݹ sSo4pOs^ÓK=nH$F =n>-Dw⣃|5RMwp vè r#[ | 7D.uDHI?ue,K :!v,+tWsGT,L-[$Bj~\ (9,|ݣ7KQ˰r B4 ESVώi&@  : ?.WrXKT5/ ݭFqJ!|-#AlQ$!y0=(aOÅb((AْFKѤFfAvYZ^( ,8pJ,M<(75 tV3r/̫?nl9~ /Ag{~$Q{Fa*.ź!}e\ c4#-% ۷qrubXm$#TE4xL^7/Ta3%xRzQ:I iyKLO1Fz% v>>WP BRp0+X#ꄬE_X; +0ݵGmPK<3ӷed%|C[r1Ngcx[=^ym8wr_Hzq54p :^;t־6$DiC01"h#kHrTEjuShC ZsNLV[,&0,P aԸ;F>|IXtm̵|vuE Zc x~~ʻ5斤tuL,7)~7@PM}E`юyU-Ԅx~cH;u+'!,^U"mLP~e@ACS[E@*-R-o:^2tڄOhlq ̆襧ƀl0B71 /Ud L{pp6V[Rš+ Բ$M(7uԊ EC׻C9[WU'ctUFr`pWTTF_U6#(z {NB,߈ כG&% a|)uKVv;/83!Z=z.-`Yd;`#2촭gX0g0*Hi+} }SAUjJeJ.8D 1*>b@R1q,Pێhލ0L}i]D֗dW]jn\$!S5H¶W:Sq@km~Լ UT #z̭mEїKڑgb2-$01{7U5$:X0 rEd*Kb~;'W QBs,?jrG89(/Q<ȩH_xw[8G?[<кWZ<QvG+ A#!p~S]Yz7Ѣg/+V~l$Vـ. B[\l$ȁ(Aă;2 (%LD P ſ l{N2/PAdⴐTuݨ 7_%y! %WЮ0{FҤBP'ĻaN _$6Gr5hxPAomъh ]stZmqCgann$ʋd =xTހno6 cUNjy2#?DED'Λ-n&/?CD -YvZ1FUϹ+ls_֫'0TW|( S/J=` HS'͖Ojnyq܂oR!ؽ)oP*[T4/Ė6F^\Bof%V4/N8Vˤ~R(he$艒.}*]BTyWauJ')HtJ"T(hSEI~-I)!N^|)K Ro7 P&gRVf ˄@dv Ñ`GM-j`QԤit*Cflq ֞Y唋/pҿNd}t),rF1B $D]eTUꃈG~P,H='"_:1',pq"بWT?eI4'r&Se i!?92  #t_Z'mQŤ(C yBPrq#6ƻT 7rGzAU[;Av9__ЭTv> } 2]wd5[BʁO4_`-+4U4oM\P7zt|Y} #"NKm9NAѣ G /S`:%>׊.Q.d73&1VԿdA{ۮ0LJNC;Rg C -ܩ:=B0c۝mhNPlrj`$\ƣࡒG9uB(45Faoޛgs".rS^⣚@^ (H\ybmTgI~h;ʫ\=[/=%:߶J0Ժ=y)=;XI9\5}Z*mQvT)W/jvS`vyqJ#׈TiûYMz0"pVs4#h;'ͧu;s٠Y5%MT.0kM'>īlĦd!b-QAGVt'4𭽞1"-g/?_P3BMT^_I58cZ-RO0+425Z[#7#[O Gbjd⪇F5Lt8*p@m''[P?` YB֔ʓ6NqM5},ºDIUvqRF_o;!vn#G}UѿC- #wDٛ{.9^Dq注;h8jA6ieuMbFܷ$ؖ2B $/ ljֆ+wP#k*`aߕ]sgRRwLv+ixO,8N_DI7oIxXIcn9^nkWzJOx3UB)'AF|;/"Qp@Ҩ|،ːdG @8,@/8qzo#TmHzA>htVA Ur0=U#8kˤT%nl1kx .ӆe:HNq-ž;^=S-t3@FkxtQ"Y%M6r{eHE@i1Ntn\^)Gf\cM]܀E.8K0AB׳:x+ЋdjUWh*Yq.ʭMx[sDGUhFJ!\o V![Z;n.g x8f2gӒwi݈8wy\٦4$ X9ٳ[XUMЏwAIrr֝VCErm#ڣG@b,a"8K{, )+㶯d?s,JaE1ؙ:NX}EB*\s{t f&u`l?DKF;ׯNhfFPL>I)ES2efJWk5['Ri!kZ!jhςkm9yf@w/sx"ԡP8> ɃAhj`~)/o+c7=q~X2VK_:{ZWS_j/ݫ(on{sydVh6PZZ/eҳEE$$ /藲Ff๛* %&!ZYݥf|bY8!o&^Bi gtղx .I5SIg>TƼJ'.5>Ж*+$c %;jp =~o5+~8Xڗ7k[haZڱ +lFGs+˅u*/hM^$4Rشny]Ӯ%LvK*fUnG% u,ՀxhUy Bxrx<Ǥ2U'dW@wYá:9EipI6QT芽{Qas`ٱR99.y> #T z9IY+zm"[D 7J*kWwSO\<ͿZ+Od,-n^E6 =ֲ+\ B'AZ\ErDZD&RVͨ>bi= )WbBe#@Q>qL ԛף&Ȣ[Rv܇H0$n.\`Ӓ# 2]X + :/#}4CFA7JF:<*o@~nDVf~E e$Mێm"8x!4[x%˕7)J33 6U}dҠQIgx1bd/U,t3O-kC,~QәXl27<^Eʌϛ>ԙīQ}2 c-NL-}ɿTs]#o h!Rth#~HGId CoMfCrйel;N5~`-kqaN 娕hv6Kn)1n9]L04E0"$廯n@$(];LJf`F8y\~s˟ 3X]V5~^D8vݝJEVinXѥ2qQ<\Ѧ&bes:7n S:Ӕ X<0%Rex奝 bwQ8evDF9Y&״E?uY+!qQ=kQRPi~$ 9rr;zܗRFԐ_mJh슑J#02=9 sz Yۣ#W]+&˜ܴrЏlZ5Id֕ `Q~Բ%NĤE˯&,9~-]YU1Ikڂɸ K~`O+fCT~wQHOOup1(mR:01Ҙ:*1s_\=SWTNk5qhj$oq0ڹ(+Y>1r5 .h4p|WrjxeK| !$;hIqy# 7`Gx/&$EG6Uy# SiX7Z'(8S/Ȭ :;o{W2[aTM SU0 2199'mxzIZʎ_m5#K,D(׀]Bj|Y]3nm.o8R>)dprU~5`z B^dzKͳ |!~3VE+-:_uxwdQBDqOy`^82N9@ ΅洺滎]ȒkJ|RĎ!~ hBwQ=0Aޝoh&6򆫮Uʗm>f ffvSQ|\ _;}o!xle%II+B!suO~lV6 IkC:dl]Te=vVLGE7%Ā1y#璀}.+ [(+ *iGp5ۭy;9ɉϻLKk*t=?δ L@856KTwwJ#ujte`ZI!q fG@~b@ _BU) l|%Ɖg>Ʉ@_;j/dBvϧ%'ؼU'vSeiܪ!rb^_d,'Lq2Mў)e>ObI]+rMyhg[ŧgz/Ԅv\ίknxR?\tK(7L-XdWQ l["17hI3Xٔ q_0R-KMK 2*#d'rIh[Ci3IL:@Uni—LfV~.p{ۭg w#b'XGpɀ`XީA7a ""@ e&t[Mi9&H)KB/'a]qyp&^E9:t_EhØv>CKpNثӚVӪw8}s~>ǙvSK b?Emp0zC-O1` 9y<&dfnJ2R74kO,v dXs@_mrUX3;|3DKkId2s|P׹dT`񏟳z|5lƓ 1A}%sg8g h#!gy :߬(c r?AwP<ێEL4-V\8(9VY܄Vِb3'_Y[o g(_4|hE'OJneiO;slLG-|v(wٌO,ekM}x&K*y'TLj~|WeG$z~rJ T+iBA:=cG=0;uvnb;6H#^MO]u86 |ߍ97co_/덩] DO jhxhG>;l2!z/p_ă̭lNw&@Nw7?鑯!Wm\ MoqJn CTQZ ^T}1:Hp/יkG*4vltgQۋo'#IbAu)EsOߢ6:z z}m.r[.D,jKtbˁxufvwex61~>~A<ҟKl+BG?}vL p z8$=$<W_^J^_5WB؀M]ou-W{YFIhtV Tsmhtlrկ4a}=_h* z'|dd ?^;*s7m~yIeɐ7 ¤B.iÒ] % laknByKC-nU/;MtG>q_b4Ls!51',aQɎD_At>!:ɰIm_:׫GK/1Ȥ$)v)Ї+jYЍ5=TnWz6J~ihglRCA6%U(grlոgyGʼiP.{ܦɕ?zhNd^F`ۿ9'I?\7|>0FܤX]rYn}8CT aLj֜~-3Piet ?]4t/1#Q{f&|}JbH_o݅AMVmvF{Xo{0tSճ(E㢞qA[IP69CB|'"J]}G"VkmڿUX@淑 ҅HiwXQw{X_T.Ƨ;Qu4d-gI9 km_,mAD4 R?-hhιSy)!m^C@:E@ÏCt|3Z#Mb 1q  9o%Yi=r)ڥAe"F_MmJ+p | d>"]z\YRprUհkG/wNؔ?od31++9Î~bе4:0sdjȄa+Ko5FЪ%„DnOnV(*m"L̝&bO$HG63މi,WrL{}b))7"Miaكm98U#3#zDB,qѼKm Z1G=PygQ|?ӕq\xWwjPDՌzaƏA# dž ՐY.C۾cͭrd^RC]H>Fl]Q_!e! oeE:_nìO9d4ɒyHqUd&uO <{BYj?PGH_HQih- - *:1v5ֈE* cq'Gl`Z.rTMV~W ;UsR@̖)hZq_ @+"ؗeV-eB:)0<xUO\ *>{6,PgfK'nÆCY7_;diw .xx4K|mB]0d@@HWF &9B.käa֦@ҤWϱ(PRӴɤkiU9 R`2q=G&"oK-Oy# .8§RSg0ݐ#! '{PmATT&YR: eZvF7&_7|JG6\ٷ wsAQ+au VDr;\򋫐|Rn,u7}ͪf`Z든kK1ULm*,tcr-2ݶ5y:=2K7}hmzKO=AHgh/i _bg aUGt[׿e]ͻ0A[t;Wtڴ!AvNhy29IHEUqbE wί!"ͽ}/q6D6Z)o,;ZۣhiZ0,`A-z'fS2*7Sk2^Rc /^,41f3`20̑ !Ps YZz~ҥ0 :tt"r, mʯJG\hx10:ƹ{pQ'k-lXa"uLU? & kV V U+n%ÑT\hA |C&eo}C[!W"6E})[wۊy@n8w6䏎څ =p@jy\i\u-?zBBQ;g/7D8y#Sݛ4V&&6eᝎwA:K?%j lߘ^b2Gu-V_ZƅAy猩jl ՙRXa{Xԅ^cj"?^φ[35Y!P# A^}X!B@ ͷ&n".^.l^4R ;8"P}8x\M;)_S5gK.>)'Q-i9\]ƱW57}gTtz|BpP3R`7@'zbHLV4r&ĺŽY*-l[kd]yUN䰬e5|^Ejt1!;>8NU"5nJgk;c.T7Qٺ/fm5>} fkI ϔ0 AO"+zPJ1[iDYWEAzKxj% ci9P!/A)@gj3ǙiPp)Wu!=6U_omLa|{0т(Z|oG,vPcx=TfW(W'>W3{wKĘwo,vi..m tb߀k&,M3/Vxtrl "p N?"r+;|OTZdCGv^ztX.^dMHù,>hczo7'5kC&x\?n[Lq3d^W, F#2uZ}];,s' i4$kHtD*r?>`L_Uw4ap+_2#5ź*XjQe h*laaf3D`VZHp4ʦJvM6Ά麧l?;=&aUqt1"O11כUi`|m, y4'~:lcۤ^СX;Fp3>Tˀ}KޗZ`$jVV-"ܟVL| IT9}/D5VEMvn?wiLiI1f5A ']o;Zy>Pk&O/<זj͟d)D>(:/a7l+ +F, e,ʸ*[=3հ!RO4o.n>p`/{ w:M9KvICp "a6b>?%Ih(}(i,۱(a%W c93EZ7xs[#Ôm#y:!$kbGȕ!1/Esin͍՟!1cua'f[gEܥPUwJ'X߻zAkc&yd5Ir+|el5 d%?5ԇ_X^~ !OKOz) SLZZ![ omQ)UXyJ3ᦏu3֝7#n?Y~nz3n0t!ρ,;guFHxo^#joƢ~-33g٧V SQOrGTKT+BX46a}4@F{U*7^J7| ֌>Ǘ¬N!AW7w* 1ۢMr/>6 L@-cW0 C RuB.F!8$tS=A4 !u^;z×qUa&G\>Yv^q"2+4,{=04wLx95G?ꄫR*џc"Եh Ӻ ()}Nl1vI*!<~*n#o%q_ZMb @T/^OPi%rDJf}%EX%k |e,3 D0P);! UBKJ v)`Rcg[w`vO •*s:Z狺q,>d1!P;~r4 %Q*044F FSL4*EGU<_WY ~È/X5 !~gu=ECx?F8H}9znvGwA0#_G {*#Zpr^ԘU.\K̀rzp?{坅#?ʫp^¹ _TPZJȅT5Gfy/J"ne|mbCQ06G^|jq^r&.Rgz*P7D:=h!]8! S1eCi+/>숥p8>6X 6\?N2qv^h!j 2,s*VrGBJp.᧭6md&.-g'=Opj uQؒumLcfpF%jkmgqZ^{3L-{y%KK#oua~z=]:*XNHZCTRdF {B}ק.AobbEi2aZ`@ rch͒pvǩEųFJ<7؝M]\[(h ᭤ } 24U9Σvs (az0HŔv&ψLj7ɝ(y J? aς~Y,,W,&C8ǣɅ[_y)WҰFWPpA\!]JEI1wP@-CqY\O!REDsdG$= O}fCb7^]aԉ6 &BI%;΅NLzW <8s8eкk*;e wf\= zhpUFr{$IK]c^@Rٹ<Ǘ¬-VS.R)Nty;}ۗ‚!}tRF`'Hr`rdn+U8Ft,.EwI.+=au"5)-&4mu 痫!rt>'!qy({ ծgB@)C58㬲j,jLu`auib,lxl27 г_{xzw;Ք1$/( #%KQb[;JSm%u(ߚizTaf\مMڪ\9*[s^VZ ^`6z;;KæJXБ,+pu 8CT&ae +,"X֩(sg% 4I-2"1QBK:b0 yK87e@>'lr;i-*D ei5ٳ(NLL_ %ܨmHLf7jV\T;}0QD (G !ʎ9e՟ncFn?H!qY*Ac%MЌ@IإbdzRCE$Ja>C m UןRl4 YޏS/n kB+=/oo5YJS]ICTHvIf+pm>p{:2A_&'s06Xu 'of1]q5P4yze4د-ktqcwW@î)go(;mq9 .+V Iϐ N'MrCJx=Knffm`> Y槳1acuLÆzE\y ,jط~"_e5_婺E}:2p6X$9W(ub0t7I0YZn2;B"D>QmdT%vUAx=0q{nDuA}+ k5<8T.955qS*'y'+}#| Vި©%gEfٙ=w_]0{qygɟ%(7rM7ĎO ePhVnm`⦺{dEoa#2ʱЍrA1x(ڏv 0DeTSei%xJўHb9S-Ԥ  3ܹ ݡ+Lܷ g&(=\W".!sJu[KkW8cU;UT=Q爠ֈ< "(V2С&?_w+p(z+S苤~N`:{P)L gE#GKW)L.A/eerAC!2r0;7lGrFa'XP1A|32qv.?fEf&07'4$`~%QFd RKF(kP~UomWJ|ge1{׏|9*nq[1po;.a/!13K`c"HfIgy 06)0T}Ҿ lTNX*dTvLc,1fFv͡tԊG"^1i{w{M<v'<2An=Or \ `k 9+y4EY1@ͤcMxM^Ҥ"8MPD K,rXy?]6ܽ_Ycn"_|?_}Yw 25 NJnc 0WF/x"NoJtϓB-GjWKy"VgGW-˵6S]ijo\.*Si|kqm *;jVg _jksXp?ܧ@lp:~A?\0e_JQoZ9ĬHtڄN[-^&q?8KdPsO.jș[Gz"#[s;#߽4ATl1TUw!&jMf)xn8t+? Rq -f(Vnh`+ߵ`­ҹB{a^:.1S>$`C(""V07jB2в/RC7mMlO& &IaCFÌ!43I4 I5f)C?9 +7`=hfxˬr')ֶSEohk!bE؟!8ͅ/pY I_#r zkÕnP98$FI󜷙§.Z,)zԓ2[N+yA"WTqD2#ڰv0L3]8'q&L1J RG#{j^ބzfYj2$5;褈!9tzF]kCbq~D ei.P ʼn27AfLkc$;]b4."`Oa{k$M&)eztK 11ɣ,GaZ=8YLoӑeF!ސL F$zKٹ9CRH}JŴoj+C_W|Y8en )/CzA 5<\N9C0b҅+?6wٌhHU11=f, )3SBŵyS#l fVߔlOo v:88G|"'|,w {ޏ6aVh^ޑȦf<窐ž Zy([YZ1*˷o"pDpQeݒPDzwEMB Ñù/buL'_ӭۂN%̀1 kv=˦*m.;FŌ,"?PXZ uU["#|XlyBt3x |m DG>+-hQ E,i`pQE<|6Drj&GXqT^6 E#B>m-M l˫Xor8O=}0YB"~*-(wWTZSΒp))-Ga *Uf WI²χQ3*>Ϥ` 5& (ѨG{3F̙?OMmymf4r>W-&z4sAx`Yi& MkޖR{13Ei7ghs!Y>gپ̭I0LchqPъttr`.),F !5 eQd~%2l"@AA 5tw4#2EjeaȚKSřdv[7\ )"an6Ԣ q4L\S=XD>z%@ 8lHB2%>oP*az21WwKW'e,Qj5:"'tء'q0űUBm}0kiOk8ez L.6ovt9sThB/H_DVv_BSTjt.xX1 &5`˪EEZ2U(J^5}X}B[c1N#C2%YϜZYg?`֞g6 `si2ls#0ѿfniŨ usп&d;MFwTE#3/BLwGk&!}F/$~4S1a{z'PB;j(9]^ėhZuosf"?]'뚚rS<ЕƎEs{؇m=>TBy:ava*odBmZJF9jpy|4Pg-,y(I1ctg8^k ¨3ҧf\DY+îBd bpvtZr-7 rR"VRV!(S nz ;D~MZseɃ ":kWpef5e.0x;S հXh !Npk>ٸ%hF j톸Jsm6&'nXFISegb0tc~OwGt[stQg^\"5:<@;9}s&;|M35*eWXR[ 2ːJL9C@'sޘ>W[JJn2uw(Meɐ"6q{ѓ3--Ze^`ө4,UzV(3[EmI%Cнcpr.B >8"a_<=d˲\cg=Bo+L{{Gjt(@5כzK۝ wmd(}|sMd'T< "źz\Zdq$j9>:9><;ΔXq0iN0F.*oZHcxMDA6]{&>cHEK, Ɍ*OvV9]a/0A\ N()%wF/.@Elqmsf:6IpBy CZ>ݕ ҋ Ԛ]5XK ˊ ".k9(/hEB^gJX~L4-∑WĜ:w rhwAFwmȝxH -uc8 *:S&qf>9٢êr'O5s2-7( !NWX&#qkL%1_(42*8m6c-3"sA K("{Y:e&rPAsl7NL8"Bd`1-zfspoD\#rG+jP`I R1 n_!$vp=]jnlP:'QPpXF^!ċʹ$u-r[H+Ӹ*owU]60=T.rf>kG rg{va:F[ ͻ, ?"ȣ\p>Yư^`Ct6e.a:SrU׷K|3/ZԮ4x'&jb#iZt4U^W7p 䢚=sV5݌{-(uy'P ,$!֮VѻhV*lF~687|^GZf1*~[uiύIueZ0U'-> stream xlzctnlyb۶m;OlęNm{b۶}޽YZg ~t]$nN@Ff^: 333 Bhf nhj@' + bmi6igbnmots6{xx{3 2ܬ k; @LIYGFQ @-:]Lvfyk3+```n/Ltq=@AB]DRIQ !Ƥ.0q0K7_G3xGbca[LpLMMri@Mj1G{'w7 @.hjbomu2n&!`}QYJZ{͕?g4vO8N9UsG;?{?&i%eU)%ͭ,jnib?MSAߊ@_%ᅭue`e0q2Xظl ߤ.& hhfQ k7$-eKbC||J]iTOVZy"ҔgdqIײ!Y|Y0.ӎ '8r)>s-y\>.bAw_O8*-^=qA豴K fK>y(qp&%H0qdO~)xKwS OpFr:c Qm?3RMq#ȳM mǾ<>1" E5:mEP_7upQxMr5!Vv0be'G!?6Oc%c24mj׻YD*2d9cPo&%W]-!Gsbok֐H'!*L {ʘ,:5̰=ůneo3}J< fm6>K=OPlu+{"S!kzUY# iՋE-PWT$Jg̺WQt*<^L;}MRp0_6}y#U˚b Bu;e.{v:hlYAj5!/1xf1IE_CW\7jvj+#$燌j`t?h] Fj"*ͬ S a{L;]Y#"@Fv[f3ӯC$ ]<"F\&3aΫD+| `Ԟ\X (6&nsZ/,ٶٜ1D6p@hMl%hZO y] "61\t1m9)+AhNJ6p9^ߏfKG7;S2rg|wZq1d6N/ UI~%[#\֫;)%)Q#s#ZĹ; ru=6'}k*$gj!SY[=Eh]Ft" FȊqcpPPE=)#7L QxjÙl`E@GT>IB܌֧Lg0L՝1)](OyfV$vޢ ؏_ jTea?.h G?:Ὤ7K1EwK-*e,oFN!_j"0Wy_>cf"E2U)[w 0pfZ"si.-f۹/)Sg{XU9xQ?Ʈ l񐫳AS `UH(64XZnFc9߬zZ/jv#ϿF5b[y,X rE,N rNC8țAdTVdZz6 !_ԝ„[T@(`#ּp-%UM!mI^Lc۶ ["o4s3$o=|!-l9omw ybն0qB";LդxO]owUr-v٘$ IQMF0B(ĮoC%{h!dDz\60)or;k'RFj.3Pq]ׄj`ZWn=TaFW=RMӑưm[R6QE"/)[2byuYF̞b~Ej8d~-wo)B  yxgUa/۷fvx[C7ҤD!_?l!m, Hk!6Ǩزso-iCp͙R a'c=.玝}oqUa "ٔLIg(_kI?A6WFIԆXa}Le=W`eNVu; 2oOq]_)" M~ {3=Xd)Lz7':o<|Z Ϟ^ͯkrJL4 j}s|+}ywX|1$ IUǝh|4;c+!öݫYV?Whߊ.Wo׆%"El60y;2T ]A^zGh~3r DUP~Җc%,yeҝlpJNYүD֪uc "CU^ڵ x4EGB@~HΧଏhdDVOhK.֊C`(Y -s@6ĺYC$NDwC7_\Շz :~'Wd'i˗,b%Zk`YQa߁FtQ?Jo赒Maޜ A\kGr0@m?z1d-f&b>t|r}J1%̆nb*K"Q)^0#S5Aȶ`g; |Tax S<$Q4rjUU`&v`n!;ob+MȊT.re&x>6g[3(3ʐA {d}$'@Y9)W`)j4b[z5{أᄿn2)`+ CT`!{9C@˕z6&h6TMHײd̎Pw "(fk,JqsBut(Uoh?f~M@:*r<^kiQ!Б+|\C)=\xA!o!W!&cZӍy<<+(ūTq󺿅iEpo \gtЊUl:0 ͶtjXZG`Vصs=ǜ`e \i²8ϒ:9'p]ptנ瑐rOdv-KTQs&zb{K ς,;i ?DŽshM{~bOJR}WH] `kƍ ɤY#W *}֒,gĬKѪ$*7Ñ;7JWpi3'.D5E\@N)I*Bn5]})ԩ>Ą^UݟjP﬘ ,5x8^DSvZ> lsIT]&wYbfdD-qm5\1N2E U055 :`|# ma< M!uB%UGY˄/ llV(=F%_~CS%BA/*s?DOeQ DI 3U;L!Y<4MG-8d{BЩN ke`zDx+T%Fe)}2s/j qh 8i yଋʢ4H?rGwВlGdh~5f0LկZ: O"(-8UetMJ|5'(]ۥӯȹºyӂ3~b)!aFg.j1};ugU 4=;ub'z0Rt qy$(O#AAkb{]"?{H1+sI/ء[,\MeG_TlRVձZ?+5wY\I?Ovv`=x+g}J ;s"6A[9v#mdv:oz)?t]\ ߯BPEXDU랤>pEz\ZY@n}mJ u<Ϛ)ɼ ٜ38rf(GErݱD&!+4#޲JbI) ϱV+6IWIqHȕ$qGT8 )fvm?cᖽ_ YgB䏷vi–kwpS\=n1*Ϫ+HW?9β#}>4 d;r{CfP bd,g`'lZnb|1U0MO~mMe^eTV9'/HX MByc&;O:M9%ȡeb]ISv_D R}.`PpJ,#A;j0@u=Σ@J)CR_^F%W }m?[;'ecEq "RTͨz:m8aA"M]!;u%v꫔8ϣQ޹ƞ!A3>w}]!oڣ#BيunD(&sP z9:ps-~{/Jq4[9JQ#<SM||d@RqX6igmG_֕8(Xȋ-x5}-7[ ֠> C[ /!Y$AQ_$X4g#԰%a|9WmTNN( i ?U8aД  $ڸû:BD"kq59Nӳ#mޮzK0n>QmGmznnBk8iIz`XېeۯVU!)h;nUMe"A>Hݵ ` mi ({WZG#b`X$Fbq6Lm Gbي/x>\ƪlS.ltGߙBu@ PuqUEF҉ n!o>7ۦH-m@O%R%9'>/xy,/{Sc;]4)/ m[Nam_ƫ_HvL7^뒾c E~_Y+5I3Ư[lBS"pxY U&91XK#7H!WwdL.NMY \1 :UQoWf#Fބx"[mpD79ɧ&v=Sh~6{ 7>Qh!*dZ2}+,y7bOvD/#vEh>ɋY-xZf.%|qyS'^.-H%S'L}I9t4OV?/-!Yqyc ? ((B >$grY쐳Q3G_Mrӟx> yթ/VYW 㗨 Եwi2Jd:7#r9Yu^L_U),a-֞<רFJFDk =9* K mb0WMfɏkY% j^9߫#n?;Vnnӑ>HSzU%wΑ}O3jV`ipҀz|0Iaz$TI % vI|s~w޲U^soloSwwxQ軠AIGғk{~ :ef60tzO7 ? d_Fa댜?;LG}V0H~@砹P;D׈&ufnH @fJM,#'`ā ee;/bDw eI;p縷`-F O_\}KNTiT>F 65o,5x&uJIb%oD̟b*5SG JZi5!Quޝo+_E' *rv\|'!Rx:~Ar}6U79/˥VRߟz@qDT)~~30+d ȃ:O攛׮/sT>Sy!8L-K/gʿ>閮 9Hg+wzA`^vWLVL O53<{ZwEB ר55+Ksl!oj̘X50 ;ں 1اM{HYGG*%MCjS(Lj B̞H"LWkV.Z7P/|<ݹ $//I"[]r=`@`@˭ePg}ՂW8+^otN(xzW2ݷı_ZI1*LWoI3~+Ʊ×A 0Z6}QaAH1#)VV#  ҇ jNg Ӊfe F#V&NmsY58U*|3aF͓{tV2o<$l QϽ?N8G1!qCdB-U0aq˨pD[f?x q`L"PWNSLgKEi@(\|YfY e"S7Jeh ҃&J")"hӕɛFG=īsOR@^`6eK+05=E3CF'[$ U#m8iӒUmn27׏4ZWFÕr/W S+i|x2Ml[X+I:Ԕ]2BJ hXZϚL_/4"N^A^DFYe.B넊sk%1P"ce cu^T#M.ڇfI͎b~“#PIf4UzDtЎ N1ҵ3 V>"<; o<+բm(?ӤV7_''OLOJ6$bsӉLV(}M?B=\bU!m \3064Ơ:nA^ɘ$}@6ߧ1H [a Y.LjT8+G|@5x2L2OsYF{֙]NSj MP8|7Yhfo2f}\hP1F> "(b&+~WˎN?O+VSɍ*g@H' sE=(;'v}Zk,lQ%FxĄ Z7Yh1韎gїn'whhK,T<=I3)$7[QP_ ZDç؅TA`֫1n K`,0?N oV*i6r}ƵÙyS!u躢LC@t<~ZJZĒr2Lm=7|'SqB!F%%[Z%q}Bc,7-_沪t[Hb©ăAmU8kU=F U.FO#bC8@;a1\bChE갈$@WSNXG`%:\ہ!7$=w'7A4~]hYN},B+ꩮWð,$ 0Qr݃òĶc7)9 m Sf4>"-c! tB5]pԬ8 Nd4];urt"pJ&V'4$,X; @^Kha7a36`>idy_$NIXmʃ]/lfH/ii2y.FwI6܍.{GRv@`E:ς{U{śK_\I6BC^ 'P\xaѠ{VlS&lP֝/8QFA1ķG#@@Pȸt&CsȐN!=hr)Eb+b3sDs<cpg):r("BBWu)^C\>(WC6ҥiWja&e YnJ/Gd7#l^긋l(ʯ%y`gVL:M F<v1%Z@u6>-6v༆"MIP380"cRd@38Կew ݌ŖvQnvs/j*RYNi8GK qlưXE$pCaci q//UE' ֖ yu).#PS;X.`ĉ8'{(F(Ҡ¢4~;$c0;1>m{Hv]Fsb~ Sޡf40]`@o.S ٳf~G$DFK [0 (u6'x}B;p}džm{X@sganeVwEs 8K)"0dLyr ~렂Y)ޙPT["=!$#Ln n=qbڳ{)]*QCstA :Vs u^jo1E.=9i@i ˪wnU5u̻( O:p79szf7$flĦN_I+C畕Ji_2/.y.e7Y5Mrㅛ[4\!͝Cohs\3^hbGoDLs\=f?pFvr[yEq&g>p\e{}, $]9B}[8 OuuuRoCvX ɟ#$Ɵ9DcRbOz$g!k0`tU*sÀ7YhQsEU7 j!kgD=~/c92bZW(0=|}H"ÄynZn{IelUDy8P}Jێ޶!Nw.Cca*gm}6 fMԘ)֨{DZ-1X:F6CC L!gU zNasqx]Z*h{ ؊ߌ$揖1iˇIe7M&{MvqN\z T&4 Um;qF ւaߊi ]*4&L! NrZpՆ{|Ѯ|iy,d(VvC.Tձsm[]ՄLp溣(YQ[o%kGF;{'6qD0=}[4 ejbSYe=lwr66=9|iŃ[* oB+w@=)37n 5<ӱ)*ZAܝoxYn>8fesYuo+!7äÛJ*B<4u SnS# M(35 I Zɹ)?׾drEV1Nu[eM Cټ"rq='լߕ*`'bligHNߋ`2_SY#בf,E`s)S^k|?86ɢjlwdOP!l,VѾf)6'XwuZ?H3U:8 8A}}ݛRj:8+|3UEDv#ތq[nk= WAH4Հ֝n8'V4drxa#^3g7dݮW\[!.WQ|(=f{k+潟'}K/>XғJn+~A*`O]"/@+K Z:ǺJgR_%r/<4o,(R$lbrvkFF`xx@5^yW8~Tt眵әꎣ 9dGDK60Oi<>.Q.@]eQ4.'Ҍ…T;R!|6l6> oc`Qմu#jGID}=sPZ9Ft+ #ar-'ig;:mfS|@UA뒖Sx$vCpwڛ8_&ct+v"|[V[W]ѕEqz}w3Pd^ /\ɉcV6dNÞx"@ Kwx ѐ Y%yG)i&cvYKZo'^.]S7,u{6w38:phXEjg`oʁvr'T1! ژ|XHH8/Dhd˚s0"Q.CsՆ{JX5:ĚcH~TM:xY%73SA +Mw3 NN_Iј<Gd! ;~iͳ\X  Uor;E[4 B%ҋ .p4^,EW F -8_scVw`F{K?b{sXDrԿYY3/[d5%dQ[! ޑ>hwPDxEG\~$1dq[R]g2 ԷHLW_[,޽`##xTQI:PA'X̩/CeFVr&jոXDe=1z;﹔ 7K1~IE0)cW Z(W.Y6{ǵ J`l($CփmNB,m٪mh83wCL on8+ @}P:m4O ~5و`՜tB\O754J?O X˝qOnP%xv t[h;5yv͒ ^ΎL=q-._ 3L|dT.RsZKUPqq ȴ&#>v.-V˄b8Wl,-GMR`»g{BYӜ׷5QE7D@2וb$R\LГ|%Zh"h;zRsT?{djN͍ZL[|?yD[AikMh[S3jk\Qh QhԜ@6)`)ŧx;=YZDCK×E@GrLvD|jiGJqd>_VO7|1 0Z/c&!k iLy4!G}f4`L,Ŭ ]􆠤y6m¦GwoBuMɿb|?=ҶxR.CaHK @G>і%X?U>Q&(ko/IV ^q;t <ːa̺S1|7p_IF|]sPWӮ90a~4MZekCj?#A>g Z=A?HXs@n\)cE &F(͊֬MN z@^0i_cf DoE>09)ΠItVdcF}%Ҩ;mz,tBXHuqeVɫ!{rYeuRi_h~^g1&N |=Vxo)¢^-f5 ̱mfz24nꡘ2ydݑ <;%b@\*+Uar71AHU”mO M{oaj,<6@3`kX\LH:/?EFʝ7~0bVmEMZ@3P}f.ߛIvKpq͢(:^m۶m۶m۶m۶lۘ3RSI'TN_u`iNem4qlD#ǘn@ 4]6c=fq-C߼_5P(}SfX"& %r|}1>ӢG0 G/v1d}m9h2bz򝸟!k ?bxҼh(ҭ*5F7ͫyߍa*)\ (ɚ1eqoA ޜ"3"5i`L xfiZLi1&QɵᐈjC Ѝr-eb_WC/,Y8+s(9T tM*;`X$S ۀ#V`NL'{g묧zw E`"1wL N;1xvUdn 07 q))\" 47}/&hA89 +|gG,{Ⱥ@Y:ҴtwH iY %ۄKw`6<', 8r l]]ȴo{urCnL.,dVL%١P 9D8C^uFփ2]ĉ!:֍P8 o%E|x* 8C"NnR ̛,Lוt1}`{(2oꭑ JꄜS*s>4"h1X\ཅkGK|~A޵t?vyFK|p^Ȱ[*JPDDmoϡ~Wu1(F RfP}Hd^Bֆ]Sߟ6,)ukYMe+Rz.x9lwH9Sgǐ7!uԁ vP@ _"(m%W|t~D9Fb)7jqUǩΕiI2&I-iܜ AhY:%uyjJ*͢4E6w17èX9O6b:Љcx0ɇEqه[O2$NII%_&wu@Y3ݼ+\2X1qPݸxn.ݱ8l69~f;S"c4G&wvHD٫nGW,V336'ƹ=IDgu&O}Xez[(DuGR;<hsooR#fzAa]LF[s`'4~&WuۢGٿ|AR1}QJۮϑXMuJ).hU&ll@D^HU,m)%T-D'mrM-gֽ҃1 cHh!@4W*(#ē}PRoo䐌d.{D +͠3up-0IwoNJ<Ǘ{:cgz@em#yU$ʀgJ*!7L?LgGk޸V֟i jfTD:jI|?$t{^-TIվ4D;sL+^xx*!8A@2sڭr K윜JoB]g2-`%vhP={:Wwe-Oj<YOws8 hyXN~R@K{l-#LUQǟ5Ehs* ى̬S?X~. #/EDU{'@DAf"X TnPO9:ٚ NAۚzd;SS1!|v)6ӻHGǍ|wch>paZ8}IB[t*Ɓ2P5 PQ`'Ͱq#'P8|PEs(2;=27Fճ?=9*%cO=;KsmpytH|LȚ?tvVt )y[ f,2z-i]jބc]f;$wRg:ᰲv')q0^5Eܘ N[V' jyV^E"hkS1jE10SՁDRnH7=5㮫ۉQX@yK;eitY.6=33 o_`@7t=C@Y͏x؛}ֹ(2L8jI40kô*CZ8D7 W4\B86y5b,dt&S:lwIx `Y߉&ChtalS!㈕msĀ⵺Ͽ v"JDž#m' .iؕ~A+$ʾQ' s|LB$?kP|К[$)a#[4sh66 Yߔݣl #8;:py,0)b^ANR,ZP PWM@'p{wy @P\A2'>^}-^qSIId"Ssx c8C6zlO q MG9J"7ǶElSVўULt`;H 1LyqR {Ұ};a>q`d7SNJ9,;1۵,=11ۈiM~ʻAe2hu;ZO#'Dܦg ;+ݽ"ʏ2R$wi(S)=? qej{vP%Dն6g4GA igتدe+׺](=z^F(g se%nU3zdDNXL" 9H,Ĩݏ(1 e}\ u~(F>OChm'5Șe5p=-kJys҅kҽ32eI7 b#j,z+*; (L~yB1Tۀ72[\FL#ee9Wp2H]ua;*ot"3Q2Ы:5!d pXG$nzC섁cP3`;Uu´1Ӥ }у,5R&?)um&~mz2x#G,.E"-:*#E1/jk´Hf]:ΐPsd0x`3v/Pr DL0U]F}AՆ~/v[WSP%tf@9ELeEklU[C5)<1A\gXW8BÔOON< <68DZ{E3lXXSEWd"uo]nB8.;'ºbJk)>g2+ap M27&uj4q!-C?{XMHƘTu }W!݃[9{<k,5/i(e-*5Ol0 KAl4B *(8t}:G-.* '`φe/9YI* @e }g(&i>eWv޾{:7S09q6!P0|3b8k7kyAenQ,1{?5LuqoZ~,uƋ;5^yok'Y.SpFW)jm1Ul5jH䢱X.^ශ*3yCK =<'>&Y[v_]`AaW]Co! l\Wޫ Jz%Q?\~!1ZH*u@PvyPRGb2…8 XX`y<7QKC [q~]vkU8؄V:kޅOrLIZBySTz$ozi[Um;įKROeVm/uȨڣ_-?bUZʐ6/=|7S&E}7Zam poUSP4% dq֗8 uqUV^X4ތrtCC%dEPEGW<: M(cgJH͔_X"Tב!X7=O[C՞t~QsoNE`WR.ʽAY>buDo:zg" M0'sy1ô,$)Y;h(&D7 ]APQ* c-׿ aZSё@ _[%oR6bCj"[crbuKLB0;@\V w8c4}rb OF ߿mF _r;Ϲe;Vf-sbO-,Uu${,iV#2;Zkv[o Ql] %P.j8Vq/ȩV2HXc@P*uMg9SM|9#UAv3X9d6n`f|u֗YX8dПPBVyB%x?=gvrJuxp'F‘\7'x]Ghvm.J-%"@˒csv]M(>} د=R`W:ƪLښF2.cJ*2>^G/3NL7FRxT5q P 'A>y2Q4ss.{6}6i_Fka[8d=>U0t#l[tsҩՒ,1hvR߉֬ l 5Xc+V<ЍF4<©QhSH{%F~6j <,Ghk QWBݡ)O%tVJ]ߍND=?Ⅻm蛏_Mb>Cc71i@vm FȳLՐ$=]Cg7x^ ë^ R8Iߟܓ|VʵmXi FwҨJb8 d#`<, LXN ]n+ZT-J_6TpގՇjbՖE:.ducIJd'9Wl6,I`W'I,ɛpH{ c7I _)v& áR6" [2[MvFEsMN<)^o%/W%nL`h U"ʄSyZ}<#(n୫>TfAVdѸC+udֻx?d{fe(1o9qWWA qcOA\&5[ylLݗ^[$#v}\\Ќ1v4˔ 0!$ҿpbV%*7*ə5WR,!/Z8V8T r^F.\r@S`qVy/a$koQӜr؏]qt)*(ZO#V}6+:MXxm@i Z|,{'`0\,:լq1.u yqz 9׳s(]JhgYRL]FHC_)eOj,ԧx)2>syiR7+I,$D6&L.EOKh/D YcgvlyuycD{$D*fe-_܍J lyGШJ/'@oK!,PC}氰߶<$)@' :{DSɇ6`j³[Dm؂>9 X }մ:#Bj &rC0S 0G9 f㤷(r/eBMeJ6kJHx|L0^6ʪ#-5T,~z`yuA 82#z9 uMX2` gzϱ^('L 4*u30B_'&pjNTX5en?zzc6yy!=ohamI&wX{cTl~=$N6 t(Vt#pCFVIm-W"vx `@x۟mHDJUQ6Fn:Zwc*庮U@FzEIfSj-{UMP-eXm]=9Je:.|w"&޲ֻ5bsmbnڭ +gm'=/ dz4la] V]уϟkA=BœhrU ؋E*~wL- iL@Ce\7x¢ uǠA|kiU6(Ҫi?הug~?#lĦ41x13>ٽi>3yʨ1)01%IJ+VF(7|L\ t.IL:dzk$_MEhPq^ (a9<,ѫ 9Y%sz-Cx- i?;rX^qGGA DH(=3?&t;UDiraa0%mΉhpAJyY'NbVs}j(:H21m~(!y2KS>g kon#2:ng''"Ȼ}p/8UnE$jpوQ,)/"F4PK5UF䒂,{ zP͆,iv|)DҶfv6!XQ2+SHً̅RP[\rwUЃ:轛> ;cD7'6).BЩt 5y҃Euv5۝y&mqRǫ.qcQ.C,;}=7i+ /Fb㩔mD6F3Wc}V?|KlPdYS9. Z殿T`2)GU4Q#,ebMI>=Jgg:>*[8Mb>,2 1k"PWZZ?{PjR!H]N*!$akkGhj ,1r65j`-F?b/X]*Y%ɉO)s`mNkQq/QymT:/JO(j ֭y9^xV{lXuzBo-eqqqLg6߰7ěՀL75G{6$Ґ-k7@\~&yKK(N$ypH0K>g!* !RH\'9;<m8v6"'r˻4b!~I+ ` dU`mw?KU}'=J' g@ M-tKV?hfd^9h61y&Zud,t_¿ G/Â?d0)館U4%Ì2CT`\l% w(Y9SCyڥt  -Qh;;eG51" O͗4:# jФHzȉsf_B\:&4dQz}s+uR6=\"f:S+$㌛lc p/k  =(޼:١/3a~9Z k!W9O˱V5h= 3(HdV;GmE#HM㯸tbCF1:+y^۽8%{<3\qfzVaS9u@{X;f}62Lc s;z fJ|2 ic_7s($eRnrA#Inc5)k?i0{9`{,~G([UU ^a`9*d ;ϤMSAyv$$ԚÍD B̬.glǛW<5j9(5 o SWg-_<<,B1wobfNmMi;ope;e+ cG! -ǒ7'Jc[Iq|3ҙDHgTF2B@E9%D?VoO@!*>@As:T;~Xe8VߧI'LU:Zc pR!h> K1!sx>zJCG(ۭ[UF%Dd1;XBX=Aك5c0+2ѾOq^Եqx(5ؘg LjLjNI} JLeI@0pNPEQ %=tM.u y9%١^8~[Zb8X2mcG;Zw{$bH@K?Z4 Le\!!k`ҫ (CWl 9X[)I|&0,7'Yߊ"+X2z;QoBF  +g1X9_Y ]B1IL^en,98=PKg5KN併;3bP]!D%(lSU8Sn̊Dzm6Xh͞osu}q]?.1JM#7{ĵz:Oͳ+RN|%x$r*_ч&u~\(i!ftDl~; #rΨt׏אrz-=kg_!%qƴv2I?+ZxKMv+=>z_,4V=Ԗ\^ c*`":dP8L䧫JZ(Uj%/{su\zF`7~fhc7v~PoU3z'5+ zh *#5CCf`}kdwjXƮ-a"e^ͤjl R==*QhuFD;{ ɒnOnMƖၚ)L#6/pjC!1㝽Nż~ad]ݿI*gVIvRqHW:@.uқMp3x0Xc߫rn|!L#Jq6>9Z)5=_XO'PfCs^`eQu Rsl#נb b:RiHǚ^#x)Kpy |Ǖ}U4 1C1(9ժ o}"&(q2T0w,|Aq`̀ O\PjfqS¶&49gvLV7NޅVs"$h}j_a>ktfM kh]pCZGM 1C"np t?r߸#k4VBg\F&acȰ/ϟkݭE 1yuu9*/BϺKCFfxiY@ giRlΙW ~g wBhqYT} ZOC/wT 8 t- wu"j`?^ ̭6 k'NKMF8I;낁\v|نy* ixyKJi;e'@K*quL/da99_ A1[&:`-ef^$\Ռ`#S.k R@mPpEo\ k0 :cvS@-T.TldP1V; ݰ0f%WWuʻ }u"A)mzqaQbWD+?* f@x,U2:QTT4ϣ{ 2 t`iMg(n  &V_3ֲk5{WۗZCl Ts3ʫ*4In)T=y/Ϫܠ !䋟6iyTS |vSxf`8zGQ+@Ѩ{v6l+'9z,hBJRecW`UIY?I1F~9 hC@[W߂1o/߮rv):EHod}JT,)ZqkVBoY2C,:Hns1X4 ~KAOs@/G* P$HE#wxRUc0U.?p=j7X~FV 17UnLb7_ϳ -l=-o'R%dPU$:I`Ql<͊sZ= *\2lZzY{#7Aֻp=24po .S.}wJ%8z[M1~!=hVP.d׺SᢰZaCy_r|(}EñM+mH|Vϸ* DS2֔RUe1:HᡄRԖW2bd7^v|͚)8}k>xWA\*LŲg@ ԭ_/XW#.^ @dg}dֵV j+D@f\d# yShwt:X7a1^ VaAhSU, 6$b!DyWױ4!h;a .ԛ&͑:_,MuQQ-NK c=Vۡ1X I~*ՔuD3+^-JA[Hֻ U\ዯ3nvGlteXqדs(-%t-Ur ѣ*Ub N^*PsZڇQ Bw@}Ċ6ԧ dv# 'cPdaRQOQyƉJc3k dhʙCWh-cͤq`h-k4%5ԁQP(k7!A͂ (zJ|AǕ.,`\<\YcdԈ󇧉r9`2fj^gt#㟺~= g:ULGM_ xi9ex$`Ox٨jƞ4>,:o/T (Yp *򗑃0'f=JO%6=Reœ}m.J6$ir2Mո+E+$-#J75P>{'/aVN^X=j@,{+s关 [lp7Z+Zmf(竤K@᳕S9Kgk9Ȇ"S\;ȣ?e|#uK'Hx[71W 8:m%8[n,K6vq5.<U+CjҏuK-?Qe¹uAQgL9ϊrZ\+ץ־ l\(](Ɛ mtؕj_trO]YKzg*$٭4f+;*1Z۷"ߑKrxz5Ak5b%͋Xz[IaT98'voK!*4 ;`?+ wRdXتTAG73Nsb )9m~w'(MN%u5C[Q>a{4/卉]/T0Ai贽K9I2c`== TT\T,,LZ m^կGzX 7ۦ h."(_\1]+t-p"^wYn n8I:]p͐]mX2<د<͕Vj͞2t2R%ky:wt1`nV;_>{:Kt"wrjM"8 8:M0 撗fhUJq?*iAp 5﵎)YAr*bCm(sJ5߶M4¸snbpB?onp+zIȁXwFP03M˫kJ;9=G|;,v™?s9M>&-NiWR_}9uBi9ab"6E" 'ˀu~,a宼xz[PZ񶗚z*Z#ҾQ20ǝQzXU0C :Q&)veуZخ(ћ|Mx Xt]b `;22=r\ž=kb]Vϊy~IzFM*kͣ <.gT/dc?P0.Xo 2uTn1 B ΑO nʩ,`E"qW$`CZPER<2[_Ν!r~v`#5Jܓ%$m Z值L'>(`#O6?08[h}9mqr1/ 򞡕æSpJ[yЏLWr%vüBd?TRP6eDw^DZ WB$έI:$iwK7J?0n ɀT+-rpE4oLٷX-F7Y*"lZ~d \2 es$`5NuY"iJBdHiњ\p,L{?.5@)/'BD,MZOR">C̶Sῗ1BhڇF?"PU_2?.[żH8d}os͊!x&}˒|f0%#2QܣG:YX./*>0>,G2VnlQ p,5wȓpm=25ODLk/?"<5쌰M˜\PxEat$@ 깅f|Z 7+֯**έLü/to 37;߄4 EaB sSL}K8 Pp|5ʓ7uĵ{^}|;ܼI!'mKD 8 /&Ob9,V M.v[B0lk9Xs!iB\:.?V0V^r brĜbDS_(?B/90(A(DC>znsAmBf4BҔFN9;&u^t@3k[5P Q?53sB._àp̔UAFK 碦 YxU38gĄ} i,QKEj7&8A ^G3ljI hI76aa(D[9R}sGBP-PUߓ)F) 8B48.O>e aK'4qI>L6OCf=tdnV .a<4 & ;|XbnTx7 s)`e&T#1gL7DGK}_(熀Oד:+; XF%$ye&GC7q;{RfE 43X͊;8O\7uOG;Q' z͌')!lcv3sa'[:֬e1eέo};$~/?'jâcO8? JRqj@$u`u7|F̓j#/iVKƌI(fv^y: 6*nQ'c\`oFlhCZ=C;@T͞`l:m3JwWH4Ev ʗs(Iﶕ`xҰPqQoImu9ǻEyZD0Ӣ [kvK'%DV+$먛 ̤De`Kq7#5Vu.~G?!w$*ߞR- 5#"kvn%.Kx?#0LDH<9?{9C䔕4Bzwpf#%.6cz u6 J+Oh<ֲCQu@9K˿$G41@K,UI,}A0n5ڃ> stream xlzspmmۜmĶmۚNsw뫺vګwuUw=vΪ@fzf&*37;\hlag+f hM*@{3  jghaf 2wnmhbacPs06𹺺 :080ŚS k @TQIKZA@% J.F9 c`j05''u::C /*, eTښ)uv 4vvWwt 2&#-t5p'lb?GCj  @%jgc tۙm6_EH;#?1'd$a4QpGogG#ߢ4[cglZ+^8WI*bط#h5ԺYVwJ-.ҖI28[DAI׊>ݫqT 'Qir'h2i~smbΣEl]^N"!w/7hJ }1GKrKb~Ep1$%Ho^#LjiqjZ;h4p9m_uLQ9v 74]?吶 pa(L)6r/psҟ@~A;&ELLd%ʼSEh`cG;$↓G u=|x}^t޴`}g3NVbg2#Q}+l>?^hU˵vvVZ=~ G'Q{6i QfN|00c2.I<ˏc##* 16q9aqZ4h.<=t0#t+ǯCC9/ϿuJ}T8$psqiFĬN=d*EEɥ9<`P3O5~7}P7_v6[2 g;zOH8_WUI|~5%V/ʭfh(`~ʈ9 @0a5^&]@5}< ޥ[[`޳EO A]M`ݏďPzξx5d}|N IHTeΩāм,QIU8]RQgOV37%g [T~$R5[K0[P:POg2vbUjN*\t5PA-P^NMQ||[W=&(<[v4*MB'Tx$<#Ǘ>?Y^˲? 豕R7T 7ڳ^EQbpvHA7J&z\N/* - 9) ,O蒽vwS۾=TWЗ])la?olI8DkygҮcm'8|!+V[' >2'gY0c[d}쏷v3Yys9K,kjU aN4|fэ8"UmL (,  ZS q9ӣz U˽Yݽϰ/M|:-V'.b*g%E-G(~&6RO]i ۨ:O*lK5֯(Ԣ`-U50mfSL`5E }G;l} u/Gש1L?9suRt9q"bVoFH/˩[4Z`]$L$N$E"QgVU;d3m_GD: Nn/w37\8xmrk Ye%TCu g1-4]x Q!ٍDkw ^ߪ+_hS߫gFPsBG˹S@4#nDRzaMx^9eBUE¦<>)IE|Dj:dJ$$+]fvtzȭOfȑrfIi^M4@з+- |k7-X6pO-]gknF}93lɒol@5DtУdegl7kEcJ;/p"st!rz($(E`d.xj"Z[; I~"wEGRW[ 6(g0Y6{HeIeA췾of <'yl4Jj՛v .ŜYS⩈)X~{Fw 0.Pc,nNnxGKbLNJAn6`y ;0hMJp*F+'R58bb}F=!}RAy+G9ӆuB-B!_ms^i^ƽN)Z;z5 @2U('xȋf1w (!ц%[f04eس/GD'##W2n)c' 0ݶeg|g^މ A6Zq`˙EpG]tpmLYB{P.Vp$oϨ]c|&0XVt=.x[ q`>-`x1c+=T u»W&ƾ8EͲjBBԎ ݠԄΛ(SeN%%@A8g9D=|DOlU3)=f4F`7EV׌ OpHU{-獅.~dB?/'XR=Agʛ Wu7jo oHXsnpUSL1 Ε`h~Rm}f %9.մU[)[n&CS?5u2wxl5HcM Ktsu6_E~~knC"rmU#2i%U+=6tJkM vŽݖq59UV v]e*)eYX@0iwHn%QQfVt9ٛ=:9T^Xg'P<7,Q.c"&cBG-m6y5*Dˣz&?_gM`5HZbdA0>ŗʡJDU9z>q;RGb<3ޮ/E-,JlEƾ 7*sO\{A7BU _c*A.v "C Q@·? S);6HLQo 8N ^pǡG´K5OPúA]L6 ?&RZsJi0YU1 &ߟ-e鯙jaO_;$K6`T!鲒fYF2>i%ŤZ$@L, LW \)Q"q  RDzY^(?ʓhː}MGJEOwߡh<"_3\^%8W.X-(aNa2:߉p IBl|bJ9`^WJ$2>Z]M-V,i wsطkD OE#{O̹G%QsN<?2J Z\Xn"Ϲv=MqIY;;^9N 쯚A3J-bpq5d"*(H&Ѐx>G >:З3XTpOaP!B=loO>Ga1q[)ݯ\jQo̗gΩrjh߉;V5a/#Q NN-|ݖBU4"<~ЩOuد\BayTXHi7vah/a .(%qYF z Rjk [9^q o}j`H¡7#A$=?OT ۉ\/9}GQh\LiL PLxoQ.rv}jNNZ>;1n;mQ9RiqZ2gAXUIE6. w!5 $"C‡IErJtQ(:Ě _aTU,ZZd)߿ë 8vd *fl$V rAjݺiwl;ռʡ%qkTT9FL=<>7sDT.ݤrdBl CU6[ i!~[Q.wS{U͛NfȩO"o. fHA"&@Cv9q6\. RҖ^D䠬e{U@ kt+WA*g]MG@=;:BS+ifdsxw?*|Eۭcc6&<R8S/ pMF`AF;#!$n^ 2M^ί l)"3#e_w:J N|Uq`_/"xcV \= >2V[mtz> z <% ueٗΕl4_a撏T]![6]1^&R?X-2ABCD8{}G)Zq\|ܶA&yο:N9%mq{I6Z P*p<P>o)p +zeJN"\~4(W "/P ߙ;J.#8sg:k} >?gZZ #=1tmOMt>4YAn!K!f޴Li0o+gƬlWC0 U}L\XrSYdJ{xnnYGּ/kh y'zK/g<^s@+³sJs(oCeNZ|Q{+_=F*>d-Rwdxis po GwEALD6Fe$py%mg _;ӮVaB`|y/lkťmg΂&_ʯK?(,㧍Or|5HK )h|!)-Эl083o҃9KGDćo=05?j4# Pqwz$iKx$7*AYd a R Oj 1f^:Uk Ԍ<` hw*~@M$o W@\t){+LyjX؍da@9pʳ^]]p75[o>nw T80H^gСlRsyYUֶb,X#2F⯼^IYR\`.)=D?kTAUkNj+8HQҗ7 `e7ɮQnطL[A7Ay2歬B'2z{B! I~u$FT sN J()T4t/GѩqjUW0h]AMi$ g"WY7bgca.m1Nx5 E?vU&gtBՅub7Bʳ=Z̯q@[?f@JÒNR :Kb\W1)U >#{]#W%L깙 UTMu#T/u`xŭQo+=uJǏ61IϐZCl}7L eCq<Dfcӡ!g z0;"\>J Kd6fSIM ,sV(<Ͳ RH)<ؚ&%q>ȥ"/5mFs|id Ƈ8ns&jg4uZ;cO)HDU$_ -[qbuEcXRF ^jI ?r@P>.}K=H]8O'[>UT:/W͢]ݵ|mBO*7MEzW/?"w$हVs@^4]kb`ܬvh^KT۪T'ZVZ|lkuVAsa@ٜ7Ăwo\nBܪ 7KrGD-e7{ ɼ,69;D|@A.uH%e2%|@֋m|?u37]8DO}w4|rk-Jh@p8 vEI3/A;HTԋnWYC5SE)H}58U}hE C{bt YK 7K=]xMU `;~$IO#K_ (<[9ۿSkahqW^`1O3nITxB{S0'SoSYb=ĺ]7 0< u^Q"޳ VA1&V6XnUE)?Crbuh}wLxo)>іc0*v?Ɛ2F Q`UYlhvW䑸jAG]Mly9.MGj44n'xgr y#Q\ Gz6[Nw =hMU?m+3Lio׬9_-3&F02(,wNHDwE9J0.EwGH}6r5N.=[+@ZLf]]v|Ca(&zڕW>6;fx)Իv#nO ڧv!@B҂%ktQ04UCMa$|1 0҇аYC)ܟ!!ډdHH4a(~w'!UPvk=TI{JSqL;"yC}(rJЋ dCΌ&%P^Wx> `g;$dZu6X;ҡ~7hh5cAI%/^ ;*1 \m;z9~i^)wkYS8Ev.D,5n5%w,ͫh cv^y#a)Hv&BD&^ϓA.6tx;ó.s [/me$8cѲH&ʣC9hGv{͑,:ۅ(?g`!(Td LLOTP̝k4")"ޭ B*`7Mi s-@4oʻ VFle,{[3Nr.֩#`H&n{cl[PC9t1 6yEEK$jpCYwhE<y氝⌑7u2*\Z>? 9-!1i[!ڹN;ӮmJ74b{M Tcuu(AT t1l>ǾG%?jf#S=huH 7Ce1ŽWbBcۢ r2JBy QSiӇq¹O'<&(Blrij]uELO#4dQW鹿0ٖDϞUv,)o*rԵЁ׸{3m$aSKWZ_@hb޶ ٺB/G7 mN 1¤KzJ=sdh.r-s q/(*+3DST$o6KaB.4yRbh+=&kUTZO{}Y1j"8[ɢy(yK;$Nwk؈}7bV] rnR 6~p y:Ž[PTG?U1)_ sXqnh+GSM)Yqo#2(ʜZ޷^ t7XČ-(ܚÞ+_%.sXGƓz9Ț6E[հ-<^=o(\kN!,,?WxsQ`P`Xl5y`H9}kRKݼW`a nG`IILO VQWʎ.z2.2|T#) 6O:IHBq Pby>*q dSAyzLZ:Kb/M( ^ V Ύhײ@&՚u P&ғ=e)U=&s‰$p-ۂd94Iaqϳ>k%Wq怪 Km!?bɕC!8j;XL=v݂}M̖[8)JlntQs N1'B:?lq V}M6lC0\ ]K(t-,5s},2lѫ'U~ |E܉nA8Surk0V0܁D_!lf#3q ZO&\7;S? 71X.Jx~UfW)ݙU:^ZQO6+ v{蠠[lzsPF0M69_r._+,d qV k'q,KP1 pyW'UpHȈ^̖#_`j=\Bb|h=A4eb֗F10EՏ`(9ns<sܧ(˓}:mҭlZ"3sD0&.Nk ɻ2bB `nHDAƒHftZ$μ8,BPčQ!κբv-M]\g?Rnqӏ!xzBpÃ:%<{G&Eދl/{`s%W]]]O/cH$mVv¢!}m 9*׀34E)GJ= ; ]iq?BbXsL;,:n?k:OFG. =夈d-ZQ`459'k$GERMFދ1a[>xvn淒g|#)oR5}1܏Y=l^T;vPNH "TpcbgͰ `U٣2&$ WͤԒmz!~W;lĘnFp}%A#fc}D鑠̐YG(5qb 6jֶQ1-l'Abt֔ZuMdB֣1'.x2G 5~Q;ZGHXI|Yo&mQ2Djh6*x}x\R]3͠^]6o3KlI$p/51x D+ajy~›{|'j.Q={6 ~>] mcWxQ{ku!h+֗X/)-Q(>QijX݆ٜ7vi,6Ł=jp&QzOlT16NI7D'n`ˇɸqg%#g"􀓽ɹx6F 9RhxI<1Cu9X6{(>YI8%Nq! V.HHLΧZU^.gXѲT&"Ġk-Da25"=O#FLi`DA T۷"caRxf|L$w6M>Ȑ80 M.g&0PyxO~-ReW( zL#Qx¤X]6 9i0w9g4n%0xrK>{^mS27$v!|E5 YޭTK ~/03z~¶?wFM<'zjQq2Y^p{v>X٩ޭ{|U&vhOD,b1x&]tfJg'wߐ y.:_|֥7a@@s̚eI2?T4EGC"DZAs^E|k m= r̡G8pQM-C,p J+ԋ)h6D=ly;[0UuEfoZ8GS,r{bO#-~.t TIc8 K[tꮛuVKrϰ8{q䡋~ęj\,Б9F`=PG.&0ya|7jo0R]n?9ǛB֬",ǻC+sr,Q/ݷv:Ee<7Dz[!TnʋQy󛊓=ѫFl^=;L3gd_]L xa,QE멣8I%TtN\u4Y6(b4gnkz$:ȒC'ވ[%U8Siw[?4f┅t#uv4l*{>l73[lP0=У"0a甬,o* ll@u saQƕƒ8\ |oe%{ +-evD9[Y(=s K%_V좗X4_Uec, d}I_ A&d{ݹ)kڜ=ʼs 0^bÖ?uD޾\ǺQ[oƝs[=fN!Ŭd' 1% AU꜈y } }O<c(++FUJ6p .J'@QK]9wDއ̹ނy( FvϤO Zu?+:FOuKL)&b!Ќ$?;af!yT]H.ZqF;"%[)?*Ur|Hyޡu$ y'ʨHvuj>Mc1ㅮyWcjvk>9-b*$3zl>p@zK+.<IQOFFzJkQ.{/*oA}nP"noXogvᤳekLHzFxӊiOq !FKgRE]Rv%TSX)*珗3'd]!'琀h=sԾ,ͶڽƢ~8T \whHʌ2lCŻ GWKE&|:SfN _ 7 }+޶eN3r(ϣ9JYo 'hO7oP\q 63\. ՗0Q˄L#oE;S|^wmJC ]߆#vk(9Jn2-]NM`!a>_]VY@{Lim&g{_~BQ6>U 8!0}H_^dnR yz0#z{ѯDN=Q` bt<1''R݀Fl#zLɎ? = do`(I:]:5 Ğh7j¾p:LzF@PM1jy$([]3FJk{q ő{J{(g;&Zo)dDI' } gꓥ^-5_Wd0iV|n!j/}J!љ>u1_@i@8U/LqsF捾濩ygLXʩrKR5o?3%gL 7GN["zUdOs<HrA±i{}PDS^q8S5"q5L/kw"|Bi\c]xo8d ]_OIi5^(PeXXUf .*c[KID{ؤ,73 VRbQ8qs;|W(36Ȉ!,R)qܣ:5Xa z"O\Ḍb g}+=(r HŚTtYW&e>j ;їEܤcjr 1a1X/7ÂpAlN%R9NZbXvQvtxh|r0vYZKx+Y[Vd:)y\& O9v91ݦXgAkZ lA<:?4z|,ad+(IM?zYqEL}&6g$Q\`shǕ`y RQMYQ̒R6W$4>E.DyQh: ~JinA3DZvHU. βj&<^S-$݋[(:u.s㑶 uP낑]/ Ny<x`P>sSV1ǕGk?B!P_w39Nr5h|i)!j0iHb2N,bnrTeW2⽤%S {P: h$%{נ!ix?g/zB83,T\md=hKT1Mǐ4G<L{}1/"*mQc~[r:T9{R6Acӫ:pM}D(Wf%h#G(O0w>a}J{`p=|Jm7QZe;=lѤ kceW8#;@; ?'M¾x֍C()Cy Kzu$G.;XSTY+# ~.oxa[C;㾹 S/* r5B۬ vZ툰1VBzvɾ+{lnVhu8<涮uS4LǮC'e#c. >4 eu`{f~Mv8.HLFXe^R8ju^ AKfȋ7q /y|̃pD"( ݿ \QK97b <þ:z?2nYBrYmIv+qv:uqI.''Mw"ہq߶DcةY&( U"-l~'V' )` q`wip䭝'e 줸Hq~h woW,,ɜwz-ݰ@=~9> B\n9@un Kѵ@v\h% gRDj/jEzGBD1xK`Vl9yI{6*E2̯8\v!:&8CqM΢C:m8ؼE0" l|}jʥG(6}f8IbXJPbLzN"6;w)UeźkݎdW=AiKO>}5Djr(ߘk]cZ|R4}LpUT%CXG^Q)*^< Jn svakqCt;ųH܀bWD+!nf" ͉-i%in5@K @f4 "3 ώZvXT y#򠤭6喗VAΛG>g(#;6 Q^&,QU>8hB}CzB 97ԁJmm(JpcEfJ;4H|\O/ZoN?H'[]+b} ]*\+IW}+h>>2^+s$i&ŗA %.6~jOjHq;({c ƐөktP}]W*̹<&E5! PA&Hf rUsIr1I y'7JQCquTl]ᴺiiX)Ϗn 6Zv{ `& }nTb ?>Xvw`rR;M\{oԯ;z a)I_ۯZ ͆'I_ݥ s 4)Vh '7QJٲ1>kEL&!x/s4U18qMim)#A[?^87FY98P sjuze[AY߶.`U7#{:pMRf1AI;6 SG_sqJ[qxNKzlml7pfô8{=w`P'iYPYޛڮɲ\X˦~7]6_$B[h^H9S%KNҞf~fڶ7I!pg- n\gΈ/Et:>[m0OxWC NmER3YxI!COיa񉸙MqgqvɠܫfV%@8pgTz>A 9̕%>E~|*H<}H=~NuS4z̮ıY,@!b}RYsF>/7-!#ꕔ .Pc91?GU1 ߿*+veh,voٽ2/(#]C&EI;Z,.C•e/  %W|%:~]N S5>rR8tG9&5O a$ 2 ^)$#L$<)gNlƌ ͙_!ª!J' W5G6MS4-ձ=s{0Vg|ÕQZl=TRW7S;x~FR\>$iEr bu,4 Umu-8aMݢ9\Q/jY[_{WydC{jt |fcLL]qRw0=(Ϣ%(FZ]ѭia * t{ڗx&On\ VیI֋  9O^(O4JG_Ӝ$ ~vg=i-O8)Lޛt^孢 [Oi-eLO3NֱKv4ox7ӸY?LOFM?Grt0FJQq"C K *.'sLR$ɨ 61fe*fc3p0ܧ>"$5pNMs0u]>8o `sW8E.^@,MpmǕG 1IeCꉏGxļWJ" fI ]/`UQ} JM?m?$k@-,ˠuoFtiG@Jf) ALH9zIsE9`@xKvf03İ]̿ |mY92[p ~d6^PlX@Vs G~[מZM,8q~zxduE 2+t#ג-hG$T =Ò=_j̚ //Wn 0A)d?<ۊ{' 'A{^3P&s,IpVR4,01!+Χ-%[T9 ͡y<\ԎAAU2: PNOUYi|rOֿ5E) j زD׳J;dEMyfS)Po;HXޱ 7)_Y}? ccl$ť;"xxYՌU]:yw׾qh/.-OųY-!V_y@kdGk:9!,/V ֋52kCѸL>f }Rro/ة$^!0]}*fJy *o#{6F(ڸFۯf帓fy/.K>k mh~g8ЩR&nj͛-/"6p|\WqP{ޠ*xaơm? +jւZoɷTŌ|h^#*FM{D6;Zo`p/ fm>D jb:k4r$xaD,Ծ-끬G(Bsa g~)86U4 Y`ϊ+)WO5W,pika?qhe\s\FpupUvV%6̥%WV)Cq%#j' 9AH2}wW/pjHNДP!qCR!./EݓEI(?{ |@k znu}1TJm,ijdx(Q;ZPEy_gbg~x=ep1Ճas;ṗ.-'2[zp uJƜ<4]ez>aJTEzO;CL0y}t}Sm ,vgO]Lن!dW0/psfE=F\oDk͘m襡~lAMi2=ߚ 8'̌T j]2wt}swi?)nsLE?&"88db3w E?%%=#>mybm2/+']zt j0q96tK_6*]{V||IQŊ|8INl1c>^c7GŒ SNߔ7ZǧG5zBMvb7⼱_#GrY] :⫇ICRRו̮,Lq494x7yY$c8PQw *JO BMl;LU!%πZ t4 )Eiߺ]p}uP9t󧆯]nQAƀTGo;AigDI' 9rOѫVH.IC$f= ,.e|SgZڎˎ*M4,bPgzK2xRy\o$Qw@"(f: +UcHx>{+B}@jzPȤ'`mz3LXXW G.5>PhNjCO-mLIf`kI/ӵ=dLڒv[qu\F}َpnԗ3n߃w8H' 1Aa"?՛C.B_O(sWtdhiV9Tj\Dz.oi׼A@?Qc4P 3BY M{nWĆfgQxcjݎopĀP6fRQtyz(VxUtՕAgL,WwB'bRw؏ o] Q (/2q;>kţz4Z/^;0ohZb2Z!<2JTybMp0f*Kx:y֋2Oe4hv̭z3^=еL D Xeˣ^ !Dќ(aj)asZfM|,t"п6魉K0{"gCN:\tD~c}1xYuTDVfU "Zh9}sNf=!FC79`4Π̮ PlVc7?;׃?t6hJ\a,@OKbFR1@b079GMBl 9Azփjzʖ2)7dI:QiQފYhT jE=s[ zuΥe5rpNqK]>2gE`{3P_2ٖ\@*UO]g^1R^32֠49 %[o? ٢D\=L9zG kԃ(+q&=CFsġ̣6\?%yXtb`l/kB~ZҚjĐZD,%캊2_-awEIMwNA/u B;crY eD\lȏ[AmE,U_|xe:|MvI/ºWP(Po8h:f),3U<:zA͆*3J@k vpS&a3ðMqޏZ"o R, (k_N ?}Pa tiaxcd*z^"o%vHǚ&-UG`Y>lP%i6I`, oh bc<*SC,{CN6qcRD֟VK`6%r'z (;r^Jb{= ==^\fМ^ق0,gChǢ\y\1hvV)&}CaSnft[xT~8L9 -vȣY ,kRQ*,Z(ُ`Bu|&F-*3IY;HyG5Ox32P Uheq]z6q$4!ũŤِ͞W#؉~5*ۤ9*ٓ8ٍ*Y56W2sހ 7gԈ6b܈uLH4͗۰ݶ<#٩X *!/q-.}ꯀ^0f;7cB__[Mgt 1bpqyrA6cLOU#&34q}"R,itN|ؠ1NV/iq`5'GE}Sg(s9W}1s@<4r i󼉕ѻExB0dBjv&~|WTْ#)DSOx~-$ .8+ΖcOE~C[%6_MMx| B,i9cJT!x>#\T܆%X{V]Ee ŅHAס\-I]2;8ƊfJR> 囙F `VW _G޵jm|nȳ6mpޜ;c8˞ӕft|М cOpW"e]ӞҁU[8 rRmC4]>]MC5wGfL{D@|=A!9E{){AE)t=a:pŐm5-GͽMj+>%kp2}_q8٦.Ru,,hs!w=3GRX'91.2rP- u)`a==Bw=6_2⾂r7WN,ÁDMǼyG =Pqǵ)IJ0' b Ǘ.RR|/8,JtJsmΙ[,=FڪsV%J!ޫ/c J>N-vJUf%om]@_%9_X,1 j(s r?p%۽~ B~ZX. {ZنڀGzqW<}9L2R9AӁ-6z]d5P?T%+| ?ˣ[L7X_+!U_ tF/%$NT8?A/ Ѳʅj K+_z!jŔaԍR@OxkiBA.OuNL>[ "?sdnvНq767g0!Ƴ߶4Zn N#gy)oD.%IQΧW=Ns>WASs!=Q'P%(cm-hٟgGRz:&^{mbphLx +j}>7&.{2 J9)bhzD P#2IfY}q.+Q/ZeVZh,%wK6J 4c`*驛ѤSԖxXqM01nH>2-ii{z-Wp;mR:!0%3^@,2հ"~Lw^W6?ӈ#'߲lZ%'.[e"$HOY#$&x*L䊿Yo1=mH(hTkFX/EiEoW~sOJIyw%$)H$++h8; jKu`ms0]y~XÁdTI3Yz* |5mdݳȬE`BEJҋ't\yZqzh籏=r`YϿZn]!uhGwSeՂIjY,K؇*8'R:C FOq}9hB-I3JO.#zsČP8>o?B2@.m ;bG)o:X<>oDL+*λ;b\eJ+OT^4 ȍ|B8b( W&w,B0TΩ z1%|%Je_:nuq@i2Vzḇ)u Y[nW/y3V!kyIuid7U JHBjSexGb7'gFKP:d}^nAY|VG=Pf #C->iX|#y1h!PV51U` }Co=DJޭaNYɗE:[Er,@;ve0MPS:\mV޿R0pD6e/|l1gn/^(5jF3"ar{)vZ۵\e3$gc;XlJz-$ނTe8ix€*q$l2N/x 9Qzw9vDI{dI].2u^Dp4B=.1`B!͑[ Df4՚/3 wX>q#y >uEsb}6Y5\S]kZ SdeX }Ji3Ї*}Z wL|D|KcI͑ie'L_UaAAFO: XO냿0d @CC֮;~c`Xsi&(X3 8ee]D;{!TVxpu`Z:RV©뙜gARͫRʜuFv>L^Guۊ(*bncv%ApF [gz0n@<b KX.t6*D7l%-rL]&"甬:@O"M!#'DKpVA]ADKra)6Eo3=1tdp 4֨7QxmO+MyD4&t]>FK02PA^2erB chg3,[ty[>H?ͺhR]];|DzL}se iè}ՙ kyNfX 1tv*4Ы[qJv1X4oVnV'_Wl2b t-+.I+keUl0J2Xy(=&-4v{%uX&٨ 3?0@TJ2d{׊\"vzZZ/l 0)0? hh65 iJL\sNJlÿݯc'9ssuҼo ,)60ATl6b=4w_ԯ#\tOYʳpN<^' 9,cZ x2\[lO9.pm렑|[xK!I&Tڪ[$BlhL2;w"SŐX8SJK=PP]r.4BG$ˍ$ٙ SwwF(g%𑺡nw<ˡބ4<'GqE c (:ԋ3b-厎XX ٩E(>J¹{bb6C]Ԫ?DbNi [VY@FҬ3~v Mt#vng;rYfk::>V{,: Zh\Ƽ,; Lw5Z Jwd_%GiAؔSղ @9=K擬 0.A6=!P!p8޳{*K>HqHA=L [oI c"3 Rn\d<$9գSK?ۦ8vIw!P]z{3PN'xPH`e8;_Eʷn,ݱ囥B?U t656hD8mVz}kG8q&#m L#'sdCv/:j,"[,4II?ٻEtQ!TƗPМ'),Sp<Ȍ6#/Duus'ˉK9V@MZ<.2g4hIj.mBFc?V.(q91pp!fL̃%4Kg02{_L3>ƱhYnO 6ѐ?Wk=!7%^j*rXޕ4dl,#پ!|h`M_FԊ}0bd—%Dt"Ѳ;krUf[pP/` %E{rE3{ ,FzQ\% dUgˮ4uك[aʋImы}4?ט!v&JÎgv-W=Їe>&9Y]$XR+1f eTB7 ,<(Fg>2uLJ6&EHfѳcl&^$`y\3,[TR`9c5Sv]rRK-6Ҡh~ʂ+T%&+5mav7~b(eP |Th@oa1M_? 0n&\~'C_Cߜn`C8M#Qnޒw$s6RL~Uۆ#Gq@{  1!w듴NyG}Qdw(WVl8E Z&W{f ۟C]'"2;8 Gt=<abMt&|?L?0>-Z!`E2n9驖ҵ45Cz{sd$Rt AIz^"CP *@gS@y}60u|Ў6W[bue! ^0}"q*W֌!? $p'#PTkq0>Ue~ Ͼ Vҝ8|Xű"_+u]x+Gԥʙ:\#_۠u6;>, Ts#Gv_(:ra5T+΄ve\ endstream endobj 1286 0 obj << /Length1 725 /Length2 937 /Length3 0 /Length 1497 /Filter /FlateDecode >> stream xmR{4y%)!ĔK \"-3d4ʘf|7}r+Db+ilB[*]%rI.6I9{~>ydH)DQNlT@t 008xAR@CtTP ,AA06T BBX)|h B@K @ld{s?K&>|X}AZ&. о| J;؛6gY*wXRm *Ӏʝ+cLAJH8K/NB5C{O[^KuKDK9n~sHň4//=Y_ᒙrrnq HR7a)y$l?S Kg>2l KytA@yv Go(50#Ψ쾬˦1}:k+}'NZYc7pOL4?d6#tU9RY|b+ o(ftZᗮUeƏ-%ڍNEk8Kchgz?rS%csuSfnZذֈ!W`*iυ^puW}\1zz+M{d=LsM7A8(46rOWs܎ºRk]yrȆ}S[U}􁛥1U /C둣W-_4>Tj*;ǎ77dvҘ<0C4NwZiVaΈ.պZ_7Vr#ShtR,|7ӠnlF2UƦ$Mz.W_z{'JFڟ–2TK ց3|#B/]R&y9ZA[cњ⸅K1qif~:=kv6ݬs-jgU/a֪j[14g# endstream endobj 1288 0 obj << /Length1 1144 /Length2 3791 /Length3 0 /Length 4539 /Filter /FlateDecode >> stream xuSy}_}݂$5DG@K87G%i `(P*p x_4"dpށLͱ *aJ0E%H$)AI87/ $<@(nlFqw)IO%3gj}H8 0,*.. #H_ <Bԅ'DZ4{6G.TK&+IK;;TLYEjZD3;#NԦ|+M#K[p@PL@(p +=;~Dws?%(uKsáh`K ?d&+[ꅢϯpC7*mhvIOT$R%% rPIE)TE$BwEc+:Gp&B? N'ՅPSɐH9P_&!`TPiB@/q(sO( w_n88{e=A6Ƒ? 8fp7sr%T'"G@T9i' DϏ k' I9ĥ<~T[nfy/Sr;xiC/ |rSw`[цiQvEj6H1sҍ(UbIJ,{JwtKsZ]faDȶܼ#F_>o(17'E#>nq5 h"t@UG>|Ʈ&}Fٷ֥҅;|lgaBX@Myȉ/q H;{I cq3` IgrjdS]{:vHg}uNA7>ێY/;iliLNK⃼T̮xH^'VRiρ h,Ic> 1[[b0.wzHIDeՏd3$ V>A"kJ2;vRsf;+ _B__O4 }Z!XN3kBM&/#dэ, LEmFG< 0@oߎc6*}+>2@_x`tDˑ|bQmje\^쥲7|v].۟hTl wci1 $a0`` jB_Gz>⌭ᶿQent1]&+c%.鑕yKY@9s̑S6d4(5-41 %Kk܃Gj,LVqR`"lǷ)d_S:%4a.dh)އ>]L郺eqLO4gd6͘saZ]%$]q}K'[bgWNMG?|I)Rlhjj}gunepeN3W\1]*2Rw=vI~\9ieB.|!u+@8jx;%/ycdc[k m8owa#uEn5u-~_f@2m-zKsM1O>k SB3t6F)Otiߍ6˝ZO~UHZ`L]-)ԎW:n@xcM}} ꮈgәWqb*{% 5{gC&"]~o|aHxfq|lܣ>>qcNNV>qEdoxԷ!2 JC2JWvr_jӑ)Ԇx6mza; ?iF:CQsG5},>Ųx^Z\]҃UG ?YZ'sY˲-'kVfg2M^8gzp)jc5V~*uy jqa֎8 aC)M7vdˠ 7 eWжˮ {znvƉ!^Wd,P#s vL+%iy3#td#Bhim%Ro@ZUaO&%'PQǒ3 G:bdVhjhGIRWc^^9٧;\{XeS5󼹂yw㥄rINC`gAz-N[;$K#!#!s |Zk5ӒҰ Yc x?^^T=fbTYD ͺOc݊Ou?9YcN[3;C*5[,f哥F(9ײ1.&k@a4v~+5af!Ek@NB,nxuJ=P-􂁱U7P o,Uy9ܻʮ9<ˍ|_L ko]IpyxcŰf;{]_m֑0N-]:$twQ}!n/m⸭b,[4Rg=k2#cn" k=}θc1*^o^ۤm{/$6ZD0miドC,*użk&BJp%NVzk8yܙ!^Fv4fKUޤrz2V!&˲*n"x έAXZ=.8o0fGdMDxohnk`*oj8Ho_k> 8FhEK)7׹~[^ͭcӫZ(X Rg?J!֢MAdu7 sQw[ ms0IWTdYe;ȴ)ӶbS=5rRٶp!qaH qJdӶm!\r&^ӤyϛǞkߺ>r_6j bCc S{yuBgGd:5*"+ {u?  u5w'Z9]}$^$>#P"Úɴ-4j`va_|d閳oBK`Cnd9^#ixʞRqA* >h z\iDn1N~'[*q^U4%rIH"LJ'Es$b$'bƬyfìdlPn"۟lG3vk^]Hۮ],JQ$X*r±WDl _z/h*zvSz 1gd0哻y|On΢k_[nIky}UE~l4l>WCyEˬuC/5ʋ5|pj м䯳OtϐAI(<]@CEl>}..ی}z萩 \,U  1g '00W='EC:vIa<LO;Ӥ1B$ƪ+]Qk7WK| endstream endobj 1290 0 obj << /Length1 1645 /Length2 1293 /Length3 0 /Length 2124 /Filter /FlateDecode >> stream xڭT{<'"]9ڔEna [h"Vn#5˼̼;q۶-EnrΊ8]HtK҅Tۮs|N<} s-P2 ڂ`:.(B\0\ah' Bc /~<XfkHFQ| \2E(vh"dr?(.H\~|=Z$B1f "I $P\NZ̀Xnb&a"0J? BCM $GAE!A0RLy.x˓#<n@FHIA+|zi'ĀƤVmBQL惧 ŤKA IDNi7X   ,R|[Yˁ%NDN?I( +g`ii#yѹ?lh^c4|-**O)%ksc}kFeY~e=WtƵ{LJ&^ȟaTmgֳrCށV.տ2{7VkԇFMS 6e Ѭ$^ɝ=7RIHQV ?_i}:ƒfv0tD%_M©jIszO[;u ~LUƱmO;¼9G9h8[rUqt}k7)ܷ(ZĈ<'sq;UX-ڡ\!PV}PL4DץöizvŦi^L_%cX6Vv7`ni-GUDGRy+lg*y 'gyyu L]U_w'f!ج3-e|7d|Ye$CpA Yw׳Z-n󾦜;47UuHYē ɭJgM9jo/)rzV' SWM^fݸc1׽Cc3A>iїN:t2N׼iQV:@[_|G‹.u-lmM.8BߓiTVxYǏwz??8zYBzN2-?LZg \2CGAN\s ̍_vzu|X2oLv`<ΏR U*0jfY̪-;N;xvKGһ$eN>Xp{Z ]u܅CV|`lhMbWK;;WQ36YwKWU՜ZW;?x\iy쉵l^L%4~vG-$NQ5eRj'Gg}r[ޟٞҟxpB endstream endobj 1292 0 obj << /Length1 1669 /Length2 2195 /Length3 0 /Length 3049 /Filter /FlateDecode >> stream xڭTy[ 23>MsF`/n)?6dy6$;Z~;tLYK+d/猎ixn[Ѻ!ĒQInEv:Q:R<%%K bB;K1?ۿgsގ+8Swc6 D&Bkrn8ˍra5Uu cW8ton[ຕ HsC$}GZdRagӼ$?~wfs}':2d?f;X}p=̋Q5kf_+ VJ<<1Aب6&10uo5fԦqE7!ñZ`{兙/cy{Fl)̺*G6Fv&;[`nR|I`q/AvLt{LLDce󩗮at +xCI2S+fkV+~+2 :-0;;ebY͉iuF~KQK^Xv`!S|_6`^ә+5s bǏH YBjwb52J)%l.a|sƑS]U 5˱GO~u#.z?<_ۦF 5e:?qY=$ {i$K{^}3Z1 Rto7VmW (NVseXRN>٦⸧#+qd|_pّі3MsIUO~% xΛeu]ny٧g1#o.@UgK|/q0e &Fx_j gu?yH_>PӴ?WnoK_+zmG>ssr3H=cEF'yVy\v;]@R W`o곚e ҾJee{(6̧G1gYW+oyw޸]bnBU?\36lsU*T4%[WiQb8>6,wZ󣲬uUMh\3ػfkMْd]R&u0=m^ϰΕ4pX61~u (g5&%+3#?1WaRaP꒕M_a5O}J}k4vASE ބʛBjn!z]ֽrLK5!ւ$gK7qݦWOOӤxUjk%Py=w ሾ& DX]_B%swix"( G[I0v)!ngeBV6m-mm|NXbcZؔNˋ" R6^Ly˟ș<#3}ʻYJa^/(?xdd~ebHpr|c  gy%Iqۋ/dwt&[ᓹ66{_88/Uf1i;L;Mr6#XA|{Rn;#U;y"fKWue^wqe![SȨ_EJZ6rsʯIݦ9R˓T*c ˔OP{W &Ź"F6qHqgbpNS00ֈyƴnW=A*ݸc |[Oψc_>Sy\j:[ʞQOCkBIKv-jV:~ѳw?tb[m@Wdslox}7 tYJDa"3vwۼI %pϜοly.H ]t6W ki5>kM $|9~(aY#*,xԱ4HZ}[!0/lY?1p IqVRLL h'5<?&!Zu)CA07/,6^߸ו z1IWi>a57GN*bu)YO >3SF ͞5 ~)ɵ7v#ܫ/ ?d^ࡱ>Vi>&%MÎng (ֳI|P/*BKX_\%QMѮܳ0]iYYE!t5v&ZHz* HiSI{x endstream endobj 1294 0 obj << /Length1 1626 /Length2 15974 /Length3 0 /Length 16828 /Filter /FlateDecode >> stream xڬeTܶ%; wwwwwR8܂www C=Qc^2\kG%:9(rpedeb(Yۛ@ @sk_;'%3 a h@3 rrrhi3ttP9\B_'W+ WVѕUH+i@g;@ Xv>@K`pqYMzq1..]&g X;ٹC/BΠ}T@..f֎UU$.Hs?- t)`nhg_0Ggpsv/ g/__}M W``B`e[omKkvE`e܁?;C9 `@`V- SO[ovrS2~cwq[y%g6l@vu5;Q˿0h"e 4Wv5X׿@g;k_]5R#+ 4l.+տ3+Jj]W /ǿG+ uFL ad0s{ rJ Ί&֞}H:YuW ܜ y@3MfNknȔ@+HcyFiq`7 3r0vs#9>;tU9m16U'7q0Q9rօv.QNvg'@r@,GG.f0 ӧGё[#xJ~\sTWMfЯ܎`nzrLI-يt9̃:LuwiZben ߥچI+_5Z\"OY8k~DYaF1@U{r${ q͖}7T#d}? 罄^ C5S%wK'ܹ7MϧPȻMs3f: Y#rϟL;_gʇ;푬Nc  VNνe j> 8ҷyK)Tq`5[˛ QYqrb$3ҏ;oMU2YxyFԏ-HȡlVsS#+M0pX9ڕinaY(7׉-S_@7 (K&BI$z5kvKxLyCDG)s/qLZ_ 4`^C6xa]\;-z֗Nv ?Z|sw-'%JsncŒ?,zǘ3j&T[nտ䰝+Sy8k[jnFP+3UnX)x sQaNrSz=VҲߪ= '4 %%m`[@Ql o=p&/E^Y7cm,?rnLy N!8brk. IA}/,޼\C*UBAfE۾GxW UOvac·@t!P/\̖ې '`gk^ ah|{ v&oթJ^ڀZ<}QlZh`lF}TC-sF,gbˀE%|U3O{pCo-Iu9Іv+QB} MVSwWP-5E7RUW\J+f%p̥Gک`R˴64 QߣuH~'Ev呻t6vw W!!(3ZHSq:`jCM/WOp, |3hP39N_hjƄ'Ղ#[{ΛǃhIx/GG#]BVbo`-ˁ8-/:+SkWi+Iy 5_#6b4Ne)^h#SNSҪJ_ת";k=PR.|Ǧх&"Gߜͅ8"CdϬQ4O9 ]Ҩr`NTдu-x  - )"J=Ilv9d$Ώj3gDd/"z̑*L~ã`i,p(Xj-߰"y G0f_nN j䖼r ŷ۬KI۵*9[ɻt^fܶeO mtZ K r0&hm%q)"EG;>"PAWs>Zp ^C3ދbl$$c3bSp(Yf8Eʹ<;)g鞬v}!k?jnHO]d!tzFX%SE䰌Bը߸2NšZOn&^aa ~w""g^V=|ilX~['oVfA_ute-WD^'.\”Ĝe\UqY9n̰7C!E,]ʊ삅b@7[IX^QDHi7EAt!Wǜ^U28J~EPJHמU2M OQVXtrbJj-멂K"!N#&\>Ikr^̫jظVa @}%e&xXHq m(ǭDJp]/ٖ\鰓-@?8xXӦ OՏﵤL eG'8A(LUS:[u5FL{64gQPGՒjV3֍S8\jm爀C'p'_Q_U7iK/.^Pl `duK)W}QsEƻc/l"{)vc]s齴~Ɗ7FGFOP߾G.9s{~\2%uP+UD8DYBZ@8XIe"pȑ ՖcȢ{br&y~~?UBE nnO]þYн34zJh`X˄!\r\̠Fϝ1Ʋ͓=e4=f^DJ5!ƅ z9P0s Hf/PI>ήCIpm}/'٨ p_87fqƼ! bi>{PT>RX'?=}@x宬-8jqBkF_Afe5c-QG=z>S3_fA$qgm3ۤ)ZQ{tȭ;(6AeDuYEg1f۷+km,,]=u=s+ku7a[K$}r(ž\)Q =D4r;8z[F`ʋ]Y=QZ;w.!.Vb4OEL0O鐃 I7ڗHEwaċD;y̴u,iS#Sƕ/B#&<˜iYաT6.sMk{2+ g:nw{Pst/9wfb2|/#>˨$#@{m&~nDGsj{wk|z7φkqK2} dc}CFSn ;˛LR]]Go5ga.(;+g28#^q;El ՎJg$޴Ѧ$,xjT61= yD%KK-Ϊ/껸3J9쨗e_GRi>+4qBJ_YVj qn}W U^g`0z F '(@,iTvT|?Lás=,~'oQ⎺H8dPlVcX8Q6ȼj> a:51 pԫs)TBON"-/{Qu_˫qEG˚5cddM}_xmq̂,k!*]qZ5!7ceS,|@~͔dixgŘJOx?CHɈ @7ݭf LO[=Yz=ׯƷc+|o;uIjDiE5un`dJbֵQW7%j+wSN@Q I iSg#]c]R݇ܬo2dt|~Ygdri |o,`(f !m:砃bkXlp-)Jy*ܯPaߢYJCjIR~ޜ:5RB T=~t99r8 tfn.L-|L^!#(F^q%{f쟑#N%/:C+qE'm@.\dޞF p(?*@$Y?|A S6Z||? qc0xK}mh3:oU6U..Ӡh@BOxB u/^ Bf7`T!+Ur+:kTFb>q" #2}fD;el\6[$d:?Nd)%ث?~m&qp,Skdv49WrQKD͸x>JcZ_V^U-kj^ йIi]fk9gJYVWVCr,j4c\g$UïDS`3KxZp#OD^´Zp le;J[8meCpe [P n"%̬᥂5>㰿Ѯ 軛Ώ ک F:!HT ~|Ġw+qe-9b?0$ցW/Ri9ݤ{ca}!Flyc)N*l-1 .SWt Эz͵9;oDV{|/L$ߒNZӪ^,PCR7u*.jQQkSQ 05'wPB^;^]72R]!YJ0`O`jbFYF4:Ȉ8h0Qky -|ݮNjtۅ)38_t ;h휎1ٖ~not zel!F56wmfv &T}d Cm/dLE`'Tx\]J<ɏXԪ.Cߚh fMfN٘{_wJ@?.lz*ju~(5sXIV3ʱ 7|!D nӸx+NXݯg1M(,K?M"LR>.U|?n3,l7z_'=~lZ^QLno͢sdz&_W 붕Iҋ3㌓"4ؾl"ѹbM1BŜdt/ob?۶XVJ-_Jkko)(TstMi>fHpagquurV@AnaAj_]XLJ 퇒; Uvl[.vx/6*YgdHU*ʼU#Z[-̵7*֖mL-cT3Ϸ3˽ݸod3vBjFfɜqxA\  G'x ^*?2^[4鷿t_J>+osEۖ8T\g:xgO 2+Z->Vor5OP\ilÿh3zé鮏;I>7Fq=&Bb}qz]Hh 4c׀wqAJ7Ŭ[WDj$TåV Cnh3̬-_eݚ|fOddE!iݲG3Ůs_`3 _TOǝ:xf'*m$o0g"c|uhS8ho/'܅e3F;6A56eEǞ~3b}rIj/PZjQ 'UXlnH$PC>@Q^J(uh_|gQ5 5ݸLI~l_NjG\JEg]_]9ŃRK^kx R롩Xo0'Ò }w,Tvz&i2}{iΝ!D4PBl1\Sqt4ǃbLļ*6U,Hh 5_H1td(WʗϹQ\΃;O짼 )&y1$]O'%9С?|_J4MwXJGӄI$0~=\&yWȝ1w?_P'W?xC$:Fץ Lt61"`L;T硸{?rAoom1EG"[);^Nix쿻h_%G/du~ʲ- Ҁy?Fj܅t:-~H>@ƌٗUVko]8=P6rw?M?%'8|'n#Ưz8pw;p}P=':x%=d-xeDhpwsT^$+Vliۏ9l!<{7k5g]);O[6J3U4o¸,)ܯMʂ5v^?3cS̋pl{ _7.b@N-Tx2#&sn ,>GouϬ(-0A)!s$~)^Cرqȍ3@.0bHFiA$溊Z9)tŀ{L;w s&S3&{% .q}5^=hCY@z(WN=^GCj-w|9L &v2!ͅ 1bpWLE&iTo r-c7ej+/Y3Q6q MA•3H$_HY9b3q7~-ݷ i#nB2lybTl5#h>}i#CJzU!%rr΍QRWN 8r?M)M(m +nofI'-i@*ǻ,-mԃql~NV&m3{ r5Lwa BsPT߰`qd̘קzHyDH[h-O10ڇS_(ab<>՘&BOW&J`7E F$_"PRAv?F0ř6#`ç7E:$3!idvql;`+I#i^[2\}CI^0D}"K~q{{McK<_@ұr+Ryޫi&2싹e]4m58%  ?OL_ ۋv8y*c9?6I<5 Y_B.fՎNsaRwsM9Sӆ^u"+2/Р&&V҂"x# 7*S=侘ů/K0T_ z9`A4w@{kG$7Noc\JͲhї5ڇ+ZM6k6>ZovI炽QGeӠU%+iN&Ngfeay4r? Tܑ*%11ϽIybzYΉm%8o?7%6lŚ޿b̏E2,T|nGF;%?{ɬLVྚD0O-w{yfA̼6JRoK|IpIIC.A/s5~n?"z }*Im1Mh4OAsnlNʖG]-Gups8Lko%ݱ֊{6d0) 7p26^4tZoj~*qbQa]\FV|h*-e:Q^&vf.,YjkG`mGzKHҒ:T-퉉OGz!񢱕%|W$U$0iV$yp1O 7ޟαp.3np>q8 i[k8閛wR*-xϋߵ j^,W''{)>o~W)O_'!HVt 떷I(H.W{Z0/R;m6[>T6b&Ep5R*e:J@^@. Jя׌6 ԎJ \Y~ȲCxA`_/tN@4[pn:8>5`#MfmY ևh;p1uN-1)\ %n㞡S#ujR׷72]+,-_]bPeNw땢 ^UHfZx>:Q{3_@duw3rS#{{cQ{zun.{/"9^VG(>>_5WF&c Nb\ϷnƪGXL D4(wmopf9X 1_t5}AQnZ͵T_Z4Uȥ*+qaVt2ޞWd2dSn2⛪]2-FkW#aoYF{;۶lO:3En<7;V,I0Uڿ)_Df5./WIF\ڦTU?93ƻ,sC?\ޭ澤;K9я(ԃlj@A`WϕJ{#^PeS`/Gf{).v,0eMz F4 9Fvjs0aD*Kqow%WPG;Hg9Oq>'-2G-0Ӟ%rω;z¢_# aPOYs((`w $,kJG)-k&[j,%AI: 3a ;uߴ9<DIQZ1@r>Wܿ§󪖌\UMAl㋩'+ZA'/pMM~d!0咾BXg˱\z'SMqa".П}ll3nnծ&!1b2i)1}Zr=c,F֑i:ZBP@*Qu[f;J>{/(jkԿ[#iF~>/۩0٫4ap|[5k10K-S>{<*LH5døThYY3'M'rD8fEКWoXN…%"#>7 /_{B_6_jAsbA02T;8'ծF5e[G&謱 ok6<|R [!ׇ?$[$U 8 18>G gG)1P 6Rn.cD?gN \!l\" g -D'0) e9‡-:kƦN{ NPq>+U|ksd ;.Z_:!T%4=[ VBo^@AOD푲3˞UBY8ْ}Q6H)幾[X.2ݒЛ4pS@ԖeP0 ^3Ii A7P>cͺ{/i˩!i+;Ri뻆DDDPRx}!P (|r's3"#eQ@ګ9X s6^#xZ l2#4U$CDW3u/}U00![bZ>S6$y]X_ y.FDpRPS Du-" Z}vDq1U3=O'*/(G|O1&xd6:]F3eLNUB Ps<*M@pRIެއ~[ߑ/hڨ~wתc&^C`hoLxP t""$-50<1|=H}&H¬_"{%=@+r`A6,Fp8IZvRA; Tѧf qξ( ;w)-R?*ExR J2*nYJ>- `}5UKɧs6& {Z &erV }]q?, jӥ^E 5לY}67w8@.IF,9bk H &]m7K?)j,?]6jnJ@4EҶG3ѣ_مDd-*v#qUQ 9P, $ک7jۤB볠Pkك1^ְ?}J*`p7!c'aaJXS Zѱ\Z{2$ TAmP񧲇!C u*PHC]'*PgdِgWIz(wy5P"ߨuPi }kfJ ㏷9'ITKyn"3Q2ML6nJ= i䒆ǡrùqj=G8 ^˷ 4e*~n7ǫc9273thtXB{9RvaӉ)Ph ܡv`8,{-\c676=wxɜx7_yL!hd@nYY9&#M4hN8-odʞ}TwgvZ橧iZ)TG֔ )[v_2.}wq0q:^r)rd] __Cڼ F p" GؾU|aY[Y"$xq}W9py'`]S&௸1ᬾjo̚b1OK}+5yMlH۶3B3ŎCdS?z5C;ŔBnM~}⎹.bH=򁷙tplr2)]%]#~h5+'[cʍm b9ʊ<`^1#L 'hɑٽ-cZYʱUt6 endstream endobj 1296 0 obj << /Length1 1630 /Length2 18072 /Length3 0 /Length 18921 /Filter /FlateDecode >> stream xڬctem&NUÊmbNvl۩vNŶm;W|o>=Xc{⚸=^$*¦vflx@c'e;y;nYze3 _9;3Vٌaf 33(v@ Kg5--J1{毧@ dgocfQ li0 ZR* y5b d&fNfs;G)Ҝb ;Nf&nf&ff6@'o@[? +!{G6u휜L΀Qž;OgK#b;v-ML\)_0F@['?@'{J hk,MAfNNabӝTodoݿg@g'393ߘ&c[m)[s;3ӿ.s5sWIق<fvCXf#[#r\_@F6;w,E2rA'jat`RF"lk& N߁f@gKo%W55smr虙Nhbm Vٚ 5dՄi7 _Yon9;yFDEgb{& / <9;:fbWϓ53gtTlMN6qqtKߪ73s73AX]3 JLwa ٠ZT_m]aV8xj~ Ms8҃֝bvOCF[II{Ȩ9L#jAvJIpwBIY pڟ̵$> 2`ރ9Fɧ$Iw &/NYjn__\cv0/X,>ce.BcXDU:V8xҬ3WcGQKidE[TB ڡL\hJZQӜlu_)PjG˓v;wUe)!0E91 iYҜE?(}Hl)ZLkDqܧ`}Cܴi$扖ʥMoJgD4>k)مhҒ+eK>{GY0llӉ:ݩ&ɱK xb٣tTOCuFŐ7``քkG?G@ɾMnBA$UmGQR{f.Vaw-4y%j7oLdY[Tj@cN t*5I-suuWzR3Ј0%/bA -5qsRG{pub/PdSDC(x"ńd*) @ɯB\<$(U=\ٲ:غqv=nb-zhΕИ>wzm ѽr(vvW J-2ѓ/ŀSNom۔|Co ~~ՖT_Ц;kLE@cO)F%[L@W8I$F&ԇn7O_{.j0⭷(%q;e4xSԕVEY>vU,j̜mB//ngM_{X7N}e3 !F#fDԲӲ|W8ӴuCEw{ʵrK=xdF 9N2͑2wtYTS'$N=yhWe[dQ4Y*ȡV><ܒDB?Tj ƥh * z4{yrJ |2Ұ)lȭ)H06DW}wYl\#C愣O!ä2ˈtOrvʦ趫wWvaQ,KAY~[OcbL%hBY%\#`}Wzc^+ڣF(2.i([c)Dlz]p|-!ӍG)us5цt˹&53]9a(\LZc>qq6W hô=0.axܹ” PˆUy҈Tlѝ4 xtX>OtqLTA*j1fReWvbzX6b\gW)v9FMp4+M=> NY6h3pv^ic5%,Hl^pt|+-P3K,&U_ύ721~jܘE 1Z/ګquo/rp3-E ɟ):oduzD(xF%|O能}NpH v-P*bTxC@ (8ĻIW˚C/_p#+%kKEE+lhRWj8s'ѳy~$b MU.&\(EH7G bo߁yyӧ/$ wO(y,O#[>I94cwdTD%,TCٖu%fO!|s{]g"c mYkt<j"+WF,>\Tp5ʽM"COFplDr "ǽ7sLQoSJV͖`A]! USܘf߶LDttۿHFPo% !#];5h7dȵ$,)qg̲ncj1(@ (R/\L&PS;Q2|XsT?RE!riU|= )JL/XM=D&`3MATnV=);>!\à0Y5??Mb}Фe% @&ĆzmD $2bm?6IzpEhOMKfuɓQ|owc|#krqzأ J{M]-DT|1΄:%.A:aYW4z@M1~\쥒JWq) {j4HqZVHF\}t$.e~:ĩFYJsR^ϑS(-KMx бFzd CT{K =w2/N5q?Wv'ia)Z~3 ig!/Qljw,x71#! B֭RZYo %ՁTkUM&^azTK'VRmߝ0W{pҜQA>O}J+OpƫCsvM2(01;fw5g[v( eNd{J#Ys`o/i=^"Zeiv6{qZ[vGrG/نnn.-}]Αm KCI} yq'NjXX,a|q<,)ʄ\0h|q؋Ł&XqP5b })a`**1+z,]٩"~#!.SYbs+qu}f?{/H OM5v8uznm`^iưU#&x]LE:@x ><"ɛC/rX(POc㯾. eppĆqU -ZzenpS/  HG)>~e_k}8F9>=l[4?RvsɺV&dW 4q0cEҭ)ؼzSnpR hWJv&]mVDuMP $kAXk h@̂p)n1\޵+j$"!./LdoنoUp DlidlwO@T O/GT5x]eo Xߪ%Y~w5Ԓz~,3?D966eN}DKkqip%N6./0o'ʘac4 ė/YzcxM C gAP6AT4 WEoHQL“L~ƥz2@ifޑ8]} AG6dgswKiOk9F0Cz.|&)޽IW)??#rz /D+ގVjI6>nAn㣔z$+g0>xtj1Q)HUtt : SiQGӢU81׊wuv6kHf/?@.7`>|!]=ݹ:X)?rSwpiraFw53X=3 Z\.85Ֆc:Y55W;r~z„ )\(ZީdZttyޡ2j D"Ny(i;N>w37`n6~~Ǝ#?]`Zi4a/Z3DpXmŝWy{Ft҆Lg8O (F,uRu}5]mAh+jND>uؑvTWAɸU&Q=[`QL3Q ?eFTaohnoӲQsN/DN`j Dy2 W*mv 1y+wl"qZ㌁$ѕ#HUu8S$nƩ%c-T^D_)R s/2Rk? TK(HwtH?E;C#2Rg+0c}nF4=7?RzX_'QyU枳nډKpf\[ߖ'6xxMۣ`eBf4tm)LBuc-ݒϋ'qzVxU ĝ7Tl ;Ω*ֿQٷ׎B\U^QoTwlW1ԍx$d6{P #((TB_OZ/r @2ב%1fgቦ^UMKg)0CMܛ ×Br:2HlvY0sѫe!OK3KokYO4œkg3֠PG|(i:e=JļYzc܄K`͜nH(bF$ܞ_ŏ/aTzj06Q{tȥeJ郧BBS2g߹/\b.Icz+!/{#Jۃy&f,j;IL)ޡRD ޱL [t7I$o]8:Oc{ e%Gjv^݁T]=,yRDX op0h~ɝ]?eѤETǺz_~ >ӁM>O5)VLfTwq>h㜈 %Mcp'9-9%S´,z] AM Vbm8)vW|Ac$ {*1IߞNڣY(!.}[-[iBۏmZG$uGa<٢ YoȖoDר5!ܺw*ЇLH;(*5~\U X<ܵf=h 7S*r@*i]FSѣ9)BϮY"?|XyK,4'Z3 V'CmR?h'Xqg Q?aSRPz{؞o/:)yK-TfD76Wt?WL*eidK~1[BGcj4rf&UolcV;H44ECκlo*OsmYU1*pld_R`{"B\ԀпY! U)Q^=Ю c.jϳ[u+ѵOPcwȬ{Y/=X;I $}ppEǏ%:GL扒Vg3V + X:wt5ċ0y:ҞTR vO_$f7kԂQo<%)%)gӫޔ'R(% [wl+jqPUTEսڧ<$hJ_A$yϦ8?hcĒF tk12/W1Uy9Rryڛ %Y6$4oQ딱s][*c <вYަ1Ajo) KO&KװVX)l488{|uDC.ݿlI%=Mk?+k!O,9NK+"Ut l n 2rh 4(t([Gh ϸZC"јP#I:+]l$a ė-`I_#&[ DyhZF͢͝Dp' F=0_T8I$_WkUcڎ`಍*A V9/NxG*B7!:]ڈZ1RMQ~\jkVaH:~~J_{T(DztRNS1#3Α2魦rfI+!Xa?mb}.RϞh_= b"ܛ0ubdZZv{>(V^\*%xœUN QZYUx?\ ɉD_k) cT<(;{z \$Exdh`ؕaEzý7)췉L-h*VΊ\Мw3M5Av&dpgT[f<:~dz |ʋPsi#˹}x3aUxӰN?E?7.- L η%\,5Hݞ"Yx׫Y𯜼(E6@&\O!(y^gj7 r#V=Ԟ<>V8>i'.>Fq$YHLq10+SҳͻES_:*|ddN=DN~'ӻ$lmnHYTjBA ʹd94BFK}L3$^Ri2myӵ+.8Gk_I|{= hq<23I\`ڹFLZt~e-ܵ<<3:6]?4F6(O4]^wv`ýN '㣈=kJ?>T_O ,DmmcHe 14\AK\^b ,)6^h"[|CIo wZ # U&HIOCui4(ܱ&7HXk ZeDLAwSe({ǿQb+= cPmTyM.~擜NG|ҹ(}`5_> ;{:/{mׅB|KشGå2_^񽗽vn~;'Zj?Nt 1$ݓR V~&B/+&wxo£jE$#ߚ{dr GT=bM8'0Y<Ac"\|M^dj& nŨ[ ؎Iz/=wO7X.f:kMi2vj)Է$ [)qAUq^6 |?nTe!eHT1C9N9#VlRNsϻ4γDrK8./@قK7R?4"oIN ݯP`drr{ϖi KMhi6!%o̡t?+-KM8DaMz¢{Myy 1{2KEn1T옾OS貟a00ŰO=W3pYf㷿ՊӚ옉 ݼHt}¼-x'YLn chg QsMr}D`I0KXeo4`P$*iq@1XHKZ{@7]}nTF(!LpM!;N;a*34Nn6왢7jϔ'퇮jZ538Ĭ&SEҵd=*vZ'N# :VA_0K]RZ50-j,WQ8YoG><u3vs$f$n%0nfW3+4i:zDNR M^SjM;6 kB)x$ouzXOWXq~20 ƭH>ɾ8M=b&2L[ݗ#%TB*Cj/#;lewg}ao7UM]V={bmߤכz(T %&3T#~)|Ywj"Jk+wZi;0(CbGjz&+KGqWT#(Ksb "b%(ӚA:<ڍyC?39?Io\N O Ĵ:jN\uscXh<=PoXTA6x@VBoXWvJ6=ǫDZ 6?)ŕ_B^ҡy/b\&gwD!iY3Cʿvԓ`w_:~@C0m!ae2\?DʄT&Kxo͡{>@P`KIczx 쭟$ c.NJ/Mb$hKh(^Bڂփ/0axU=nkg2j}cDO~S{KdKS2&r(S, ܇<+3sڂ¸},O{!+uWB7,+~ jW/!$ng ֮㵭x)ơhZKR(Dج&) %u+Ӱ#1tbsaC"D>\'!%,!AC~uV:GiCgeV(M9[Ӭ=YGwckUBol-? N*}!vE3I5W`˟N$uc},$=cNאY\6, fh0!*U?Q]sr64smWED{ ' za&XUP/O$_Zpɉ?S'cB7WQ&b:{,*hrh0`\Xa!E5NȘ>Zӈ !9hl0eKgGL qg`Tla25BLF?hKN]hʆ7} }Fo=^ZKR22P*zX+TKAsjǡ>,P tqz4|!MeޣtI `8ml<@F\a~5Ir; *(HYmf{Ő|݇N!_"{m"P_xMX.Yf<` ?KA[?G F#>(,SR־.`^ҍ򦔍Rx5~{TrR2eFtY ?\H\5~bִ>^ׇh?[^_1'7Q鬎X}!TUz27#CbCСdĒzgvu:Տ˱b8QVZOuQ`wBیv&}:pDr g3USGcIgw`Fltװ2"p+G<e?=" F(_{R-wQ+~wcOHXаm,T%Ͽf0iN ,'  K^RXYUԱou?1ϢΊYSs:TMz|p2 oQ~4^n6m%k?Mt;9[%k/֡xE2-!'m" 쟭fJQ\u͹ߺ/BPbZ^ixFOGh<t֤ľuEרv`zDv~fߴ{.,D*iZ}W9s5|ؽ~c=MοogxȤx}(JlCLT9Ed˙T6$nmsTFΓٻ֧,6dHMn.UUmHRպ``7p><%6rRoUCpC]j=(zMA1KLKl9[wqk%n@ouȁ.bo7#EG=uHj)([B1e߂r )>N^O~i>tj~@SyҨql%'.̄'cH(n=Hu?kWepk1?M7fqOAAm HAiuIO_OrB@(q ju|6]ˬ{na9kҔ g4(P02}JSDktN ˯7 Z!ɥJ Wc$ פ5ܓMmw)j00[p"@@K9"vBQ7 yi8/{`.$ܙEG;ȼl6CU劂&==䤫`:E܍Zp:?#Y_c\w<2^ͼFap@*ebix&6Hc'g+KujW2=9,CV $SuQ𗴼1Kԣ.מqt7 ifw! 45g%6u+#+ SiXu@M|"7!1WC,(͡IdATIh:HfZ;c~9!>am{WP1>Cmr`<5j5Y2q+ R'IY#/zJ_"[8ݥ74$9wKq҃ruC‡VPB+P R@PHr.icC㚎YI _V,iHq!?gI7~ W&n`Rܢ7Yl=h vRj!e  յjP̲$8#W,2ű`HY)Kݝ@FP򀗶XK3 QM(ooC-Xt dO P%|3N74Y_O$PrPU >q "#'(^g2? vhx|-pί\>'|[X<9{܀h W$XKv Eh23{U+s[~㶎e%pS >)Ϳs5.yyJIt&%_iv52υ#ajB)t(g?[s0{@+AJgD^ҡq+±Jַ2S_Ch. 8rkJBErRSMԾf_T#{$/1 Q=1Tw8wTLvh低CQYO`N}OkZ*4)O-=qeSIDӀ A/A4PDO}9B@x@Y~D?ᣣ2_3"EȦd0ѣB>P$qg<ᕅ9 Ҩ1'3$m:(l$Fac>>9+w,Nw'*( @l9] endstream endobj 1298 0 obj << /Length1 1644 /Length2 6201 /Length3 0 /Length 7047 /Filter /FlateDecode >> stream xڭTu\m&DF@jcn!%`F@nDAJBPB.ERDbGo To"E<{;q _;[2$gzߋo>78ms_/d-H'4 Aqia_A g#) D0Z v;" !CB^N?5 40ANDR/CX`~03PE J^#.5!"C0p_aAaa4lFt@A f߰'N?W?^ s1=r u =x)B^.) FugD.^<< l|ŚzmĽ4n!#"yV)'B2̟-^6[_42-;'fnw]ByN^Owד{=z;u1œrBRM=׏3%S|8mnu>K纤 qAV[5bZ/`iQI\Ie^V4-L{+7ڕ|,ZgNISmAZmaxXZFo?w>4G, \kn.U@o Utm/B:R-GK2FsfK]YӮӀڌt*|} z9FP0=.g|UPZzjUnnßۅ֡(}ӊ)^-((~(VE vy+FJkjv  ~#vH_mg~N ].| F%JBuBMަ^Wj+SVyTDz:3luoW eAMEؔނۧjb%߆v7|;5RcGޭ d04&XCʃG҂շTK5)**= [M,Cby< u[9RȈAʹ%[7cI/O݆~Zrc>w*t=bi׵̇^Wr&O'tr-b𔎰eۂvB`v95&ڦg>c2VNH6mxC\&:]s%̃ddgg Qi>'ij39RriGܽ!_gq쯳 inIgP*? +5ʏyfSj$?U 3i( S(YmPP9 Yf }wvl+ĉPM`03IW˸mGY+g+=_7%cwxktx،D~J~?[}M![U-Q&ax!n\v>T#%&@?hPFɧ}r at4M9{.=D_ )_ J'!^C %"ȕݘ7uS&6]CqiIK&B8EŽlU3I`V^+^DgwfhD7Fr:|ksoH-"0ilDdn48T%nP٨{eߎʛQ"̖ Q+W.YesE/KO-FթR?q_`50~AsxNj<p3̖;(6X GZ85%{ɫֲ,W=R+{ MӰΆfNd NK"dzj}=s;EeI#I7%ImVK.X|j.V'Z /󎨔 \4pap7xdfhrf4{lpgP'8<3j9R9'8<-rd m:T):6M&8?n1S}OyaYFA:q֔N-JS;n`ߘ?pFe5Z!HW$4k0O`{0q'7n6?QNO)m/pu2H6LJ@~hN-'0$MU6{ч1b к-V>+:Mgl>"yUk6G_)ǒ%c*sO˄z)LC\|@x(l2]\K3֑(yӵc\ OGRGES6eqBrNÓrThJ=Va*O5mĜ}wy08ZЕV$ Jfh\4sR:ack#ȬDhQY<}*Hˆ?8[s [P^i$$&#Ty.&@]f尌oJVNq unpI+ 3\BZyt.zR̹ xlGGkʸMsB kn;7m4ʀ- IҲHL&n*dJ?l4;rjQ?;m~r֯zpvK^1@ɞ t{ɾh,*.Ft- f:pVa'pVe3шLb'Q89UQ|L.V9[K%vT9,0_ryb.0/Т='=[ّxh#DX|NK]́[H~Pj|oӐ֤XU!ɱ ?{lY)nl~Y-c0<}'c-eqP'ua.0K5%< @[XlLa쑆P/pq|"ŧn Od_;q9a?sPzu^uwVd65˸Vl;$& ? {tذ$ [cN"O{y8Vkzfy\(NB^JKBvL52|nA^bxyv6 ik(PɢTӋFCބ+_?ڞc~L7xX|Wz!79A2^8Dz0̈́n5"`GT7:íNhhnSYK#P2%2F'`8p&}E%)z]!;IbKuvz΄ E 0::cSA$&+)\ǝd CzDG*m3ZڞEuw]$6˨G4&S'h #!cMaJID/^>(u|KWL_kŜ y))_̈PVK~$`V%X]K)lB.xnCc.'R#`96~w}ϼ32論bW6D/ʨt* =zoR/X< 2-#5M{j.͒r(+3~oҭ5Ap&f[Xm+m ׁLߴ'+f\%4.vk}I 2%6Nus0;w ܤD5BtK虭^T3Y5s6V,zƭF"5r<Ϙ~/O,mP:@&]*OWKЕuPrQޯjD %KR;F߮E[K3?Q۝S#|uZ~^šORLCd~4IX.,`Bˢ/+/(X".^}t!=UO -U+bi/׌b;;Գv^ps|舟Kmbl"~51FyZۥнg6^F9"U΍0XlGۼT ݷOD!4$2ki:<&|>g !)cwQIWLYAJAV#e IޅkgQw;Rž0zI-&_gGy3tPJK,yxm#늴PbfV 8ˣz3C$L ѠmI[{;Ǡn"VC8 cs-d,r3إ-BQiQ1|mR9%fXIhWF:Np[GEcq5Q6wdksBH8];iД) w#,ĽU͓e^srE 3%] F~R7u#N@ZH)SuQ7)ޤ= 7~[s5[[/ ` ]Q~qߴhhA>I%1w9L}ؗa+WR-T&7Ӝׂr. S$!Ѯ?Ҏ[ϕ41q91Eߏ"%.wݐTI[ ]Jk ݢA* HCat;6B''qssC>(sG>nGCV=X[W&!ehI2=>#p=i9+Yj;.n%E`mh$~njQ`)Vkk n#+*z_a#!̤2TWByԃ"R6/τ\u =K9W#r-M 9jP Z3iOe<ڢ3IA+Mlly=RR}$5`` |oK7au\m7>ym$-W7yW28M*dldͶ\|&_Rr[C\:o1u B4L#;|OPpaLR`WOq_y;^;I >A&g}%Y=ndxc5_7^;Ipr/Uju7}ѝZXP\95qZ/ZV̓,/c-|䣸(-r~.y_^~Tھ % ՚2Ta(k,p^?zJG? 믱j1|=1^2:4v?ʻrƇy-.rL-"=W MDԍza:NSE싌\C^xnrjLkTs* 1ki [irssQ,o[ڇ*]l g℆7hɖ+?h, endstream endobj 1300 0 obj << /Length1 1647 /Length2 14165 /Length3 0 /Length 15014 /Filter /FlateDecode >> stream xڭwUT]ۖ-wwwww݃Cpww n9njWk5!}̱&#RT232ucgm\ldM] :6822GSCg)7@ jj `f0qqqD=J5e *I2yS4~Zۘ:BULM3)@DAQKJ^@)!05u,Bh :R8lMD%08ٛ?LݍMRMmNNv_ |NS hdw|FUGv~vf&v.Slu8;`t6 f; '28:X:9}|b՝ /[{mtv26cbih ׼Hٚ!7qQ53TIZ{LL?C(g,@ +K{ZZsgk#௅4\ m[k#w?Bٚ2DF1Injt6Z6o5o:U _lCejk5|w B"R 4ͶXs*U=MIC?A ۹عt:r1? #H|I`lL#gC[O_jcGO%05u75[^3 LLw1KT |8ľA0߿ڮ/-|&qcmOzg/5EOE U_>:y~ ^ bF&7vF_JzůP_':Xa.I\Iat"7'=S ãɉ%1~Bax`腖B`Kf'R9OQr4#԰ly9|k!Gy%5BCbA#װ-@ɒq&~maj#B5^^E=1/{pTO aML_CEK"T&ۑd܈o`2Tτg*f 'uמhyh3>zR#ZEnzE6rVاB.rx_+Ch\Uo"tjne_NDZ*&fTCadMRq-"} ~ḰZD*n`݄)nA6苔i0v6"8|dNui8ERG2wE v&&/^H7#> HՒ ^[Ezq|?GErn\`sc M*KZQd}EdrQmҾRĜT5֊6aʑ&qG?tsq1|!L7F;HU2Ux.5t`>+izWYBǠ6yPc&?<,?fUL뙿=$r\~CFQlJ?YύT,N 읏C83 ֍/on˙JM$IEtvRiP!I;.<+> M-4 r:NugMT=N$Mi0[b(RYdz9f|N=[p^pQe?dF{ۜJN#T˚>Mrd1@n)L"CDg\Ҙ4t; Nt=_>>TFy\GO):/9 sۺ Ea7q45;55y>V,8dNE1 WZ\r+^\!7\Q4b^!c?'bé&.`ng~#h s @>5dF/lȇ0qk$U?kIdbf5 -Ϡ[/Es3TJx!4H! dM_=k&B ΃UXiU:W#ٗ`ͣ(:yHK1#2<%ivQ}$_u BWQvf;z$C;fc%tUU5]Wr&ér-J](IsjIC ~&sHy,*g+2FH; {3YMzm-qPi+s^Fy,ORE-GW[(9Y9*gGy^Q|9}T<υGſ 䴄o+awZk f-$$%y 2PxY(6F%w8_y 婩'c,}]DW#X_{H \x2;a,;[8X ŧ%0EZJ9q_nie %Q_U)!UG375łbتfB{!2km雞Q@q5ל[殪!aD9R/<˅&aR.T0S =ٲuBĖJ3FUeYATHPOv7k<ej8H,g9n@ĴHe;Ũ}یs 6h -ዯTq4|>4q:]um*LuB Nt&lZ86JЊDONmrnT顠3Er܁sq@}(~(ܻ&"kpq9IA[ "ư<0rFEi7n2]Ƈcl F%ZX%\gGP,P7}0DP#baԢ DEO"6҂4Z$pS|bO۹k [6澐ō@rPzOB4, 0{ f`1?ƖF6kyX z:@6=`^?6쨅 VIPALu> 7d,T.4Λ膿d(Lj9 U)B-5Z*Ah-{V ̤u]yg$hKW^$aͽ*26D$GJM6+j+MsI/[o"`X yH5 " FX]CS^=Y٩awdK{\y27Ƌ.J1d)u饅w$'z(;͛@Io'B+ܭy'TM{T葉wgɠIXU2^ǦE . 6 Mn,T*ox\Bu|K1.}ĩp4H^Ҳ6d&82JOJf8K#t]\gA+V~%5J0"9jdm11CkV/yL@*먵WK >jHٺ L8܃a'jp6f9Op#n <+0o8r2ؘ)Z2gZtE~+W6s }'\0OQPA]aZzD+JÚ8=*[%cuTwGvSCDf*1-ņW !>: t{L?RaǒHD9.bj)"BA=S$қBf7#@:(PGV)22jÆ0l,ڍPe SUͻٲ)y߯b^Ey !8gXWВTq7BWk&!I @OR"58V~N=" 7"ErZHa-dHRwA`A6Z|ӧđᴁX6]p"~Ȣ/-[#1? 75C5.ÙۋvxϸeTI.M֮l;xOHZ7ڇhWz/Yif}?ǘ".LZ3a X 8 +MpjXiʥ KPz{o#nfp^.Jk َ4x~)![ܭ[ V_^|޿.}5T+w;y֯u:fۥ~HF}3NAn`HH[h^ʼAMFMV0ohlL"D|z6O-PKsnn_Y@ ՊC>ω*sdžTM?fO(L<:OM?T1X8Dv.R9,ðW{[%<ן.-_'(AOy9W,=9֊e}*H;ȻQ*6[_wR_1J8?^1_{қoؓBb <ݏkw (򚵔H5wqx{SJP[Hµ/.^e{ͨa_ak9hp+nweevCڦ{@CI J'g}/ŽժDJZO,G93A)dDẖÑ3V|in);^=$kWaiñm>GؘP^ڻ@OUQH[r#T_s!*T\(7d+˄X(DY hok0GzDOСe6اNa2kM5Xm}a}%fl=;1é=-xUyvP*lM62*ࠏqJ7 'dMt׸9 CtCz31Zʻ*W63Eg3y)*R^Lҡxyft%?-ny͑x S&Znm:8-Uy2dFȻ ˛ph _:>+,]!,6>Hd!qqz(,ú߇)N'P7'8/s6f=[Kn(1s򪈽ɳ3"j>q;;E-̏,8h4G)sl}w/FQ?SrPVL_Zag@^/k:2M딩+f{%Gye2Zt^ulCvHeEw:PlÙIwK-q_0Iܸ _A0O8LܥH[u$ju\%_"Fe)&Lu-M%>)}Ǧb|S>ʼKO J4M'/)zզ\3œSUL+-.*5m8E\淜d{U?u*#-}ن^nuks--gkӑn^b7G:N8xR/aGB'|`KDVEj:^z9qnh3WWYW`\Һ ܋A->#UƬ)g /䈶ǎR(\ I0%MWw5 XQi\G knmЙ˒q% %.)ΠyOӳ?`mOLZE]0=X\nYFdl~Eak/x|Xз *ٵA-}vj2eAWs 7ȔC}||8 ڟc"KMe'M^s&\Y9R̼be#&X#â.Ż,:I Z>') x2ry"BBiG7a2T('Yq 2߅ֱOcy~Wx;[-|f"f;k01%4'?x嶺q:e$$n;ϾH V6w^,c/Za,mOG=^n?zp -?w̾ڲ%^S>Wh':ł WnJ€o%y&PӐL] *QfhV㷁| gN;6=\9{\R"+q) /#%6o/ 1pOؑd9 Y} յF G Ƴhp4F`Lњ H+-K` $ Q+xu~eB56 F}:W ~Y eCt֟+r&YC|g!յV!T%4>J̥AlJRZt_$L_80H/Iؐ鲦..~[7iʄ&I;Bo {z +i4sԃ[W|;F_Wr ^i&{D7K z̩0i]@5=ZtqN9tKNl U}Ef*E~6[-ۋ$&5X4ІG㩭"DH 1^#D8lvbir+˻?PTP3+q:/&H2z9d aƱz+Nmnyf;&Hrtvg-/WmQ-*/ojtMPY0ߒe|!:/ZWernui$|6=*PI}tf6.}VTȃje)v*U0EnIQVۋ|ͤ3#>zMTIQ8dh%t,ŕ)d4 AZݢArLۗ-/ U>\[ۢJfQ Ae|p헸@+8},rE~V|}?#|a\Yl2uo[#I;qv&=병Š, A~\cfF!yh2pAqp%Aͽ(8|gl3?cJ J#hf#ShCɎ6!US!|ՏDV+8;ov@D')7ƂZw&30k" V}+&]YAvxI˸t5C?ˀ(k]2>ƦT ξ&]"oe~ tc Ȳ#׭S_n ðhs-$C@}ޒG Fà9ܰ!\ dy'9#]ހUiĶ&gOkEȚ`!)uR 0 rH!ȹ(.Y+|Bo0:B}cõGPKӘ݇2(!KUr/hLv\1'g {|Tn|P exyDZmH[쌇R1^O3ڇtLQ.16 GDŽ.k>MbEMHV%2f0P?f ?^ >Ε~T+)zx`7[:n%-Vu鶾{lmuHyQ";>~!8V)JAm+`w"^Qᶓ٪fY]pmu.%8=DSٺ?':FFLAbg.<$~ٔFAqȵ^}[u]Q|1Q~W (zk5%4-A_A|?z'Q:7GDVR&O]p0᫻K,ݓ‡Ƅ-C o2☳+^Ɇy+TSz BW#(Z>#B c_`%')c~ 4$K euS#X+s. v9~|ˍ$ jlv5/TmzIhaEK.h8}+~2\,%lN{LD…{$u{-A8&ߥ>3u,olKAS;s=- Vݼf 7dxUtnl/HNomL/1QX+r1b|`#9 JEr`>%@tm(l{~Nnabf`|H <^ozAlneV!(5 3RprGƥZYR q-nK{\h-W\WV# ΂& Ԯg ϬGMS-PE]3Iwfgjj__r2C׭)Q34$ m喘@27kEEnoMx3?Pu:]Dӏ+ij?8%^JuL-h`x3 HD|FLC7䩔98Vhp{࢐ 97\Zw#2=p ">J@ fb-|Xl""n'Mkz}@QNE[Q 8jHqIXYgFJnr2ѹN傥D9+7{l>\4zZ}G})c#,s*F`80z~]{K]z 6)u{TGL3g9:̘I'YH9`^;#Mھuw-քvJU◷ޙ @tx >[8g/*?g-rvx-^;,EƲ{g' V >{>Z# ̽Kds&&\pU%*o+S܌<{~_yA^ :Am/oj\l%“jCBD >'h|yM"$vN&/]Q0I.0&!WO]CU?i)JVLה!gtjB ,E*awW}{@Ġt"_E bOt8l6< S*X>` \G2i\F'D|@7TO] :2F8?B-Y'y-\ir;k1yk<qMw˸qdTi~{c5{,ջd@emwN7WK)]&GhFQ9()~IP]o^̵A>E6@RG&*aQyZ9&#pEƝkNr G(`}3ÄߪPUd|.1%쮤<qˤwZ5xܫkÕ;DYMu-y^!S%VdLfzT<6**L9=,@d8%}EU#~? پQP# ]Pz-D»RR_-j"w)ro .3=N}4dJWN7X@5 91=i Q _Lq\ 89eiGLMҮTZUf[QU`WTgp[͓3@f6˛0ܖӟ3kŒ>o~ai[ *:.4ϴ\T;jn\KN0uaD,}r?O#2 %u4#e1k_D ;pmŕm7ׅ4Y*4 Z0l $׈)"}qٜ|ҕ >mHm,2a )ID1I߭HG7  Ha%M-r¤zVVȁe[hz@}NVv4T#tҷjO;ǐQӞh?l@ḽD@r@0Y{AZEvei/,74FFw\gt?$*VGhqG۫' GjT>C Ol|:%g#>pĸj 떃Jp^X(G 3A\4"st{d]vmdwKp2;OqOr}ijYgqa̩t wA ̨kOdj&ـ #pjP(| p>9[]BJx(UZ0n[0] ~{sLI! 0¸: *)e["8Zze]D('nL/L;8I4Gezȃ1Vq(5X~q8Ռwh93J'x[lѽaVv YP+ i~KIn@Ȅu4a5?vٻVL M7;9+xV>&1ϐ<&a%ei. :7۝hdUDʿ_'g\FO퇊tu +IB_SLq2e94%2%>S( sv8Rq{L6h{ӵ{`Yv6g3\Ս/t1q#R?r9(/AJ |jv{3<$,JNl+L-a^_Y@kڅ1Zc40j/O?v5صGI)4k<n3oݫm(> stream x[Yo8~l0 Kgĉc;qǍ` [kqWI9$j)<\L`H(lYH̸W*ScCgZli-1c(1p['yƙTpؒAKeB8TgBi2\ceY-2R6$Q[ }&Lq.S,J[c8êHɌ5(ug8YQ>'24RѬ8Ιq -ԧT(T; ИyEk )(bhY "(S'8XSrEh&77 Fl&7̬J*ALsMp>:r F e0\ ܄E8m&LR"b%H `a@YShf֏S*s`>>P( @A<Щ rgPi$4ΐN1t@ѺҀ )qgX組C09!`q-ky.KpVxFBKrZ8t'm` ;4XpֆrJdhŨ6XBuR4Cm F#E~WLwfۘp-pp !t$8%?U:+tG3mu!Wቫ5Nִ9{R~DVdps"&]8S P*d EgО\CA ,Rq@0J:Ё;G) 88z048zǕJbL[8.k%7Ig!;3]GՆ&m>䚕5$}m[]yAx9 `M߫ȡ-E+he'98,p!M-P }XLc z^lȩꯤ{ JPY򢺍2DjGez^șW9濟5UFO2 QFouMmRIw*W !URܕqW^bjᾹШ&Bh:ƶ!' W%YD5&ј9N1y>*Y@gi]QZz ?QE]]}86)cR-|? ժ IzWi@6>E2Wr \ˠ B&ZE)i؎j?A+A\ĕIXںkdN&ԁ>5vH ~NE@x =Nx^&G=r|XMzo<9?zGOQx0ͯV #^i9y<d]>®bgHu<)|Y̧ŗEyu8o7Zks+%x˧sP&%%~CFv2UR[a>oTtr2-V+rS,%YM5Y/xZw'AM KZ)Lݏ(\Fu/bVm!)ͧ fzXbo>N>mtl{ƨ^c@l0ڡ \V519.^Q1 ɴN;3[~ͧ|Re`rU1Yw=]\|/,YdVoW&:y~rʨS`CKh|ɐfZmZ4R-hc<^ 9$o;rD>bsD|I \kR#)9Y'YU%__޶)g~z,{z}C΂O,[%NL|LN)դ,1B{]N/:|%Qn&|})%`3`y溘אjyA淳 H⼀ X'v../z7(ferU|v[ˢh;],?٭~8~)}]V?K'~}"kۄgc/ [r;'edG/&|s*va;mObR۷NuW-g|fݐ aeN%eٰVYvLlΪF;E㞂yO{򺗆w*p>>>{d |yy}fufI)ک٦ݠq 'mn/Hnekr뎢nzs닓-EO`UfSaOtni|YLװGg9ko;5_xu>bvM5Y?]Blo\mdVi3K_ȡX.cŜVEW,:YVtCu蹝ٛvIF~2|j"IueG?B bxY](6K{ڬ^t [-&.إ۩^ـܙ_60G.VER*㧯ޝ=ڤM=u[OMy;U粉qqKy؊+O7z.ֲdqYί@Xe R&DL݇cJ^C>j}H1BjJ1`2:)}ء$'s}`&S퓠/qG~|S~dsd0Mj5Zm`wCqJ"[K"] u Q1 ,X5Wrt6OCWW֠\AQZ-T*AEaZaWzk9z-Rيh Ra)PDG] i+[ !xcZ J 8#@!ZxC1C!g٘il1;3;m 6fcf-!nq]mf7mK*6cU"ZE5 MKƷzp \+4ѼNfj)황ڦvlN}HvmLjTNmI|7TXUjĭ:TQFLܪCŴraU)G]UKlԥm9oV endstream endobj 1306 0 obj << /Type /ObjStm /N 100 /First 1015 /Length 6025 /Filter /FlateDecode >> stream xڍ\ێ$q}WVVyJEa0’H+CsYU1=~ '"q93Ǜw7[p[oR-b_Xo-1|ȷ ?`oB'MJ')Aߜ-In.bw| 8Hm\!P-P;SU,ҜOP4xJsP4fʀExpr)6IMb 7̣ x#D1z{ e_)[2} 2RL5Rq-DP74_n9FʀYXVlRKJ7,A'B m|v\Ɉ ~0i/kre!gjxʱb)Flxf#&g-YHs֎g!͹Hi?݆iNXewcn~.C!m8eH8M\1EG8MK3'Hox<ۇ?3?'p>-eQH;NT TO?E/?ݯM~OE;Js,X^\l6NZpƾuSQؚgrOw^ˡ<=0]g?~?~~wc9_|? gݍ8Q՛rLMVcjoMmK9*\_mڐh'>}>lxH6bB$6]+ƿCu!Nʶ@5֭Zni1̮hb$"E[r*jx1!fR-7^-pNF5 {DhaZnExfiݠs5.%-ۺ!Nn)њX*8|6ʭ+紹ĭP/ˬ2) KBdG80WérRDN˱k(e3uY[a$GHN5ʭ^]HP%3+sq9 KU!HtVTb_xN[Bn$a~.~[ Eǫ~^ ga,3~0DtSjob+ ^M@R$TԷ|SRwGPF$E*n8<[G0.h ̥v6=Æǿfa'K=7™.}13Rb}88c7+*A qQ ?kr[k)`^\W2alemY ĭE:4 ڂAn5:ۢ/nZx@P4/ګܺ"j?rVYT8큺ḁ:qM>E'DL8@~a3~[_Hs|`YYC ?kr; XNژs159)r0b\ow&8,0>m1bA+CVE{[73f!t =Dhɀ86 iw+xSx_7L)n1lf9;5/ګܪ9[]¢-ծ-4SGXP0`fR+ʭBٓfl ›2݈V#c! fi=vY7= V5tjhrcF &~l:4ք$8$$bvֵ֔Ή;ϐ}@F"? /^EK[rA@ЪUʭ *&FZR`f`6vB|غ/z'f"m@0cRTG$عbWub*nx/KYlQ!>ϟPf<\w{& g]&DړDKNZ}ii ]$|zUl|Wg}qo2?Of$?}G~c5]Iѫ/omΛ?_῏%2[/~c>~r\k0W.뺃&b+z_~{Zc/f>kS9޵յ|&@^Q]Z!O{=m> L^Oz:3x̦/bO$ 52 SbD什fFPL"$$C݋K%ž.&>YeyyϤ*BWNc޲L9x^MLËe\:r85陿Hpњᚇ"U~2W=ӯ*^/o021{H*9w"R/&I|'Z4Լ\:>~#9jfav2*i whv**f_o(^^4hEƷtQJWly@?./Ur1oJoA^7 {&| {"T"U/SIGߐQ!o_u=7ЍNbѥfnUJc##㥸j-X4P bԸA%e_z{n=5IHNoEߩ4XPF["S&F7 Fߐ7 FVZzլ/NT8J_ec޿a`y=7px͊5J =D2zaJ婁jylUBH-hRN;h*A%BQ%QVO ﹁g5 GjҌ5:h[FN*;y2eNHt%F+*o$?/42oɣB[Ji~*+++++i"[>JZLQIUI|ii^B7+iޫSTQ^I7R})<5RIw W[$` ׳({J'P+GKo)| ,`PrXkJ1)dgYqWb?<=RC^ǟypoW C޽7y ᡓ?âU endstream endobj 1417 0 obj << /Author(Bettina Eick ; Werner Nickel ; Max Horn)/Title(Polycyclic)/Subject()/Creator(LaTeX with hyperref package / GAPDoc)/Producer(pdfTeX-1.40.19)/Keywords() /CreationDate (D:20200725015539+02'00') /ModDate (D:20200725015539+02'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.14159265-2.6-1.40.19 (TeX Live 2018) kpathsea version 6.3.0) >> endobj 1398 0 obj << /Type /ObjStm /N 19 /First 179 /Length 729 /Filter /FlateDecode >> stream xڕAo1 CwmcK²=4!U*H_{4`{`22*Z+˜Q}VqY+b{BdFI-%roltV&5r2I%a#eڎGF21zZ a ,Apwbk]7^icՠCe}3 B9nFu!"@ 'i's$9S%\,٦B!9$'qVMls4%Xq: B!1F:CΞuk4;gi&W]tf23qr&N4J@2ʀ!㧹uM&m_6C\ *#GpH zs;13`HrtH '= @ rI8 <]zOPPP : W{JaiVVVE}T {4x +;tAAAw0Z sY>^w;&B"pB4%c1<|Щ!cnMQUt-OKzEڨz랗mmKKrؖ7|j}ޜͭN⺬cR]U}o#o endstream endobj 1418 0 obj << /Type /XRef /Index [0 1419] /Size 1419 /W [1 3 1] /Root 1416 0 R /Info 1417 0 R /ID [<68C734EB9D9350883DCEE5E7AFDF52FC> <68C734EB9D9350883DCEE5E7AFDF52FC>] /Length 3262 /Filter /FlateDecode >> stream x%i%]:g}ٷ}z}_zW%JB:h SBEBƼ1h!Q UH EvunSNEQ_(zE*hj zjh`l!S/QۤoNUԜ E aX큚+-jҖ~ި{`^18^sTۯNsVVs U{ךaZ|+͍IKͰE ͭMs06Lsŵ< ޫ= {jڤ^اUԶ=<Ԯ=< G^=< P{yN-W< j[kj<gjjgh^b`jmKpYm-+pUmMkp]m M kFiwn]Ռ P->2j5{\-.7~~ bp(jO 5$l(P!uc4Sⷣ?51-' 8 p NCб5ô1TlX= Ycfbq/ɨLŞ?(бYc:l̓ yg~q{ǐug1`T-w̅E0;>/Md>ĭc! c %L,8F4n0 &րb-Ĕɍ`(lHųE/!^[al18;R##qNa}A{%8Oktpq8'aNi8g pnT|U]p .<~(݃ x10Wsg<ƒW[O?So^긩hEG=RSF͍57jn-!jԱp`IK2;`E*x:>#ܨcUza j޲9|XðC: RqrX+a50k!ք!V!!V}Tۖ?y;2on' xܨ~h=jfqVd:l&/GS`5z,FρE \kpncNo]2~%JjX5 IZG*WDVmLߣAe_ |%JA+Gv$@J]QzktTAŦ*@%LJ_^xPAŃT<\]Ni8#*U {ZxPKii&%hQѢEE]D(ףT^-n#:nt\FϜ JwI* \g#HG-:wS:%cIǒ3=tѭwA~Ɏ ?ҙ}:t%K:t,XQIۛ?:vTś%ߝHVF%]<(:nt ƍNݥ^\qXұow=x A:t@K:w)}jVkҕjV,!)>~,}{ ,XҽA| HKfj`̆90ÂESODm1,V*0Lm gLm6-Kw$޲vn{42a x@: hJ>:8,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,W_s4[+qDLLLLLLls4 ̧ "K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K?K? >H῎oIuɐɐcHC?q<3b3+ɈbČxfh1{LZ,H _^xԛZ-JOq8>P[z8ZzA-K&dH4ùOMNrI??G#ihm8Zv}8ڌ,gd9tJa,aXa ,eVJXa ZXa?K2a lmFaNa}A8Ŀzq8'aNi8g p.eW\pnm{]Cj=& Matrix Representations This chapter describes functions which compute with matrix representations for pcp-groups. So far the routines in this package are only able to compute matrix representations for torsion-free nilpotent groups.

Unitriangular matrix groups computes a faithful representation of a torsion-free nilpotent group G as unipotent lower triangular matrices over the integers. The pc-presentation for G must not contain any power relations. The algorithm is described in . checks if the map defined by mapping the i-th generator of the pcp-group G to the i-th matrix of matrices defines a homomorphism.
Upper unitriangular matrix groups We call a matrix upper unitriangular if it is an upper triangular matrix with ones on the main diagonal. The weight of an upper unitriangular matrix is the number of diagonals above the main diagonal that contain zeroes only.

The subgroup of all upper unitriangular matrices of GL(n,&ZZ;) is torsion-free nilpotent. The k-th term of its lower central series is the set of all matrices of weight k-1. The &ZZ;-rank of the k-th term of the lower central series modulo the (k+1)-th term is n-k. takes a group G generated by upper unitriangular matrices over the integers and computes a polycylic presentation for the group. The function returns an isomorphism from the matrix group to the pcp group. Note that a group generated by upper unitriangular matrices is necessarily torsion-free nilpotent. takes a group G generated by upper unitriangular matrices over the integers and returns a recursive data structure L with the following properties: L contains a polycyclic generating sequence for G, using L one can decide if a given upper unitriangular matrix is contained in G, a given element of G can be written as a word in the polycyclic generating sequence. L is a representation of a chain of subgroups of G refining the lower centrals series of G.. It contains for each subgroup in the chain a minimal generating set. takes the data structure returned by SiftUpperUnitriMat and prints the &ZZ;-rank of each the subgroup in L. creates one level of the data structure returned by SiftUpperUnitriMat and initialises it with weight m. takes the generators gens of an upper unitriangular group, the data structure returned level by SiftUpperUnitriMat and another upper unitriangular matrix M. It sift M through level and adds M at the appropriate place if M is not contained in the subgroup represented by level.

The function SiftUpperUnitriMatGroup illustrates the use of SiftUpperUnitriMat. takes the data structure level returned by SiftUpperUnitriMatGroup and a upper unitriangular matrix M and decomposes M into a word in the polycyclic generating sequence of level.

polycyclic-2.16/doc/methods.xml0000644000076600000240000010143013706672341015565 0ustar mhornstaff Higher level methods for pcp-groups This is a description of some higher level functions of the &Polycyclic; package of GAP 4. Throughout this chapter we let G be a pc-presented group and we consider algorithms for subgroups U and V of G. For background and a description of the underlying algorithms we refer to .

Subgroup series in pcp-groups Many algorithm for pcp-groups work by induction using some series through the group. In this section we provide a number of useful series for pcp-groups. An efa series is a normal series with elementary or free abelian factors. See for outlines on the algorithms of a number of the available series. returns the polycyclic series of U defined by an igs of U. returns a normal series of U with elementary or free abelian factors. returns an efa series of U such that every factor in the series is semisimple as a module for U over a finite field or over the rationals. the derived series of U. the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the top. the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the bottom. the lower central series of U. If U does not have a largest nilpotent quotient group, then this function may not terminate. the upper central series of U. This function always terminates, but it may terminate at a proper subgroup of U. returns an efa series of U such that all torsion-free factors are at the top and all finite factors are at the bottom. Such a series might not exist for U and in this case the function returns fail. G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ] gap> Igs(G); [ g1, g2, g3, g4 ] gap> PcpSeries(G); [ Pcp-group with orders [ 2, 0, 0, 0 ], Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ 0, 0 ], Pcp-group with orders [ 0 ], Pcp-group with orders [ ] ] gap> List( PcpSeries(G), Igs ); [ [ g1, g2, g3, g4 ], [ g2, g3, g4 ], [ g3, g4 ], [ g4 ], [ ] ] ]]> Algorithms for pcp-groups often use an efa series of G and work down over the factors of this series. Usually, pcp's of the factors are more useful than the actual factors. Hence we provide the following. returns a list of pcp's corresponding to the factors of the series. If the parameter flag is present and equals the string snf, then each pcp corresponds to a decomposition of the abelian groups into direct factors. returns a list of pcps corresponding to an efa series of U. G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ] gap> PcpsBySeries( DerivedSeriesOfGroup(G)); [ Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ], Pcp [ g2^-2, g3^-2, g4^2 ] with orders [ 0, 0, 4 ], Pcp [ g4^8 ] with orders [ 0 ] ] gap> PcpsBySeries( RefinedDerivedSeries(G)); [ Pcp [ g1, g2, g3 ] with orders [ 2, 2, 2 ], Pcp [ g4 ] with orders [ 2 ], Pcp [ g2^2, g3^2 ] with orders [ 0, 0 ], Pcp [ g4^2 ] with orders [ 2 ], Pcp [ g4^4 ] with orders [ 2 ], Pcp [ g4^8 ] with orders [ 0 ] ] gap> PcpsBySeries( DerivedSeriesOfGroup(G), "snf" ); [ Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ], Pcp [ g4^2, g3^-2, g2^2*g4^2 ] with orders [ 4, 0, 0 ], Pcp [ g4^8 ] with orders [ 0 ] ] gap> G.1^4 in DerivedSubgroup( G ); true gap> G.1^2 = G.4; true gap> PcpsOfEfaSeries( G ); [ Pcp [ g1 ] with orders [ 2 ], Pcp [ g2 ] with orders [ 0 ], Pcp [ g3 ] with orders [ 0 ], Pcp [ g4 ] with orders [ 0 ] ] ]]>
Orbit stabilizer methods for pcp-groups Let U be a pcp-group which acts on a set \Omega. One of the fundamental problems in algorithmic group theory is the determination of orbits and stabilizers of points in \Omega under the action of U. We distinguish two cases: the case that all considered orbits are finite and the case that there are infinite orbits. In the latter case, an orbit cannot be listed and a description of the orbit and its corresponding stabilizer is much harder to obtain.

If the considered orbits are finite, then the following two functions can be applied to compute the considered orbits and their corresponding stabilizers. The input gens can be an igs or a pcp of a pcp-group U. The elements in the list gens act as the elements in the list acts via the function oper on the given points; that is, oper( point, acts[i] ) applies the ith generator to a given point. Thus the group defined by acts must be a homomorphic image of the group defined by gens. The first function returns a record containing the orbit as component 'orbit' and and igs for the stabilizer as component 'stab'. The second function returns a list of records, each record contains 'repr' and 'stab'. Both of these functions run forever on infinite orbits. G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> mats := [ [[-1,0],[0,1]], [[1,1],[0,1]] ];; gap> pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ] gap> PcpOrbitStabilizer( [0,1], pcp, mats, OnRight ); rec( orbit := [ [ 0, 1 ] ], stab := [ g1, g2 ], word := [ [ [ 1, 1 ] ], [ [ 2, 1 ] ] ] ) ]]> If the considered orbits are infinite, then it may not always be possible to determine a description of the orbits and their stabilizers. However, as shown in and , it is possible to determine stabilizers and check if two elements are contained in the same orbit if the given action of the polycyclic group is a unimodular linear action on a vector space. The following functions are available for this case. The first function computes the stabilizer in U of the vector v where the pcp group U acts via mats on an integral space and v and w are elements in this integral space. The second function checks whether v and w are in the same orbit and the function returns either false or a record containing an element in U mapping v to w and the stabilizer of v. The first function computes the normalizer in U of the lattice with the basis B, where the pcp group U acts via mats on an integral space and B is a subspace of this integral space. The second functions checks whether the two lattices with the bases B and C are contained in the same orbit under U. The function returns either false or a record with an element in U mapping B to C and the stabilizer of B. G := ExamplesOfSomePcpGroups(8); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> efa := EfaSeries(G); [ Pcp-group with orders [ 0, 0, 0, 0, 0 ], Pcp-group with orders [ 0, 0, 0, 0 ], Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ ] ] gap> N := efa[3]; Pcp-group with orders [ 0, 0, 0 ] gap> IsFreeAbelian(N); true # create conjugation action on N gap> mats := LinearActionOnPcp(Igs(G), Pcp(N)); [ [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ], [ [ 0, 0, 1 ], [ 1, -1, 1 ], [ 0, 1, 0 ] ], [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ], [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ], [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ] # take an arbitrary vector and compute its stabilizer gap> StabilizerIntegralAction(G,mats, [2,3,4]); Pcp-group with orders [ 0, 0, 0, 0 ] gap> Igs(last); [ g1, g3, g4, g5 ] # check orbits with some other vectors gap> OrbitIntegralAction(G,mats, [2,3,4],[3,1,5]); rec( stab := Pcp-group with orders [ 0, 0, 0, 0 ], prei := g2 ) gap> OrbitIntegralAction(G,mats, [2,3,4], [4,6,8]); false # compute the orbit of a subgroup of Z^3 under the action of G gap> NormalizerIntegralAction(G, mats, [[1,0,0],[0,1,0]]); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> Igs(last); [ g1, g2^2, g3, g4, g5 ] ]]>

Centralizers, Normalizers and Intersections In this section we list a number of operations for which there are methods installed to compute the corresponding features in polycyclic groups. These functions solve the conjugacy problem for elements in pcp-groups and they can be used to compute centralizers. The first method returns a subgroup of the given group U, the second method either returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in . For nilpotent groups, an algorithm to solve the conjugacy problem for elements is described in . These three functions solve the conjugacy problem for subgroups and compute centralizers and normalizers of subgroups. The first two functions return subgroups of the input group U, the third function returns a conjugating element or false if no such element exists.

The methods are based on the orbit stabilizer algorithms described in . For nilpotent groups, an algorithm to solve the conjugacy problems for subgroups is described in . A general method to compute intersections of subgroups of a pcp-group is described in , but it is not yet implemented here. However, intersections of subgroups U, N \leq G can be computed if N is normalising U. See for an outline of the algorithm.

Finite subgroups There are various finite subgroups of interest in polycyclic groups. See for a description of the algorithms underlying the functions in this section. If the set of elements of finite order forms a subgroup, then we call it the torsion subgroup. This function determines the torsion subgroup of U, if it exists, and returns fail otherwise. Note that a torsion subgroup does always exist if U is nilpotent. Each polycyclic groups has a unique largest finite normal subgroup. This function computes it for U. This function checks if U is torsion free. It returns true or false. There exist only finitely many conjugacy classes of finite subgroups in a polycyclic group U and this function can be used to compute them. The algorithm underlying this function proceeds by working down a normal series of U with elementary or free abelian factors. The following function can be used to give the algorithm a specific series. G := ExamplesOfSomePcpGroups(15); Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0 ] gap> TorsionSubgroup(G); Pcp-group with orders [ 5, 2 ] gap> NormalTorsionSubgroup(G); Pcp-group with orders [ 5, 2 ] gap> IsTorsionFree(G); false gap> FiniteSubgroupClasses(G); [ Pcp-group with orders [ 5, 2 ]^G, Pcp-group with orders [ 2 ]^G, Pcp-group with orders [ 5 ]^G, Pcp-group with orders [ ]^G ] gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> TorsionSubgroup(G); fail gap> NormalTorsionSubgroup(G); Pcp-group with orders [ ] gap> IsTorsionFree(G); false gap> FiniteSubgroupClasses(G); [ Pcp-group with orders [ 2 ]^G, Pcp-group with orders [ 2 ]^G, Pcp-group with orders [ ]^G ] ]]>
Subgroups of finite index and maximal subgroups Here we outline functions to determine various types of subgroups of finite index in polycyclic groups. Again, see for a description of the algorithms underlying the functions in this section. Also, we refer to for an alternative approach. Each maximal subgroup of a polycyclic group U has p-power index for some prime p. This function can be used to determine the conjugacy classes of all maximal subgroups of p-power index for a given prime p. There are only finitely many subgroups of a given index in a polycyclic group U. This function computes conjugacy classes of all subgroups of index n in U. This function computes the normal subgroups of index n in U. This function returns a normal subgroup N of finite index in U such that N is nilpotent-by-abelian. Such a subgroup exists in every polycyclic group and this function computes such a subgroup using LowIndexNormal. However, we note that this function is not very efficient and the function NilpotentByAbelianByFiniteSeries may well be more efficient on this task. G := ExamplesOfSomePcpGroups(2); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] gap> MaximalSubgroupClassesByIndex( G, 61 );; gap> max := List( last, Representative );; gap> List( max, x -> Index( G, x ) ); [ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 226981 ] gap> LowIndexSubgroupClasses( G, 61 );; gap> low := List( last, Representative );; gap> List( low, x -> Index( G, x ) ); [ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61 ] ]]>
Further attributes for pcp-groups based on the Fitting subgroup In this section we provide a variety of other attributes for pcp-groups. Most of the methods below are based or related to the Fitting subgroup of the given group. We refer to for a description of the underlying methods. returns the Fitting subgroup of U; that is, the largest nilpotent normal subgroup of U. checks whether the Fitting subgroup of U has finite index. returns the centre of U. returns the FC-centre of U; that is, the subgroup containing all elements having a finite conjugacy class in U. returns a normal subgroup N of finite index in U, such that N has a polycyclic series with infinite factors only. returns a normal series 1 \leq F \leq A \leq U such that F is nilpotent, A/F is abelian and U/A is finite. This series is computed using the Fitting subgroup and the centre of the Fitting factor.
Functions for nilpotent groups There are (very few) functions which are available for nilpotent groups only. First, there are the different central series. These are available for all groups, but for nilpotent groups they terminate and provide series through the full group. Secondly, the determination of a minimal generating set is available for nilpotent groups only. G := ExamplesOfSomePcpGroups(14); Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0, 5, 5, 4, 0, 6, 5, 5, 4, 0, 10, 6 ] gap> IsNilpotent(G); true gap> PcpsBySeries( LowerCentralSeriesOfGroup(G)); [ Pcp [ g1, g2 ] with orders [ 0, 0 ], Pcp [ g3 ] with orders [ 0 ], Pcp [ g4 ] with orders [ 0 ], Pcp [ g5 ] with orders [ 0 ], Pcp [ g6, g7 ] with orders [ 0, 0 ], Pcp [ g8 ] with orders [ 0 ], Pcp [ g9, g10 ] with orders [ 0, 0 ], Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ], Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ], Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ] gap> PcpsBySeries( UpperCentralSeriesOfGroup(G)); [ Pcp [ g1, g2 ] with orders [ 0, 0 ], Pcp [ g3 ] with orders [ 0 ], Pcp [ g4 ] with orders [ 0 ], Pcp [ g5 ] with orders [ 0 ], Pcp [ g6, g7 ] with orders [ 0, 0 ], Pcp [ g8 ] with orders [ 0 ], Pcp [ g9, g10 ] with orders [ 0, 0 ], Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ], Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ], Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ] gap> MinimalGeneratingSet(G); [ g1, g2 ] ]]>
Random methods for pcp-groups Below we introduce a function which computes orbit and stabilizer using a random method. This function tries to approximate the orbit and the stabilizer, but the returned orbit or stabilizer may be incomplete. This function is used in the random methods to compute normalizers and centralizers. Note that deterministic methods for these purposes are also available.

G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> mats := [[[-1, 0],[0,1]], [[1,1],[0,1]]]; [ [ [ -1, 0 ], [ 0, 1 ] ], [ [ 1, 1 ], [ 0, 1 ] ] ] gap> pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ] gap> RandomPcpOrbitStabilizer( [1,0], pcp, mats, OnRight ).stab; #I Orbit longer than limit: exiting. [ ] gap> g := Igs(G)[1]; g1 gap> RandomCentralizerPcpGroup( G, g ); #I Stabilizer not increasing: exiting. Pcp-group with orders [ 2 ] gap> Igs(last); [ g1 ] ]]>

Non-abelian tensor product and Schur extensions Let G be a polycyclic group with a polycyclic generating sequence consisting of n elements. This function computes the largest central extension H of G such that H is generated by n elements. If F/R is the underlying polycyclic presentation for G, then H is isomorphic to F/[R,F]. G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> Centre( G ); Pcp-group with orders [ ] gap> H := SchurExtension( G ); Pcp-group with orders [ 2, 0, 0, 0 ] gap> Centre( H ); Pcp-group with orders [ 0, 0 ] gap> H/Centre(H); Pcp-group with orders [ 2, 0 ] gap> Subgroup( H, [H.1,H.2] ) = H; true ]]> returns the projection from the Schur extension G^{*} of G onto G. See the function SchurExtension. The kernel of this epimorphism is the direct product of the Schur multiplicator of G and a direct product of n copies of &ZZ; where n is the number of generators in the polycyclic presentation for G. The Schur multiplicator is the intersection of the kernel and the derived group of the source. See also the function SchurCover. gl23 := Range( IsomorphismPcpGroup( GL(2,3) ) ); Pcp-group with orders [ 2, 3, 2, 2, 2 ] gap> SchurExtensionEpimorphism( gl23 ); [ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 ] -> [ g1, g2, g3, g4, g5, id, id, id, id, id ] gap> Kernel( last ); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> AbelianInvariantsMultiplier( gl23 ); [ ] gap> Intersection( Kernel(epi), DerivedSubgroup( Source(epi) ) ); [ ] ]]> There is a crossed pairing from G into (G^{*})' which can be defined via this epimorphism: G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> epi := SchurExtensionEpimorphism( G ); [ g1, g2, g3, g4 ] -> [ g1, g2, id, id ] gap> PreImagesRepresentative( epi, G.1 ); g1 gap> PreImagesRepresentative( epi, G.2 ); g2 gap> Comm( last, last2 ); g2^-2*g4 ]]> computes a Schur covering group of the polycyclic group G. A Schur covering is a largest central extension H of G such that the kernel M of the projection of H onto G is contained in the commutator subgroup of H.

If G is given by a presentation F/R, then M is isomorphic to the subgroup R \cap [F,F] / [R,F]. Let C be a complement to R \cap [F,F] / [R,F] in R/[R,F]. Then F/C is isomorphic to H and R/C is isomorphic to M. G := AbelianPcpGroup( 3,[] ); Pcp-group with orders [ 0, 0, 0 ] gap> ext := SchurCover( G ); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] gap> Centre( ext ); Pcp-group with orders [ 0, 0, 0 ] gap> IsSubgroup( DerivedSubgroup( ext ), last ); true ]]> returns a list of the abelian invariants of the Schur multiplier of G.

Note that the Schur multiplicator of a polycyclic group is a finitely generated abelian group. G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> DirectProduct( G, AbelianPcpGroup( 2, [] ) ); Pcp-group with orders [ 0, 0, 2, 0 ] gap> AbelianInvariantsMultiplier( last ); [ 0, 2, 2, 2, 2 ] ]]> returns the epimorphism of the non-abelian exterior square of a polycyclic group G onto the derived group of G. The non-abelian exterior square can be defined as the derived subgroup of a Schur cover of G. The isomorphism type of the non-abelian exterior square is unique despite the fact that the isomorphism type of a Schur cover of a polycyclic groups need not be unique. The derived group of a Schur cover has a natural projection onto the derived group of G which is what the function returns.

The kernel of the epimorphism is isomorphic to the Schur multiplicator of G. G := ExamplesOfSomePcpGroups( 3 ); Pcp-group with orders [ 0, 0 ] gap> G := DirectProduct( G,G ); Pcp-group with orders [ 0, 0, 0, 0 ] gap> AbelianInvariantsMultiplier( G ); [ [ 0, 1 ], [ 2, 3 ] ] gap> epi := NonAbelianExteriorSquareEpimorphism( G ); [ g2^-2*g5, g4^-2*g10, g6, g7, g8, g9 ] -> [ g2^-2, g4^-2, id, id, id, id ] gap> Kernel( epi ); Pcp-group with orders [ 0, 2, 2, 2 ] gap> Collected( AbelianInvariants( last ) ); [ [ 0, 1 ], [ 2, 3 ] ] ]]> computes the non-abelian exterior square of a polycylic group G. See the explanation for NonAbelianExteriorSquareEpimorphism. The natural projection of the non-abelian exterior square onto the derived group of G is stored in the component !.epimorphism.

There is a crossed pairing from G into G\wedge G. See the function SchurExtensionEpimorphism for details. The crossed pairing is stored in the component !.crossedPairing. This is the crossed pairing \lambda in . G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> GwG := NonAbelianExteriorSquare( G ); Pcp-group with orders [ 0 ] gap> lambda := GwG!.crossedPairing; function( g, h ) ... end gap> lambda( G.1, G.2 ); g2^2*g4^-1 ]]> returns for a polycyclic group G the projection of the non-abelian tensor square G\otimes G onto the non-abelian exterior square G\wedge G. The range of that epimorphism has the component !.epimorphism set to the projection of the non-abelian exterior square onto the derived group of G. See also the function NonAbelianExteriorSquare.

With the result of this function one can compute the groups in the commutative diagram at the beginning of the paper . The kernel of the returned epimorphism is the group \nabla(G). The kernel of the composition of this epimorphism and the above mention projection onto G' is the group J(G). G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> G := DirectProduct(G,G); Pcp-group with orders [ 2, 0, 2, 0 ] gap> alpha := NonAbelianTensorSquareEpimorphism( G ); [ g9*g25^-1, g10*g26^-1, g11*g27, g12*g28, g13*g29, g14*g30, g15, g16, g17, g18, g19, g20, g21, g22, g23, g24 ] -> [ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11, id, id, id, id, id, id, id, id, id, id ] gap> gamma := Range( alpha )!.epimorphism; [ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11 ] -> [ g2^-2, g4^-2, id, id, id, id ] gap> JG := Kernel( alpha * gamma ); Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ] gap> Image( alpha, JG ); Pcp-group with orders [ 2, 2, 2, 2 ] gap> AbelianInvariantsMultiplier( G ); [ [ 2, 4 ] ] ]]> computes for a polycyclic group G the non-abelian tensor square G\otimes G. G := AlternatingGroup( IsPcGroup, 4 ); gap> PcGroupToPcpGroup( G ); Pcp-group with orders [ 3, 2, 2 ] gap> NonAbelianTensorSquare( last ); Pcp-group with orders [ 2, 2, 2, 3 ] gap> PcpGroupToPcGroup( last ); gap> DirectFactorsOfGroup( last ); [ Group([ f1, f2, f3 ]), Group([ f4 ]) ] gap> List( last, Size ); [ 8, 3 ] gap> IdGroup( last2[1] ); [ 8, 4 ] # the quaternion group of Order 8 gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> ten := NonAbelianTensorSquare( G ); Pcp-group with orders [ 0, 2, 2, 2 ] gap> IsAbelian( ten ); true ]]> returns an embedding from the non-abelian exterior square G\wedge G into an extensions of G\wedge G by G\times G. For the significance of the group see the paper . The range of the epimorphism is the group \tau(G) in that paper. returns an epimorphisms of \nu(G) onto \tau(G). The group \nu(G) is an extension of the non-abelian tensor square G\otimes G of G by G\times G. The group \tau(G) is an extension of the non-abelian exterior square G\wedge G by G\times G. For details see . returns the group \nu(G) in . returns Whitehead's universal quadratic functor of G, see for a description.

Schur covers This section contains a function to determine the Schur covers of a finite p-group up to isomorphism. Let G be a finite p-group defined as a pcp group. This function returns a complete and irredundant set of isomorphism types of Schur covers of G. The algorithm implements a method of Nickel's Phd Thesis.
polycyclic-2.16/doc/chap3.txt0000644000076600000240000004552613706672367015164 0ustar mhornstaff 3 Collectors Let G be a group defined by a pc-presentation as described in the Chapter 'Introduction to polycyclic presentations'. The process for computing the collected form for an arbitrary word in the generators of G is called collection. The basic idea in collection is the following. Given a word in the defining generators, one scans the word for occurrences of adjacent generators (or their inverses) in the wrong order or occurrences of subwords g_i^e_i with i∈ I and e_i not in the range 0... r_i-1. In the first case, the appropriate conjugacy relation is used to move the generator with the smaller index to the left. In the second case, one uses the appropriate power relation to move the exponent of g_i into the required range. These steps are repeated until a collected word is obtained. There exist a number of different strategies for collecting a given word to collected form. The strategies implemented in this package are collection from the left as described by [LGS90] and [Sim94] and combinatorial collection from the left by [VL90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LGS98]. The first step in defining a pc-presented group is setting up a data structure that knows the pc-presentation and has routines that perform the collection algorithm with words in the generators of the presentation. Such a data structure is called a collector. To describe the right hand sides of the relations in a pc-presentation we use generator exponent lists; the word g_i_1^e_1g_i_2^e_2... g_i_k^e_k is represented by the generator exponent list [i_1,e_1,i_2,e_2,...,i_k,e_k]. 3.1 Constructing a Collector A collector for a group given by a pc-presentation starts by setting up an empty data structure for the collector. Then the relative orders, the power relations and the conjugate relations are added into the data structure. The construction is finalised by calling a routine that completes the data structure for the collector. The following functions provide the necessary tools for setting up a collector. 3.1-1 FromTheLeftCollector FromTheLeftCollector( n )  operation returns an empty data structure for a collector with n generators. No generator has a relative order, no right hand sides of power and conjugate relations are defined. Two generators for which no right hand side of a conjugate relation is defined commute. Therefore, the collector returned by this function can be used to define a free abelian group of rank n.  Example  gap> ftl := FromTheLeftCollector( 4 ); <> gap> PcpGroupByCollector( ftl ); Pcp-group with orders [ 0, 0, 0, 0 ] gap> IsAbelian(last); true  If the relative order of a generators has been defined (see SetRelativeOrder (3.1-2)), but the right hand side of the corresponding power relation has not, then the order and the relative order of the generator are the same. 3.1-2 SetRelativeOrder SetRelativeOrder( coll, i, ro )  operation SetRelativeOrderNC( coll, i, ro )  operation set the relative order in collector coll for generator i to ro. The parameter coll is a collector as returned by the function FromTheLeftCollector (3.1-1), i is a generator number and ro is a non-negative integer. The generator number i is an integer in the range 1,...,n where n is the number of generators of the collector. If ro is 0, then the generator with number i has infinite order and no power relation can be specified. As a side effect in this case, a previously defined power relation is deleted. If ro is the relative order of a generator with number i and no power relation is set for that generator, then ro is the order of that generator. The NC version of the function bypasses checks on the range of i.  Example  gap> ftl := FromTheLeftCollector( 4 ); <> gap> for i in [1..4] do SetRelativeOrder( ftl, i, 3 ); od; gap> G := PcpGroupByCollector( ftl ); Pcp-group with orders [ 3, 3, 3, 3 ] gap> IsElementaryAbelian( G ); true  3.1-3 SetPower SetPower( coll, i, rhs )  operation SetPowerNC( coll, i, rhs )  operation set the right hand side of the power relation for generator i in collector coll to (a copy of) rhs. An attempt to set the right hand side for a generator without a relative order results in an error. Right hand sides are by default assumed to be trivial. The parameter coll is a collector, i is a generator number and rhs is a generators exponent list or an element from a free group. The no-check (NC) version of the function bypasses checks on the range of i and stores rhs (instead of a copy) in the collector. 3.1-4 SetConjugate SetConjugate( coll, j, i, rhs )  operation SetConjugateNC( coll, j, i, rhs )  operation set the right hand side of the conjugate relation for the generators j and i with j larger than i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group. Conjugate relations are by default assumed to be trivial. The generator number i can be negative in order to define conjugation by the inverse of a generator. The no-check (NC) version of the function bypasses checks on the range of i and j and stores rhs (instead of a copy) in the collector. 3.1-5 SetCommutator SetCommutator( coll, j, i, rhs )  operation set the right hand side of the conjugate relation for the generators j and i with j larger than i by specifying the commutator of j and i. The parameter coll is a collector, j and i are generator numbers and rhs is a generator exponent list or an element from a free group. The generator number i can be negative in order to define the right hand side of a commutator relation with the second generator being the inverse of a generator. 3.1-6 UpdatePolycyclicCollector UpdatePolycyclicCollector( coll )  operation completes the data structures of a collector. This is usually the last step in setting up a collector. Among the steps performed is the completion of the conjugate relations. For each non-trivial conjugate relation of a generator, the corresponding conjugate relation of the inverse generator is calculated. Note that UpdatePolycyclicCollector is automatically called by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)). 3.1-7 IsConfluent IsConfluent( coll )  property tests if the collector coll is confluent. The function returns true or false accordingly. Compare Chapter 2 for a definition of confluence. Note that confluence is automatically checked by the function PcpGroupByCollector (see PcpGroupByCollector (4.3-1)). The following example defines a collector for a semidirect product of the cyclic group of order 3 with the free abelian group of rank 2. The action of the cyclic group on the free abelian group is given by the matrix \pmatrix{ 0 & 1 \cr -1 & -1}.  This leads to the following polycyclic presentation: \langle g_1,g_2,g_3 | g_1^3, g_2^{g_1}=g_3, g_3^{g_1}=g_2^{-1}g_3^{-1}, g_3^{g_2}=g_3\rangle.   Example  gap> ftl := FromTheLeftCollector( 3 ); <> gap> SetRelativeOrder( ftl, 1, 3 ); gap> SetConjugate( ftl, 2, 1, [3,1] ); gap> SetConjugate( ftl, 3, 1, [2,-1,3,-1] ); gap> UpdatePolycyclicCollector( ftl ); gap> IsConfluent( ftl ); true  The action of the inverse of g_1 on ⟨ g_2,g_2⟩ is given by the matrix \pmatrix{ -1 & -1 \cr 1 & 0}.  The corresponding conjugate relations are automatically computed by UpdatePolycyclicCollector. It is also possible to specify the conjugation by inverse generators. Note that you need to run UpdatePolycyclicCollector after one of the set functions has been used.  Example  gap> SetConjugate( ftl, 2, -1, [2,-1,3,-1] ); gap> SetConjugate( ftl, 3, -1, [2,1] ); gap> IsConfluent( ftl ); Error, Collector is out of date called from CollectWordOrFail( coll, ev1, [ j, 1, i, 1 ] ); called from ( ) called from read-eval-loop Entering break read-eval-print loop ... you can 'quit;' to quit to outer loop, or you can 'return;' to continue brk> gap> UpdatePolycyclicCollector( ftl ); gap> IsConfluent( ftl ); true  3.2 Accessing Parts of a Collector 3.2-1 RelativeOrders RelativeOrders( coll )  attribute returns (a copy of) the list of relative order stored in the collector coll. 3.2-2 GetPower GetPower( coll, i )  operation GetPowerNC( coll, i )  operation returns a copy of the generator exponent list stored for the right hand side of the power relation of the generator i in the collector coll. The no-check (NC) version of the function bypasses checks on the range of i and does not create a copy before returning the right hand side of the power relation. 3.2-3 GetConjugate GetConjugate( coll, j, i )  operation GetConjugateNC( coll, j, i )  operation returns a copy of the right hand side of the conjugate relation stored for the generators j and i in the collector coll as generator exponent list. The generator j must be larger than i. The no-check (NC) version of the function bypasses checks on the range of i and j and does not create a copy before returning the right hand side of the power relation. 3.2-4 NumberOfGenerators NumberOfGenerators( coll )  operation returns the number of generators of the collector coll. 3.2-5 ObjByExponents ObjByExponents( coll, expvec )  operation returns a generator exponent list for the exponent vector expvec. This is the inverse operation to ExponentsByObj. See ExponentsByObj (3.2-6) for an example. 3.2-6 ExponentsByObj ExponentsByObj( coll, genexp )  operation returns an exponent vector for the generator exponent list genexp. This is the inverse operation to ObjByExponents. The function assumes that the generators in genexp are given in the right order and that the exponents are in the right range.  Example  gap> G := UnitriangularPcpGroup( 4, 0 ); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] gap> coll := Collector ( G ); <> gap> ObjByExponents( coll, [6,-5,4,3,-2,1] ); [ 1, 6, 2, -5, 3, 4, 4, 3, 5, -2, 6, 1 ] gap> ExponentsByObj( coll, last ); [ 6, -5, 4, 3, -2, 1 ]  3.3 Special Features In this section we descibe collectors for nilpotent groups which make use of the special structure of the given pc-presentation. 3.3-1 IsWeightedCollector IsWeightedCollector( coll )  property checks if there is a function w from the generators of the collector coll into the positive integers such that w(g) ≥ w(x)+w(y) for all generators x, y and all generators g in (the normal of) [x,y]. If such a function does not exist, false is returned. If such a function exists, it is computed and stored in the collector. In addition, the default collection strategy for this collector is set to combinatorial collection. 3.3-2 AddHallPolynomials AddHallPolynomials( coll )  function is applicable to a collector which passes IsWeightedCollector and computes the Hall multiplication polynomials for the presentation stored in coll. The default strategy for this collector is set to evaluating those polynomial when multiplying two elements. 3.3-3 String String( coll )  attribute converts a collector coll into a string. 3.3-4 FTLCollectorPrintTo FTLCollectorPrintTo( file, name, coll )  function stores a collector coll in the file file such that the file can be read back using the function 'Read' into GAP and would then be stored in the variable name. 3.3-5 FTLCollectorAppendTo FTLCollectorAppendTo( file, name, coll )  function appends a collector coll in the file file such that the file can be read back into GAP and would then be stored in the variable name. 3.3-6 UseLibraryCollector UseLibraryCollector  global variable this property can be set to true for a collector to force a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines. 3.3-7 USE_LIBRARY_COLLECTOR USE_LIBRARY_COLLECTOR  global variable this global variable can be set to true to force all collectors to use a simple from-the-left collection strategy implemented in the GAP language to be used. Its main purpose is to help debug the collection routines. 3.3-8 DEBUG_COMBINATORIAL_COLLECTOR DEBUG_COMBINATORIAL_COLLECTOR  global variable this global variable can be set to true to force the comparison of results from the combinatorial collector with the result of an identical collection performed by a simple from-the-left collector. Its main purpose is to help debug the collection routines. 3.3-9 USE_COMBINATORIAL_COLLECTOR USE_COMBINATORIAL_COLLECTOR  global variable this global variable can be set to false in order to prevent the combinatorial collector to be used. polycyclic-2.16/doc/chapBib.html0000644000076600000240000003106413706672374015631 0ustar mhornstaff GAP (polycyclic) - References
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

References

[BCRS91] Baumslag, G., Cannonito, F. B., Robinson, D. J. S. and Segal, D., The algorithmic theory of polycyclic-by-finite groups, J. Algebra, 142 (1991), 118--149.

[BK00] Beuerle, J. R. and Kappe, L.-C., Infinite metacyclic groups and their non-abelian tensor squares, Proc. Edinburgh Math. Soc. (2), 43 (3) (2000), 651--662.

[dGN02] de Graaf, W. A. and Nickel, W., Constructing faithful representations of finitely-generated torsion-free nilpotent groups, J. Symbolic Comput., 33 (1) (2002), 31--41.

[Eic00] Eick, B., Computing with infinite polycyclic groups, in Groups and Computation III, (DIMACS, 1999), Amer. Math. Soc. DIMACS Series (2000).

[Eic01a] Eick, B., Computations with polycyclic groups (2001), Habilitationsschrift, Kassel.

[Eic01b] Eick, B., On the Fitting subgroup of a polycyclic-by-finite group and its applications, J. Algebra, 242 (2001), 176--187.

[Eic02] Eick, B., Orbit-stabilizer problems and computing normalizers for polycyclic groups, J. Symbolic Comput., 34 (2002), 1--19.

[EN08] Eick, B. and Nickel, W., Computing the Schur multiplicator and the non-abelian tensor square of a polycyclic group, J. Algebra, 320 (2) (2008), 927–-944.

[EO02] Eick, B. and Ostheimer, G., On the orbit stabilizer problem for integral matrix actions of polycyclic groups, Accepted by Math. Comp (2002).

[Hir38a] Hirsch, K. A., On Infinite Soluble Groups (I), Proc. London Math. Soc., 44 (2) (1938), 53-60.

[Hir38b] Hirsch, K. A., On Infinite Soluble Groups (II), Proc. London Math. Soc., 44 (2) (1938), 336-414.

[Hir46] Hirsch, K. A., On Infinite Soluble Groups (III), J. London Math. Soc., 49 (2) (1946), 184-94.

[Hir52] Hirsch, K. A., On Infinite Soluble Groups (IV), J. London Math. Soc., 27 (1952), 81-85.

[Hir54] Hirsch, K. A., On Infinite Soluble Groups (V), J. London Math. Soc., 29 (1954), 250-251.

[LGS90] Leedham-Green, C. R. and Soicher, L. H., Collection from the left and other strategies, J. Symbolic Comput., 9 (5-6) (1990), 665--675.

[LGS98] Leedham-Green, C. R. and Soicher, L. H., Symbolic collection using Deep Thought, LMS J. Comput. Math., 1 (1998), 9--24 (electronic).

[Lo98a] Lo, E. H., Enumerating finite index subgroups of polycyclic groups (1998), Unpublished report.

[Lo98b] Lo, E. H., Finding intersection and normalizer in finitely generated nilpotent groups, J. Symbolic Comput., 25 (1998), 45--59.

[Mer97] Merkwitz, W. W., Symbolische Multiplikation in nilpotenten Gruppen mit Deep Thought, Diplomarbeit, RWTH Aachen (1997).

[Rob82] Robinson, D. J., A Course in the Theory of Groups, Springer-Verlag, Graduate Texts in Math., 80, New York, Heidelberg, Berlin (1982).

[Seg83] Segal, D., Polycyclic Groups, Cambridge University Press, Cambridge (1983).

[Seg90] Segal, D., Decidable properties of polycyclic groups, Proc. London Math. Soc. (3), 61 (1990), 497-528.

[Sim94] Sims, C. C., Computation with finitely presented groups, Cambridge University Press, Encyclopedia of Mathematics and its Applications, 48, Cambridge (1994).

[VL90] Vaughan-Lee, M. R., Collection from the left, J. Symbolic Comput., 9 (5-6) (1990), 725--733.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/preface.xml0000644000076600000240000000531113706672341015530 0ustar mhornstaff Preface A group G is called polycyclic if there exists a subnormal series in G with cyclic factors. Every polycyclic group is soluble and every supersoluble group is polycyclic. The class of polycyclic groups is closed with respect to forming subgroups, factor groups and extensions. Polycyclic groups can also be characterised as those soluble groups in which each subgroup is finitely generated.

K. A. Hirsch has initiated the investigation of polycyclic groups in 1938, see , , , , , and their central position in infinite group theory has been recognised since.

A well-known result of Hirsch asserts that each polycyclic group is finitely presented. In fact, a polycyclic group has a presentation which exhibits its polycyclic structure: a pc-presentation as defined in the Chapter . Pc-presentations allow efficient computations with the groups they define. In particular, the word problem is efficiently solvable in a group given by a pc-presentation. Further, subgroups and factor groups of groups given by a pc-presentation can be handled effectively.

The &GAP; 4 package &Polycyclic; is designed for computations with polycyclic groups which are given by a pc-presentation. The package contains methods to solve the word problem in such groups and to handle subgroups and factor groups of polycyclic groups. Based on these basic algorithms we present a collection of methods to construct polycyclic groups and to investigate their structure.

In and the theory of problems which are decidable in polycyclic-by-finite groups has been started. As a result of these investigation we know that a large number of group theoretic problems are decidable by algorithms in polycyclic groups. However, practical algorithms which are suitable for computer implementations have not been obtained by this study. We have developed a new set of practical methods for groups given by pc-presentations, see for example , and this package is a collection of implementations for these and other methods.

We refer to , page 147ff, and for background on polycyclic groups. Further, in a variation of the basic methods for groups with pc-presentation is introduced. Finally, we note that the main GAP library contains many practical algorithms to compute with finite polycyclic groups. This is described in the Section on polycyclic groups in the reference manual. polycyclic-2.16/doc/chap0.html0000644000076600000240000011156013706672374015274 0ustar mhornstaff GAP (polycyclic) - Contents

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Polycyclic

Computation with polycyclic groups

2.16

25 July 2020

Bettina Eick
Email: beick@tu-bs.de
Homepage: http://www.iaa.tu-bs.de/beick
Address:
Institut Analysis und Algebra
TU Braunschweig
Universitätsplatz 2
D-38106 Braunschweig
Germany

Werner Nickel
Homepage: http://www.mathematik.tu-darmstadt.de/~nickel/

Max Horn
Email: horn@mathematik.uni-kl.de
Homepage: https://www.quendi.de/math
Address:
Fachbereich Mathematik
TU Kaiserslautern
Gottlieb-Daimler-Straße 48
67663 Kaiserslautern
Germany

Copyright

© 2003-2018 by Bettina Eick, Max Horn and Werner Nickel

The Polycyclic package is free software;you can redistribute it and/or modify it under the terms of theGNU General Public Licenseas published by the Free Software Foundation; either version 2 of the License,or (at your option) any later version.

Acknowledgements

We appreciate very much all past and future comments, suggestions andcontributions to this package and its documentation provided by GAPusers and developers.

Contents

5 Basic methods and functions for pcp-groups
7 Higher level methods for pcp-groups

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap1.txt0000644000076600000240000000553013706672367015151 0ustar mhornstaff 1 Preface A group G is called polycyclic if there exists a subnormal series in G with cyclic factors. Every polycyclic group is soluble and every supersoluble group is polycyclic. The class of polycyclic groups is closed with respect to forming subgroups, factor groups and extensions. Polycyclic groups can also be characterised as those soluble groups in which each subgroup is finitely generated. K. A. Hirsch has initiated the investigation of polycyclic groups in 1938, see [Hir38a], [Hir38b], [Hir46], [Hir52], [Hir54], and their central position in infinite group theory has been recognised since. A well-known result of Hirsch asserts that each polycyclic group is finitely presented. In fact, a polycyclic group has a presentation which exhibits its polycyclic structure: a pc-presentation as defined in the Chapter 'Introduction to polycyclic presentations'. Pc-presentations allow efficient computations with the groups they define. In particular, the word problem is efficiently solvable in a group given by a pc-presentation. Further, subgroups and factor groups of groups given by a pc-presentation can be handled effectively. The GAP 4 package Polycyclic is designed for computations with polycyclic groups which are given by a pc-presentation. The package contains methods to solve the word problem in such groups and to handle subgroups and factor groups of polycyclic groups. Based on these basic algorithms we present a collection of methods to construct polycyclic groups and to investigate their structure. In [BCRS91] and [Seg90] the theory of problems which are decidable in polycyclic-by-finite groups has been started. As a result of these investigation we know that a large number of group theoretic problems are decidable by algorithms in polycyclic groups. However, practical algorithms which are suitable for computer implementations have not been obtained by this study. We have developed a new set of practical methods for groups given by pc-presentations, see for example [Eic00], and this package is a collection of implementations for these and other methods. We refer to [Rob82], page 147ff, and [Seg83] for background on polycyclic groups. Further, in [Sim94] a variation of the basic methods for groups with pc-presentation is introduced. Finally, we note that the main GAP library contains many practical algorithms to compute with finite polycyclic groups. This is described in the Section on polycyclic groups in the reference manual. polycyclic-2.16/doc/polycyclicbib.xml0000644000076600000240000002355113706672341016760 0ustar mhornstaff D. J.Robinson A Course in the Theory of Groups Springer-Verlag 1982 80 Graduate Texts in Math.
New York, Heidelberg, Berlin
D.Segal Polycyclic Groups Cambridge University Press 1983
Cambridge
D.Segal Decidable properties of polycyclic groups Proc. London Math. Soc. (3) 1990 61 497-528 MR1069513 20F10 (03D40 20F16)
K. A.Hirsch On Infinite Soluble Groups <C>(I)</C> Proc. London Math. Soc. 1938 44 2 53-60
K. A.Hirsch On Infinite Soluble Groups <C>(II)</C> Proc. London Math. Soc. 1938 44 2 336-414
K. A.Hirsch On Infinite Soluble Groups <C>(III)</C> J. London Math. Soc. 1946 49 2 184-94
K. A.Hirsch On Infinite Soluble Groups <C>(IV)</C> J. London Math. Soc. 1952 27 81-85
K. A.Hirsch On Infinite Soluble Groups <C>(V)</C> J. London Math. Soc. 1954 29 250-251
G.Baumslag F. B.Cannonito D. J. S.Robinson D.Segal The algorithmic theory of polycyclic-by-finite groups J. Algebra 1991 142 118--149
Charles C.Sims Computation with finitely presented groups Cambridge University Press 1994 48 Encyclopedia of Mathematics and its Applications
Cambridge
0-521-43213-8 95f:20053 20F05 (20-02 68Q40 68Q42) Friedrich Otto
C. R.Leedham-Green L. H.Soicher Collection from the left and other strategies J. Symbolic Comput. 1990 9 5-6 665--675 0747-7171 92b:20021 20D10 (68Q25) M. Greendlinger
M. R.Vaughan-Lee Collection from the left J. Symbolic Comput. 1990 9 5-6 725--733 0747-7171 92c:20065 20F12 (20-04 20D15 20F18) M. Greendlinger
James R.Beuerle Luise-CharlotteKappe Infinite metacyclic groups and their non-abelian tensor squares Proc. Edinburgh Math. Soc. (2) 2000 43 3 651--662 0013-0915 2003d:20037 20F05 Graham J. Ellis
Wolfgang W.Merkwitz <C>Symbolische Multiplikation in nilpotenten Gruppen mit Deep Thought</C> RWTH Aachen 1997 Diplomarbeit
C. R.Leedham-Green Leonard H.Soicher Symbolic collection using <C>D</C>eep <C>T</C>hought LMS J. Comput. Math. 1998 1 9--24 (electronic) 1461-1570 99f:20002 20-04 (20F18) Martyn R. Dixon
BettinaEick Computing with infinite polycyclic groups Groups and Computation III 2000 Amer. Math. Soc. DIMACS Series (DIMACS, 1999)
BettinaEick GretchenOstheimer On the orbit stabilizer problem for integral matrix actions of polycyclic groups Accepted by Math. Comp 2002
BettinaEick On the <C>Fitting</C> subgroup of a polycyclic-by-finite group and its applications J. Algebra 2001 242 176--187
BettinaEick Computations with polycyclic groups Habilitationsschrift, Kassel 2001
BettinaEick Orbit-stabilizer problems and computing normalizers for polycyclic groups J. Symbolic Comput. 2002 34 1--19
Eddie H.Lo Enumerating finite index subgroups of polycyclic groups Unpublished report 1998
Eddie H.Lo GretchenOstheimer A practical algorithm for finding matrix representations for polycyclic groups J. Symbolic Comput. 1999 28 339--360
E. H.Lo Finding intersection and normalizer in finitely generated nilpotent groups J. Symbolic Comput. 1998 25 45--59
G.Ostheimer Practical algorithms for polycyclic matrix groups J. Symbolic Comput. 1999 28 361--379
Willem A.de Graaf WernerNickel Constructing faithful representations of finitely-generated torsion-free nilpotent groups J. Symbolic Comput. 2002 33 1 31--41 0747-7171 MR1876310 20C15 (20F18)
BettinaEick WernerNickel Computing the Schur multiplicator and the non-abelian tensor square of a polycyclic group J. Algebra 2008 320 2 927–-944 MR2422322 20J05 (20-04 20E22 20F05)
polycyclic-2.16/doc/chap0.txt0000644000076600000240000002312413706672367015147 0ustar mhornstaff  Polycyclic   Computation with polycyclic groups  2.16 25 July 2020 Bettina Eick Werner Nickel Max Horn Bettina Eick Email: mailto:beick@tu-bs.de Homepage: http://www.iaa.tu-bs.de/beick Address: Institut Analysis und Algebra TU Braunschweig Universitätsplatz 2 D-38106 Braunschweig Germany Werner Nickel Homepage: http://www.mathematik.tu-darmstadt.de/~nickel/ Max Horn Email: mailto:horn@mathematik.uni-kl.de Homepage: https://www.quendi.de/math Address: Fachbereich Mathematik TU Kaiserslautern Gottlieb-Daimler-Straße 48 67663 Kaiserslautern Germany ------------------------------------------------------- Copyright © 2003-2018 by Bettina Eick, Max Horn and Werner Nickel The Polycyclic package is free software;you can redistribute it and/or modify it under the terms of theGNU General Public License (http://www.fsf.org/licenses/gpl.html)as published by the Free Software Foundation; either version 2 of the License,or (at your option) any later version. ------------------------------------------------------- Acknowledgements We appreciate very much all past and future comments, suggestions andcontributions to this package and its documentation provided by GAPusers and developers. ------------------------------------------------------- Contents (polycyclic) 1 Preface 2 Introduction to polycyclic presentations 3 Collectors 3.1 Constructing a Collector 3.1-1 FromTheLeftCollector 3.1-2 SetRelativeOrder 3.1-3 SetPower 3.1-4 SetConjugate 3.1-5 SetCommutator 3.1-6 UpdatePolycyclicCollector 3.1-7 IsConfluent 3.2 Accessing Parts of a Collector 3.2-1 RelativeOrders 3.2-2 GetPower 3.2-3 GetConjugate 3.2-4 NumberOfGenerators 3.2-5 ObjByExponents 3.2-6 ExponentsByObj 3.3 Special Features 3.3-1 IsWeightedCollector 3.3-2 AddHallPolynomials 3.3-3 String 3.3-4 FTLCollectorPrintTo 3.3-5 FTLCollectorAppendTo 3.3-6 UseLibraryCollector 3.3-7 USE_LIBRARY_COLLECTOR 3.3-8 DEBUG_COMBINATORIAL_COLLECTOR 3.3-9 USE_COMBINATORIAL_COLLECTOR 4 Pcp-groups - polycyclically presented groups 4.1 Pcp-elements -- elements of a pc-presented group 4.1-1 PcpElementByExponentsNC 4.1-2 PcpElementByGenExpListNC 4.1-3 IsPcpElement 4.1-4 IsPcpElementCollection 4.1-5 IsPcpElementRep 4.1-6 IsPcpGroup 4.2 Methods for pcp-elements 4.2-1 Collector 4.2-2 Exponents 4.2-3 GenExpList 4.2-4 NameTag 4.2-5 Depth 4.2-6 LeadingExponent 4.2-7 RelativeOrder 4.2-8 RelativeIndex 4.2-9 FactorOrder 4.2-10 NormingExponent 4.2-11 NormedPcpElement 4.3 Pcp-groups - groups of pcp-elements 4.3-1 PcpGroupByCollector 4.3-2 Group 4.3-3 Subgroup 5 Basic methods and functions for pcp-groups 5.1 Elementary methods for pcp-groups 5.1-1 \= 5.1-2 Size 5.1-3 Random 5.1-4 Index 5.1-5 \in 5.1-6 Elements 5.1-7 ClosureGroup 5.1-8 NormalClosure 5.1-9 HirschLength 5.1-10 CommutatorSubgroup 5.1-11 PRump 5.1-12 SmallGeneratingSet 5.2 Elementary properties of pcp-groups 5.2-1 IsSubgroup 5.2-2 IsNormal 5.2-3 IsNilpotentGroup 5.2-4 IsAbelian 5.2-5 IsElementaryAbelian 5.2-6 IsFreeAbelian 5.3 Subgroups of pcp-groups 5.3-1 Igs 5.3-2 Ngs 5.3-3 Cgs 5.3-4 SubgroupByIgs 5.3-5 AddToIgs 5.4 Polycyclic presentation sequences for subfactors 5.4-1 Pcp 5.4-2 GeneratorsOfPcp 5.4-3 \[\] 5.4-4 Length 5.4-5 RelativeOrdersOfPcp 5.4-6 DenominatorOfPcp 5.4-7 NumeratorOfPcp 5.4-8 GroupOfPcp 5.4-9 OneOfPcp 5.4-10 ExponentsByPcp 5.4-11 PcpGroupByPcp 5.5 Factor groups of pcp-groups 5.5-1 NaturalHomomorphismByNormalSubgroup 5.5-2 \/ 5.6 Homomorphisms for pcp-groups 5.6-1 GroupHomomorphismByImages 5.6-2 Kernel 5.6-3 Image 5.6-4 PreImage 5.6-5 PreImagesRepresentative 5.6-6 IsInjective 5.7 Changing the defining pc-presentation 5.7-1 RefinedPcpGroup 5.7-2 PcpGroupBySeries 5.8 Printing a pc-presentation 5.8-1 PrintPcpPresentation 5.9 Converting to and from a presentation 5.9-1 IsomorphismPcpGroup 5.9-2 IsomorphismPcpGroupFromFpGroupWithPcPres 5.9-3 IsomorphismPcGroup 5.9-4 IsomorphismFpGroup 6 Libraries and examples of pcp-groups 6.1 Libraries of various types of polycyclic groups 6.1-1 AbelianPcpGroup 6.1-2 DihedralPcpGroup 6.1-3 UnitriangularPcpGroup 6.1-4 SubgroupUnitriangularPcpGroup 6.1-5 InfiniteMetacyclicPcpGroup 6.1-6 HeisenbergPcpGroup 6.1-7 MaximalOrderByUnitsPcpGroup 6.1-8 BurdeGrunewaldPcpGroup 6.2 Some assorted example groups 6.2-1 ExampleOfMetabelianPcpGroup 6.2-2 ExamplesOfSomePcpGroups 7 Higher level methods for pcp-groups 7.1 Subgroup series in pcp-groups 7.1-1 PcpSeries 7.1-2 EfaSeries 7.1-3 SemiSimpleEfaSeries 7.1-4 DerivedSeriesOfGroup 7.1-5 RefinedDerivedSeries 7.1-6 RefinedDerivedSeriesDown 7.1-7 LowerCentralSeriesOfGroup 7.1-8 UpperCentralSeriesOfGroup 7.1-9 TorsionByPolyEFSeries 7.1-10 PcpsBySeries 7.1-11 PcpsOfEfaSeries 7.2 Orbit stabilizer methods for pcp-groups 7.2-1 PcpOrbitStabilizer 7.2-2 StabilizerIntegralAction 7.2-3 NormalizerIntegralAction 7.3 Centralizers, Normalizers and Intersections 7.3-1 Centralizer 7.3-2 Centralizer 7.3-3 Intersection 7.4 Finite subgroups 7.4-1 TorsionSubgroup 7.4-2 NormalTorsionSubgroup 7.4-3 IsTorsionFree 7.4-4 FiniteSubgroupClasses 7.4-5 FiniteSubgroupClassesBySeries 7.5 Subgroups of finite index and maximal subgroups 7.5-1 MaximalSubgroupClassesByIndex 7.5-2 LowIndexSubgroupClasses 7.5-3 LowIndexNormalSubgroups 7.5-4 NilpotentByAbelianNormalSubgroup 7.6 Further attributes for pcp-groups based on the Fitting subgroup 7.6-1 FittingSubgroup 7.6-2 IsNilpotentByFinite 7.6-3 Centre 7.6-4 FCCentre 7.6-5 PolyZNormalSubgroup 7.6-6 NilpotentByAbelianByFiniteSeries 7.7 Functions for nilpotent groups 7.7-1 MinimalGeneratingSet 7.8 Random methods for pcp-groups 7.8-1 RandomCentralizerPcpGroup 7.9 Non-abelian tensor product and Schur extensions 7.9-1 SchurExtension 7.9-2 SchurExtensionEpimorphism 7.9-3 SchurCover 7.9-4 AbelianInvariantsMultiplier 7.9-5 NonAbelianExteriorSquareEpimorphism 7.9-6 NonAbelianExteriorSquare 7.9-7 NonAbelianTensorSquareEpimorphism 7.9-8 NonAbelianTensorSquare 7.9-9 NonAbelianExteriorSquarePlusEmbedding 7.9-10 NonAbelianTensorSquarePlusEpimorphism 7.9-11 NonAbelianTensorSquarePlus 7.9-12 WhiteheadQuadraticFunctor 7.10 Schur covers 7.10-1 SchurCovers 8 Cohomology for pcp-groups 8.1 Cohomology records 8.1-1 CRRecordByMats 8.1-2 CRRecordBySubgroup 8.2 Cohomology groups 8.2-1 OneCoboundariesCR 8.2-2 TwoCohomologyModCR 8.3 Extended 1-cohomology 8.3-1 OneCoboundariesEX 8.3-2 OneCocyclesEX 8.3-3 OneCohomologyEX 8.4 Extensions and Complements 8.4-1 ComplementCR 8.4-2 ComplementsCR 8.4-3 ComplementClassesCR 8.4-4 ComplementClassesEfaPcps 8.4-5 ComplementClasses 8.4-6 ExtensionCR 8.4-7 ExtensionsCR 8.4-8 ExtensionClassesCR 8.4-9 SplitExtensionPcpGroup 8.5 Constructing pcp groups as extensions 9 Matrix Representations 9.1 Unitriangular matrix groups 9.1-1 UnitriangularMatrixRepresentation 9.1-2 IsMatrixRepresentation 9.2 Upper unitriangular matrix groups 9.2-1 IsomorphismUpperUnitriMatGroupPcpGroup 9.2-2 SiftUpperUnitriMatGroup 9.2-3 RanksLevels 9.2-4 MakeNewLevel 9.2-5 SiftUpperUnitriMat 9.2-6 DecomposeUpperUnitriMat A Obsolete Functions and Name Changes  polycyclic-2.16/doc/chap4_mj.html0000644000076600000240000005343113706672374015770 0ustar mhornstaff GAP (polycyclic) - Chapter 4: Pcp-groups - polycyclically presented groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

4 Pcp-groups - polycyclically presented groups

4.1 Pcp-elements -- elements of a pc-presented group

A pcp-element is an element of a group defined by a consistent pc-presentation given by a collector. Suppose that \(g_1, \ldots, g_n\) are the defining generators of the collector. Recall that each element \(g\) in this group can be written uniquely as a collected word \(g_1^{e_1} \cdots g_n^{e_n}\) with \(e_i \in ℤ\) and \(0 \leq e_i < r_i\) for \(i \in I\). The integer vector \([e_1, \ldots, e_n]\) is called the exponent vector of \(g\). The following functions can be used to define pcp-elements via their exponent vector or via an arbitrary generator exponent word as introduced in Chapter 3.

4.1-1 PcpElementByExponentsNC
‣ PcpElementByExponentsNC( coll, exp )( function )
‣ PcpElementByExponents( coll, exp )( function )

returns the pcp-element with exponent vector exp. The exponent vector is considered relative to the defining generators of the pc-presentation.

4.1-2 PcpElementByGenExpListNC
‣ PcpElementByGenExpListNC( coll, word )( function )
‣ PcpElementByGenExpList( coll, word )( function )

returns the pcp-element with generators exponent list word. This list word consists of a sequence of generator numbers and their corresponding exponents and is of the form \([i_1, e_{i_1}, i_2, e_{i_2}, \ldots, i_r, e_{i_r}]\). The generators exponent list is considered relative to the defining generators of the pc-presentation.

These functions return pcp-elements in the category IsPcpElement. Presently, the only representation implemented for this category is IsPcpElementRep. (This allows us to be a little sloppy right now. The basic set of operations for IsPcpElement has not been defined yet. This is going to happen in one of the next version, certainly as soon as the need for different representations arises.)

4.1-3 IsPcpElement
‣ IsPcpElement( obj )( category )

returns true if the object obj is a pcp-element.

4.1-4 IsPcpElementCollection
‣ IsPcpElementCollection( obj )( category )

returns true if the object obj is a collection of pcp-elements.

4.1-5 IsPcpElementRep
‣ IsPcpElementRep( obj )( representation )

returns true if the object obj is represented as a pcp-element.

4.1-6 IsPcpGroup
‣ IsPcpGroup( obj )( filter )

returns true if the object obj is a group and also a pcp-element collection.

4.2 Methods for pcp-elements

Now we can describe attributes and functions for pcp-elements. The four basic attributes of a pcp-element, Collector, Exponents, GenExpList and NameTag are computed at the creation of the pcp-element. All other attributes are determined at runtime.

Let g be a pcp-element and \(g_1, \ldots, g_n\) a polycyclic generating sequence of the underlying pc-presented group. Let \(C_1, \ldots, C_n\) be the polycyclic series defined by \(g_1, \ldots, g_n\).

The depth of a non-trivial element \(g\) of a pcp-group (with respect to the defining generators) is the integer \(i\) such that \(g \in C_i \setminus C_{i+1}\). The depth of the trivial element is defined to be \(n+1\). If \(g\not=1\) has depth \(i\) and \(g_i^{e_i} \cdots g_n^{e_n}\) is the collected word for \(g\), then \(e_i\) is the leading exponent of \(g\).

If \(g\) has depth \(i\), then we call \(r_i = [C_i:C_{i+1}]\) the factor order of \(g\). If \(r < \infty\), then the smallest positive integer \(l\) with \(g^l \in C_{i+1}\) is the called relative order of \(g\). If \(r=\infty\), then the relative order of \(g\) is defined to be \(0\). The index \(e\) of \(\langle g,C_{i+1}\rangle\) in \(C_i\) is called relative index of \(g\). We have that \(r = el\).

We call a pcp-element normed, if its leading exponent is equal to its relative index. For each pcp-element \(g\) there exists an integer \(e\) such that \(g^e\) is normed.

4.2-1 Collector
‣ Collector( g )( operation )

the collector to which the pcp-element g belongs.

4.2-2 Exponents
‣ Exponents( g )( operation )

returns the exponent vector of the pcp-element g with respect to the defining generating set of the underlying collector.

4.2-3 GenExpList
‣ GenExpList( g )( operation )

returns the generators exponent list of the pcp-element g with respect to the defining generating set of the underlying collector.

4.2-4 NameTag
‣ NameTag( g )( operation )

the name used for printing the pcp-element g. Printing is done by using the name tag and appending the generator number of g.

4.2-5 Depth
‣ Depth( g )( operation )

returns the depth of the pcp-element g relative to the defining generators.

4.2-6 LeadingExponent
‣ LeadingExponent( g )( operation )

returns the leading exponent of pcp-element g relative to the defining generators. If g is the identity element, the functions returns 'fail'

4.2-7 RelativeOrder
‣ RelativeOrder( g )( attribute )

returns the relative order of the pcp-element g with respect to the defining generators.

4.2-8 RelativeIndex
‣ RelativeIndex( g )( attribute )

returns the relative index of the pcp-element g with respect to the defining generators.

4.2-9 FactorOrder
‣ FactorOrder( g )( attribute )

returns the factor order of the pcp-element g with respect to the defining generators.

4.2-10 NormingExponent
‣ NormingExponent( g )( function )

returns a positive integer \(e\) such that the pcp-element g raised to the power of \(e\) is normed.

4.2-11 NormedPcpElement
‣ NormedPcpElement( g )( function )

returns the normed element corresponding to the pcp-element g.

4.3 Pcp-groups - groups of pcp-elements

A pcp-group is a group consisting of pcp-elements such that all pcp-elements in the group share the same collector. Thus the group \(G\) defined by a polycyclic presentation and all its subgroups are pcp-groups.

4.3-1 PcpGroupByCollector
‣ PcpGroupByCollector( coll )( function )
‣ PcpGroupByCollectorNC( coll )( function )

returns a pcp-group build from the collector coll.

The function calls UpdatePolycyclicCollector (3.1-6) and checks the confluence (see IsConfluent (3.1-7)) of the collector.

The non-check version bypasses these checks.

4.3-2 Group
‣ Group( gens, id )( function )

returns the group generated by the pcp-elements gens with identity id.

4.3-3 Subgroup
‣ Subgroup( G, gens )( function )

returns a subgroup of the pcp-group G generated by the list gens of pcp-elements from G.

gap>  ftl := FromTheLeftCollector( 2 );;
gap>  SetRelativeOrder( ftl, 1, 2 );
gap>  SetConjugate( ftl, 2, 1, [2,-1] );
gap>  UpdatePolycyclicCollector( ftl );
gap>  G:= PcpGroupByCollectorNC( ftl );
Pcp-group with orders [ 2, 0 ]
gap> Subgroup( G, GeneratorsOfGroup(G){[2]} );
Pcp-group with orders [ 0 ]
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/manual.css0000644000076600000240000001575413706672374015412 0ustar mhornstaff/* manual.css Frank Lübeck */ /* This is the default CSS style sheet for GAPDoc HTML manuals. */ /* basic settings, fonts, sizes, colors, ... */ body { position: relative; background: #ffffff; color: #000000; width: 70%; margin: 0pt; padding: 15pt; font-family: Helvetica,Verdana,Arial,sans-serif; text-align: justify; } /* no side toc on title page, bib and index */ body.chap0 { width: 95%; } body.chapBib { width: 95%; } body.chapInd { width: 95%; } h1 { font-size: 200%; } h2 { font-size: 160%; } h3 { font-size: 160%; } h4 { font-size: 130%; } h5 { font-size: 100%; } p.foot { font-size: 60%; font-style: normal; } a:link { color: #00008e; text-decoration: none; } a:visited { color: #00008e; text-decoration: none; } a:active { color: #000000; text-decoration: none; } a:hover { background: #eeeeee; } pre { font-family: "Courier New",Courier,monospace; font-size: 100%; color:#111111; } tt,code { font-family: "Courier New",Courier,monospace; font-size: 110%; color: #000000; } var { } /* general alignment classes */ .pcenter { text-align: center; } .pleft { text-align: left; } .pright { text-align: right; } /* layout for the definitions of functions, variables, ... */ div.func { background: #e0e0e0; margin: 0pt 0pt; } /* general and special table settings */ table { border-collapse: collapse; margin-left: auto; margin-right: auto; } td, th { border-style: none; } table.func { padding: 0pt 1ex; margin-left: 1ex; margin-right: 1ex; background: transparent; /* line-height: 1.1; */ width: 100%; } table.func td.tdright { padding-right: 2ex; } /* Example elements (for old converted manuals, now in div+pre */ table.example { background: #efefef; border-style: none; border-width: 0pt; padding: 0px; width: 100% } table.example td { border-style: none; border-width: 0pt; padding: 0ex 1ex; } /* becomes ... */ div.example { background: #efefef; padding: 0ex 1ex; /* overflow-x: auto; */ overflow: auto; } /* Links to chapters in all files at top and bottom. */ /* If there are too many chapters then use 'display: none' here. */ div.chlinktop { background: #dddddd; border-style: solid; border-width: thin; margin: 2px; text-align: center; } div.chlinktop a { margin: 3px; } div.chlinktop a:hover { background: #ffffff; } div.chlinkbot { background: #dddddd; border-style: solid; border-width: thin; margin: 2px; text-align: center; /* width: 100%; */ } div.chlinkbot a { margin: 3px; } span.chlink1 { } /* and this is for the "Top", "Prev", "Next" links */ div.chlinkprevnexttop { background: #dddddd; border-style: solid; border-width: thin; text-align: center; margin: 2px; } div.chlinkprevnexttop a:hover { background: #ffffff; } div.chlinkprevnextbot { background: #dddddd; border-style: solid; border-width: thin; text-align: center; margin: 2px; } div.chlinkprevnextbot a:hover { background: #ffffff; } /* table of contents, initially don't display subsections */ div.ContSSBlock { display: none; } div.ContSSBlock br { display: none; } /* format in separate lines */ span.tocline { display: block; width: 100%; } div.ContSSBlock a { display: block; } /* this is for the main table of contents */ div.ContChap { } div.ContChap div.ContSect:hover div.ContSSBlock { display: block; position: absolute; background: #eeeeee; border-style: solid; border-width: 1px 4px 4px 1px; border-color: #666666; padding-left: 0.5ex; color: #000000; left: 20%; width: 40%; z-index: 10000; } div.ContSSBlock a:hover { background: #ffffff; } /* and here for the side menu of contents in the chapter files */ div.ChapSects { } div.ChapSects a:hover { background: #eeeeee; } div.ChapSects a:hover { display: block; width: 100%; background: #eeeeee; color: #000000; } div.ChapSects div.ContSect:hover div.ContSSBlock { display: block; position: fixed; background: #eeeeee; border-style: solid; border-width: 1px 2px 2px 1px; border-color: #666666; padding-left: 0ex; padding-right: 0.5ex; color: #000000; left: 54%; width: 25%; z-index: 10000; } div.ChapSects div.ContSect:hover div.ContSSBlock a { display: block; margin-left: 3px; } div.ChapSects div.ContSect:hover div.ContSSBlock a:hover { display: block; background: #ffffff; } div.ContSect { text-align: left; margin-left: 1em; } div.ChapSects { position: fixed; left: 75%; font-size: 90%; overflow: auto; top: 10px; bottom: 0px; } /* Table elements */ table.GAPDocTable { border-collapse: collapse; border-style: none; border-color: black; } table.GAPDocTable td, table.GAPDocTable th { padding: 3pt; border-width: thin; border-style: solid; border-color: #555555; } caption.GAPDocTable { caption-side: bottom; width: 70%; margin-top: 1em; margin-left: auto; margin-right: auto; } td.tdleft { text-align: left; } table.GAPDocTablenoborder { border-collapse: collapse; border-style: none; border-color: black; } table.GAPDocTablenoborder td, table.GAPDocTable th { padding: 3pt; border-width: 0pt; border-style: solid; border-color: #555555; } caption.GAPDocTablenoborder { caption-side: bottom; width: 70%; margin-top: 1em; margin-left: auto; margin-right: auto; } td.tdleft { text-align: left; } td.tdright { text-align: right; } td.tdcenter { text-align: center; } /* Colors and fonts can be overwritten for some types of elements. */ /* Verb elements */ pre.normal { color: #000000; } /* Func-like elements and Ref to Func-like */ code.func { color: #000000; } /* K elements */ code.keyw { color: #770000; } /* F elements */ code.file { color: #8e4510; } /* C elements */ code.code { } /* Item elements */ code.i { } /* Button elements */ strong.button { } /* Headings */ span.Heading { } /* Arg elements */ var.Arg { color: #006600; } /* Example elements, is in tables, see above */ div.Example { } /* Package elements */ strong.pkg { } /* URL-like elements */ span.URL { } /* Mark elements */ strong.Mark { } /* Ref elements */ b.Ref { } span.Ref { } /* this contains the contents page */ div.contents { } /* this contains the index page */ div.index { } /* ignore some text for non-css layout */ span.nocss { display: none; } /* colors for ColorPrompt like examples */ span.GAPprompt { color: #000097; font-weight: normal; } span.GAPbrkprompt { color: #970000; font-weight: normal; } span.GAPinput { color: #970000; } /* Bib entries */ p.BibEntry { } span.BibKey { color: #005522; } span.BibKeyLink { } b.BibAuthor { } i.BibTitle { } i.BibBookTitle { } span.BibEditor { } span.BibJournal { } span.BibType { } span.BibPublisher { } span.BibSchool { } span.BibEdition { } span.BibVolume { } span.BibSeries { } span.BibNumber { } span.BibPages { } span.BibOrganization { } span.BibAddress { } span.BibYear { } span.BibPublisher { } span.BibNote { } span.BibHowpublished { } polycyclic-2.16/doc/chap4.txt0000644000076600000240000002357013706672367015160 0ustar mhornstaff 4 Pcp-groups - polycyclically presented groups 4.1 Pcp-elements -- elements of a pc-presented group A pcp-element is an element of a group defined by a consistent pc-presentation given by a collector. Suppose that g_1, ..., g_n are the defining generators of the collector. Recall that each element g in this group can be written uniquely as a collected word g_1^e_1 ⋯ g_n^e_n with e_i ∈ ℤ and 0 ≤ e_i < r_i for i ∈ I. The integer vector [e_1, ..., e_n] is called the exponent vector of g. The following functions can be used to define pcp-elements via their exponent vector or via an arbitrary generator exponent word as introduced in Chapter 3. 4.1-1 PcpElementByExponentsNC PcpElementByExponentsNC( coll, exp )  function PcpElementByExponents( coll, exp )  function returns the pcp-element with exponent vector exp. The exponent vector is considered relative to the defining generators of the pc-presentation. 4.1-2 PcpElementByGenExpListNC PcpElementByGenExpListNC( coll, word )  function PcpElementByGenExpList( coll, word )  function returns the pcp-element with generators exponent list word. This list word consists of a sequence of generator numbers and their corresponding exponents and is of the form [i_1, e_i_1, i_2, e_i_2, ..., i_r, e_i_r]. The generators exponent list is considered relative to the defining generators of the pc-presentation. These functions return pcp-elements in the category IsPcpElement. Presently, the only representation implemented for this category is IsPcpElementRep. (This allows us to be a little sloppy right now. The basic set of operations for IsPcpElement has not been defined yet. This is going to happen in one of the next version, certainly as soon as the need for different representations arises.) 4.1-3 IsPcpElement IsPcpElement( obj )  Category returns true if the object obj is a pcp-element. 4.1-4 IsPcpElementCollection IsPcpElementCollection( obj )  Category returns true if the object obj is a collection of pcp-elements. 4.1-5 IsPcpElementRep IsPcpElementRep( obj )  Representation returns true if the object obj is represented as a pcp-element. 4.1-6 IsPcpGroup IsPcpGroup( obj )  Filter returns true if the object obj is a group and also a pcp-element collection. 4.2 Methods for pcp-elements Now we can describe attributes and functions for pcp-elements. The four basic attributes of a pcp-element, Collector, Exponents, GenExpList and NameTag are computed at the creation of the pcp-element. All other attributes are determined at runtime. Let g be a pcp-element and g_1, ..., g_n a polycyclic generating sequence of the underlying pc-presented group. Let C_1, ..., C_n be the polycyclic series defined by g_1, ..., g_n. The depth of a non-trivial element g of a pcp-group (with respect to the defining generators) is the integer i such that g ∈ C_i ∖ C_i+1. The depth of the trivial element is defined to be n+1. If gnot=1 has depth i and g_i^e_i ⋯ g_n^e_n is the collected word for g, then e_i is the leading exponent of g. If g has depth i, then we call r_i = [C_i:C_i+1] the factor order of g. If r < ∞, then the smallest positive integer l with g^l ∈ C_i+1 is the called relative order of g. If r=∞, then the relative order of g is defined to be 0. The index e of ⟨ g,C_i+1⟩ in C_i is called relative index of g. We have that r = el. We call a pcp-element normed, if its leading exponent is equal to its relative index. For each pcp-element g there exists an integer e such that g^e is normed. 4.2-1 Collector Collector( g )  operation the collector to which the pcp-element g belongs. 4.2-2 Exponents Exponents( g )  operation returns the exponent vector of the pcp-element g with respect to the defining generating set of the underlying collector. 4.2-3 GenExpList GenExpList( g )  operation returns the generators exponent list of the pcp-element g with respect to the defining generating set of the underlying collector. 4.2-4 NameTag NameTag( g )  operation the name used for printing the pcp-element g. Printing is done by using the name tag and appending the generator number of g. 4.2-5 Depth Depth( g )  operation returns the depth of the pcp-element g relative to the defining generators. 4.2-6 LeadingExponent LeadingExponent( g )  operation returns the leading exponent of pcp-element g relative to the defining generators. If g is the identity element, the functions returns 'fail' 4.2-7 RelativeOrder RelativeOrder( g )  attribute returns the relative order of the pcp-element g with respect to the defining generators. 4.2-8 RelativeIndex RelativeIndex( g )  attribute returns the relative index of the pcp-element g with respect to the defining generators. 4.2-9 FactorOrder FactorOrder( g )  attribute returns the factor order of the pcp-element g with respect to the defining generators. 4.2-10 NormingExponent NormingExponent( g )  function returns a positive integer e such that the pcp-element g raised to the power of e is normed. 4.2-11 NormedPcpElement NormedPcpElement( g )  function returns the normed element corresponding to the pcp-element g. 4.3 Pcp-groups - groups of pcp-elements A pcp-group is a group consisting of pcp-elements such that all pcp-elements in the group share the same collector. Thus the group G defined by a polycyclic presentation and all its subgroups are pcp-groups. 4.3-1 PcpGroupByCollector PcpGroupByCollector( coll )  function PcpGroupByCollectorNC( coll )  function returns a pcp-group build from the collector coll. The function calls UpdatePolycyclicCollector (3.1-6) and checks the confluence (see IsConfluent (3.1-7)) of the collector. The non-check version bypasses these checks. 4.3-2 Group Group( gens, id )  function returns the group generated by the pcp-elements gens with identity id. 4.3-3 Subgroup Subgroup( G, gens )  function returns a subgroup of the pcp-group G generated by the list gens of pcp-elements from G.  Example  gap>  ftl := FromTheLeftCollector( 2 );; gap>  SetRelativeOrder( ftl, 1, 2 ); gap>  SetConjugate( ftl, 2, 1, [2,-1] ); gap>  UpdatePolycyclicCollector( ftl ); gap>  G:= PcpGroupByCollectorNC( ftl ); Pcp-group with orders [ 2, 0 ] gap> Subgroup( G, GeneratorsOfGroup(G){[2]} ); Pcp-group with orders [ 0 ]  polycyclic-2.16/doc/basics.xml0000644000076600000240000004673713706672341015410 0ustar mhornstaff Basic methods and functions for pcp-groups Pcp-groups are groups in the &GAP; sense and hence all generic &GAP; methods for groups can be applied for pcp-groups. However, for a number of group theoretic questions &GAP; does not provide generic methods that can be applied to pcp-groups. For some of these questions there are functions provided in &Polycyclic;.
Elementary methods for pcp-groups In this chapter we describe some important basic functions which are available for pcp-groups. A number of higher level functions are outlined in later sections and chapters.

Let U, V and N be subgroups of a pcp-group. decides if U and V are equal as sets. returns the size of U. returns a random element of U. returns the index of V in U if V is a subgroup of U. The function does not check if V is a subgroup of U and if it is not, the result is not meaningful. checks if g is an element of U. returns a list containing all elements of U if U is finite and it returns the list [fail] otherwise. returns the group generated by U and V. returns the normal closure of V under action of U. returns the Hirsch length of U. returns the group generated by all commutators [u,v] with u in U and v in V. returns the subgroup U'U^p of U where p is a prime number. returns a small generating set for U.

Elementary properties of pcp-groups tests if V is a subgroup of U. tests if V is normal in U. checks whether U is nilpotent. checks whether U is abelian. checks whether U is elementary abelian. checks whether U is free abelian.
Subgroups of pcp-groups A subgroup of a pcp-group G can be defined by a set of generators as described in Section . However, many computations with a subgroup U need an induced generating sequence or igs of U. An igs is a sequence of generators of U whose list of exponent vectors form a matrix in upper triangular form. Note that there may exist many igs of U. The first one calculated for U is stored as an attribute.

An induced generating sequence of a subgroup of a pcp-group G is a list of elements of G. An igs is called normed, if each element in the list is normed. Moreover, it is canonical, if the exponent vector matrix is in Hermite Normal Form. The following functions can be used to compute induced generating sequence for a given subgroup U of G. returns an induced generating sequence of the subgroup U of a pcp-group. In the second form the subgroup is given via a generating set gens. The third form computes an igs for the subgroup generated by gens carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list. returns a normed induced generating sequence of the subgroup U of a pcp-group. The second form takes an igs as input and norms it. returns a canonical generating sequence of the subgroup U of a pcp-group. In the second form the function takes an igs as input and returns a canonical generating sequence. The third version takes a generating set and computes a canonical generating sequence carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list.

For a large number of methods for pcp-groups U we will first of all determine an igs for U. Hence it might speed up computations, if a known igs for a group U is set a priori. The following functions can be used for this purpose. returns the subgroup of the pcp-group G generated by the elements of the induced generating sequence igs. Note that igs must be an induced generating sequence of the subgroup generated by the elements of the igs. In the second form igs is a igs for a subgroup and gens are some generators. The function returns the subgroup generated by igs and gens. sifts the elements in the list gens into igs. The second version has the same functionality and carries shadows. This means that each operation that is applied to the first list and the element gens is also applied to the second list and the element gens2. The third version is available for efficiency reasons and assumes that the second list igs2 is not only a generating set, but an igs.

Polycyclic presentation sequences for subfactors A subfactor of a pcp-group G is again a polycyclic group for which a polycyclic presentation can be computed. However, to compute a polycyclic presentation for a given subfactor can be time-consuming. Hence we introduce polycyclic presentation sequences or Pcp to compute more efficiently with subfactors. (Note that a subgroup is also a subfactor and thus can be handled by a pcp)

A pcp for a pcp-group U or a subfactor U / N can be created with one of the following functions. returns a polycyclic presentation sequence for the subgroup U or the quotient group U modulo N. If the parameter flag is present and equals the string snf, the function can only be applied to an abelian subgroup U or abelian subfactor U/N. The pcp returned will correspond to a decomposition of the abelian group into a direct product of cyclic groups. A pcp is a component object which behaves similar to a list representing an igs of the subfactor in question. The basic functions to obtain the stored values of this component object are as follows. Let pcp be a pcp for a subfactor U/N of the defining pcp-group G. this returns a list of elements of U corresponding to an igs of U/N. returns the i-th element of pcp. returns the number of generators in pcp. the relative orders of the igs in U/N. returns an igs of N. returns an igs of U. returns U. returns the identity element of G. The main feature of a pcp are the possibility to compute exponent vectors without having to determine an explicit pcp-group corresponding to the subfactor that is represented by the pcp. Nonetheless, it is possible to determine this subfactor. returns the exponent vector of g with respect to the generators of pcp. This is the exponent vector of gN with respect to the igs of U/N. let pcp be a Pcp of a subgroup or a factor group of a pcp-group. This function computes a new pcp-group whose defining generators correspond to the generators in pcp. G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ] gap> pcp[1]; g1 gap> Length(pcp); 2 gap> RelativeOrdersOfPcp(pcp); [ 2, 0 ] gap> DenominatorOfPcp(pcp); [ ] gap> NumeratorOfPcp(pcp); [ g1, g2 ] gap> GroupOfPcp(pcp); Pcp-group with orders [ 2, 0 ] gap> OneOfPcp(pcp); identity ]]> G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ] gap> D := DerivedSubgroup( G ); Pcp-group with orders [ 0, 0, 0 ] gap> GeneratorsOfGroup( G ); [ g1, g2, g3, g4 ] gap> GeneratorsOfGroup( D ); [ g2^-2, g3^-2, g4^2 ] # an ordinary pcp for G / D gap> pcp1 := Pcp( G, D ); Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ] # a pcp for G/D in independent generators gap> pcp2 := Pcp( G, D, "snf" ); Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ] gap> g := Random( G ); g1*g2^-4*g3*g4^2 # compute the exponent vector of g in G/D with respect to pcp1 gap> ExponentsByPcp( pcp1, g ); [ 1, 0, 1, 0 ] # compute the exponent vector of g in G/D with respect to pcp2 gap> ExponentsByPcp( pcp2, g ); [ 0, 1, 1 ] ]]>

Factor groups of pcp-groups Pcp's for subfactors of pcp-groups have already been described above. These are usually used within algorithms to compute with pcp-groups. However, it is also possible to explicitly construct factor groups and their corresponding natural homomorphisms. returns the natural homomorphism G \to G/N. Its image is the factor group G/N. returns the desired factor as pcp-group without giving the explicit homomorphism. This function is just a wrapper for PcpGroupByPcp( Pcp( G, N ) ).
Homomorphisms for pcp-groups &Polycyclic; provides code for defining group homomorphisms by generators and images where either the source or the range or both are pcp groups. All methods provided by GAP for such group homomorphisms are supported, in particular the following: returns the homomorphism from the (pcp-) group G to the pcp-group H mapping the generators of G in the list gens to the corresponding images in the list imgs of elements of H. returns the kernel of the homomorphism hom from a pcp-group to a pcp-group. returns the image of the whole group, of U and of g, respectively, under the homomorphism hom. returns the complete preimage of the subgroup U under the homomorphism hom. If the domain of hom is not a pcp-group, then this function only works properly if hom is injective. returns a preimage of the element g under the homomorphism hom. checks if the homomorphism hom is injective.
Changing the defining pc-presentation returns a new pcp-group isomorphic to G whose defining polycyclic presentation is refined; that is, the corresponding polycyclic series has prime or infinite factors only. If H is the new group, then H!.bijection is the isomorphism G \to H. returns a new pcp-group isomorphic to the first subgroup G of the given series ser such that its defining pcp refines the given series. The series must be subnormal and H!.bijection is the isomorphism G \to H. If the parameter flag is present and equals the string snf, the series must have abelian factors. The pcp of the group returned corresponds to a decomposition of each abelian factor into a direct product of cyclic groups. G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> U := Subgroup( G, [Pcp(G)[2]^1440]); Pcp-group with orders [ 0 ] gap> F := G/U; Pcp-group with orders [ 2, 1440 ] gap> RefinedPcpGroup(F); Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 3, 3, 5 ] gap> ser := [G, U, TrivialSubgroup(G)]; [ Pcp-group with orders [ 2, 0 ], Pcp-group with orders [ 0 ], Pcp-group with orders [ ] ] gap> PcpGroupBySeries(ser); Pcp-group with orders [ 2, 1440, 0 ] ]]>
Printing a pc-presentation By default, a pcp-group is printed using its relative orders only. The following methods can be used to view the pcp presentation of the group. prints the pcp presentation defined by the igs of G or the pcp pcp. By default, the trivial conjugator relations are omitted from this presentation to shorten notation. Also, the relations obtained from conjugating with inverse generators are included only if the conjugating generator has infinite order. If this generator has finite order, then the conjugation relation is a consequence of the remaining relations. If the parameter flag is present and equals the string all, all conjugate relations are printed, including the trivial conjugate relations as well as those involving conjugation with inverses.
Converting to and from a presentation returns an isomorphism from G onto a pcp-group H. There are various methods installed for this operation and some of these methods are part of the &Polycyclic; package, while others may be part of other packages.

For example, &Polycyclic; contains methods for this function in the case that G is a finite pc-group or a finite solvable permutation group.

Other examples for methods for IsomorphismPcpGroup are the methods for the case that G is a crystallographic group (see &Cryst;) or the case that G is an almost crystallographic group (see &AClib;). A method for the case that G is a rational polycyclic matrix group is included in the &Polenta; package. This function can convert a finitely presented group with a polycyclic presentation into a pcp group. pc-groups are a representation for finite polycyclic groups. This function can convert finite pcp-groups to pc-groups. This function can convert pcp-groups to a finitely presented group.

polycyclic-2.16/doc/chap5.txt0000644000076600000240000005777513706672367015177 0ustar mhornstaff 5 Basic methods and functions for pcp-groups Pcp-groups are groups in the GAP sense and hence all generic GAP methods for groups can be applied for pcp-groups. However, for a number of group theoretic questions GAP does not provide generic methods that can be applied to pcp-groups. For some of these questions there are functions provided in Polycyclic. 5.1 Elementary methods for pcp-groups In this chapter we describe some important basic functions which are available for pcp-groups. A number of higher level functions are outlined in later sections and chapters. Let U, V and N be subgroups of a pcp-group. 5.1-1 \= \=( U, V )  method decides if U and V are equal as sets. 5.1-2 Size Size( U )  method returns the size of U. 5.1-3 Random Random( U )  method returns a random element of U. 5.1-4 Index Index( U, V )  method returns the index of V in U if V is a subgroup of U. The function does not check if V is a subgroup of U and if it is not, the result is not meaningful. 5.1-5 \in \in( g, U )  method checks if g is an element of U. 5.1-6 Elements Elements( U )  method returns a list containing all elements of U if U is finite and it returns the list [fail] otherwise. 5.1-7 ClosureGroup ClosureGroup( U, V )  method returns the group generated by U and V. 5.1-8 NormalClosure NormalClosure( U, V )  method returns the normal closure of V under action of U. 5.1-9 HirschLength HirschLength( U )  method returns the Hirsch length of U. 5.1-10 CommutatorSubgroup CommutatorSubgroup( U, V )  method returns the group generated by all commutators [u,v] with u in U and v in V. 5.1-11 PRump PRump( U, p )  method returns the subgroup U'U^p of U where p is a prime number. 5.1-12 SmallGeneratingSet SmallGeneratingSet( U )  method returns a small generating set for U. 5.2 Elementary properties of pcp-groups 5.2-1 IsSubgroup IsSubgroup( U, V )  function tests if V is a subgroup of U. 5.2-2 IsNormal IsNormal( U, V )  function tests if V is normal in U. 5.2-3 IsNilpotentGroup IsNilpotentGroup( U )  method checks whether U is nilpotent. 5.2-4 IsAbelian IsAbelian( U )  method checks whether U is abelian. 5.2-5 IsElementaryAbelian IsElementaryAbelian( U )  method checks whether U is elementary abelian. 5.2-6 IsFreeAbelian IsFreeAbelian( U )  property checks whether U is free abelian. 5.3 Subgroups of pcp-groups A subgroup of a pcp-group G can be defined by a set of generators as described in Section 4.3. However, many computations with a subgroup U need an induced generating sequence or igs of U. An igs is a sequence of generators of U whose list of exponent vectors form a matrix in upper triangular form. Note that there may exist many igs of U. The first one calculated for U is stored as an attribute. An induced generating sequence of a subgroup of a pcp-group G is a list of elements of G. An igs is called normed, if each element in the list is normed. Moreover, it is canonical, if the exponent vector matrix is in Hermite Normal Form. The following functions can be used to compute induced generating sequence for a given subgroup U of G. 5.3-1 Igs Igs( U )  attribute Igs( gens )  function IgsParallel( gens, gens2 )  function returns an induced generating sequence of the subgroup U of a pcp-group. In the second form the subgroup is given via a generating set gens. The third form computes an igs for the subgroup generated by gens carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list. 5.3-2 Ngs Ngs( U )  attribute Ngs( igs )  function returns a normed induced generating sequence of the subgroup U of a pcp-group. The second form takes an igs as input and norms it. 5.3-3 Cgs Cgs( U )  attribute Cgs( igs )  function CgsParallel( gens, gens2 )  function returns a canonical generating sequence of the subgroup U of a pcp-group. In the second form the function takes an igs as input and returns a canonical generating sequence. The third version takes a generating set and computes a canonical generating sequence carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list. For a large number of methods for pcp-groups U we will first of all determine an igs for U. Hence it might speed up computations, if a known igs for a group U is set a priori. The following functions can be used for this purpose. 5.3-4 SubgroupByIgs SubgroupByIgs( G, igs )  function SubgroupByIgs( G, igs, gens )  function returns the subgroup of the pcp-group G generated by the elements of the induced generating sequence igs. Note that igs must be an induced generating sequence of the subgroup generated by the elements of the igs. In the second form igs is a igs for a subgroup and gens are some generators. The function returns the subgroup generated by igs and gens. 5.3-5 AddToIgs AddToIgs( igs, gens )  function AddToIgsParallel( igs, gens, igs2, gens2 )  function AddIgsToIgs( igs, igs2 )  function sifts the elements in the list gens into igs. The second version has the same functionality and carries shadows. This means that each operation that is applied to the first list and the element gens is also applied to the second list and the element gens2. The third version is available for efficiency reasons and assumes that the second list igs2 is not only a generating set, but an igs. 5.4 Polycyclic presentation sequences for subfactors A subfactor of a pcp-group G is again a polycyclic group for which a polycyclic presentation can be computed. However, to compute a polycyclic presentation for a given subfactor can be time-consuming. Hence we introduce polycyclic presentation sequences or Pcp to compute more efficiently with subfactors. (Note that a subgroup is also a subfactor and thus can be handled by a pcp) A pcp for a pcp-group U or a subfactor U / N can be created with one of the following functions. 5.4-1 Pcp Pcp( U[, flag] )  function Pcp( U, N[, flag] )  function returns a polycyclic presentation sequence for the subgroup U or the quotient group U modulo N. If the parameter flag is present and equals the string snf, the function can only be applied to an abelian subgroup U or abelian subfactor U/N. The pcp returned will correspond to a decomposition of the abelian group into a direct product of cyclic groups. A pcp is a component object which behaves similar to a list representing an igs of the subfactor in question. The basic functions to obtain the stored values of this component object are as follows. Let pcp be a pcp for a subfactor U/N of the defining pcp-group G. 5.4-2 GeneratorsOfPcp GeneratorsOfPcp( pcp )  function this returns a list of elements of U corresponding to an igs of U/N. 5.4-3 \[\] \[\]( pcp, i )  method returns the i-th element of pcp. 5.4-4 Length Length( pcp )  method returns the number of generators in pcp. 5.4-5 RelativeOrdersOfPcp RelativeOrdersOfPcp( pcp )  function the relative orders of the igs in U/N. 5.4-6 DenominatorOfPcp DenominatorOfPcp( pcp )  function returns an igs of N. 5.4-7 NumeratorOfPcp NumeratorOfPcp( pcp )  function returns an igs of U. 5.4-8 GroupOfPcp GroupOfPcp( pcp )  function returns U. 5.4-9 OneOfPcp OneOfPcp( pcp )  function returns the identity element of G. The main feature of a pcp are the possibility to compute exponent vectors without having to determine an explicit pcp-group corresponding to the subfactor that is represented by the pcp. Nonetheless, it is possible to determine this subfactor. 5.4-10 ExponentsByPcp ExponentsByPcp( pcp, g )  function returns the exponent vector of g with respect to the generators of pcp. This is the exponent vector of gN with respect to the igs of U/N. 5.4-11 PcpGroupByPcp PcpGroupByPcp( pcp )  function let pcp be a Pcp of a subgroup or a factor group of a pcp-group. This function computes a new pcp-group whose defining generators correspond to the generators in pcp.  Example  gap>  G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap>  pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ] gap>  pcp[1]; g1 gap>  Length(pcp); 2 gap>  RelativeOrdersOfPcp(pcp); [ 2, 0 ] gap>  DenominatorOfPcp(pcp); [ ] gap>  NumeratorOfPcp(pcp); [ g1, g2 ] gap>  GroupOfPcp(pcp); Pcp-group with orders [ 2, 0 ] gap> OneOfPcp(pcp); identity   Example  gap> G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ] gap> D := DerivedSubgroup( G ); Pcp-group with orders [ 0, 0, 0 ] gap>  GeneratorsOfGroup( G ); [ g1, g2, g3, g4 ] gap>  GeneratorsOfGroup( D ); [ g2^-2, g3^-2, g4^2 ]  # an ordinary pcp for G / D gap> pcp1 := Pcp( G, D ); Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ]  # a pcp for G/D in independent generators gap>  pcp2 := Pcp( G, D, "snf" ); Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ]  gap>  g := Random( G ); g1*g2^-4*g3*g4^2  # compute the exponent vector of g in G/D with respect to pcp1 gap> ExponentsByPcp( pcp1, g ); [ 1, 0, 1, 0 ]  # compute the exponent vector of g in G/D with respect to pcp2 gap>  ExponentsByPcp( pcp2, g ); [ 0, 1, 1 ]  5.5 Factor groups of pcp-groups Pcp's for subfactors of pcp-groups have already been described above. These are usually used within algorithms to compute with pcp-groups. However, it is also possible to explicitly construct factor groups and their corresponding natural homomorphisms. 5.5-1 NaturalHomomorphismByNormalSubgroup NaturalHomomorphismByNormalSubgroup( G, N )  method returns the natural homomorphism G -> G/N. Its image is the factor group G/N. 5.5-2 \/ \/( G, N )  method FactorGroup( G, N )  method returns the desired factor as pcp-group without giving the explicit homomorphism. This function is just a wrapper for PcpGroupByPcp( Pcp( G, N ) ). 5.6 Homomorphisms for pcp-groups Polycyclic provides code for defining group homomorphisms by generators and images where either the source or the range or both are pcp groups. All methods provided by GAP for such group homomorphisms are supported, in particular the following: 5.6-1 GroupHomomorphismByImages GroupHomomorphismByImages( G, H, gens, imgs )  function returns the homomorphism from the (pcp-) group G to the pcp-group H mapping the generators of G in the list gens to the corresponding images in the list imgs of elements of H. 5.6-2 Kernel Kernel( hom )  function returns the kernel of the homomorphism hom from a pcp-group to a pcp-group. 5.6-3 Image Image( hom )  operation Image( hom, U )  function Image( hom, g )  function returns the image of the whole group, of U and of g, respectively, under the homomorphism hom. 5.6-4 PreImage PreImage( hom, U )  function returns the complete preimage of the subgroup U under the homomorphism hom. If the domain of hom is not a pcp-group, then this function only works properly if hom is injective. 5.6-5 PreImagesRepresentative PreImagesRepresentative( hom, g )  method returns a preimage of the element g under the homomorphism hom. 5.6-6 IsInjective IsInjective( hom )  method checks if the homomorphism hom is injective. 5.7 Changing the defining pc-presentation 5.7-1 RefinedPcpGroup RefinedPcpGroup( G )  function returns a new pcp-group isomorphic to G whose defining polycyclic presentation is refined; that is, the corresponding polycyclic series has prime or infinite factors only. If H is the new group, then H!.bijection is the isomorphism G -> H. 5.7-2 PcpGroupBySeries PcpGroupBySeries( ser[, flag] )  function returns a new pcp-group isomorphic to the first subgroup G of the given series ser such that its defining pcp refines the given series. The series must be subnormal and H!.bijection is the isomorphism G -> H. If the parameter flag is present and equals the string snf, the series must have abelian factors. The pcp of the group returned corresponds to a decomposition of each abelian factor into a direct product of cyclic groups.  Example  gap> G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap>  U := Subgroup( G, [Pcp(G)[2]^1440]); Pcp-group with orders [ 0 ] gap>  F := G/U; Pcp-group with orders [ 2, 1440 ] gap> RefinedPcpGroup(F); Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 3, 3, 5 ]  gap> ser := [G, U, TrivialSubgroup(G)]; [ Pcp-group with orders [ 2, 0 ],  Pcp-group with orders [ 0 ],  Pcp-group with orders [ ] ] gap>  PcpGroupBySeries(ser); Pcp-group with orders [ 2, 1440, 0 ]  5.8 Printing a pc-presentation By default, a pcp-group is printed using its relative orders only. The following methods can be used to view the pcp presentation of the group. 5.8-1 PrintPcpPresentation PrintPcpPresentation( G[, flag] )  function PrintPcpPresentation( pcp[, flag] )  function prints the pcp presentation defined by the igs of G or the pcp pcp. By default, the trivial conjugator relations are omitted from this presentation to shorten notation. Also, the relations obtained from conjugating with inverse generators are included only if the conjugating generator has infinite order. If this generator has finite order, then the conjugation relation is a consequence of the remaining relations. If the parameter flag is present and equals the string all, all conjugate relations are printed, including the trivial conjugate relations as well as those involving conjugation with inverses. 5.9 Converting to and from a presentation 5.9-1 IsomorphismPcpGroup IsomorphismPcpGroup( G )  attribute returns an isomorphism from G onto a pcp-group H. There are various methods installed for this operation and some of these methods are part of the Polycyclic package, while others may be part of other packages. For example, Polycyclic contains methods for this function in the case that G is a finite pc-group or a finite solvable permutation group. Other examples for methods for IsomorphismPcpGroup are the methods for the case that G is a crystallographic group (see Cryst) or the case that G is an almost crystallographic group (see AClib). A method for the case that G is a rational polycyclic matrix group is included in the Polenta package. 5.9-2 IsomorphismPcpGroupFromFpGroupWithPcPres IsomorphismPcpGroupFromFpGroupWithPcPres( G )  function This function can convert a finitely presented group with a polycyclic presentation into a pcp group. 5.9-3 IsomorphismPcGroup IsomorphismPcGroup( G )  method pc-groups are a representation for finite polycyclic groups. This function can convert finite pcp-groups to pc-groups. 5.9-4 IsomorphismFpGroup IsomorphismFpGroup( G )  method This function can convert pcp-groups to a finitely presented group. polycyclic-2.16/doc/ragged.css0000644000076600000240000000023113706672374015346 0ustar mhornstaff/* times.css Frank Lübeck */ /* Change default CSS to use Times font. */ body { text-align: left; } polycyclic-2.16/doc/chap1.html0000644000076600000240000001276513706672374015304 0ustar mhornstaff GAP (polycyclic) - Chapter 1: Preface
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

1 Preface

A group G is called polycyclic if there exists a subnormal series in G with cyclic factors. Every polycyclic group is soluble and every supersoluble group is polycyclic. The class of polycyclic groups is closed with respect to forming subgroups, factor groups and extensions. Polycyclic groups can also be characterised as those soluble groups in which each subgroup is finitely generated.

K. A. Hirsch has initiated the investigation of polycyclic groups in 1938, see [Hir38a], [Hir38b], [Hir46], [Hir52], [Hir54], and their central position in infinite group theory has been recognised since.

A well-known result of Hirsch asserts that each polycyclic group is finitely presented. In fact, a polycyclic group has a presentation which exhibits its polycyclic structure: a pc-presentation as defined in the Chapter Introduction to polycyclic presentations. Pc-presentations allow efficient computations with the groups they define. In particular, the word problem is efficiently solvable in a group given by a pc-presentation. Further, subgroups and factor groups of groups given by a pc-presentation can be handled effectively.

The GAP 4 package Polycyclic is designed for computations with polycyclic groups which are given by a pc-presentation. The package contains methods to solve the word problem in such groups and to handle subgroups and factor groups of polycyclic groups. Based on these basic algorithms we present a collection of methods to construct polycyclic groups and to investigate their structure.

In [BCRS91] and [Seg90] the theory of problems which are decidable in polycyclic-by-finite groups has been started. As a result of these investigation we know that a large number of group theoretic problems are decidable by algorithms in polycyclic groups. However, practical algorithms which are suitable for computer implementations have not been obtained by this study. We have developed a new set of practical methods for groups given by pc-presentations, see for example [Eic00], and this package is a collection of implementations for these and other methods.

We refer to [Rob82], page 147ff, and [Seg83] for background on polycyclic groups. Further, in [Sim94] a variation of the basic methods for groups with pc-presentation is introduced. Finally, we note that the main GAP library contains many practical algorithms to compute with finite polycyclic groups. This is described in the Section on polycyclic groups in the reference manual.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chap5_mj.html0000644000076600000240000014112613706672374015770 0ustar mhornstaff GAP (polycyclic) - Chapter 5: Basic methods and functions for pcp-groups
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

5 Basic methods and functions for pcp-groups

5 Basic methods and functions for pcp-groups

Pcp-groups are groups in the GAP sense and hence all generic GAP methods for groups can be applied for pcp-groups. However, for a number of group theoretic questions GAP does not provide generic methods that can be applied to pcp-groups. For some of these questions there are functions provided in Polycyclic.

5.1 Elementary methods for pcp-groups

In this chapter we describe some important basic functions which are available for pcp-groups. A number of higher level functions are outlined in later sections and chapters.

Let \(U, V\) and \(N\) be subgroups of a pcp-group.

5.1-1 \=
‣ \=( U, V )( method )

decides if U and V are equal as sets.

5.1-2 Size
‣ Size( U )( method )

returns the size of U.

5.1-3 Random
‣ Random( U )( method )

returns a random element of U.

5.1-4 Index
‣ Index( U, V )( method )

returns the index of V in U if V is a subgroup of U. The function does not check if V is a subgroup of U and if it is not, the result is not meaningful.

5.1-5 \in
‣ \in( g, U )( method )

checks if g is an element of U.

5.1-6 Elements
‣ Elements( U )( method )

returns a list containing all elements of U if U is finite and it returns the list [fail] otherwise.

5.1-7 ClosureGroup
‣ ClosureGroup( U, V )( method )

returns the group generated by U and V.

5.1-8 NormalClosure
‣ NormalClosure( U, V )( method )

returns the normal closure of V under action of U.

5.1-9 HirschLength
‣ HirschLength( U )( method )

returns the Hirsch length of U.

5.1-10 CommutatorSubgroup
‣ CommutatorSubgroup( U, V )( method )

returns the group generated by all commutators \([u,v]\) with \(u\) in U and \(v\) in V.

5.1-11 PRump
‣ PRump( U, p )( method )

returns the subgroup \(U'U^p\) of U where p is a prime number.

5.1-12 SmallGeneratingSet
‣ SmallGeneratingSet( U )( method )

returns a small generating set for U.

5.2 Elementary properties of pcp-groups

5.2-1 IsSubgroup
‣ IsSubgroup( U, V )( function )

tests if V is a subgroup of U.

5.2-2 IsNormal
‣ IsNormal( U, V )( function )

tests if V is normal in U.

5.2-3 IsNilpotentGroup
‣ IsNilpotentGroup( U )( method )

checks whether U is nilpotent.

5.2-4 IsAbelian
‣ IsAbelian( U )( method )

checks whether U is abelian.

5.2-5 IsElementaryAbelian
‣ IsElementaryAbelian( U )( method )

checks whether U is elementary abelian.

5.2-6 IsFreeAbelian
‣ IsFreeAbelian( U )( property )

checks whether U is free abelian.

5.3 Subgroups of pcp-groups

A subgroup of a pcp-group \(G\) can be defined by a set of generators as described in Section 4.3. However, many computations with a subgroup \(U\) need an induced generating sequence or igs of \(U\). An igs is a sequence of generators of \(U\) whose list of exponent vectors form a matrix in upper triangular form. Note that there may exist many igs of \(U\). The first one calculated for \(U\) is stored as an attribute.

An induced generating sequence of a subgroup of a pcp-group \(G\) is a list of elements of \(G\). An igs is called normed, if each element in the list is normed. Moreover, it is canonical, if the exponent vector matrix is in Hermite Normal Form. The following functions can be used to compute induced generating sequence for a given subgroup U of G.

5.3-1 Igs
‣ Igs( U )( attribute )
‣ Igs( gens )( function )
‣ IgsParallel( gens, gens2 )( function )

returns an induced generating sequence of the subgroup U of a pcp-group. In the second form the subgroup is given via a generating set gens. The third form computes an igs for the subgroup generated by gens carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list.

5.3-2 Ngs
‣ Ngs( U )( attribute )
‣ Ngs( igs )( function )

returns a normed induced generating sequence of the subgroup U of a pcp-group. The second form takes an igs as input and norms it.

5.3-3 Cgs
‣ Cgs( U )( attribute )
‣ Cgs( igs )( function )
‣ CgsParallel( gens, gens2 )( function )

returns a canonical generating sequence of the subgroup U of a pcp-group. In the second form the function takes an igs as input and returns a canonical generating sequence. The third version takes a generating set and computes a canonical generating sequence carrying gens2 through as shadows. This means that each operation that is applied to the first list is also applied to the second list.

For a large number of methods for pcp-groups U we will first of all determine an igs for U. Hence it might speed up computations, if a known igs for a group U is set a priori. The following functions can be used for this purpose.

5.3-4 SubgroupByIgs
‣ SubgroupByIgs( G, igs )( function )
‣ SubgroupByIgs( G, igs, gens )( function )

returns the subgroup of the pcp-group G generated by the elements of the induced generating sequence igs. Note that igs must be an induced generating sequence of the subgroup generated by the elements of the igs. In the second form igs is a igs for a subgroup and gens are some generators. The function returns the subgroup generated by igs and gens.

5.3-5 AddToIgs
‣ AddToIgs( igs, gens )( function )
‣ AddToIgsParallel( igs, gens, igs2, gens2 )( function )
‣ AddIgsToIgs( igs, igs2 )( function )

sifts the elements in the list \(gens\) into \(igs\). The second version has the same functionality and carries shadows. This means that each operation that is applied to the first list and the element gens is also applied to the second list and the element gens2. The third version is available for efficiency reasons and assumes that the second list igs2 is not only a generating set, but an igs.

5.4 Polycyclic presentation sequences for subfactors

A subfactor of a pcp-group \(G\) is again a polycyclic group for which a polycyclic presentation can be computed. However, to compute a polycyclic presentation for a given subfactor can be time-consuming. Hence we introduce polycyclic presentation sequences or Pcp to compute more efficiently with subfactors. (Note that a subgroup is also a subfactor and thus can be handled by a pcp)

A pcp for a pcp-group \(U\) or a subfactor \(U / N\) can be created with one of the following functions.

5.4-1 Pcp
‣ Pcp( U[, flag] )( function )
‣ Pcp( U, N[, flag] )( function )

returns a polycyclic presentation sequence for the subgroup U or the quotient group U modulo N. If the parameter flag is present and equals the string "snf", the function can only be applied to an abelian subgroup U or abelian subfactor U/N. The pcp returned will correspond to a decomposition of the abelian group into a direct product of cyclic groups.

A pcp is a component object which behaves similar to a list representing an igs of the subfactor in question. The basic functions to obtain the stored values of this component object are as follows. Let \(pcp\) be a pcp for a subfactor \(U/N\) of the defining pcp-group \(G\).

5.4-2 GeneratorsOfPcp
‣ GeneratorsOfPcp( pcp )( function )

this returns a list of elements of \(U\) corresponding to an igs of \(U/N\).

5.4-3 \[\]
‣ \[\]( pcp, i )( method )

returns the i-th element of pcp.

5.4-4 Length
‣ Length( pcp )( method )

returns the number of generators in pcp.

5.4-5 RelativeOrdersOfPcp
‣ RelativeOrdersOfPcp( pcp )( function )

the relative orders of the igs in U/N.

5.4-6 DenominatorOfPcp
‣ DenominatorOfPcp( pcp )( function )

returns an igs of N.

5.4-7 NumeratorOfPcp
‣ NumeratorOfPcp( pcp )( function )

returns an igs of U.

5.4-8 GroupOfPcp
‣ GroupOfPcp( pcp )( function )

returns U.

5.4-9 OneOfPcp
‣ OneOfPcp( pcp )( function )

returns the identity element of G.

The main feature of a pcp are the possibility to compute exponent vectors without having to determine an explicit pcp-group corresponding to the subfactor that is represented by the pcp. Nonetheless, it is possible to determine this subfactor.

5.4-10 ExponentsByPcp
‣ ExponentsByPcp( pcp, g )( function )

returns the exponent vector of g with respect to the generators of pcp. This is the exponent vector of g\(N\) with respect to the igs of U/N.

5.4-11 PcpGroupByPcp
‣ PcpGroupByPcp( pcp )( function )

let pcp be a Pcp of a subgroup or a factor group of a pcp-group. This function computes a new pcp-group whose defining generators correspond to the generators in pcp.

gap>  G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap>  pcp := Pcp(G);
Pcp [ g1, g2 ] with orders [ 2, 0 ]
gap>  pcp[1];
g1
gap>  Length(pcp);
2
gap>  RelativeOrdersOfPcp(pcp);
[ 2, 0 ]
gap>  DenominatorOfPcp(pcp);
[  ]
gap>  NumeratorOfPcp(pcp);
[ g1, g2 ]
gap>  GroupOfPcp(pcp);
Pcp-group with orders [ 2, 0 ]
gap> OneOfPcp(pcp);
identity
gap> G := ExamplesOfSomePcpGroups(5);
Pcp-group with orders [ 2, 0, 0, 0 ]
gap> D := DerivedSubgroup( G );
Pcp-group with orders [ 0, 0, 0 ]
gap>  GeneratorsOfGroup( G );
[ g1, g2, g3, g4 ]
gap>  GeneratorsOfGroup( D );
[ g2^-2, g3^-2, g4^2 ]

# an ordinary pcp for G / D
gap> pcp1 := Pcp( G, D );
Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ]

# a pcp for G/D in independent generators
gap>  pcp2 := Pcp( G, D, "snf" );
Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ]

gap>  g := Random( G );
g1*g2^-4*g3*g4^2

# compute the exponent vector of g in G/D with respect to pcp1
gap> ExponentsByPcp( pcp1, g );
[ 1, 0, 1, 0 ]

# compute the exponent vector of g in G/D with respect to pcp2
gap>  ExponentsByPcp( pcp2, g );
[ 0, 1, 1 ]

5.5 Factor groups of pcp-groups

Pcp's for subfactors of pcp-groups have already been described above. These are usually used within algorithms to compute with pcp-groups. However, it is also possible to explicitly construct factor groups and their corresponding natural homomorphisms.

5.5-1 NaturalHomomorphismByNormalSubgroup
‣ NaturalHomomorphismByNormalSubgroup( G, N )( method )

returns the natural homomorphism \(G \to G/N\). Its image is the factor group \(G/N\).

5.5-2 \/
‣ \/( G, N )( method )
‣ FactorGroup( G, N )( method )

returns the desired factor as pcp-group without giving the explicit homomorphism. This function is just a wrapper for PcpGroupByPcp( Pcp( G, N ) ).

5.6 Homomorphisms for pcp-groups

Polycyclic provides code for defining group homomorphisms by generators and images where either the source or the range or both are pcp groups. All methods provided by GAP for such group homomorphisms are supported, in particular the following:

5.6-1 GroupHomomorphismByImages
‣ GroupHomomorphismByImages( G, H, gens, imgs )( function )

returns the homomorphism from the (pcp-) group G to the pcp-group H mapping the generators of G in the list gens to the corresponding images in the list imgs of elements of H.

5.6-2 Kernel
‣ Kernel( hom )( function )

returns the kernel of the homomorphism hom from a pcp-group to a pcp-group.

5.6-3 Image
‣ Image( hom )( operation )
‣ Image( hom, U )( function )
‣ Image( hom, g )( function )

returns the image of the whole group, of U and of g, respectively, under the homomorphism hom.

5.6-4 PreImage
‣ PreImage( hom, U )( function )

returns the complete preimage of the subgroup U under the homomorphism hom. If the domain of hom is not a pcp-group, then this function only works properly if hom is injective.

5.6-5 PreImagesRepresentative
‣ PreImagesRepresentative( hom, g )( method )

returns a preimage of the element g under the homomorphism hom.

5.6-6 IsInjective
‣ IsInjective( hom )( method )

checks if the homomorphism hom is injective.

5.7 Changing the defining pc-presentation

5.7-1 RefinedPcpGroup
‣ RefinedPcpGroup( G )( function )

returns a new pcp-group isomorphic to G whose defining polycyclic presentation is refined; that is, the corresponding polycyclic series has prime or infinite factors only. If \(H\) is the new group, then \(H!.bijection\) is the isomorphism \(G \to H\).

5.7-2 PcpGroupBySeries
‣ PcpGroupBySeries( ser[, flag] )( function )

returns a new pcp-group isomorphic to the first subgroup \(G\) of the given series ser such that its defining pcp refines the given series. The series must be subnormal and \(H!.bijection\) is the isomorphism \(G \to H\). If the parameter flag is present and equals the string "snf", the series must have abelian factors. The pcp of the group returned corresponds to a decomposition of each abelian factor into a direct product of cyclic groups.

gap> G := DihedralPcpGroup(0);
Pcp-group with orders [ 2, 0 ]
gap>  U := Subgroup( G, [Pcp(G)[2]^1440]);
Pcp-group with orders [ 0 ]
gap>  F := G/U;
Pcp-group with orders [ 2, 1440 ]
gap> RefinedPcpGroup(F);
Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 3, 3, 5 ]

gap> ser := [G, U, TrivialSubgroup(G)];
[ Pcp-group with orders [ 2, 0 ],
  Pcp-group with orders [ 0 ],
  Pcp-group with orders [  ] ]
gap>  PcpGroupBySeries(ser);
Pcp-group with orders [ 2, 1440, 0 ]

5.8 Printing a pc-presentation

By default, a pcp-group is printed using its relative orders only. The following methods can be used to view the pcp presentation of the group.

5.8-1 PrintPcpPresentation
‣ PrintPcpPresentation( G[, flag] )( function )
‣ PrintPcpPresentation( pcp[, flag] )( function )

prints the pcp presentation defined by the igs of G or the pcp pcp. By default, the trivial conjugator relations are omitted from this presentation to shorten notation. Also, the relations obtained from conjugating with inverse generators are included only if the conjugating generator has infinite order. If this generator has finite order, then the conjugation relation is a consequence of the remaining relations. If the parameter flag is present and equals the string "all", all conjugate relations are printed, including the trivial conjugate relations as well as those involving conjugation with inverses.

5.9 Converting to and from a presentation

5.9-1 IsomorphismPcpGroup
‣ IsomorphismPcpGroup( G )( attribute )

returns an isomorphism from G onto a pcp-group H. There are various methods installed for this operation and some of these methods are part of the Polycyclic package, while others may be part of other packages.

For example, Polycyclic contains methods for this function in the case that G is a finite pc-group or a finite solvable permutation group.

Other examples for methods for IsomorphismPcpGroup are the methods for the case that G is a crystallographic group (see Cryst) or the case that G is an almost crystallographic group (see AClib). A method for the case that G is a rational polycyclic matrix group is included in the Polenta package.

5.9-2 IsomorphismPcpGroupFromFpGroupWithPcPres
‣ IsomorphismPcpGroupFromFpGroupWithPcPres( G )( function )

This function can convert a finitely presented group with a polycyclic presentation into a pcp group.

5.9-3 IsomorphismPcGroup
‣ IsomorphismPcGroup( G )( method )

pc-groups are a representation for finite polycyclic groups. This function can convert finite pcp-groups to pc-groups.

5.9-4 IsomorphismFpGroup
‣ IsomorphismFpGroup( G )( method )

This function can convert pcp-groups to a finitely presented group.

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/toggless.js0000644000076600000240000000420513706672374015575 0ustar mhornstaff/* toggless.js Frank Lübeck */ /* this file contains two functions: mergeSideTOCHooks: this changes div.ContSect elements to the class ContSectClosed and includes a hook to toggle between ContSectClosed and ContSectOpen. openclosetoc: this function does the toggling, the rest is done by CSS */ closedTOCMarker = "▶ "; openTOCMarker = "▼ "; noTOCMarker = " "; /* merge hooks into side toc for opening/closing subsections with openclosetoc */ function mergeSideTOCHooks() { var hlist = document.getElementsByTagName("div"); for (var i = 0; i < hlist.length; i++) { if (hlist[i].className == "ContSect") { var chlds = hlist[i].childNodes; var el = document.createElement("span"); var oncl = document.createAttribute("class"); oncl.nodeValue = "toctoggle"; el.setAttributeNode(oncl); var cont; if (chlds.length > 2) { var oncl = document.createAttribute("onclick"); oncl.nodeValue = "openclosetoc(event)"; el.setAttributeNode(oncl); cont = document.createTextNode(closedTOCMarker); } else { cont = document.createTextNode(noTOCMarker); } el.appendChild(cont); hlist[i].firstChild.insertBefore(el, hlist[i].firstChild.firstChild); hlist[i].className = "ContSectClosed"; } } } function openclosetoc (event) { /* first two steps to make it work in most browsers */ var evt=window.event || event; if (!evt.target) evt.target=evt.srcElement; var markClosed = document.createTextNode(closedTOCMarker); var markOpen = document.createTextNode(openTOCMarker); var par = evt.target.parentNode.parentNode; if (par.className == "ContSectOpen") { par.className = "ContSectClosed"; evt.target.replaceChild(markClosed, evt.target.firstChild); } else if (par.className == "ContSectClosed") { par.className = "ContSectOpen"; evt.target.replaceChild(markOpen, evt.target.firstChild); } } /* adjust jscontent which is called onload */ jscontentfuncs.push(mergeSideTOCHooks); polycyclic-2.16/doc/chapA.txt0000644000076600000240000000264113706672367015171 0ustar mhornstaff A Obsolete Functions and Name Changes Over time, the interface of Polycyclic has changed. This was done to get the names of Polycyclic functions to agree with the general naming conventions used throughout GAP. Also, some Polycyclic operations duplicated functionality that was already available in the core of GAP under a different name. In these cases, whenever possible we now install the Polycyclic code as methods for the existing GAP operations instead of introducing new operations. For backward compatibility, we still provide the old, obsolete names as aliases. However, please consider switching to the new names as soon as possible. The old names may be completely removed at some point in the future. The following function names were changed. OLD │ NOW USE ──────────────────┼──────────────────────────────────── SchurCovering │ SchurCover (7.9-3) SchurMultPcpGroup │ AbelianInvariantsMultiplier (7.9-4) polycyclic-2.16/doc/chap7.txt0000644000076600000240000012711413706672367015162 0ustar mhornstaff 7 Higher level methods for pcp-groups This is a description of some higher level functions of the Polycyclic package of GAP 4. Throughout this chapter we let G be a pc-presented group and we consider algorithms for subgroups U and V of G. For background and a description of the underlying algorithms we refer to [Eic01a]. 7.1 Subgroup series in pcp-groups Many algorithm for pcp-groups work by induction using some series through the group. In this section we provide a number of useful series for pcp-groups. An efa series is a normal series with elementary or free abelian factors. See [Eic00] for outlines on the algorithms of a number of the available series. 7.1-1 PcpSeries PcpSeries( U )  function returns the polycyclic series of U defined by an igs of U. 7.1-2 EfaSeries EfaSeries( U )  attribute returns a normal series of U with elementary or free abelian factors. 7.1-3 SemiSimpleEfaSeries SemiSimpleEfaSeries( U )  attribute returns an efa series of U such that every factor in the series is semisimple as a module for U over a finite field or over the rationals. 7.1-4 DerivedSeriesOfGroup DerivedSeriesOfGroup( U )  method the derived series of U. 7.1-5 RefinedDerivedSeries RefinedDerivedSeries( U )  function the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the top. 7.1-6 RefinedDerivedSeriesDown RefinedDerivedSeriesDown( U )  function the derived series of U refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the bottom. 7.1-7 LowerCentralSeriesOfGroup LowerCentralSeriesOfGroup( U )  method the lower central series of U. If U does not have a largest nilpotent quotient group, then this function may not terminate. 7.1-8 UpperCentralSeriesOfGroup UpperCentralSeriesOfGroup( U )  method the upper central series of U. This function always terminates, but it may terminate at a proper subgroup of U. 7.1-9 TorsionByPolyEFSeries TorsionByPolyEFSeries( U )  function returns an efa series of U such that all torsion-free factors are at the top and all finite factors are at the bottom. Such a series might not exist for U and in this case the function returns fail.  Example  gap> G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ] gap> Igs(G); [ g1, g2, g3, g4 ]  gap> PcpSeries(G); [ Pcp-group with orders [ 2, 0, 0, 0 ],  Pcp-group with orders [ 0, 0, 0 ],  Pcp-group with orders [ 0, 0 ],  Pcp-group with orders [ 0 ],  Pcp-group with orders [ ] ]  gap> List( PcpSeries(G), Igs ); [ [ g1, g2, g3, g4 ], [ g2, g3, g4 ], [ g3, g4 ], [ g4 ], [ ] ]  Algorithms for pcp-groups often use an efa series of G and work down over the factors of this series. Usually, pcp's of the factors are more useful than the actual factors. Hence we provide the following. 7.1-10 PcpsBySeries PcpsBySeries( ser[, flag] )  function returns a list of pcp's corresponding to the factors of the series. If the parameter flag is present and equals the string snf, then each pcp corresponds to a decomposition of the abelian groups into direct factors. 7.1-11 PcpsOfEfaSeries PcpsOfEfaSeries( U )  attribute returns a list of pcps corresponding to an efa series of U.  Example  gap> G := ExamplesOfSomePcpGroups(5); Pcp-group with orders [ 2, 0, 0, 0 ]  gap> PcpsBySeries( DerivedSeriesOfGroup(G)); [ Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ],  Pcp [ g2^-2, g3^-2, g4^2 ] with orders [ 0, 0, 4 ],  Pcp [ g4^8 ] with orders [ 0 ] ] gap> PcpsBySeries( RefinedDerivedSeries(G)); [ Pcp [ g1, g2, g3 ] with orders [ 2, 2, 2 ],  Pcp [ g4 ] with orders [ 2 ],  Pcp [ g2^2, g3^2 ] with orders [ 0, 0 ],  Pcp [ g4^2 ] with orders [ 2 ],  Pcp [ g4^4 ] with orders [ 2 ],  Pcp [ g4^8 ] with orders [ 0 ] ]  gap> PcpsBySeries( DerivedSeriesOfGroup(G), "snf" ); [ Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ],  Pcp [ g4^2, g3^-2, g2^2*g4^2 ] with orders [ 4, 0, 0 ],  Pcp [ g4^8 ] with orders [ 0 ] ] gap> G.1^4 in DerivedSubgroup( G ); true gap> G.1^2 = G.4; true  gap>  PcpsOfEfaSeries( G ); [ Pcp [ g1 ] with orders [ 2 ],  Pcp [ g2 ] with orders [ 0 ],  Pcp [ g3 ] with orders [ 0 ],  Pcp [ g4 ] with orders [ 0 ] ]  7.2 Orbit stabilizer methods for pcp-groups Let U be a pcp-group which acts on a set Ω. One of the fundamental problems in algorithmic group theory is the determination of orbits and stabilizers of points in Ω under the action of U. We distinguish two cases: the case that all considered orbits are finite and the case that there are infinite orbits. In the latter case, an orbit cannot be listed and a description of the orbit and its corresponding stabilizer is much harder to obtain. If the considered orbits are finite, then the following two functions can be applied to compute the considered orbits and their corresponding stabilizers. 7.2-1 PcpOrbitStabilizer PcpOrbitStabilizer( point, gens, acts, oper )  function PcpOrbitsStabilizers( points, gens, acts, oper )  function The input gens can be an igs or a pcp of a pcp-group U. The elements in the list gens act as the elements in the list acts via the function oper on the given points; that is, oper( point, acts[i] ) applies the ith generator to a given point. Thus the group defined by acts must be a homomorphic image of the group defined by gens. The first function returns a record containing the orbit as component 'orbit' and and igs for the stabilizer as component 'stab'. The second function returns a list of records, each record contains 'repr' and 'stab'. Both of these functions run forever on infinite orbits.  Example  gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> mats := [ [[-1,0],[0,1]], [[1,1],[0,1]] ];; gap> pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ] gap> PcpOrbitStabilizer( [0,1], pcp, mats, OnRight ); rec( orbit := [ [ 0, 1 ] ],  stab := [ g1, g2 ],  word := [ [ [ 1, 1 ] ], [ [ 2, 1 ] ] ] )  If the considered orbits are infinite, then it may not always be possible to determine a description of the orbits and their stabilizers. However, as shown in [EO02] and [Eic02], it is possible to determine stabilizers and check if two elements are contained in the same orbit if the given action of the polycyclic group is a unimodular linear action on a vector space. The following functions are available for this case. 7.2-2 StabilizerIntegralAction StabilizerIntegralAction( U, mats, v )  function OrbitIntegralAction( U, mats, v, w )  function The first function computes the stabilizer in U of the vector v where the pcp group U acts via mats on an integral space and v and w are elements in this integral space. The second function checks whether v and w are in the same orbit and the function returns either false or a record containing an element in U mapping v to w and the stabilizer of v. 7.2-3 NormalizerIntegralAction NormalizerIntegralAction( U, mats, B )  function ConjugacyIntegralAction( U, mats, B, C )  function The first function computes the normalizer in U of the lattice with the basis B, where the pcp group U acts via mats on an integral space and B is a subspace of this integral space. The second functions checks whether the two lattices with the bases B and C are contained in the same orbit under U. The function returns either false or a record with an element in U mapping B to C and the stabilizer of B.  Example  # get a pcp group and a free abelian normal subgroup gap> G := ExamplesOfSomePcpGroups(8); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> efa := EfaSeries(G); [ Pcp-group with orders [ 0, 0, 0, 0, 0 ],  Pcp-group with orders [ 0, 0, 0, 0 ],  Pcp-group with orders [ 0, 0, 0 ],  Pcp-group with orders [ ] ] gap> N := efa[3]; Pcp-group with orders [ 0, 0, 0 ] gap> IsFreeAbelian(N); true  # create conjugation action on N gap> mats := LinearActionOnPcp(Igs(G), Pcp(N)); [ [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],  [ [ 0, 0, 1 ], [ 1, -1, 1 ], [ 0, 1, 0 ] ],  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ],  [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]  # take an arbitrary vector and compute its stabilizer gap> StabilizerIntegralAction(G,mats, [2,3,4]); Pcp-group with orders [ 0, 0, 0, 0 ] gap> Igs(last); [ g1, g3, g4, g5 ]  # check orbits with some other vectors gap> OrbitIntegralAction(G,mats, [2,3,4],[3,1,5]); rec( stab := Pcp-group with orders [ 0, 0, 0, 0 ], prei := g2 )  gap> OrbitIntegralAction(G,mats, [2,3,4], [4,6,8]); false  # compute the orbit of a subgroup of Z^3 under the action of G gap> NormalizerIntegralAction(G, mats, [[1,0,0],[0,1,0]]); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> Igs(last); [ g1, g2^2, g3, g4, g5 ]  7.3 Centralizers, Normalizers and Intersections In this section we list a number of operations for which there are methods installed to compute the corresponding features in polycyclic groups. 7.3-1 Centralizer Centralizer( U, g )  method IsConjugate( U, g, h )  method These functions solve the conjugacy problem for elements in pcp-groups and they can be used to compute centralizers. The first method returns a subgroup of the given group U, the second method either returns a conjugating element or false if no such element exists. The methods are based on the orbit stabilizer algorithms described in [EO02]. For nilpotent groups, an algorithm to solve the conjugacy problem for elements is described in [Sim94]. 7.3-2 Centralizer Centralizer( U, V )  method Normalizer( U, V )  method IsConjugate( U, V, W )  method These three functions solve the conjugacy problem for subgroups and compute centralizers and normalizers of subgroups. The first two functions return subgroups of the input group U, the third function returns a conjugating element or false if no such element exists. The methods are based on the orbit stabilizer algorithms described in [Eic02]. For nilpotent groups, an algorithm to solve the conjugacy problems for subgroups is described in [Lo98b]. 7.3-3 Intersection Intersection( U, N )  function A general method to compute intersections of subgroups of a pcp-group is described in [Eic01a], but it is not yet implemented here. However, intersections of subgroups U, N ≤ G can be computed if N is normalising U. See [Sim94] for an outline of the algorithm. 7.4 Finite subgroups There are various finite subgroups of interest in polycyclic groups. See [Eic00] for a description of the algorithms underlying the functions in this section. 7.4-1 TorsionSubgroup TorsionSubgroup( U )  attribute If the set of elements of finite order forms a subgroup, then we call it the torsion subgroup. This function determines the torsion subgroup of U, if it exists, and returns fail otherwise. Note that a torsion subgroup does always exist if U is nilpotent. 7.4-2 NormalTorsionSubgroup NormalTorsionSubgroup( U )  attribute Each polycyclic groups has a unique largest finite normal subgroup. This function computes it for U. 7.4-3 IsTorsionFree IsTorsionFree( U )  property This function checks if U is torsion free. It returns true or false. 7.4-4 FiniteSubgroupClasses FiniteSubgroupClasses( U )  attribute There exist only finitely many conjugacy classes of finite subgroups in a polycyclic group U and this function can be used to compute them. The algorithm underlying this function proceeds by working down a normal series of U with elementary or free abelian factors. The following function can be used to give the algorithm a specific series. 7.4-5 FiniteSubgroupClassesBySeries FiniteSubgroupClassesBySeries( U, pcps )  function  Example  gap> G := ExamplesOfSomePcpGroups(15); Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0 ] gap> TorsionSubgroup(G); Pcp-group with orders [ 5, 2 ] gap> NormalTorsionSubgroup(G); Pcp-group with orders [ 5, 2 ] gap> IsTorsionFree(G); false gap> FiniteSubgroupClasses(G); [ Pcp-group with orders [ 5, 2 ]^G,  Pcp-group with orders [ 2 ]^G,  Pcp-group with orders [ 5 ]^G,  Pcp-group with orders [ ]^G ]  gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> TorsionSubgroup(G); fail gap> NormalTorsionSubgroup(G); Pcp-group with orders [ ] gap> IsTorsionFree(G); false gap> FiniteSubgroupClasses(G); [ Pcp-group with orders [ 2 ]^G,  Pcp-group with orders [ 2 ]^G,  Pcp-group with orders [ ]^G ]  7.5 Subgroups of finite index and maximal subgroups Here we outline functions to determine various types of subgroups of finite index in polycyclic groups. Again, see [Eic00] for a description of the algorithms underlying the functions in this section. Also, we refer to [Lo98a] for an alternative approach. 7.5-1 MaximalSubgroupClassesByIndex MaximalSubgroupClassesByIndex( U, p )  operation Each maximal subgroup of a polycyclic group U has p-power index for some prime p. This function can be used to determine the conjugacy classes of all maximal subgroups of p-power index for a given prime p. 7.5-2 LowIndexSubgroupClasses LowIndexSubgroupClasses( U, n )  operation There are only finitely many subgroups of a given index in a polycyclic group U. This function computes conjugacy classes of all subgroups of index n in U. 7.5-3 LowIndexNormalSubgroups LowIndexNormalSubgroups( U, n )  operation This function computes the normal subgroups of index n in U. 7.5-4 NilpotentByAbelianNormalSubgroup NilpotentByAbelianNormalSubgroup( U )  function This function returns a normal subgroup N of finite index in U such that N is nilpotent-by-abelian. Such a subgroup exists in every polycyclic group and this function computes such a subgroup using LowIndexNormal. However, we note that this function is not very efficient and the function NilpotentByAbelianByFiniteSeries may well be more efficient on this task.  Example  gap> G := ExamplesOfSomePcpGroups(2); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ]  gap> MaximalSubgroupClassesByIndex( G, 61 );; gap> max := List( last, Representative );; gap> List( max, x -> Index( G, x ) ); [ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61, 226981 ]  gap> LowIndexSubgroupClasses( G, 61 );; gap> low := List( last, Representative );; gap> List( low, x -> Index( G, x ) ); [ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,  61, 61, 61, 61, 61, 61 ]  7.6 Further attributes for pcp-groups based on the Fitting subgroup In this section we provide a variety of other attributes for pcp-groups. Most of the methods below are based or related to the Fitting subgroup of the given group. We refer to [Eic01b] for a description of the underlying methods. 7.6-1 FittingSubgroup FittingSubgroup( U )  attribute returns the Fitting subgroup of U; that is, the largest nilpotent normal subgroup of U. 7.6-2 IsNilpotentByFinite IsNilpotentByFinite( U )  property checks whether the Fitting subgroup of U has finite index. 7.6-3 Centre Centre( U )  method returns the centre of U. 7.6-4 FCCentre FCCentre( U )  method returns the FC-centre of U; that is, the subgroup containing all elements having a finite conjugacy class in U. 7.6-5 PolyZNormalSubgroup PolyZNormalSubgroup( U )  function returns a normal subgroup N of finite index in U, such that N has a polycyclic series with infinite factors only. 7.6-6 NilpotentByAbelianByFiniteSeries NilpotentByAbelianByFiniteSeries( U )  function returns a normal series 1 ≤ F ≤ A ≤ U such that F is nilpotent, A/F is abelian and U/A is finite. This series is computed using the Fitting subgroup and the centre of the Fitting factor. 7.7 Functions for nilpotent groups There are (very few) functions which are available for nilpotent groups only. First, there are the different central series. These are available for all groups, but for nilpotent groups they terminate and provide series through the full group. Secondly, the determination of a minimal generating set is available for nilpotent groups only. 7.7-1 MinimalGeneratingSet MinimalGeneratingSet( U )  method  Example  gap> G := ExamplesOfSomePcpGroups(14); Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0, 5, 5, 4, 0, 6,  5, 5, 4, 0, 10, 6 ] gap> IsNilpotent(G); true  gap> PcpsBySeries( LowerCentralSeriesOfGroup(G)); [ Pcp [ g1, g2 ] with orders [ 0, 0 ],  Pcp [ g3 ] with orders [ 0 ],  Pcp [ g4 ] with orders [ 0 ],  Pcp [ g5 ] with orders [ 0 ],  Pcp [ g6, g7 ] with orders [ 0, 0 ],  Pcp [ g8 ] with orders [ 0 ],  Pcp [ g9, g10 ] with orders [ 0, 0 ],  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]  gap> PcpsBySeries( UpperCentralSeriesOfGroup(G)); [ Pcp [ g1, g2 ] with orders [ 0, 0 ],  Pcp [ g3 ] with orders [ 0 ],  Pcp [ g4 ] with orders [ 0 ],  Pcp [ g5 ] with orders [ 0 ],  Pcp [ g6, g7 ] with orders [ 0, 0 ],  Pcp [ g8 ] with orders [ 0 ],  Pcp [ g9, g10 ] with orders [ 0, 0 ],  Pcp [ g11, g12, g13 ] with orders [ 5, 4, 0 ],  Pcp [ g14, g15, g16, g17, g18 ] with orders [ 5, 5, 4, 0, 6 ],  Pcp [ g19, g20, g21, g22, g23, g24 ] with orders [ 5, 5, 4, 0, 10, 6 ] ]  gap> MinimalGeneratingSet(G); [ g1, g2 ]  7.8 Random methods for pcp-groups Below we introduce a function which computes orbit and stabilizer using a random method. This function tries to approximate the orbit and the stabilizer, but the returned orbit or stabilizer may be incomplete. This function is used in the random methods to compute normalizers and centralizers. Note that deterministic methods for these purposes are also available. 7.8-1 RandomCentralizerPcpGroup RandomCentralizerPcpGroup( U, g )  function RandomCentralizerPcpGroup( U, V )  function RandomNormalizerPcpGroup( U, V )  function  Example  gap> G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> mats := [[[-1, 0],[0,1]], [[1,1],[0,1]]]; [ [ [ -1, 0 ], [ 0, 1 ] ], [ [ 1, 1 ], [ 0, 1 ] ] ] gap> pcp := Pcp(G); Pcp [ g1, g2 ] with orders [ 2, 0 ]  gap> RandomPcpOrbitStabilizer( [1,0], pcp, mats, OnRight ).stab; #I Orbit longer than limit: exiting. [ ]  gap> g := Igs(G)[1]; g1 gap> RandomCentralizerPcpGroup( G, g ); #I Stabilizer not increasing: exiting. Pcp-group with orders [ 2 ] gap> Igs(last); [ g1 ]  7.9 Non-abelian tensor product and Schur extensions 7.9-1 SchurExtension SchurExtension( G )  attribute Let G be a polycyclic group with a polycyclic generating sequence consisting of n elements. This function computes the largest central extension H of G such that H is generated by n elements. If F/R is the underlying polycyclic presentation for G, then H is isomorphic to F/[R,F].  Example  gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> Centre( G ); Pcp-group with orders [ ] gap> H := SchurExtension( G ); Pcp-group with orders [ 2, 0, 0, 0 ] gap> Centre( H ); Pcp-group with orders [ 0, 0 ] gap> H/Centre(H); Pcp-group with orders [ 2, 0 ] gap> Subgroup( H, [H.1,H.2] ) = H; true  7.9-2 SchurExtensionEpimorphism SchurExtensionEpimorphism( G )  attribute returns the projection from the Schur extension G^* of G onto G. See the function SchurExtension. The kernel of this epimorphism is the direct product of the Schur multiplicator of G and a direct product of n copies of ℤ where n is the number of generators in the polycyclic presentation for G. The Schur multiplicator is the intersection of the kernel and the derived group of the source. See also the function SchurCover.  Example  gap> gl23 := Range( IsomorphismPcpGroup( GL(2,3) ) ); Pcp-group with orders [ 2, 3, 2, 2, 2 ] gap> SchurExtensionEpimorphism( gl23 ); [ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 ] -> [ g1, g2, g3, g4, g5, id, id, id, id, id ] gap> Kernel( last ); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> AbelianInvariantsMultiplier( gl23 ); [ ] gap> Intersection( Kernel(epi), DerivedSubgroup( Source(epi) ) ); [ ]  There is a crossed pairing from G into (G^*)' which can be defined via this epimorphism:  Example  gap> G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> epi := SchurExtensionEpimorphism( G ); [ g1, g2, g3, g4 ] -> [ g1, g2, id, id ] gap> PreImagesRepresentative( epi, G.1 ); g1 gap> PreImagesRepresentative( epi, G.2 ); g2 gap> Comm( last, last2 ); g2^-2*g4  7.9-3 SchurCover SchurCover( G )  function computes a Schur covering group of the polycyclic group G. A Schur covering is a largest central extension H of G such that the kernel M of the projection of H onto G is contained in the commutator subgroup of H. If G is given by a presentation F/R, then M is isomorphic to the subgroup R ∩ [F,F] / [R,F]. Let C be a complement to R ∩ [F,F] / [R,F] in R/[R,F]. Then F/C is isomorphic to H and R/C is isomorphic to M.  Example  gap> G := AbelianPcpGroup( 3,[] ); Pcp-group with orders [ 0, 0, 0 ] gap> ext := SchurCover( G ); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] gap> Centre( ext ); Pcp-group with orders [ 0, 0, 0 ] gap> IsSubgroup( DerivedSubgroup( ext ), last ); true  7.9-4 AbelianInvariantsMultiplier AbelianInvariantsMultiplier( G )  attribute returns a list of the abelian invariants of the Schur multiplier of G. Note that the Schur multiplicator of a polycyclic group is a finitely generated abelian group.  Example  gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> DirectProduct( G, AbelianPcpGroup( 2, [] ) ); Pcp-group with orders [ 0, 0, 2, 0 ] gap> AbelianInvariantsMultiplier( last ); [ 0, 2, 2, 2, 2 ]  7.9-5 NonAbelianExteriorSquareEpimorphism NonAbelianExteriorSquareEpimorphism( G )  function returns the epimorphism of the non-abelian exterior square of a polycyclic group G onto the derived group of G. The non-abelian exterior square can be defined as the derived subgroup of a Schur cover of G. The isomorphism type of the non-abelian exterior square is unique despite the fact that the isomorphism type of a Schur cover of a polycyclic groups need not be unique. The derived group of a Schur cover has a natural projection onto the derived group of G which is what the function returns. The kernel of the epimorphism is isomorphic to the Schur multiplicator of G.  Example  gap> G := ExamplesOfSomePcpGroups( 3 ); Pcp-group with orders [ 0, 0 ] gap> G := DirectProduct( G,G ); Pcp-group with orders [ 0, 0, 0, 0 ] gap> AbelianInvariantsMultiplier( G ); [ [ 0, 1 ], [ 2, 3 ] ] gap> epi := NonAbelianExteriorSquareEpimorphism( G ); [ g2^-2*g5, g4^-2*g10, g6, g7, g8, g9 ] -> [ g2^-2, g4^-2, id, id, id, id ] gap> Kernel( epi ); Pcp-group with orders [ 0, 2, 2, 2 ] gap> Collected( AbelianInvariants( last ) ); [ [ 0, 1 ], [ 2, 3 ] ]  7.9-6 NonAbelianExteriorSquare NonAbelianExteriorSquare( G )  attribute computes the non-abelian exterior square of a polycylic group G. See the explanation for NonAbelianExteriorSquareEpimorphism. The natural projection of the non-abelian exterior square onto the derived group of G is stored in the component !.epimorphism. There is a crossed pairing from G into G∧ G. See the function SchurExtensionEpimorphism for details. The crossed pairing is stored in the component !.crossedPairing. This is the crossed pairing λ in [EN08].  Example  gap> G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> GwG := NonAbelianExteriorSquare( G ); Pcp-group with orders [ 0 ] gap> lambda := GwG!.crossedPairing; function( g, h ) ... end gap> lambda( G.1, G.2 ); g2^2*g4^-1  7.9-7 NonAbelianTensorSquareEpimorphism NonAbelianTensorSquareEpimorphism( G )  function returns for a polycyclic group G the projection of the non-abelian tensor square G⊗ G onto the non-abelian exterior square G∧ G. The range of that epimorphism has the component !.epimorphism set to the projection of the non-abelian exterior square onto the derived group of G. See also the function NonAbelianExteriorSquare. With the result of this function one can compute the groups in the commutative diagram at the beginning of the paper [EN08]. The kernel of the returned epimorphism is the group ∇(G). The kernel of the composition of this epimorphism and the above mention projection onto G' is the group J(G).  Example  gap> G := DihedralPcpGroup(0); Pcp-group with orders [ 2, 0 ] gap> G := DirectProduct(G,G); Pcp-group with orders [ 2, 0, 2, 0 ] gap> alpha := NonAbelianTensorSquareEpimorphism( G ); [ g9*g25^-1, g10*g26^-1, g11*g27, g12*g28, g13*g29, g14*g30, g15, g16, g17,  g18, g19, g20, g21, g22, g23, g24 ] -> [ g2^-2*g6, g4^-2*g12, g8,  g9, g10,  g11, id, id, id, id, id, id, id, id, id, id ] gap> gamma := Range( alpha )!.epimorphism; [ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11 ] -> [ g2^-2, g4^-2, id, id, id, id ] gap> JG := Kernel( alpha * gamma ); Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ] gap> Image( alpha, JG ); Pcp-group with orders [ 2, 2, 2, 2 ] gap> AbelianInvariantsMultiplier( G ); [ [ 2, 4 ] ]  7.9-8 NonAbelianTensorSquare NonAbelianTensorSquare( G )  attribute computes for a polycyclic group G the non-abelian tensor square G⊗ G.  Example  gap> G := AlternatingGroup( IsPcGroup, 4 );  gap> PcGroupToPcpGroup( G ); Pcp-group with orders [ 3, 2, 2 ] gap> NonAbelianTensorSquare( last ); Pcp-group with orders [ 2, 2, 2, 3 ] gap> PcpGroupToPcGroup( last );  gap> DirectFactorsOfGroup( last ); [ Group([ f1, f2, f3 ]), Group([ f4 ]) ] gap> List( last, Size ); [ 8, 3 ] gap> IdGroup( last2[1] ); [ 8, 4 ] # the quaternion group of Order 8  gap> G := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> ten := NonAbelianTensorSquare( G ); Pcp-group with orders [ 0, 2, 2, 2 ] gap> IsAbelian( ten ); true  7.9-9 NonAbelianExteriorSquarePlusEmbedding NonAbelianExteriorSquarePlusEmbedding( G )  function returns an embedding from the non-abelian exterior square G∧ G into an extensions of G∧ G by G× G. For the significance of the group see the paper [EN08]. The range of the epimorphism is the group τ(G) in that paper. 7.9-10 NonAbelianTensorSquarePlusEpimorphism NonAbelianTensorSquarePlusEpimorphism( G )  function returns an epimorphisms of ν(G) onto τ(G). The group ν(G) is an extension of the non-abelian tensor square G⊗ G of G by G× G. The group τ(G) is an extension of the non-abelian exterior square G∧ G by G× G. For details see [EN08]. 7.9-11 NonAbelianTensorSquarePlus NonAbelianTensorSquarePlus( G )  function returns the group ν(G) in [EN08]. 7.9-12 WhiteheadQuadraticFunctor WhiteheadQuadraticFunctor( G )  function returns Whitehead's universal quadratic functor of G, see [EN08] for a description. 7.10 Schur covers This section contains a function to determine the Schur covers of a finite p-group up to isomorphism. 7.10-1 SchurCovers SchurCovers( G )  function Let G be a finite p-group defined as a pcp group. This function returns a complete and irredundant set of isomorphism types of Schur covers of G. The algorithm implements a method of Nickel's Phd Thesis. polycyclic-2.16/doc/chap6.txt0000644000076600000240000001254513706672367015162 0ustar mhornstaff 6 Libraries and examples of pcp-groups 6.1 Libraries of various types of polycyclic groups There are the following generic pcp-groups available. 6.1-1 AbelianPcpGroup AbelianPcpGroup( n, rels )  function constructs the abelian group on n generators such that generator i has order rels[i]. If this order is infinite, then rels[i] should be either unbound or 0. 6.1-2 DihedralPcpGroup DihedralPcpGroup( n )  function constructs the dihedral group of order n. If n is an odd integer, then 'fail' is returned. If n is zero or not an integer, then the infinite dihedral group is returned. 6.1-3 UnitriangularPcpGroup UnitriangularPcpGroup( n, c )  function returns a pcp-group isomorphic to the group of upper triangular in GL(n, R) where R = ℤ if c = 0 and R = F_p if c = p. The natural unitriangular matrix representation of the returned pcp-group G can be obtained as G!.isomorphism. 6.1-4 SubgroupUnitriangularPcpGroup SubgroupUnitriangularPcpGroup( mats )  function mats should be a list of upper unitriangular n × n matrices over ℤ or over F_p. This function returns the subgroup of the corresponding 'UnitriangularPcpGroup' generated by the matrices in mats. 6.1-5 InfiniteMetacyclicPcpGroup InfiniteMetacyclicPcpGroup( n, m, r )  function Infinite metacyclic groups are classified in [BK00]. Every infinite metacyclic group G is isomorphic to a finitely presented group G(m,n,r) with two generators a and b and relations of the form a^m = b^n = 1 and [a,b] = a^1-r, where (differing from the conventions used by GAP) we have [a,b] = a b a^-1 b^-1, and m,n,r are three non-negative integers with mn=0 and r relatively prime to m. If r ≡ -1 mod m then n is even, and if r ≡ 1 mod m then m=0. Also m and n must not be 1. Moreover, G(m,n,r)≅ G(m',n',s) if and only if m=m', n=n', and either r ≡ s or r ≡ s^-1 mod m. This function returns the metacyclic group with parameters n, m and r as a pcp-group with the pc-presentation ⟨ x,y | x^n, y^m, y^x = y^r⟩. This presentation is easily transformed into the one above via the mapping x ↦ b^-1, y ↦ a. 6.1-6 HeisenbergPcpGroup HeisenbergPcpGroup( n )  function returns the Heisenberg group on 2n+1 generators as pcp-group. This gives a group of Hirsch length 2n+1. 6.1-7 MaximalOrderByUnitsPcpGroup MaximalOrderByUnitsPcpGroup( f )  function takes as input a normed, irreducible polynomial over the integers. Thus f defines a field extension F over the rationals. This function returns the split extension of the maximal order O of F by the unit group U of O, where U acts by right multiplication on O. 6.1-8 BurdeGrunewaldPcpGroup BurdeGrunewaldPcpGroup( s, t )  function returns a nilpotent group of Hirsch length 11 which has been constructed by Burde und Grunewald. If s is not 0, then this group has no faithful 12-dimensional linear representation. 6.2 Some assorted example groups The functions in this section provide some more example groups to play with. They come with no further description and their investigation is left to the interested user. 6.2-1 ExampleOfMetabelianPcpGroup ExampleOfMetabelianPcpGroup( a, k )  function returns an example of a metabelian group. The input parameters must be two positive integers greater than 1. 6.2-2 ExamplesOfSomePcpGroups ExamplesOfSomePcpGroups( n )  function this function takes values n in 1 up to 16 and returns for each input an example of a pcp-group. The groups in this example list have been used as test groups for the functions in this package. polycyclic-2.16/doc/chapInd_mj.html0000644000076600000240000005645513706672374016350 0ustar mhornstaff GAP (polycyclic) - Index
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Index

ComplementClasses 8.4-5
ComplementClassesCR 8.4-3
ComplementClassesEfaPcps 8.4-4
ComplementCR 8.4-1
ComplementsCR 8.4-2
\/ 5.5-2
\= 5.1-1
\[\] 5.4-3
\in 5.1-5
AbelianInvariantsMultiplier 7.9-4
AbelianPcpGroup 6.1-1
AddHallPolynomials 3.3-2
AddIgsToIgs 5.3-5
AddToIgs 5.3-5
AddToIgsParallel 5.3-5
BurdeGrunewaldPcpGroup 6.1-8
Centralizer 7.3-1 7.3-2
Centre 7.6-3
Cgs 5.3-3 5.3-3
CgsParallel 5.3-3
ClosureGroup 5.1-7
Collector 4.2-1
CommutatorSubgroup 5.1-10
ConjugacyIntegralAction 7.2-3
CRRecordByMats 8.1-1
CRRecordByPcp 8.1-2
CRRecordBySubgroup 8.1-2
DEBUG_COMBINATORIAL_COLLECTOR 3.3-8
DecomposeUpperUnitriMat 9.2-6
DenominatorOfPcp 5.4-6
Depth 4.2-5
DerivedSeriesOfGroup 7.1-4
DihedralPcpGroup 6.1-2
EfaSeries 7.1-2
Elements 5.1-6
ExampleOfMetabelianPcpGroup 6.2-1
ExamplesOfSomePcpGroups 6.2-2
Exponents 4.2-2
ExponentsByObj 3.2-6
ExponentsByPcp 5.4-10
ExtensionClassesCR 8.4-8
ExtensionCR 8.4-6
ExtensionsCR 8.4-7
FactorGroup 5.5-2
FactorOrder 4.2-9
FCCentre 7.6-4
FiniteSubgroupClasses 7.4-4
FiniteSubgroupClassesBySeries 7.4-5
FittingSubgroup 7.6-1
FromTheLeftCollector 3.1-1
FTLCollectorAppendTo 3.3-5
FTLCollectorPrintTo 3.3-4
GeneratorsOfPcp 5.4-2
GenExpList 4.2-3
GetConjugate 3.2-3
GetConjugateNC 3.2-3
GetPower 3.2-2
GetPowerNC 3.2-2
Group 4.3-2
GroupHomomorphismByImages 5.6-1
GroupOfPcp 5.4-8
HeisenbergPcpGroup 6.1-6
HirschLength 5.1-9
Igs 5.3-1 5.3-1
IgsParallel 5.3-1
Image 5.6-3 5.6-3 5.6-3
Index 5.1-4
InfiniteMetacyclicPcpGroup 6.1-5
Intersection 7.3-3
IsAbelian 5.2-4
IsConfluent 3.1-7
IsConjugate 7.3-1 7.3-2
IsElementaryAbelian 5.2-5
IsFreeAbelian 5.2-6
IsInjective 5.6-6
IsMatrixRepresentation 9.1-2
IsNilpotentByFinite 7.6-2
IsNilpotentGroup 5.2-3
IsNormal 5.2-2
IsomorphismFpGroup 5.9-4
IsomorphismPcGroup 5.9-3
IsomorphismPcpGroup 5.9-1
IsomorphismPcpGroupFromFpGroupWithPcPres 5.9-2
IsomorphismUpperUnitriMatGroupPcpGroup 9.2-1
IsPcpElement 4.1-3
IsPcpElementCollection 4.1-4
IsPcpElementRep 4.1-5
IsPcpGroup 4.1-6
IsSubgroup 5.2-1
IsTorsionFree 7.4-3
IsWeightedCollector 3.3-1
Kernel 5.6-2
LeadingExponent 4.2-6
Length 5.4-4
License .-1
LowerCentralSeriesOfGroup 7.1-7
LowIndexNormalSubgroups 7.5-3
LowIndexSubgroupClasses 7.5-2
MakeNewLevel 9.2-4
MaximalOrderByUnitsPcpGroup 6.1-7
MaximalSubgroupClassesByIndex 7.5-1
MinimalGeneratingSet 7.7-1
NameTag 4.2-4
NaturalHomomorphismByNormalSubgroup 5.5-1
Ngs 5.3-2 5.3-2
NilpotentByAbelianByFiniteSeries 7.6-6
NilpotentByAbelianNormalSubgroup 7.5-4
NonAbelianExteriorSquare 7.9-6
NonAbelianExteriorSquareEpimorphism 7.9-5
NonAbelianExteriorSquarePlusEmbedding 7.9-9
NonAbelianTensorSquare 7.9-8
NonAbelianTensorSquareEpimorphism 7.9-7
NonAbelianTensorSquarePlus 7.9-11
NonAbelianTensorSquarePlusEpimorphism 7.9-10
NormalClosure 5.1-8
Normalizer 7.3-2
NormalizerIntegralAction 7.2-3
NormalTorsionSubgroup 7.4-2
NormedPcpElement 4.2-11
NormingExponent 4.2-10
NumberOfGenerators 3.2-4
NumeratorOfPcp 5.4-7
ObjByExponents 3.2-5
OneCoboundariesCR 8.2-1
OneCoboundariesEX 8.3-1
OneCocyclesCR 8.2-1
OneCocyclesEX 8.3-2
OneCohomologyCR 8.2-1
OneCohomologyEX 8.3-3
OneOfPcp 5.4-9
OrbitIntegralAction 7.2-2
Pcp 5.4-1 5.4-1
PcpElementByExponents 4.1-1
PcpElementByExponentsNC 4.1-1
PcpElementByGenExpList 4.1-2
PcpElementByGenExpListNC 4.1-2
PcpGroupByCollector 4.3-1
PcpGroupByCollectorNC 4.3-1
PcpGroupByPcp 5.4-11
PcpGroupBySeries 5.7-2
PcpOrbitsStabilizers 7.2-1
PcpOrbitStabilizer 7.2-1
PcpsBySeries 7.1-10
PcpSeries 7.1-1
PcpsOfEfaSeries 7.1-11
PolyZNormalSubgroup 7.6-5
PreImage 5.6-4
PreImagesRepresentative 5.6-5
PrintPcpPresentation 5.8-1 5.8-1
PRump 5.1-11
Random 5.1-3
RandomCentralizerPcpGroup 7.8-1 7.8-1
RandomNormalizerPcpGroup 7.8-1
RanksLevels 9.2-3
RefinedDerivedSeries 7.1-5
RefinedDerivedSeriesDown 7.1-6
RefinedPcpGroup 5.7-1
RelativeIndex 4.2-8
RelativeOrder 4.2-7
RelativeOrders 3.2-1
RelativeOrdersOfPcp 5.4-5
SchurCover 7.9-3
SchurCovering A.
SchurCovers 7.10-1
SchurExtension 7.9-1
SchurExtensionEpimorphism 7.9-2
SchurMultPcpGroup A.
SemiSimpleEfaSeries 7.1-3
SetCommutator 3.1-5
SetConjugate 3.1-4
SetConjugateNC 3.1-4
SetPower 3.1-3
SetPowerNC 3.1-3
SetRelativeOrder 3.1-2
SetRelativeOrderNC 3.1-2
SiftUpperUnitriMat 9.2-5
SiftUpperUnitriMatGroup 9.2-2
Size 5.1-2
SmallGeneratingSet 5.1-12
SplitExtensionPcpGroup 8.4-9
StabilizerIntegralAction 7.2-2
String 3.3-3
Subgroup 4.3-3
SubgroupByIgs 5.3-4 5.3-4
SubgroupUnitriangularPcpGroup 6.1-4
TorsionByPolyEFSeries 7.1-9
TorsionSubgroup 7.4-1
TwoCoboundariesCR 8.2-1
TwoCocyclesCR 8.2-1
TwoCohomologyCR 8.2-1
TwoCohomologyModCR 8.2-2
UnitriangularMatrixRepresentation 9.1-1
UnitriangularPcpGroup 6.1-3
UpdatePolycyclicCollector 3.1-6
UpperCentralSeriesOfGroup 7.1-8
USE_COMBINATORIAL_COLLECTOR 3.3-9
USE_LIBRARY_COLLECTOR 3.3-7
UseLibraryCollector 3.3-6
WhiteheadQuadraticFunctor 7.9-12

Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

generated by GAPDoc2HTML

polycyclic-2.16/doc/chooser.html0000644000076600000240000000745613706672374015753 0ustar mhornstaff GAPDoc Style Chooser

Setting preferences for GAPDoc manuals

Unfold subsections in menus only by mouse clicks: no (default)     yes

Show GAP examples as in sessions with ColorPrompt(true): yes (default)     no

Display side of table of contents within chapters: right (default)     left

Main document font: Helvetica/sans serif (default)     Times/serif

Paragraph formatting: left-right justified (default)     ragged right

Apply settings to last page.

polycyclic-2.16/doc/defins.xml0000644000076600000240000002072113706672341015375 0ustar mhornstaff Pcp-groups - polycyclically presented groups
Pcp-elements -- elements of a pc-presented group A pcp-element is an element of a group defined by a consistent pc-presentation given by a collector. Suppose that g_1, \ldots, g_n are the defining generators of the collector. Recall that each element g in this group can be written uniquely as a collected word g_1^{e_1} \cdots g_n^{e_n} with e_i \in &ZZ; and 0 \leq e_i < r_i for i \in I. The integer vector [e_1, \ldots, e_n] is called the exponent vector of g. The following functions can be used to define pcp-elements via their exponent vector or via an arbitrary generator exponent word as introduced in Chapter . returns the pcp-element with exponent vector exp. The exponent vector is considered relative to the defining generators of the pc-presentation. returns the pcp-element with generators exponent list word. This list word consists of a sequence of generator numbers and their corresponding exponents and is of the form [i_1, e_{i_1}, i_2, e_{i_2}, \ldots, i_r, e_{i_r}]. The generators exponent list is considered relative to the defining generators of the pc-presentation.

These functions return pcp-elements in the category IsPcpElement. Presently, the only representation implemented for this category is IsPcpElementRep. (This allows us to be a little sloppy right now. The basic set of operations for IsPcpElement has not been defined yet. This is going to happen in one of the next version, certainly as soon as the need for different representations arises.) returns true if the object obj is a pcp-element. returns true if the object obj is a collection of pcp-elements. returns true if the object obj is represented as a pcp-element. returns true if the object obj is a group and also a pcp-element collection.

Methods for pcp-elements Now we can describe attributes and functions for pcp-elements. The four basic attributes of a pcp-element, Collector, Exponents, GenExpList and NameTag are computed at the creation of the pcp-element. All other attributes are determined at runtime.

Let g be a pcp-element and g_1, \ldots, g_n a polycyclic generating sequence of the underlying pc-presented group. Let C_1, \ldots, C_n be the polycyclic series defined by g_1, \ldots, g_n.

The depth of a non-trivial element g of a pcp-group (with respect to the defining generators) is the integer i such that g \in C_i \setminus C_{i+1}. The depth of the trivial element is defined to be n+1. If g\not=1 has depth i and g_i^{e_i} \cdots g_n^{e_n} is the collected word for g, then e_i is the leading exponent of g.

If g has depth i, then we call r_i = [C_i:C_{i+1}] the factor order of g. If r < \infty, then the smallest positive integer l with g^l \in C_{i+1} is the called relative order of g. If r=\infty, then the relative order of g is defined to be 0. The index e of \langle g,C_{i+1}\rangle in C_i is called relative index of g. We have that r = el.

We call a pcp-element normed, if its leading exponent is equal to its relative index. For each pcp-element g there exists an integer e such that g^e is normed. the collector to which the pcp-element g belongs. returns the exponent vector of the pcp-element g with respect to the defining generating set of the underlying collector. returns the generators exponent list of the pcp-element g with respect to the defining generating set of the underlying collector. the name used for printing the pcp-element g. Printing is done by using the name tag and appending the generator number of g. returns the depth of the pcp-element g relative to the defining generators. returns the leading exponent of pcp-element g relative to the defining generators. If g is the identity element, the functions returns 'fail' returns the relative order of the pcp-element g with respect to the defining generators. returns the relative index of the pcp-element g with respect to the defining generators. returns the factor order of the pcp-element g with respect to the defining generators. returns a positive integer e such that the pcp-element g raised to the power of e is normed. returns the normed element corresponding to the pcp-element g.

Pcp-groups - groups of pcp-elements A pcp-group is a group consisting of pcp-elements such that all pcp-elements in the group share the same collector. Thus the group G defined by a polycyclic presentation and all its subgroups are pcp-groups. returns a pcp-group build from the collector coll.

The function calls and checks the confluence (see ) of the collector.

The non-check version bypasses these checks. returns the group generated by the pcp-elements gens with identity id. returns a subgroup of the pcp-group G generated by the list gens of pcp-elements from G. ftl := FromTheLeftCollector( 2 );; gap> SetRelativeOrder( ftl, 1, 2 ); gap> SetConjugate( ftl, 2, 1, [2,-1] ); gap> UpdatePolycyclicCollector( ftl ); gap> G:= PcpGroupByCollectorNC( ftl ); Pcp-group with orders [ 2, 0 ] gap> Subgroup( G, GeneratorsOfGroup(G){[2]} ); Pcp-group with orders [ 0 ] ]]>

polycyclic-2.16/tst/0000755000076600000240000000000013706672341013446 5ustar mhornstaffpolycyclic-2.16/tst/isom.tst0000644000076600000240000000315313706672341015153 0ustar mhornstaffgap> START_TEST("Test of isomorphisms from/to pcp groups"); # # Extreme case: Test with trivial group # gap> K:=TrivialGroup(IsPcpGroup); Pcp-group with orders [ ] gap> iso:=IsomorphismPcGroup(K); [ ] -> [ ] gap> IsTrivial(Image(iso)); true gap> iso:=IsomorphismPermGroup(K); [ ] -> [ ] gap> IsTrivial(Image(iso)); true gap> iso:=IsomorphismFpGroup(K); [ ] -> [ ] gap> IsTrivial(Image(iso)); true # gap> iso:=IsomorphismPcpGroup(TrivialGroup(IsPcGroup)); [ ] -> [ ] gap> IsTrivial(Image(iso)); true # gap> iso:=IsomorphismPcpGroup(TrivialGroup(IsPermGroup)); [ ] -> [ ] gap> IsTrivial(Image(iso)); true # # Test with finite cyclic group # gap> K:=CyclicGroup(IsPcpGroup, 420); Pcp-group with orders [ 420 ] # gap> iso:=IsomorphismPcGroup(K); [ g1 ] -> [ f1 ] gap> Size(Image(iso)); 420 gap> IsCyclic(Image(iso)); true # gap> iso:=IsomorphismPermGroup(K);; gap> Size(Image(iso)); 420 gap> IsCyclic(Image(iso)); true # gap> iso:=IsomorphismFpGroup(K); [ g1 ] -> [ f1 ] gap> Size(Image(iso)); 420 gap> IsCyclic(Image(iso)); true # # Test with infinite cyclic group # gap> K:=CyclicGroup(IsPcpGroup, infinity); Pcp-group with orders [ 0 ] gap> iso:=IsomorphismFpGroup(K); [ g1 ] -> [ f1 ] gap> IsCyclic(Image(iso)); true gap> IsFinite(Image(iso)); false # # Test with dihedral group # gap> K:=DihedralGroup(IsPcpGroup, 16);; gap> IdSmallGroup(K); [ 16, 7 ] gap> iso:=IsomorphismPermGroup(K);; gap> IdSmallGroup(Image(iso)); [ 16, 7 ] gap> iso:=IsomorphismPcGroup(K);; gap> IdSmallGroup(Image(iso)); [ 16, 7 ] gap> iso:=IsomorphismFpGroup(K);; gap> IdSmallGroup(Image(iso)); [ 16, 7 ] # gap> STOP_TEST( "homs.tst", 10000000); polycyclic-2.16/tst/bugfix.tst0000644000076600000240000004144713706672341015500 0ustar mhornstaffgap> START_TEST("Test for various former bugs"); # gap> # The following used to trigger an error starting with: gap> # "SolutionMat: matrix and vector incompatible called from" gap> K:=AbelianPcpGroup([3,3,3]);; gap> A:=Subgroup(K,[K.1]);; gap> cr:=CRRecordBySubgroup(K,A);; gap> ExtensionsCR(cr);; # # Comparing homomorphisms used to be broken gap> K:=AbelianPcpGroup(1,[3]);; gap> hom1:=GroupHomomorphismByImages(K,K,[K.1],[K.1]);; gap> hom2:=GroupHomomorphismByImages(K,K,[K.1^2],[K.1^2]);; gap> hom1=hom2; true gap> hom1=IdentityMapping(K); true gap> hom2=IdentityMapping(K); true # gap> # The following incorrectly triggered an error at some point gap> IsTorsionFree(ExamplesOfSomePcpGroups(5)); true # gap> # Verify IsGeneratorsOfMagmaWithInverses warnings are silenced gap> IsGeneratorsOfMagmaWithInverses(GeneratorsOfGroup(ExamplesOfSomePcpGroups(5))); true # gap> # Check for a bug reported 2012-01-19 by Robert Morse gap> g := PcGroupToPcpGroup(SmallGroup(48,1)); Pcp-group with orders [ 2, 2, 2, 2, 3 ] gap> # The next two commands used to trigger errors gap> NonAbelianTensorSquare(Centre(g)); Pcp-group with orders [ 8 ] gap> NonAbelianExteriorSquare(Centre(g)); Pcp-group with orders [ ] # gap> # Check for a bug reported 2012-01-19 by Robert Morse gap> F := FreeGroup("x","y"); gap> x := F.1;; y := F.2;; gap> G := F/[x^2/y^24, y^24, y^x/y^23]; gap> iso := IsomorphismPcGroup(G); [ x, y ] -> [ f1, f2*f5 ] gap> iso1 := IsomorphismPcpGroup(Image(iso)); [ f1, f2, f3, f4, f5 ] -> [ g1, g2, g3, g4, g5 ] gap> G := Image(iso*iso1); Pcp-group with orders [ 2, 2, 2, 2, 3 ] gap> # The next command used to trigger an error gap> NonAbelianTensorSquare(Image(iso*iso1)); Pcp-group with orders [ 2, 2, 3, 2, 2, 2, 2 ] # gap> # The problem with the previous example is/was that Igs(G) gap> # is set to a non-standard value: gap> Igs(G); [ g1, g2*g5, g3*g4*g5^2, g4*g5, g5 ] gap> # Unfortunately, it seems that a lot of code that gap> # really should be using Ngs or Cgs is using Igs incorrectly. gap> # For example, direct products could return *invalid* embeddings: gap> D := DirectProduct(G, G); Pcp-group with orders [ 2, 2, 2, 2, 3, 2, 2, 2, 2, 3 ] gap> hom:=Embedding(D,1);; gap> mapi:=MappingGeneratorsImages(hom);; gap> GroupHomomorphismByImages(Source(hom),Range(hom),mapi[1],mapi[2]) <> fail; true gap> hom:=Projection(D,1);; gap> mapi:=MappingGeneratorsImages(hom);; gap> GroupHomomorphismByImages(Source(hom),Range(hom),mapi[1],mapi[2]) <> fail; true # gap> # Check for bug computing Schur extension of infinite cyclic groups, gap> # found by Max Horn 2012-05-25 gap> G:=AbelianPcpGroup(1,[0]); Pcp-group with orders [ 0 ] gap> # The next command used to trigger an error gap> SchurExtension(G); Pcp-group with orders [ 0 ] # gap> # Check for bug computing Schur extensions of subgroups, found by MH 2012-05-25. gap> G:=HeisenbergPcpGroup(2); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> H:=Subgroup(G,[G.2^3*G.3^2, G.1^9]); Pcp-group with orders [ 0, 0, 0 ] gap> # The next command used to trigger an error gap> SchurExtension(H); Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] # gap> # Check for bug computing Schur extensions of subgroups, found by MH 2012-05-25. gap> G:=HeisenbergPcpGroup(2); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> H:=Subgroup(G,[G.1, G.2]); Pcp-group with orders [ 0, 0 ] gap> # The next command used to trigger an error gap> SchurExtension(H); Pcp-group with orders [ 0, 0, 0 ] # gap> # Check for bug computing normalizer of two subgroups, found by MH 2012-05-30. gap> # The problem was caused by incorrect resp. overly restrictive use of Parent(). gap> G:=HeisenbergPcpGroup(2); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> A:=Subgroup(Subgroup(G,[G.2,G.3,G.4,G.5]), [G.3]); Pcp-group with orders [ 0 ] gap> B:=Subgroup(Subgroup(G,[G.1,G.4,G.5]), [G.4]); Pcp-group with orders [ 0 ] gap> Normalizer(A,B); Pcp-group with orders [ 0 ] gap> # The following used to trigger the error "arguments must have a common parent group" gap> Normalizer(B,A); Pcp-group with orders [ 0 ] # gap> # In polycyclic 2.9 and 2.10, the code for 2-cohomology computations was broken. gap> G := UnitriangularPcpGroup(3,0); Pcp-group with orders [ 0, 0, 0 ] gap> mats := G!.mats; [ [ [ 1, 1, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ], [ [ 1, 0, 0 ], [ 0, 1, 1 ], [ 0, 0, 1 ] ], [ [ 1, 0, 1 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ] gap> C := CRRecordByMats(G,mats);; gap> cc := TwoCohomologyCR(C);; gap> cc.factor.rels; [ 2, 0, 0 ] gap> c := cc.factor.prei[2]; [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ] gap> cc.gcb; [ [ 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1 ], [ -1, 0, 1, 1, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ], [ 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1 ] ] gap> cc.gcc; [ [ 1, 0, 0, 0, 0, -2, -1, 0, 1, 1, -1, -1, 0, 0, 0, 0, 0, 0 ], [ 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 1, 0, 0, -2, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 1, 0, 0, -1, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1 ] ] # gap> # LowerCentralSeriesOfGroup for non-nilpotent pcp-groups used to trigger gap> # an infinite recursion gap> G := PcGroupToPcpGroup(SmallGroup(6,1)); Pcp-group with orders [ 2, 3 ] gap> LowerCentralSeriesOfGroup(G); [ Pcp-group with orders [ 2, 3 ], Pcp-group with orders [ 3 ] ] # # bug in StabilizerIntegralAction, see https://github.com/gap-packages/polycyclic/issues/15 # gap> P:=PolynomialRing(Integers);; x:=P.1;; gap> G:=MaximalOrderByUnitsPcpGroup(x^4+x^3+x^2+x+1); Pcp-group with orders [ 10, 0, 0, 0, 0, 0 ] gap> pcps := PcpsOfEfaSeries(G); [ Pcp [ g1 ] with orders [ 2 ], Pcp [ g1^2 ] with orders [ 5 ], Pcp [ g2 ] with orders [ 0 ], Pcp [ g3, g4, g5, g6 ] with orders [ 0, 0, 0, 0 ] ] gap> mats := AffineActionByElement( Pcp(G), pcps[4], G.2 );; gap> e := [ 0, 0, 0, 0, 1 ]; [ 0, 0, 0, 0, 1 ] gap> stab := StabilizerIntegralAction( G, mats, e ); Pcp-group with orders [ 10, 0 ] gap> CheckStabilizer(G, stab, mats, e); #I Stabilizer not increasing: exiting. true # # bug in OrbitIntegralAction, see https://github.com/gap-packages/polycyclic/issues/21 # gap> G:=ExamplesOfSomePcpGroups(8); Pcp-group with orders [ 0, 0, 0, 0, 0 ] gap> mats:=Representation(Collector(G));; gap> e:=[8,-4,5,2,13,-17,9];; gap> f := e * MappedVector( [ -2, 2, 0, 5, 5 ], mats ); [ 8, -4, 19, 34, 51, -17, 5 ] gap> o := OrbitIntegralAction( G, mats, e, f ); rec( prei := g1^-90*g2^2*g3^-44*g4^16*g5^16, stab := Pcp-group with orders [ 0 ] ) gap> CheckOrbit(G, o.prei, mats, e, f); true gap> CheckStabilizer(G, o.stab, mats, e); #I Orbit longer than limit: exiting. true # # bug in IsConjugate: it should return a boolean, but instead of 'true' it # returned a conjugating element. See # gap> H := DihedralPcpGroup( 0 ); Pcp-group with orders [ 2, 0 ] gap> IsConjugate(H,One(H),One(H)); true gap> IsConjugate(H,H.1, H.2); false gap> IsConjugate(H,H.1, H.1^Random(H)); true # # bug in AddToIgs: in infinite pcp groups, we must also take inverses of # generators into account. See # gap> ftl := FromTheLeftCollector( 26 );; gap> SetRelativeOrder( ftl, 1, 5 ); gap> SetPower( ftl, 1, [ 2, 1, 3, 1, 4, 1, 5, 1, 6, 1 ] ); gap> SetRelativeOrder( ftl, 2, 5 ); gap> SetPower( ftl, 2, [] ); gap> SetRelativeOrder( ftl, 3, 5 ); gap> SetPower( ftl, 3, [] ); gap> SetRelativeOrder( ftl, 4, 5 ); gap> SetPower( ftl, 4, [] ); gap> SetRelativeOrder( ftl, 5, 5 ); gap> SetPower( ftl, 5, [] ); gap> SetRelativeOrder( ftl, 6, 5 ); gap> SetPower( ftl, 6, [] ); gap> SetConjugate( ftl, 2, 1, [ 6, 1 ] ); gap> SetConjugate( ftl, 3, 1, [ 2, 1 ] ); gap> SetConjugate( ftl, 4, 1, [ 3, 1 ] ); gap> SetConjugate( ftl, 5, 1, [ 4, 1 ] ); gap> SetConjugate( ftl, 6, 1, [ 5, 1 ] ); gap> SetConjugate( ftl, 7, 1, [ 26, -1 ] ); gap> SetConjugate( ftl, 7, 6, [ 22, -1 ] ); gap> SetConjugate( ftl, 8, 1, [ 7, 1 ] ); gap> SetConjugate( ftl, 8, 2, [ 23, -1 ] ); gap> SetConjugate( ftl, 9, 1, [ 8, 1 ] ); gap> SetConjugate( ftl, 9, 3, [ 24, -1 ] ); gap> SetConjugate( ftl, 10, 1, [ 9, 1 ] ); gap> SetConjugate( ftl, 10, 4, [ 25, -1 ] ); gap> SetConjugate( ftl, 11, 1, [ 10, 1 ] ); gap> SetConjugate( ftl, 11, 5, [ 26, -1 ] ); gap> SetConjugate( ftl, 12, 1, [ 11, 1, 26, -1 ] ); gap> SetConjugate( ftl, 12, 6, [ 7, 1, 22, -1 ] ); gap> SetConjugate( ftl, 13, 1, [ 12, 1 ] ); gap> SetConjugate( ftl, 13, 2, [ 8, 1, 23, -1 ] ); gap> SetConjugate( ftl, 14, 1, [ 13, 1 ] ); gap> SetConjugate( ftl, 14, 3, [ 9, 1, 24, -1 ] ); gap> SetConjugate( ftl, 15, 1, [ 14, 1 ] ); gap> SetConjugate( ftl, 15, 4, [ 10, 1, 25, -1 ] ); gap> SetConjugate( ftl, 16, 1, [ 15, 1 ] ); gap> SetConjugate( ftl, 16, 5, [ 11, 1, 26, -1 ] ); gap> SetConjugate( ftl, 17, 1, [ 16, 1, 26, -1 ] ); gap> SetConjugate( ftl, 17, 6, [ 12, 1, 22, -1 ] ); gap> SetConjugate( ftl, 18, 1, [ 17, 1 ] ); gap> SetConjugate( ftl, 18, 2, [ 13, 1, 23, -1 ] ); gap> SetConjugate( ftl, 19, 1, [ 18, 1 ] ); gap> SetConjugate( ftl, 19, 3, [ 14, 1, 24, -1 ] ); gap> SetConjugate( ftl, 20, 1, [ 19, 1 ] ); gap> SetConjugate( ftl, 20, 4, [ 15, 1, 25, -1 ] ); gap> SetConjugate( ftl, 21, 1, [ 20, 1 ] ); gap> SetConjugate( ftl, 21, 5, [ 16, 1, 26, -1 ] ); gap> SetConjugate( ftl, 22, 1, [ 21, 1, 26, -1 ] ); gap> SetConjugate( ftl, 22, 6, [ 17, 1, 22, -1 ] ); gap> SetConjugate( ftl, 23, 1, [ 22, 1 ] ); gap> SetConjugate( ftl, 23, 2, [ 18, 1, 23, -1 ] ); gap> SetConjugate( ftl, 24, 1, [ 23, 1 ] ); gap> SetConjugate( ftl, 24, 3, [ 19, 1, 24, -1 ] ); gap> SetConjugate( ftl, 25, 1, [ 24, 1 ] ); gap> SetConjugate( ftl, 25, 4, [ 20, 1, 25, -1 ] ); gap> SetConjugate( ftl, 26, 1, [ 25, 1 ] ); gap> SetConjugate( ftl, 26, 5, [ 21, 1, 26, -1 ] ); gap> SetConjugate( ftl, -7, 1, [ 26, 1 ] ); gap> SetConjugate( ftl, -7, 6, [ 22, 1 ] ); gap> SetConjugate( ftl, -8, 1, [ 7, -1 ] ); gap> SetConjugate( ftl, -8, 2, [ 23, 1 ] ); gap> SetConjugate( ftl, -9, 1, [ 8, -1 ] ); gap> SetConjugate( ftl, -9, 3, [ 24, 1 ] ); gap> SetConjugate( ftl, -10, 1, [ 9, -1 ] ); gap> SetConjugate( ftl, -10, 4, [ 25, 1 ] ); gap> SetConjugate( ftl, -11, 1, [ 10, -1 ] ); gap> SetConjugate( ftl, -11, 5, [ 26, 1 ] ); gap> SetConjugate( ftl, -12, 1, [ 11, -1, 26, 1 ] ); gap> SetConjugate( ftl, -12, 6, [ 7, -1, 22, 1 ] ); gap> SetConjugate( ftl, -13, 1, [ 12, -1 ] ); gap> SetConjugate( ftl, -13, 2, [ 8, -1, 23, 1 ] ); gap> SetConjugate( ftl, -14, 1, [ 13, -1 ] ); gap> SetConjugate( ftl, -14, 3, [ 9, -1, 24, 1 ] ); gap> SetConjugate( ftl, -15, 1, [ 14, -1 ] ); gap> SetConjugate( ftl, -15, 4, [ 10, -1, 25, 1 ] ); gap> SetConjugate( ftl, -16, 1, [ 15, -1 ] ); gap> SetConjugate( ftl, -16, 5, [ 11, -1, 26, 1 ] ); gap> SetConjugate( ftl, -17, 1, [ 16, -1, 26, 1 ] ); gap> SetConjugate( ftl, -17, 6, [ 12, -1, 22, 1 ] ); gap> SetConjugate( ftl, -18, 1, [ 17, -1 ] ); gap> SetConjugate( ftl, -18, 2, [ 13, -1, 23, 1 ] ); gap> SetConjugate( ftl, -19, 1, [ 18, -1 ] ); gap> SetConjugate( ftl, -19, 3, [ 14, -1, 24, 1 ] ); gap> SetConjugate( ftl, -20, 1, [ 19, -1 ] ); gap> SetConjugate( ftl, -20, 4, [ 15, -1, 25, 1 ] ); gap> SetConjugate( ftl, -21, 1, [ 20, -1 ] ); gap> SetConjugate( ftl, -21, 5, [ 16, -1, 26, 1 ] ); gap> SetConjugate( ftl, -22, 1, [ 21, -1, 26, 1 ] ); gap> SetConjugate( ftl, -22, 6, [ 17, -1, 22, 1 ] ); gap> SetConjugate( ftl, -23, 1, [ 22, -1 ] ); gap> SetConjugate( ftl, -23, 2, [ 18, -1, 23, 1 ] ); gap> SetConjugate( ftl, -24, 1, [ 23, -1 ] ); gap> SetConjugate( ftl, -24, 3, [ 19, -1, 24, 1 ] ); gap> SetConjugate( ftl, -25, 1, [ 24, -1 ] ); gap> SetConjugate( ftl, -25, 4, [ 20, -1, 25, 1 ] ); gap> SetConjugate( ftl, -26, 1, [ 25, -1 ] ); gap> SetConjugate( ftl, -26, 5, [ 21, -1, 26, 1 ] ); gap> gap> G := PcpGroupByCollector(ftl); Pcp-group with orders [ 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] gap> a := G.1;; gap> b := G.1^4*G.2^4*G.3^4*G.4^4*G.5^4*G.6^3*G.7;; gap> c := G.1^4*G.2^4*G.3^4*G.4^4*G.5^4*G.6^4*G.7;; gap> H := Subgroup(G,[a,b,c]); Pcp-group with orders [ 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] gap> G.1 in H; true gap> G.26 in H; true gap> G.1*G.26 in H; true gap> G = H; true # # Fix a bug computing ComplementClassesCR, see # # gap> G:=PcGroupToPcpGroup(PcGroupCode(37830811398924985638637008775811, 144)); Pcp-group with orders [ 2, 2, 2, 2, 3, 3 ] gap> classes := FiniteSubgroupClasses(G);; gap> Length(classes); 86 gap> Collected(List(classes, c -> Size(Representative(c)))); [ [ 1, 1 ], [ 2, 7 ], [ 3, 2 ], [ 4, 11 ], [ 6, 14 ], [ 8, 7 ], [ 9, 1 ], [ 12, 14 ], [ 16, 1 ], [ 18, 7 ], [ 24, 2 ], [ 36, 11 ], [ 72, 7 ], [ 144, 1 ] ] # # Fix a bug computing NormalizerPcpGroup, see # # gap> P2:=SylowSubgroup(GL(IsPermGroup,7,2),2); gap> iso := IsomorphismPcpGroup(P2);; gap> G:=Image(iso);; gap> U := Subgroup(G,[G.3*G.5*G.8*G.9*G.10*G.13*G.15*G.17*G.18*G.19,G.15*G.17]);; gap> N := NormalizerPcpGroup( G, U );; gap> Images(iso, Normalizer( P2, PreImages(iso, U) )) = N; true # # Fix a bug in OneCoboundariesCR which lead to an error in OneCohomologyCR. # # gap> G:=HeisenbergPcpGroup(1); Pcp-group with orders [ 0, 0, 0 ] gap> N:=Center(G); Pcp-group with orders [ 0 ] gap> C:=CRRecordBySubgroup(G,N);; gap> OneCoboundariesCR(C); [ ] gap> OneCohomologyCR(C); rec( factor := rec( denom := [ ], gens := [ [ 1, 0 ], [ 0, 1 ] ], imgs := [ [ 1, 0 ], [ 0, 1 ] ], prei := [ [ 1, 0 ], [ 0, 1 ] ], rels := [ 0, 0 ] ), gcb := [ ], gcc := [ [ 1, 0 ], [ 0, 1 ] ] ) # # Test for a regression in TorsionSubgroup (reported by Sam Tertooy) # gap> TorsionSubgroup(AbelianPcpGroup([4,3])); Pcp-group with orders [ 4, 3 ] # # Fix a bug in NormalClosureOp which resulted in a non-abelian group # being in the IsAbelian (and even the IsCyclic) filter. # # gap> G:=PcGroupToPcpGroup(SmallGroup(5^6,500)); Pcp-group with orders [ 5, 5, 5, 5, 5, 5 ] gap> N:=NormalClosure(Group(G.2), Group(G.3)); Pcp-group with orders [ 5, 5, 5, 5 ] gap> IsCyclic(N); false gap> IsAbelian(N); false gap> PrintPcpPresentation(N); g1^5 = id g2^5 = id g3^5 = id g4^5 = id g2 ^ g1 = g2 * g4 # # Fix a bug in Intersection where relative orders were not properly taken into # account, which lead to a too small result. # # gap> free := FreeGroup(4);; gap> AssignGeneratorVariables(free); #I Assigned the global variables [ f1, f2, f3, f4 ] gap> q := free / [ f1^2*f3^-1*f2^-2, > f1^-1*f2*f1*f3^-1*f2^-1, > f3^2, > f1^-1*f3*f1*f4^-1*f3^-1, > f2^-1*f3*f2*f4^-1*f3^-1, > f2*f3*f2^-1*f4^-1*f3^-1, > f4^2, > f1^-1*f4*f1*f4^-1, > f2^-1*f4*f2*f4^-1, > f2*f4*f2^-1*f4^-1, > f3^-1*f4*f3*f4^-1 ];; gap> pcpq := PcpGroupFpGroupPcPres(q); Pcp-group with orders [ 2, 0, 2, 2 ] gap> AssignGeneratorVariables(pcpq); #I Assigned the global variables [ g1, g2, g3, g4 ] gap> sub := Subgroup(pcpq, [pcpq.2, pcpq.4]); Pcp-group with orders [ 0, 2 ] gap> sub2 := sub^pcpq.1; Pcp-group with orders [ 0, 2 ] gap> Pcp(Intersection(sub, sub2)); Pcp [ g2^2, g4 ] with orders [ 0, 2 ] # # For trivial homomorphisms, only the identity has a preimage! # # gap> G := AbelianPcpGroup( [ 2 ] ); Pcp-group with orders [ 2 ] gap> phi := GroupHomomorphismByImages( G, G, [ G.1 ], [ Identity( G ) ] ); [ g1 ] -> [ id ] gap> PreImagesRepresentative( phi, One(G) ); id gap> PreImagesRepresentative( phi, G.1 ); fail # gap> G := AbelianPcpGroup( [ 2, 2 ] ); Pcp-group with orders [ 2, 2 ] gap> phi := GroupHomomorphismByImages( G, G, [ G.1, G.2 ], [ Identity( G ), G.2 ] ); [ g1, g2 ] -> [ id, g2 ] gap> PreImagesRepresentative( phi, One(G) ); id gap> PreImagesRepresentative( phi, G.2 ); g2 gap> PreImagesRepresentative( phi, G.1 ); fail # # Fix a bug in AddToIgs causing wrong results for abelian groups # # # gap> g := AbelianPcpGroup(3);; gap> h := Subgroup(g, [ g.1, g.1^-1*g.2, g.2^2*g.3 ]);; gap> Index(g,h); 1 gap> g=h; true gap> H := ExamplesOfSomePcpGroups( 15 );; gap> G := H/LowerCentralSeriesOfGroup( H )[8];; gap> g := (G.2^-2*G.3^3*G.5^2*G.8^-6*G.9^45*G.10^-24);; gap> srcs := [ G.2*G.5^-1, G.3, G.6, G.7, G.8, G.9, G.10 ];; gap> M := Subgroup( G, srcs );; gap> N := LowerCentralSeriesOfGroup( G )[7];; gap> idG := Identity( G );; gap> imgs := [ G.9^-3*G.10^-3, G.9^-2, G.9^4, G.10^-2, idG, idG, idG ];; gap> diff := GroupHomomorphismByImages( M, N, srcs, imgs);; gap> Ker := Kernel( diff );; gap> g in Ker; true gap> HirschLength( Ker ); 5 # gap> STOP_TEST( "bugfix.tst", 10000000); polycyclic-2.16/tst/inters.tst0000644000076600000240000001101013706672341015477 0ustar mhornstaffgap> START_TEST("Test for the intersection algorithm"); #some trivial tests gap> G := SmallGroup(48,3);; gap> iso := IsomorphismPcpGroup(G);; gap> H := Image(iso, G);; gap> T := TrivialSubgroup(H);; gap> Intersection(H,T); Pcp-group with orders [ ] gap> Intersection(H,H) = H; true gap> G := ExamplesOfSomePcpGroups(2);; gap> T := TrivialSubgroup(G);; gap> Intersection(G,T); Pcp-group with orders [ ] gap> Intersection(G,G) = G; true # a working test with an infinite pcp-group gap> G := ExamplesOfSomePcpGroups(1);; gap> efa := EfaSeries(G);; gap> F := FittingSubgroup(G);; gap> List(efa, gp->Intersection(F,gp)); [ Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ 0, 0 ], Pcp-group with orders [ ] ] gap> Pcp(F); Pcp [ g2^2, g3, g4 ] with orders [ 0, 0, 0 ] gap> List(efa,gp->Pcp(gp)); [ Pcp [ g1, g2, g3, g4 ] with orders [ 0, 0, 0, 0 ], Pcp [ g2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g3, g4 ] with orders [ 0, 0 ], Pcp [ ] with orders [ ] ] gap> List(efa, gp->Pcp(Intersection(F,gp))); [ Pcp [ g2^2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g2^2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g3, g4 ] with orders [ 0, 0 ], Pcp [ ] with orders [ ] ] # a working test with an infinite pcp-group gap> G := ExamplesOfSomePcpGroups(5);; gap> efa := EfaSeries(G);; gap> F := FittingSubgroup(G);; gap> List(efa, gp->Intersection(F,gp)); [ Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ 0, 0, 0 ], Pcp-group with orders [ 0, 0 ], Pcp-group with orders [ 0 ], Pcp-group with orders [ ] ] gap> Pcp(F); Pcp [ g2, g3, g4 ] with orders [ 0, 0, 0 ] gap> List(efa,gp->Pcp(gp)); [ Pcp [ g1, g2, g3, g4 ] with orders [ 2, 0, 0, 0 ], Pcp [ g2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g3, g4 ] with orders [ 0, 0 ], Pcp [ g4 ] with orders [ 0 ], Pcp [ ] with orders [ ] ] gap> List(efa, gp->Pcp(Intersection(F,gp))); [ Pcp [ g2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g2, g3, g4 ] with orders [ 0, 0, 0 ], Pcp [ g3, g4 ] with orders [ 0, 0 ], Pcp [ g4 ] with orders [ 0 ], Pcp [ ] with orders [ ] ] # a working test with a finite group (pc converted to pcp) gap> G := SmallGroup(3^2*5^2*7,7);; gap> iso := IsomorphismPcpGroup(G);; gap> g := GeneratorsOfGroup(G);; gap> G1 := Subgroup(G, [g[2]*g[1]^2, g[3]*g[4]]); Group([ f1^2*f2, f3*f4 ]) gap> G2 := Subgroup(G, [g[3]*g[5]^2, g[4]^2]); Group([ f3*f5^2, f4^2 ]) gap> I := Intersection(G1,G2); Group([ f3^4, f4^4 ]) gap> H1 := ImagesSet(iso, G1); Pcp-group with orders [ 3, 3, 5, 5 ] gap> H2 := ImagesSet(iso, G2); Pcp-group with orders [ 5, 5, 7 ] gap> J := Intersection(H1, H2); Pcp-group with orders [ 5, 5 ] gap> ImagesSet(iso,I) = J; true # infinite group example where the intersection algorithm isn't implemented (non-normalizing case) gap> G := ExamplesOfSomePcpGroups(8);; gap> g := GeneratorsOfGroup(G);; gap> H1:=Subgroup(G,[g[2], g[3]*g[4]]); Pcp-group with orders [ 0, 0, 0, 0 ] gap> H2:=Subgroup(G,[g[1], g[4]*g[5]]); Pcp-group with orders [ 0, 0 ] gap> Intersection(H1,H2); Error, sorry: intersection for non-normal groups not yet installed # finite group example where the intersection isn't impl. when represented as a pcp-group (non-normalizing case) gap> G := SmallGroup(2^2*3^4*5,8);; gap> iso := IsomorphismPcpGroup(G);; gap> H := Image(iso);; gap> h := GeneratorsOfGroup(H);; gap> H1 := Subgroup(H,[h[2], h[3]*h[4], h[6]^3]); Pcp-group with orders [ 5, 3, 3, 3, 2 ] gap> H2 := Subgroup(H,[h[1]*h[7], h[4]*h[5]]); Pcp-group with orders [ 3, 3, 3, 3 ] gap> J := Intersection(H1,H2); Error, sorry: intersection for non-normal groups not yet installed gap> G1 := PreImages(iso,H1); Group([ f2, f3*f4, f6 ]) gap> G2 := PreImages(iso,H2); Group([ f1*f7, f4*f5 ]) gap> I:= Intersection(G1,G2); Group([ f3^2*f4*f5^2, f4^2*f5, f5^2 ]) gap> Image(iso,I); Pcp-group with orders [ 3, 3, 3 ] # finite group example where the intersection isn't impl. when represented as a pcp-group (non-normalizing case) gap> G := SmallGroup(2^2*3^5,250);; gap> iso := IsomorphismPcpGroup(G);; gap> H := Image(iso);; gap> h := GeneratorsOfGroup(H);; gap> H1 := Subgroup(H,[h[2], h[3]*h[4], h[6]^2]); Pcp-group with orders [ 2, 3, 3, 3 ] gap> H2 := Subgroup(H,[h[1]*h[7], h[4]*h[5]^2]); Pcp-group with orders [ 2, 3, 3, 3 ] gap> Intersection(H1,H2); Error, sorry: intersection for non-normal groups not yet installed gap> G1 := PreImages(iso,H1); Group([ f2, f3*f4, f6^2 ]) gap> G2 := PreImages(iso,H2); Group([ f1*f7, f4*f5^2 ]) gap> I:= Intersection(G1,G2); Group([ f6^2, f7^2 ]) gap> Image(iso,I); Pcp-group with orders [ 3, 3 ] # gap> STOP_TEST( "inters.tst", 10000000); polycyclic-2.16/tst/AddToIgs.tst0000644000076600000240000001267413706672341015652 0ustar mhornstaffgap> START_TEST("AddToIgs.tst"); # This example was sent to us by Heiko Dietrich. It formerly was very slow to # compute and suffered from exponent size explosion. # See also . gap> ftl := FromTheLeftCollector( 26 ); <> gap> SetRelativeOrder( ftl, 1, 5 ); gap> SetPower( ftl, 1, [ 2, 1, 3, 1, 4, 1, 5, 1, 6, 1 ] ); gap> SetRelativeOrder( ftl, 2, 5 ); gap> SetPower( ftl, 2, [] ); gap> SetRelativeOrder( ftl, 3, 5 ); gap> SetPower( ftl, 3, [] ); gap> SetRelativeOrder( ftl, 4, 5 ); gap> SetPower( ftl, 4, [] ); gap> SetRelativeOrder( ftl, 5, 5 ); gap> SetPower( ftl, 5, [] ); gap> SetRelativeOrder( ftl, 6, 5 ); gap> SetPower( ftl, 6, [] ); gap> SetConjugate( ftl, 2, 1, [ 6, 1 ] ); gap> SetConjugate( ftl, 3, 1, [ 2, 1 ] ); gap> SetConjugate( ftl, 4, 1, [ 3, 1 ] ); gap> SetConjugate( ftl, 5, 1, [ 4, 1 ] ); gap> SetConjugate( ftl, 6, 1, [ 5, 1 ] ); gap> SetConjugate( ftl, 7, 1, [ 26, -1 ] ); gap> SetConjugate( ftl, 7, 6, [ 22, -1 ] ); gap> SetConjugate( ftl, 8, 1, [ 7, 1 ] ); gap> SetConjugate( ftl, 8, 2, [ 23, -1 ] ); gap> SetConjugate( ftl, 9, 1, [ 8, 1 ] ); gap> SetConjugate( ftl, 9, 3, [ 24, -1 ] ); gap> SetConjugate( ftl, 10, 1, [ 9, 1 ] ); gap> SetConjugate( ftl, 10, 4, [ 25, -1 ] ); gap> SetConjugate( ftl, 11, 1, [ 10, 1 ] ); gap> SetConjugate( ftl, 11, 5, [ 26, -1 ] ); gap> SetConjugate( ftl, 12, 1, [ 11, 1, 26, -1 ] ); gap> SetConjugate( ftl, 12, 6, [ 7, 1, 22, -1 ] ); gap> SetConjugate( ftl, 13, 1, [ 12, 1 ] ); gap> SetConjugate( ftl, 13, 2, [ 8, 1, 23, -1 ] ); gap> SetConjugate( ftl, 14, 1, [ 13, 1 ] ); gap> SetConjugate( ftl, 14, 3, [ 9, 1, 24, -1 ] ); gap> SetConjugate( ftl, 15, 1, [ 14, 1 ] ); gap> SetConjugate( ftl, 15, 4, [ 10, 1, 25, -1 ] ); gap> SetConjugate( ftl, 16, 1, [ 15, 1 ] ); gap> SetConjugate( ftl, 16, 5, [ 11, 1, 26, -1 ] ); gap> SetConjugate( ftl, 17, 1, [ 16, 1, 26, -1 ] ); gap> SetConjugate( ftl, 17, 6, [ 12, 1, 22, -1 ] ); gap> SetConjugate( ftl, 18, 1, [ 17, 1 ] ); gap> SetConjugate( ftl, 18, 2, [ 13, 1, 23, -1 ] ); gap> SetConjugate( ftl, 19, 1, [ 18, 1 ] ); gap> SetConjugate( ftl, 19, 3, [ 14, 1, 24, -1 ] ); gap> SetConjugate( ftl, 20, 1, [ 19, 1 ] ); gap> SetConjugate( ftl, 20, 4, [ 15, 1, 25, -1 ] ); gap> SetConjugate( ftl, 21, 1, [ 20, 1 ] ); gap> SetConjugate( ftl, 21, 5, [ 16, 1, 26, -1 ] ); gap> SetConjugate( ftl, 22, 1, [ 21, 1, 26, -1 ] ); gap> SetConjugate( ftl, 22, 6, [ 17, 1, 22, -1 ] ); gap> SetConjugate( ftl, 23, 1, [ 22, 1 ] ); gap> SetConjugate( ftl, 23, 2, [ 18, 1, 23, -1 ] ); gap> SetConjugate( ftl, 24, 1, [ 23, 1 ] ); gap> SetConjugate( ftl, 24, 3, [ 19, 1, 24, -1 ] ); gap> SetConjugate( ftl, 25, 1, [ 24, 1 ] ); gap> SetConjugate( ftl, 25, 4, [ 20, 1, 25, -1 ] ); gap> SetConjugate( ftl, 26, 1, [ 25, 1 ] ); gap> SetConjugate( ftl, 26, 5, [ 21, 1, 26, -1 ] ); gap> SetConjugate( ftl, -7, 1, [ 26, 1 ] ); gap> SetConjugate( ftl, -7, 6, [ 22, 1 ] ); gap> SetConjugate( ftl, -8, 1, [ 7, -1 ] ); gap> SetConjugate( ftl, -8, 2, [ 23, 1 ] ); gap> SetConjugate( ftl, -9, 1, [ 8, -1 ] ); gap> SetConjugate( ftl, -9, 3, [ 24, 1 ] ); gap> SetConjugate( ftl, -10, 1, [ 9, -1 ] ); gap> SetConjugate( ftl, -10, 4, [ 25, 1 ] ); gap> SetConjugate( ftl, -11, 1, [ 10, -1 ] ); gap> SetConjugate( ftl, -11, 5, [ 26, 1 ] ); gap> SetConjugate( ftl, -12, 1, [ 11, -1, 26, 1 ] ); gap> SetConjugate( ftl, -12, 6, [ 7, -1, 22, 1 ] ); gap> SetConjugate( ftl, -13, 1, [ 12, -1 ] ); gap> SetConjugate( ftl, -13, 2, [ 8, -1, 23, 1 ] ); gap> SetConjugate( ftl, -14, 1, [ 13, -1 ] ); gap> SetConjugate( ftl, -14, 3, [ 9, -1, 24, 1 ] ); gap> SetConjugate( ftl, -15, 1, [ 14, -1 ] ); gap> SetConjugate( ftl, -15, 4, [ 10, -1, 25, 1 ] ); gap> SetConjugate( ftl, -16, 1, [ 15, -1 ] ); gap> SetConjugate( ftl, -16, 5, [ 11, -1, 26, 1 ] ); gap> SetConjugate( ftl, -17, 1, [ 16, -1, 26, 1 ] ); gap> SetConjugate( ftl, -17, 6, [ 12, -1, 22, 1 ] ); gap> SetConjugate( ftl, -18, 1, [ 17, -1 ] ); gap> SetConjugate( ftl, -18, 2, [ 13, -1, 23, 1 ] ); gap> SetConjugate( ftl, -19, 1, [ 18, -1 ] ); gap> SetConjugate( ftl, -19, 3, [ 14, -1, 24, 1 ] ); gap> SetConjugate( ftl, -20, 1, [ 19, -1 ] ); gap> SetConjugate( ftl, -20, 4, [ 15, -1, 25, 1 ] ); gap> SetConjugate( ftl, -21, 1, [ 20, -1 ] ); gap> SetConjugate( ftl, -21, 5, [ 16, -1, 26, 1 ] ); gap> SetConjugate( ftl, -22, 1, [ 21, -1, 26, 1 ] ); gap> SetConjugate( ftl, -22, 6, [ 17, -1, 22, 1 ] ); gap> SetConjugate( ftl, -23, 1, [ 22, -1 ] ); gap> SetConjugate( ftl, -23, 2, [ 18, -1, 23, 1 ] ); gap> SetConjugate( ftl, -24, 1, [ 23, -1 ] ); gap> SetConjugate( ftl, -24, 3, [ 19, -1, 24, 1 ] ); gap> SetConjugate( ftl, -25, 1, [ 24, -1 ] ); gap> SetConjugate( ftl, -25, 4, [ 20, -1, 25, 1 ] ); gap> SetConjugate( ftl, -26, 1, [ 25, -1 ] ); gap> SetConjugate( ftl, -26, 5, [ 21, -1, 26, 1 ] ); # gap> g := PcpGroupByCollector(ftl); Pcp-group with orders [ 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] gap> gen:=[ g.1, g.1^4*g.2^4*g.3^4*g.4^4*g.6*g.7*g.25^-1*g.26^2 ];; gap> U := Subgroup(g,gen); Pcp-group with orders [ 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] gap> Igs(gen); [ g1, g2*g3*g4*g5*g6, g3*g4*g5^3*g23^-1*g24^3*g25^2*g26, g4*g5^2*g24^-1*g25^2*g26^-1, g5*g6^4*g22*g23^-1*g24^2*g26^-2, g6*g22*g23^2*g24*g25^3*g26^-2, g7*g22^-4, g8*g23^-4, g9*g24, g10*g25, g11*g26^-4, g12*g22^-3, g13*g23^-3, g14*g24^2, g15*g25^-3, g16*g26^-3, g17*g22^-2, g18*g23^-2, g19*g24^-2, g20*g25^3, g21*g26^3, g22^5, g23^5, g24^5, g25^5, g26^5 ] # gap> STOP_TEST( "AddToIgs.tst", 1); polycyclic-2.16/tst/exam/0000755000076600000240000000000013706672341014400 5ustar mhornstaffpolycyclic-2.16/tst/exam/generic.tst0000644000076600000240000000103513706672341016547 0ustar mhornstaff# # UnitriangularPcpGroup # # gap> G:=UnitriangularPcpGroup(4,4); fail gap> G:=UnitriangularPcpGroup(0,2); fail # gap> G:=UnitriangularPcpGroup(4,3); Pcp-group with orders [ 3, 3, 3, 3, 3, 3 ] gap> IsConfluent(Collector(G)); true gap> hom:=GroupHomomorphismByImages( G, Group(G!.mats), Igs(G), G!.mats);; gap> IsBijective(hom); true # gap> G:=UnitriangularPcpGroup(5,0); Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] gap> IsConfluent(Collector(G)); true gap> hom:=GroupHomomorphismByImages( G, Group(G!.mats), Igs(G), G!.mats);; polycyclic-2.16/tst/testall.g0000644000076600000240000000020313706672341015261 0ustar mhornstaffLoadPackage("polycyclic"); TestDirectory(DirectoriesPackageLibrary("polycyclic", "tst"), rec(exitGAP := true)); FORCE_QUIT_GAP(1); polycyclic-2.16/tst/factor.tst0000644000076600000240000000133513706672341015462 0ustar mhornstaffgap> START_TEST("Test of factor groups and natural homomorphisms"); # gap> G:=HeisenbergPcpGroup(2); Pcp-group with orders [ 0, 0, 0, 0, 0 ] # gap> H:=Subgroup(G,[G.2,G.3,G.4,G.5]); Pcp-group with orders [ 0, 0, 0, 0 ] gap> K:=G/H; Pcp-group with orders [ 0 ] gap> NaturalHomomorphismByNormalSubgroup(G, H); [ g1, g2, g3, g4, g5 ] -> [ g1, id, id, id, id ] # gap> A:=Subgroup(H, [G.3]); Pcp-group with orders [ 0 ] gap> B:=Subgroup(Subgroup(G,[G.1,G.4,G.5]), [G.4]); Pcp-group with orders [ 0 ] gap> Normalizer(A,B); Pcp-group with orders [ 0 ] gap> # The following used to trigger the error "arguments must have a common parent group" gap> Normalizer(B,A); Pcp-group with orders [ 0 ] # gap> STOP_TEST( "factor.tst", 10000000); polycyclic-2.16/tst/homs.tst0000644000076600000240000002036613706672341015157 0ustar mhornstaffgap> START_TEST("Test of homs between various group types"); # gap> TestHomHelper := function(A,B,gens_A,gens_B) > local map, inv, H; > > map:=GroupGeneralMappingByImages(A,B,gens_A,gens_B); > inv:=GroupGeneralMappingByImages(B,A,gens_B,gens_A); > > Display(HasIsAbelian(ImagesSource(map))); > Display(HasIsAbelian(PreImagesRange(map))); > > Display(inv = InverseGeneralMapping(map)); > Display(List([IsTotal,IsSingleValued,IsSurjective,IsInjective], f->f(map))); > Display(List([IsTotal,IsSingleValued,IsSurjective,IsInjective], f->f(inv))); > Display(List([PreImagesRange(map),CoKernel(map),ImagesSource(map),Kernel(map)],Size)); > Display(List([PreImagesRange(inv),CoKernel(inv),ImagesSource(inv),Kernel(inv)],Size)); > end;; gap> TestHomFromFilterToFilter := function(f1, f2) > local A, B, iA, iB, gens_A, gens_B; > A:=AbelianGroup(f1,[35,15]);; > B:=AbelianGroup(f2,[35,15]);; > iA := IndependentGeneratorsOfAbelianGroup(A); > iB := IndependentGeneratorsOfAbelianGroup(B); > > TestHomHelper(A,B,iA,iB); > > gens_A:=ShallowCopy(iA); > gens_B:=ShallowCopy(iB); > gens_A:=gens_A{[1..3]}; > gens_B:=gens_B{[1..3]}; > TestHomHelper(A,B,gens_A,gens_B); > > gens_A[1]:=One(gens_A[1]);; > gens_A[2]:=MappedVector([ 0, 1, 0, 6 ], iA);; > gens_B[3]:=One(gens_B[3]);; > > TestHomHelper(A,B,gens_A,gens_B); > > gens_A[1]:=MappedVector([ 2, 1, 1, 0 ], iA); > TestHomHelper(A,B,gens_A,gens_B); > end;; # gap> TestHomFromFilterToFilter(IsPermGroup,IsPermGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPermGroup,IsPcGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPermGroup,IsPcpGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcGroup,IsPermGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcGroup,IsPcGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcGroup,IsPcpGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcpGroup,IsPermGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcpGroup,IsPcGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] gap> TestHomFromFilterToFilter(IsPcpGroup,IsPcpGroup); true true true [ true, true, true, true ] [ true, true, true, true ] [ 525, 1, 525, 1 ] [ 525, 1, 525, 1 ] true true true [ false, true, false, true ] [ false, true, false, true ] [ 75, 1, 75, 1 ] [ 75, 1, 75, 1 ] true true true [ false, false, false, false ] [ false, false, false, false ] [ 175, 3, 15, 35 ] [ 15, 35, 175, 3 ] true true true [ true, false, false, false ] [ false, false, true, false ] [ 525, 5, 15, 175 ] [ 15, 175, 525, 5 ] # gap> G:=AbelianGroup(IsPcpGroup,[2,3,2]);; gap> map:=GroupGeneralMappingByImages(G,G,[G.1],[G.3]);; gap> Size(PreImagesSet(map,G)); 2 gap> List([IsTotal,IsSingleValued,IsSurjective,IsInjective], f->f(map)); [ false, true, false, true ] gap> map2:=map*map;; gap> Size(PreImagesSet(map2,G)); 1 gap> Size(ImagesSet(map2,G)); 1 gap> Size(ImagesSource(map2)); 1 gap> Size(PreImagesRange(map2)); 1 # test that our custom IsSingleValue method really works, i.e. w don't fall # back to CoKernelOfMultiplicativeGeneralMapping, which won't terminate in # this example because it tries to compute a normal closure inside an infinite # matrix group. gap> H:=Group( [ [ [ -89, -144, 0 ], [ -144, -233, 0 ], [ 0, 0, 1 ] ], > [ [ 63245986, 102334155, 0 ], [ 102334155, 165580141, 0 ], [ 0, 0, 1 ] ], > [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 3, -3, 1 ] ], > [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 3, 6, 1 ] ], > [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 3, 1 ] ], > [ [ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1 ] ] ]);; gap> coll := FromTheLeftCollector( 6 );; gap> SetRelativeOrder( coll, 1, 10 );; gap> SetPower( coll, 1, [ 2, 3 ] );; gap> SetConjugate( coll, 3, 1, [ 3, -89, 5, 144, 6, 144 ] );; gap> SetConjugate( coll, 3, -1, [ 3, -233, 5, -144, 6, -144 ] );; gap> SetConjugate( coll, 3, 2, [ 3, 63245986, 5, -102334155, 6, -102334155 ] );; gap> SetConjugate( coll, 3, -2, [ 3, 165580141, 5, 102334155, 6, 102334155 ] );; gap> SetConjugate( coll, 4, 1, [ 3, -144, 4, -233, 5, -144 ] );; gap> SetConjugate( coll, 4, -1, [ 3, 144, 4, -89, 5, 144 ] );; gap> SetConjugate( coll, 4, 2, [ 3, 102334155, 4, 165580141, 5, 102334155 ] );; gap> SetConjugate( coll, 4, -2, [ 3, -102334155, 4, 63245986, 5, -102334155 ] );; gap> SetConjugate( coll, 5, 1, [ 4, -144, 5, -233, 6, -144 ] );; gap> SetConjugate( coll, 5, -1, [ 4, 144, 5, -89, 6, 144 ] );; gap> SetConjugate( coll, 5, 2, [ 4, 102334155, 5, 165580141, 6, 102334155 ] );; gap> SetConjugate( coll, 5, -2, [ 4, -102334155, 5, 63245986, 6, -102334155 ] );; gap> SetConjugate( coll, 6, 1, [ 3, 144, 4, 144, 6, -89 ] );; gap> SetConjugate( coll, 6, -1, [ 3, -144, 4, -144, 6, -233 ] );; gap> SetConjugate( coll, 6, 2, [ 3, -102334155, 4, -102334155, 6, 63245986 ] );; gap> SetConjugate( coll, 6, -2, [ 3, 102334155, 4, 102334155, 6, 165580141 ] );; gap> UpdatePolycyclicCollector( coll ); gap> G := PcpGroupByCollectorNC( coll ); Pcp-group with orders [ 10, 0, 0, 0, 0, 0 ] gap> hom := GroupHomomorphismByImages(G, H); fail # gap> STOP_TEST( "homs.tst", 10000000); polycyclic-2.16/init.g0000644000076600000240000000256713706672341013761 0ustar mhornstaff############################################################################# ## #W init.g GAP 4 package 'polycyclic' Bettina Eick #W Werner Nickel #W Max Horn ## ############################################################################# ## #D Read .gd files ## ReadPackage( "polycyclic", "gap/matrix/matrix.gd"); ReadPackage( "polycyclic", "gap/basic/infos.gd"); ReadPackage( "polycyclic", "gap/basic/collect.gd"); ReadPackage( "polycyclic", "gap/basic/pcpelms.gd"); ReadPackage( "polycyclic", "gap/basic/pcpgrps.gd"); ReadPackage( "polycyclic", "gap/basic/pcppcps.gd"); ReadPackage( "polycyclic", "gap/basic/grphoms.gd"); ReadPackage( "polycyclic", "gap/basic/basic.gd"); ReadPackage( "polycyclic", "gap/cohom/cohom.gd"); ReadPackage( "polycyclic", "gap/matrep/matrep.gd"); ReadPackage( "polycyclic", "gap/matrep/unitri.gd"); ReadPackage( "polycyclic", "gap/pcpgrp/pcpgrp.gd"); ReadPackage( "polycyclic", "gap/pcpgrp/torsion.gd"); ReadPackage( "polycyclic", "gap/exam/exam.gd"); ## ## Load list of obsolete names. In GAP before 4.5, this is always done; ## starting with GAP 4.5, we honors the "ReadObsolete" user preference. ## if UserPreference( "ReadObsolete" ) <> false then ReadPackage( "polycyclic", "gap/obsolete.gd"); fi; polycyclic-2.16/read.g0000644000076600000240000001447513706672341013732 0ustar mhornstaff############################################################################# ## #W read.g GAP 4 package 'polycyclic' Bettina Eick #W Werner Nickel #W Max Horn ## ############################################################################# ## ## Introduce various global variables to steer the behavior of polycyclic ## if not IsBound( CHECK_CENT@ ) then CHECK_CENT@ := false; fi; if not IsBound( CHECK_IGS@ ) then CHECK_IGS@ := false; fi; if not IsBound( CHECK_INTNORM@ ) then CHECK_INTNORM@ := false; fi; if not IsBound( CHECK_INTSTAB@ ) then CHECK_INTSTAB@ := false; fi; if not IsBound( CHECK_NORM@ ) then CHECK_NORM@ := false; fi; if not IsBound( CHECK_SCHUR_PCP@ ) then CHECK_SCHUR_PCP@ := false; fi; if not IsBound( CODEONLY@ ) then CODEONLY@ := false; fi; if not IsBound( USE_ALNUTH@ ) then USE_ALNUTH@ := true; fi; if not IsBound( USE_CANONICAL_PCS@ ) then USE_CANONICAL_PCS@ := true; fi; if not IsBound( USE_NFMI@ ) then USE_NFMI@ := false; fi; if not IsBound( USE_NORMED_PCS@ ) then USE_NORMED_PCS@ := false; fi; if not IsBound( USED_PRIMES@ ) then USED_PRIMES@ := [3]; fi; if not IsBound( VERIFY@ ) then VERIFY@ := true; fi; ## ## Starting with GAP 4.10, the kernel function CollectPolycyclic does not use ## the stacks inside the pcp collector objects anymore, so we can omit them, ## to considerably reduce their size. To simplify the transition to this while ## GAP 4.10 is under development, GAP versions which have the modified ## 'CollectPolycyclic' set the global constant NO_STACKS_INSIDE_COLLECTORS to ## true. If this global is missing, that means the stacks are in fact needed, ## and thus we set NO_STACKS_INSIDE_COLLECTORS to false in that case. ## if not IsBound(NO_STACKS_INSIDE_COLLECTORS) then BindGlobal("NO_STACKS_INSIDE_COLLECTORS", false); fi; ## ## Starting with GAP 4.11, MultRowVector has been renamed to MultVector. ## In order to stay compatible with older GAP releases, we define MultVector ## if it is missing. ## if not IsBound(MultVector) then DeclareSynonym( "MultVector", MultRowVector ); fi; ## ## matrix -- basics about matrices, rational spaces, lattices and modules ## ReadPackage( "polycyclic", "gap/matrix/rowbases.gi"); ReadPackage( "polycyclic", "gap/matrix/latbases.gi"); ReadPackage( "polycyclic", "gap/matrix/lattices.gi"); ReadPackage( "polycyclic", "gap/matrix/modules.gi"); ReadPackage( "polycyclic", "gap/matrix/triangle.gi"); ReadPackage( "polycyclic", "gap/matrix/hnf.gi"); ## ## ## basic -- basic functions for pcp groups ## ReadPackage( "polycyclic", "gap/basic/collect.gi"); ReadPackage( "polycyclic", "gap/basic/colftl.gi"); ReadPackage( "polycyclic", "gap/basic/colcom.gi"); ReadPackage( "polycyclic", "gap/basic/coldt.gi"); ReadPackage( "polycyclic", "gap/basic/colsave.gi"); ReadPackage( "polycyclic", "gap/basic/pcpelms.gi"); ReadPackage( "polycyclic", "gap/basic/pcppcps.gi"); ReadPackage( "polycyclic", "gap/basic/pcpgrps.gi"); ReadPackage( "polycyclic", "gap/basic/pcppara.gi"); ReadPackage( "polycyclic", "gap/basic/pcpexpo.gi"); ReadPackage( "polycyclic", "gap/basic/pcpsers.gi"); ReadPackage( "polycyclic", "gap/basic/grphoms.gi"); ReadPackage( "polycyclic", "gap/basic/pcpfact.gi"); ReadPackage( "polycyclic", "gap/basic/chngpcp.gi"); ReadPackage( "polycyclic", "gap/basic/convert.gi"); ReadPackage( "polycyclic", "gap/basic/orbstab.gi"); ReadPackage( "polycyclic", "gap/basic/construct.gi"); ## ## cohomology - extensions and complements ## ReadPackage( "polycyclic", "gap/cohom/cohom.gi"); ReadPackage( "polycyclic", "gap/cohom/addgrp.gi"); ReadPackage( "polycyclic", "gap/cohom/general.gi"); ReadPackage( "polycyclic", "gap/cohom/solabel.gi"); ReadPackage( "polycyclic", "gap/cohom/solcohom.gi"); ReadPackage( "polycyclic", "gap/cohom/twocohom.gi"); ReadPackage( "polycyclic", "gap/cohom/intcohom.gi"); ReadPackage( "polycyclic", "gap/cohom/onecohom.gi"); ReadPackage( "polycyclic", "gap/cohom/grpext.gi"); ReadPackage( "polycyclic", "gap/cohom/grpcom.gi"); ReadPackage( "polycyclic", "gap/cohom/norcom.gi"); ## ## action - under polycyclic matrix groups ## ReadPackage( "polycyclic", "gap/action/extend.gi"); ReadPackage( "polycyclic", "gap/action/basepcgs.gi"); ReadPackage( "polycyclic", "gap/action/freegens.gi"); ReadPackage( "polycyclic", "gap/action/dixon.gi"); ReadPackage( "polycyclic", "gap/action/kernels.gi"); ReadPackage( "polycyclic", "gap/action/orbstab.gi"); ReadPackage( "polycyclic", "gap/action/orbnorm.gi"); ## ## some more high level functions for pcp groups ## ReadPackage( "polycyclic", "gap/pcpgrp/general.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/inters.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/grpinva.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/torsion.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/maxsub.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/findex.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/nindex.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/nilpot.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/polyz.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/pcpattr.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/wreath.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/fitting.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/centcon.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/normcon.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/schur.gi"); ReadPackage( "polycyclic", "gap/pcpgrp/tensor.gi"); ## ## matrep -- computing a matrix representation ## ReadPackage( "polycyclic", "gap/matrep/matrep.gi"); ReadPackage( "polycyclic", "gap/matrep/affine.gi"); ReadPackage( "polycyclic", "gap/matrep/unitri.gi"); ## ## examples - generic groups and an example list ## ReadPackage( "polycyclic", "gap/exam/pcplib.gi"); ReadPackage( "polycyclic", "gap/exam/matlib.gi"); ReadPackage( "polycyclic", "gap/exam/nqlib.gi"); ReadPackage( "polycyclic", "gap/exam/generic.gi"); ReadPackage( "polycyclic", "gap/exam/bgnilp.gi"); ReadPackage( "polycyclic", "gap/exam/metacyc.gi"); ReadPackage( "polycyclic", "gap/exam/metagrp.gi"); ## ## schur covers of p-groups ## ReadPackage( "polycyclic", "gap/cover/const/bas.gi"); # basic stuff ReadPackage( "polycyclic", "gap/cover/const/orb.gi"); # orbits ReadPackage( "polycyclic", "gap/cover/const/aut.gi"); # automorphisms ReadPackage( "polycyclic", "gap/cover/const/com.gi"); # complements ReadPackage( "polycyclic", "gap/cover/const/cov.gi"); # Schur covers